Port from bitbucket

This commit is contained in:
Madpeter 2020-04-23 18:19:18 +01:00
commit 4ce4c1b769
156 changed files with 8779 additions and 0 deletions

24
.dockerignore Normal file
View file

@ -0,0 +1,24 @@
**/.classpath
**/.dockerignore
**/.env
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

23
.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
compile.bat
*.user
*.userprefs
*.suo
*.cache
*.pfx
[Oo]bj/
[Bb]in/
packages/
.idea/
.vs/
/Programs/examples/TestClient/Properties/launchSettings.json
.appveyor.yml
LICENSE.txt
mapgen.bat
PrimMesher/
Programs/
BetterSecondBot/publish
Publish/
secondbotr5.json
CLI/settings.env
CLI/Dockerfile

View file

@ -0,0 +1,154 @@
using BetterSecondBotShared.logs;
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BetterSecondBotShared.API
{
public abstract class API_interface
{
public string CommandName { get { return GetType().Name.ToLowerInvariant(); } }
public virtual int MinArgs { get { return 0; } }
public virtual string Helpfile { get { return "No help given"; } }
public virtual string[] ArgTypes { get { return new string[] { }; } }
public virtual string[] ArgHints { get { return new string[] { }; } }
}
public class API_supported_interface
{
protected Type API_type;
protected Dictionary<string, Type> subtype_map = new Dictionary<string, Type>();
protected Dictionary<string, string> subtype_workspace_map = new Dictionary<string, string>();
public virtual int ApiCommandsCount { get { return subtype_map.Count(); } }
protected API_interface GetCommand(string command)
{
if (KnownCommand(command) == true)
{
return Activator.CreateInstance(subtype_map[command]) as API_interface;
}
return null;
}
protected virtual void LoadCommandsList()
{
foreach (var com in GetAPICommandClasses())
{
if (com != null)
{
if (com.IsAbstract == false)
{
API_interface D = (API_interface)Activator.CreateInstance(com);
if (D != null)
{
if (subtype_map.ContainsKey(D.CommandName) == false)
{
string worknamespace = com.Namespace;
worknamespace = worknamespace.Replace("BSB.Commands.", "");
worknamespace = worknamespace.Replace("BSB.RLV.", "");
worknamespace = worknamespace.Replace("BetterSecondBot.HttpServer.View.", "");
worknamespace = worknamespace.Replace("BetterSecondBot.HttpServer.Control.", "");
worknamespace = worknamespace.Replace("CMD_", "");
subtype_map.Add(D.CommandName, D.GetType());
subtype_workspace_map.Add(D.CommandName, worknamespace);
}
else
{
ConsoleLog.Crit("[CMD] command: " + D.CommandName + " already defined!");
}
}
}
}
}
}
protected IEnumerable<Type> GetAPICommandClasses()
{
if (API_type != null)
{
return AppDomain.CurrentDomain.GetAssemblies().Where(Ass => {
bool reply = false;
if (Ass.FullName.StartsWith("BSB") == true) { reply = true; }
else if (Ass.FullName.StartsWith("BetterSecondBot") == true){ reply = true; }
return reply;
}).SelectMany(assembly => assembly.GetTypes()).Where(type => type.IsSubclassOf(API_type));
}
else
{
ConsoleLog.Crit(this.GetType() + " Attempted to call GetSubTypes without setting API_TYPE first!");
}
return null;
}
public virtual string[] GetCommandsList()
{
return subtype_map.Keys.ToArray<string>();
}
public virtual string GetCommandWorkspace(string cmd)
{
if (subtype_workspace_map.ContainsKey(cmd) == true)
{
return subtype_workspace_map[cmd];
}
return "";
}
protected virtual bool KnownCommand(string command)
{
if (subtype_map.Count() == 0)
{
LoadCommandsList();
}
return subtype_map.ContainsKey(command);
}
public virtual string GetCommandHelp(string cmd)
{
API_interface cmdGet = GetCommand(cmd);
if (cmdGet != null)
{
return cmdGet.Helpfile;
}
return "None given";
}
public virtual int GetCommandArgs(string cmd)
{
API_interface cmdGet = GetCommand(cmd);
if (cmdGet != null)
{
return cmdGet.MinArgs;
}
return 0;
}
public virtual string[] GetCommandArgTypes(string cmd)
{
API_interface CmdGet = GetCommand(cmd);
if (CmdGet != null)
{
return CmdGet.ArgTypes;
}
return new string[] { };
}
public virtual string[] GetCommandArgHints(string cmd)
{
API_interface CmdGet = GetCommand(cmd);
if (CmdGet != null)
{
return CmdGet.ArgHints;
}
return new string[] { };
}
public virtual List<string> GetAllWorkspaces()
{
List<string> reply = new List<string>();
foreach (string c in GetCommandsList())
{
string workspace = GetCommandWorkspace(c);
if (reply.Contains(workspace) == false)
{
reply.Add(workspace);
}
}
return reply;
}
}
}

View file

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Configurations>Debug;Release;DockerBuild</Configurations>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DockerBuild|x64'">
<ErrorReport>none</ErrorReport>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<PublishTrimmed>false</PublishTrimmed>
<PublishSingleFile>false</PublishSingleFile>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\libremetaverse-core\Libremetaverse.structureddata\LibreMetaverse.StructuredData.csproj" />
<ProjectReference Include="..\libremetaverse-core\Libremetaversetypes\LibreMetaverse.Types.csproj" />
<ProjectReference Include="..\libremetaverse-core\Libremetaverse\LibreMetaverse.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using BetterSecondBotShared.Static;
using OpenMetaverse;
namespace BetterSecondBotShared.bottypes
{
public abstract class BasicBot
{
public BasicBot()
{
LastStatusMessage = "No status";
}
protected bool killMe;
protected JsonConfig myconfig;
protected string version = "NotSet V1.0.0.0";
protected bool reconnect;
public string MyVersion { get { return version; } }
public string Name { get { return myconfig.userName; } }
public string LastStatusMessage { get; set; }
public bool KillMe { get { return killMe; } }
protected Dictionary<UUID, Group> mygroups = new Dictionary<UUID, Group>();
public Dictionary<UUID, Group> MyGroups { get { return mygroups; } }
protected GridClient Client;
public GridClient GetClient { get { return Client; } }
public virtual void KillMePlease()
{
killMe = true;
}
public virtual void Setup(JsonConfig config,string Version)
{
version = Version;
myconfig = config;
if(myconfig.code.Length < 8)
{
myconfig.code = helpers.GetSHA1("" + myconfig.userName + "" + myconfig.password + ""+ helpers.UnixTimeNow().ToString()+"").Substring(0, 8);
ConsoleLog.Warn("Given code is not acceptable (min length 8)");
}
List<string> bits = myconfig.userName.Split(' ').ToList();
if (bits.Count == 1)
{
bits.Add("Resident");
myconfig.userName = String.Join(' ', bits);
}
bits = myconfig.master.Split(new[] { " " }, StringSplitOptions.None).ToList();
if (bits.Count == 1)
{
bits.Add("Resident");
}
myconfig.master = String.Join(" ", bits);
ConsoleLog.Info("Build: " + version);
}
public void Start()
{
Client = new GridClient();
Client.Settings.LOG_RESENDS = false;
Client.Settings.ALWAYS_REQUEST_OBJECTS = true;
Client.Settings.AVATAR_TRACKING = true;
List<string> bits = myconfig.userName.Split(' ').ToList();
LoginParams Lp = new LoginParams(Client, bits[0], bits[1], myconfig.password, "BetterSecondBot", "BetterSecondBot "+version);
if (reconnect == false)
{
BotStartHandler();
}
Client.Network.BeginLogin(Lp);
}
protected virtual void BotStartHandler()
{
ConsoleLog.Debug("BotStartHandler proc not overridden");
}
protected string GetSimPositionAsString()
{
if(Client.Network.Connected == true)
{
if (Client.Network.CurrentSim != null)
{
int X = Convert.ToInt32(Math.Floor(Client.Self.SimPosition.X));
int Y = Convert.ToInt32(Math.Floor(Client.Self.SimPosition.Y));
int Z = Convert.ToInt32(Math.Floor(Client.Self.SimPosition.Z));
return "" + X.ToString() + "," + Y.ToString() + "," + Z.ToString() + "";
}
}
return "0,0,0";
}
public virtual string GetStatus()
{
if (Client != null)
{
if (Client.Network != null)
{
if (Client.Network.Connected)
{
if (Client.Network.CurrentSim != null)
{
return "Sim: " + Client.Network.CurrentSim.Name + " "+GetSimPositionAsString()+"";
}
return "Sim: Not on sim";
}
return "Network: Not connected";
}
return "Network: Not ready";
}
return "Info: No client";
}
protected virtual void LoginHandler(object o, LoginProgressEventArgs e)
{
ConsoleLog.Debug("LoginHandler proc not overridden");
}
protected virtual void AfterBotLoginHandler()
{
}
protected virtual void GroupInvite(InstantMessageEventArgs e)
{
string[] stage1 = myconfig.master.ToLowerInvariant().Split(' ');
string bad_master_name = String.Join('.', stage1);
if (e.IM.FromAgentName == bad_master_name)
{
GroupInvitationEventArgs G = new GroupInvitationEventArgs(e.Simulator, e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
Client.Self.GroupInviteRespond(G.AgentID, e.IM.IMSessionID, true);
Client.Groups.RequestCurrentGroups(); // reload my groups to include the new group
}
}
protected virtual void FriendshipOffer(UUID IMSessionID,string FromAgentName,UUID FromAgentID)
{
if(FromAgentName == myconfig.master)
{
Client.Friends.AcceptFriendship(FromAgentID, IMSessionID);
}
}
protected virtual void RequestLure(UUID IMSessionID, string FromAgentName, UUID FromAgentID)
{
if (FromAgentName == myconfig.master)
{
Client.Self.SendTeleportLure(FromAgentID);
}
}
protected virtual void RequestTeleport(UUID IMSessionID, string FromAgentName, UUID FromAgentID)
{
if (FromAgentName == myconfig.master)
{
ResetAnimations();
Client.Self.TeleportLureRespond(FromAgentID, IMSessionID, true);
}
}
protected virtual void ChatInputHandler(object sender, ChatEventArgs e)
{
ConsoleLog.Debug("ChatInputHandler proc not overridden");
}
protected virtual void CoreCommandLib(UUID fromUUID, bool from_master, string command, string arg)
{
CoreCommandLib(fromUUID, from_master, command, arg, "");
}
protected virtual void CoreCommandLib(UUID fromUUID,bool from_master,string command,string arg,string signing_code)
{
ConsoleLog.Debug("CoreCommandLib proc not overridden");
}
public virtual void ResetAnimations()
{
ConsoleLog.Debug("reset_animations not enabled at this level");
}
}
}

84
BSBshared/IO/SimpleIO.cs Normal file
View file

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Newtonsoft.Json;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.Static;
namespace BetterSecondBotShared.IO
{
public class SimpleIO
{
protected string root_folder = "";
public void ChangeRoot(string A)
{
root_folder = A + "/";
create_dir(root_folder);
}
public void create_dir(string target_folder)
{
if (dir_exists(root_folder) == false)
{
System.IO.Directory.CreateDirectory(root_folder);
}
}
public static bool dir_exists(string target_folder)
{
return System.IO.Directory.Exists(target_folder);
}
public void Delete(string filename)
{
System.IO.File.Delete(root_folder+filename);
}
public void writefile(string filename,string content)
{
if(Exists(filename))
{
Delete(filename);
}
System.IO.File.WriteAllText(@""+root_folder + "" + filename, content);
}
public void WriteJsonConfig(JsonConfig Config, string targetfile)
{
JsonSerializer serializer = new JsonSerializer();
using (StreamWriter sw = new StreamWriter(@""+root_folder + "" + targetfile))
using (JsonWriter writer = new JsonTextWriter(sw) { Formatting = Formatting.Indented })
{
serializer.Serialize(writer, Config);
}
}
public void makeOld(string targetfile)
{
System.IO.File.Move(@""+root_folder + "" + targetfile, @"" + root_folder + "" + targetfile+".old");
}
public bool Exists(string targetfile)
{
return File.Exists(@""+root_folder+"" + targetfile);
}
public static bool FileType(string targetfile, string matchtype)
{
string[] bits = targetfile.Split('.');
if (bits[^1] == matchtype)
{
return true;
}
return false;
}
public string ReadFile(string targetfile)
{
if (Exists(targetfile) == true)
{
string return_text = "";
using (var Stream = new FileStream(@"" + root_folder + "" + targetfile, FileMode.Open, FileAccess.Read))
using (var StreamReader = new StreamReader(Stream))
{
return_text = StreamReader.ReadToEnd();
}
return return_text;
}
return "";
}
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterSecondBotShared.Json
{
public class JsonConfig
{
public string userName { get; set; }
public string password { get; set; }
public string master { get; set; }
public string code { get; set; }
public string discord { get; set; }
public bool allowRLV { get; set; }
public string discordGroupTarget { get; set; }
public string[] homeRegion { get; set; }
public bool EnableHttp { get; set; }
public int Httpport { get; set; }
public string Httpkey { get; set; }
public bool HttpRequireSigned { get; set; }
public string HttpHost { get; set; }
public bool HttpAsCnC { get; set; }
public bool DiscordFullServer { get; set; }
public string DiscordClientToken { get; set; }
public ulong DiscordServerID { get; set; }
public int DiscordServerImHistoryHours { get; set; }
public string DefaultSitUUID { get; set; }
}
}

View file

@ -0,0 +1,315 @@
using BetterSecondBotShared.API;
using BetterSecondBotShared.logs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace BetterSecondBotShared.Json
{
public class MakeJsonConfig : API_supported_interface
{
public override string GetCommandWorkspace(string cmd)
{
cmd = cmd.ToLowerInvariant();
switch (cmd)
{
case "username": { return "Required"; }
case "password": { return "Required"; }
case "master": { return "Required"; }
case "homeregion": { return "AtHome"; }
case "defaultsituuid": { return "AtHome"; }
case "discordgrouptarget": { return "DiscordRelay"; }
case "discord": { return "DiscordRelay"; }
case "allowrlv": { return "RLV"; }
case "code": { return "Required"; }
case "enablehttp": { return "HTTP"; }
case "httpport": { return "HTTP"; }
case "httpkey": { return "HTTP"; }
case "httprequiresigned": { return "HTTP"; }
case "httphost": { return "HTTP"; }
case "httpascnc": { return "HTTP"; }
case "discordfullserver": { return "DiscordFull"; }
case "discordclienttoken": { return "DiscordFull"; }
case "discordserverid": { return "DiscordFull"; }
case "discordserverimhistoryhours": { return "DiscordFull"; }
default: { return "Unknown"; }
}
}
public override string GetCommandHelp(string cmd)
{
cmd = cmd.ToLowerInvariant();
switch (cmd)
{
case "username": { return "The full username of the bot"; }
case "password": { return "Raw password OR $1$ followed by the md5 of your password"; }
case "master": { return "A vaild avatar name, you can skip Resident if thats the last name"; }
case "defaultsituuid": { return "The UUID to sit on when at home region [Any invaild UUID to disable like \"none\"]"; }
case "homeregion": { return "An array of SLurl strings to teleport to to avoid a sim shutdown"; }
case "discordgrouptarget": { return "The target group UUID to relay group chat from, leave empty for all groups"; }
case "discord": { return "A vaild discord hook URL to relay group chat onto<br/>leave as \"\" to disable"; }
case "allowrlv": { return "Enable RLV commands interface for the bot"; }
case "code": { return "A known secret between your secondlife scripts and the bot<br/>Used to support IM commands"; }
case "enablehttp": { return "Enable the HTTP interface (using the HTTP settings)"; }
case "httpport": { return "What port the HTTP interface should be on<br/> Example: 8080"; }
case "httpkey": { return "A known secret between used for HTTP calls<br/>Example: \"DontUseThis\""; }
case "httprequiresigned": { return "Force HTTP calls to be Signed with a hash (Not yet KnownCommand)"; }
case "httphost": { return "The URL to listen for HTTP connections on"; }
case "httpascnc": { return "Switchs the software into command and control mode"; }
case "discordfullserver": { return "Should we switch to a full server control discord link"; }
case "discordclienttoken": { return "What is the bots client token"; }
case "discordserverid": { return "What is the Discord server id we should be using"; }
case "discordserverimhistoryhours": { return "How long to keep history messages in Group/IM chats in hours"; }
default: { return "None given"; }
}
}
public override int ApiCommandsCount { get { return GetCommandsList().Length; } }
public override string[] GetCommandsList()
{
JsonConfig Obj = new JsonConfig();
return Obj.GetType().GetProperties().Select(X => X.Name).ToArray();
}
public override string[] GetCommandArgTypes(string cmd)
{
JsonConfig Obj = new JsonConfig();
Type valuetype = Obj.GetType().GetProperties().Where(X => X.Name == cmd).Select(X => X.PropertyType).FirstOrDefault();
if (valuetype.Name == "Int32") { return new[] { "Number" }; }
else if (valuetype.Name == "String") { return new[] { "Text" }; }
else if (valuetype.Name == "Boolean") { return new[] { "True|False" }; }
else if (valuetype.Name == "String[]") { return new[] { "Collection" }; }
else if (valuetype.Name == "UInt64") { return new[] { "BigNumber" }; }
return new[] { "Unknown" };
}
public override string[] GetCommandArgHints(string cmd)
{
cmd = cmd.ToLowerInvariant();
switch (cmd)
{
case "username": { return new[] { "\"Firstname LastName\"" }; }
case "password": { return new[] { "\"PassWord\"" }; }
case "master": { return new[] { "\"Madpeter Zond\"" }; }
case "homeregion": { return new[] { "[\"SLurl1\",\"SLurl2\"]" }; }
case "discordgrouptarget": { return new[] { "\"" + OpenMetaverse.UUID.Zero + "\"" }; }
case "discord": { return new[] { "\"https://discordapp.com/api/webhooks/XXXX/YYYY\"" }; }
case "allowrlv": { return new[] { "False" }; }
case "code": { return new[] { "\"DontTellAnyoneThis\"" }; }
case "enablehttp": { return new[] { "False" }; }
case "httpport": { return new[] { "8080" }; }
case "httpkey": { return new[] { "\"ThisIsAKeyYo\"" }; }
case "httprequiresigned": { return new[] { "False" }; }
case "httphost": { return new[] { "\"http://localhost\"" }; }
case "httpascnc": { return new[] { "False" }; }
case "discordfullserver": { return new[] { "False" }; }
case "discordclienttoken": { return new[] { "https://discordapp.com/developers/ Bot" }; }
case "discordserverid": { return new[] { "1234567890" }; }
case "discordserverimhistoryhours": { return new[] { "24" }; }
case "defaultsituuid": { return new[] { "\"" + OpenMetaverse.UUID.Zero + "\"" }; }
default: { return new string[] { }; }
}
}
public override int GetCommandArgs(string cmd)
{
return 1;
}
public string GetProp(JsonConfig reply, string arg)
{
return reply.GetType().GetProperty(arg, BindingFlags.Public | BindingFlags.Instance).GetValue(reply).ToString();
}
protected JsonConfig process_prop(JsonConfig reply,string arg,string arg_value_default)
{
string arg_type = GetCommandArgTypes(arg).First();
if (arg_type != "")
{
if (arg_value_default != null)
{
Type Dtype = reply.GetType();
PropertyInfo prop = Dtype.GetProperty(arg, BindingFlags.Public | BindingFlags.Instance);
if (prop != null)
{
if (prop.CanWrite == true)
{
if (arg_type == "Number")
{
if (int.TryParse(arg_value_default, out int result) == true)
{
prop.SetValue(reply, result, null);
}
}
else if (arg_type == "Text")
{
prop.SetValue(reply, arg_value_default, null);
}
else if (arg_type == "True|False")
{
if (bool.TryParse(arg_value_default, out bool result) == true)
{
prop.SetValue(reply, result, null);
}
}
else if (arg_type == "Collection")
{
prop.SetValue(reply, arg_value_default.Split(','), null);
}
else if (arg_type == "BigNumber")
{
if (ulong.TryParse(arg_value_default, out ulong result) == true)
{
prop.SetValue(reply, result, null);
}
}
else
{
ConsoleLog.Debug("unsupported arg_type: " + arg_type + " for " + arg + "");
}
}
else
{
ConsoleLog.Warn("Unable to write to " + arg + "");
}
}
else
{
ConsoleLog.Crit("unknown prop " + arg + "");
}
}
}
else
{
ConsoleLog.Debug("unknown arg_type for " + arg + "");
}
return reply;
}
public static JsonConfig http_config_check(JsonConfig jsonConfig)
{
if ((jsonConfig.Httpkey != null) && (jsonConfig.HttpHost != null))
{
if ((jsonConfig.Httpport < 80) || (jsonConfig.Httpkey.Length < 12) || (jsonConfig.HttpHost.StartsWith("http") == false))
{
jsonConfig.EnableHttp = false;
}
}
else
{
jsonConfig.EnableHttp = false;
}
if (jsonConfig.EnableHttp == false)
{
jsonConfig.HttpAsCnC = false;
}
return jsonConfig;
}
protected JsonConfig FromDockerENV()
{
string[] args = this.GetCommandsList();
JsonConfig reply = new JsonConfig();
foreach (string arg in args)
{
string arg_value_default = Environment.GetEnvironmentVariable(arg);
if (arg_value_default != null)
{
arg_value_default = arg_value_default.Replace("[", "");
arg_value_default = arg_value_default.Replace("]", "");
arg_value_default = arg_value_default.Replace("\"", "");
}
reply = process_prop(reply, arg, arg_value_default);
}
return reply;
}
public static JsonConfig FromENV()
{
return new MakeJsonConfig().FromDockerENV();
}
protected JsonConfig MakeDefault()
{
string[] args = this.GetCommandsList();
JsonConfig reply = new JsonConfig();
foreach (string arg in args)
{
string arg_type = GetCommandArgTypes(arg).First();
string arg_value_default = null;
if (arg_type != "")
{
string[] ArgHints = GetCommandArgHints(arg);
if (ArgHints.Length > 0)
{
arg_value_default = ArgHints[0];
arg_value_default = arg_value_default.Replace("[", "");
arg_value_default = arg_value_default.Replace("]", "");
arg_value_default = arg_value_default.Replace("\"", "");
}
else
{
ConsoleLog.Debug("[Notice] No args hint found for " + arg + "");
}
}
if (arg_value_default != null)
{
Type Dtype = reply.GetType();
PropertyInfo prop = Dtype.GetProperty(arg, BindingFlags.Public | BindingFlags.Instance);
if (prop != null)
{
if (prop.CanWrite == true)
{
if (arg_type == "Number")
{
if (int.TryParse(arg_value_default, out int result) == true)
{
prop.SetValue(reply, result, null);
}
}
else if (arg_type == "Text")
{
prop.SetValue(reply, arg_value_default, null);
}
else if (arg_type == "True|False")
{
if (bool.TryParse(arg_value_default, out bool result) == true)
{
prop.SetValue(reply, result, null);
}
}
else if (arg_type == "Collection")
{
prop.SetValue(reply, arg_value_default.Split(','), null);
}
else if (arg_type == "BigNumber")
{
if (ulong.TryParse(arg_value_default, out ulong result) == true)
{
prop.SetValue(reply, result, null);
}
}
else
{
ConsoleLog.Debug("unknown arg_type: " + arg_type + " for " + arg + "");
}
}
else
{
ConsoleLog.Warn("Unable to write to " + arg + "");
}
}
else
{
ConsoleLog.Crit("unknown prop " + arg + "");
}
}
}
return reply;
}
public static JsonConfig GetDefault()
{
return new MakeJsonConfig().MakeDefault();
}
}
}

View file

@ -0,0 +1,83 @@
using System;
using System.Text;
namespace BetterSecondBotShared.logs
{
public enum ConsoleLogLogLevel
{
Debug, // [Not logged unless in debug]
Info, // [Not logged unless in debug]
Status,
Warn,
Crit
};
public static class ConsoleLog
{
public static void Warn(string message)
{
Add(message, ConsoleLogLogLevel.Warn);
}
public static void Crit(string message)
{
Add(message, ConsoleLogLogLevel.Crit);
}
public static void Info(string message)
{
Add(message, ConsoleLogLogLevel.Info);
}
public static void Status(string message)
{
Add(message, ConsoleLogLogLevel.Status);
}
public static void Debug(string message)
{
Add(message, ConsoleLogLogLevel.Debug);
}
private static void Add(string message, ConsoleLogLogLevel Level)
{
var date = DateTime.Now;
StringBuilder n = new StringBuilder();
n.Append("[");
if(date.Hour < 10) n.Append("0");
n.Append(date.Hour.ToString());
n.Append(":");
if (date.Minute < 10) n.Append("0");
n.Append(date.Minute.ToString());
n.Append("] ");
switch (Level)
{
#if DEBUG
case ConsoleLogLogLevel.Debug:
{
n.Append("Debug - ");
break;
}
case ConsoleLogLogLevel.Info:
{
n.Append("Info - ");
break;
}
#endif
case ConsoleLogLogLevel.Status:
{
n.Append("Status - ");
break;
}
case ConsoleLogLogLevel.Warn:
{
n.Append("Warn - ");
break;
}
case ConsoleLogLogLevel.Crit:
{
n.Append("Crit - ");
break;
}
default:
break;
}
n.Append(message);
Console.WriteLine(n.ToString());
}
}
}

173
BSBshared/Staic/Helpers.cs Normal file
View file

@ -0,0 +1,173 @@
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace BetterSecondBotShared.Static
{
public static class StringExtensions
{
public static string FirstCharToUpper(this string input)
{
switch (input)
{
case null: throw new ArgumentNullException(nameof(input));
case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input));
default: return input.First().ToString().ToUpper() + input.Substring(1);
}
}
}
public static class helpers
{
public static string GetSHA1(string text)
{
SHA1 hash = SHA1CryptoServiceProvider.Create();
byte[] plainTextBytes = Encoding.UTF8.GetBytes(text);
byte[] hashBytes = hash.ComputeHash(plainTextBytes);
string localChecksum = BitConverter.ToString(hashBytes)
.Replace("-", "").ToLowerInvariant();
return localChecksum;
}
public static string[] ParseSLurl(string url)
{
if (url != null)
{
url = url.Replace("http://maps.secondlife.com/secondlife/", "");
string[] bits = url.Split('/');
if (bits.Length == 4)
{
return bits;
}
}
return null;
}
public static string RegionnameFromSLurl(string url)
{
string[] bits = ParseSLurl(url);
if (bits != null)
{
if (bits.Length == 4)
{
return bits[0];
}
}
return "";
}
public static long UnixTimeNow()
{
var timeSpan = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
return (long)timeSpan.TotalSeconds;
}
public static bool botRequired(JsonConfig test)
{
if (helpers.notempty(test.userName) && helpers.notempty(test.password) && helpers.notempty(test.master) && helpers.notempty(test.code))
{
// required values are set
MakeJsonConfig Testing = new MakeJsonConfig();
string[] testingfor = new[] { "userName", "password", "code" };
bool default_value_found = false;
foreach (string a in testingfor)
{
if (Testing.GetCommandArgTypes(a).First() == Testing.GetProp(test, a))
{
ConsoleLog.Warn("" + a + " is currently set to the default");
default_value_found = true;
break;
}
}
if (default_value_found == false)
{
ConsoleLog.Status("User => " + test.userName);
ConsoleLog.Status("Master => " + test.master);
}
return !default_value_found;
}
else
{
if (helpers.notempty(test.userName) == false)
{
ConsoleLog.Warn("Username is null or empty");
}
if (helpers.notempty(test.password) == false)
{
ConsoleLog.Warn("Password is null or empty");
}
if (helpers.notempty(test.master) == false)
{
ConsoleLog.Warn("Master is null or empty");
}
if (helpers.notempty(test.code) == false)
{
ConsoleLog.Warn("Code is null or empty");
}
return false;
}
}
public static string create_dirty_table(string[] items)
{
return create_dirty_table(items, 6);
}
public static string create_dirty_table(string[] items, int cols)
{
bool row_open = false;
int col_counter = 0;
StringBuilder reply = new StringBuilder();
reply.Append("<table border='1'>");
foreach (string A in items)
{
if (row_open == false)
{
reply.Append("<tr>");
row_open = true;
}
reply.Append("<td>" + A + "</td>");
col_counter++;
if (col_counter >= cols)
{
reply.Append("</tr>");
col_counter = 0;
row_open = false;
}
}
while (row_open == true)
{
reply.Append("<td></td>");
col_counter++;
if (col_counter >= cols)
{
reply.Append("</tr>");
row_open = false;
}
}
reply.Append("</table>");
return reply.ToString();
}
public static bool notempty(string V)
{
if (V != null)
{
if (V.Length > 0) return true;
else return false;
}
else
{
return false;
}
}
public static bool notempty(string[] V)
{
if (V != null)
{
return true;
}
else
{
return false;
}
}
}
}

67
BetterSecondBot.sln Normal file
View file

@ -0,0 +1,67 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29123.88
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BSB", "Core\BSB.csproj", "{077DC0CD-6B3D-4BD9-83ED-9E098768AAA8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterSecondBot", "CLI\BetterSecondBot.csproj", "{E34873C1-3860-430A-8AE5-6289C4E68AC7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BSBshared", "BSBshared\BSBshared.csproj", "{42075C8C-8055-4178-8C12-AE97DAFE8484}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreMetaverse", "libremetaverse-core\Libremetaverse\LibreMetaverse.csproj", "{748DEFDD-B7EF-4E67-907C-33C8B2F0C221}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreMetaverse.StructuredData", "libremetaverse-core\Libremetaverse.structureddata\LibreMetaverse.StructuredData.csproj", "{9B42196B-00EC-4F33-B78D-8B693B23E56D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreMetaverse.Types", "libremetaverse-core\Libremetaversetypes\LibreMetaverse.Types.csproj", "{53663A03-7508-4D44-9B08-79BEB6AAB47E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
DockerBuild|x64 = DockerBuild|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{077DC0CD-6B3D-4BD9-83ED-9E098768AAA8}.Debug|x64.ActiveCfg = Debug|x64
{077DC0CD-6B3D-4BD9-83ED-9E098768AAA8}.Debug|x64.Build.0 = Debug|x64
{077DC0CD-6B3D-4BD9-83ED-9E098768AAA8}.DockerBuild|x64.ActiveCfg = DockerBuild|x64
{077DC0CD-6B3D-4BD9-83ED-9E098768AAA8}.DockerBuild|x64.Build.0 = DockerBuild|x64
{077DC0CD-6B3D-4BD9-83ED-9E098768AAA8}.Release|x64.ActiveCfg = Release|x64
{077DC0CD-6B3D-4BD9-83ED-9E098768AAA8}.Release|x64.Build.0 = Release|x64
{E34873C1-3860-430A-8AE5-6289C4E68AC7}.Debug|x64.ActiveCfg = Debug|x64
{E34873C1-3860-430A-8AE5-6289C4E68AC7}.Debug|x64.Build.0 = Debug|x64
{E34873C1-3860-430A-8AE5-6289C4E68AC7}.DockerBuild|x64.ActiveCfg = DockerBuild|x64
{E34873C1-3860-430A-8AE5-6289C4E68AC7}.DockerBuild|x64.Build.0 = DockerBuild|x64
{E34873C1-3860-430A-8AE5-6289C4E68AC7}.Release|x64.ActiveCfg = Release|x64
{E34873C1-3860-430A-8AE5-6289C4E68AC7}.Release|x64.Build.0 = Release|x64
{42075C8C-8055-4178-8C12-AE97DAFE8484}.Debug|x64.ActiveCfg = Debug|x64
{42075C8C-8055-4178-8C12-AE97DAFE8484}.Debug|x64.Build.0 = Debug|x64
{42075C8C-8055-4178-8C12-AE97DAFE8484}.DockerBuild|x64.ActiveCfg = DockerBuild|x64
{42075C8C-8055-4178-8C12-AE97DAFE8484}.DockerBuild|x64.Build.0 = DockerBuild|x64
{42075C8C-8055-4178-8C12-AE97DAFE8484}.Release|x64.ActiveCfg = Release|x64
{42075C8C-8055-4178-8C12-AE97DAFE8484}.Release|x64.Build.0 = Release|x64
{748DEFDD-B7EF-4E67-907C-33C8B2F0C221}.Debug|x64.ActiveCfg = Debug|x64
{748DEFDD-B7EF-4E67-907C-33C8B2F0C221}.Debug|x64.Build.0 = Debug|x64
{748DEFDD-B7EF-4E67-907C-33C8B2F0C221}.DockerBuild|x64.ActiveCfg = DockerBuild|x64
{748DEFDD-B7EF-4E67-907C-33C8B2F0C221}.DockerBuild|x64.Build.0 = DockerBuild|x64
{748DEFDD-B7EF-4E67-907C-33C8B2F0C221}.Release|x64.ActiveCfg = Release|x64
{748DEFDD-B7EF-4E67-907C-33C8B2F0C221}.Release|x64.Build.0 = Release|x64
{9B42196B-00EC-4F33-B78D-8B693B23E56D}.Debug|x64.ActiveCfg = Debug|x64
{9B42196B-00EC-4F33-B78D-8B693B23E56D}.Debug|x64.Build.0 = Debug|x64
{9B42196B-00EC-4F33-B78D-8B693B23E56D}.DockerBuild|x64.ActiveCfg = DockerBuild|x64
{9B42196B-00EC-4F33-B78D-8B693B23E56D}.DockerBuild|x64.Build.0 = DockerBuild|x64
{9B42196B-00EC-4F33-B78D-8B693B23E56D}.Release|x64.ActiveCfg = Release|x64
{9B42196B-00EC-4F33-B78D-8B693B23E56D}.Release|x64.Build.0 = Release|x64
{53663A03-7508-4D44-9B08-79BEB6AAB47E}.Debug|x64.ActiveCfg = Debug|x64
{53663A03-7508-4D44-9B08-79BEB6AAB47E}.Debug|x64.Build.0 = Debug|x64
{53663A03-7508-4D44-9B08-79BEB6AAB47E}.DockerBuild|x64.ActiveCfg = DockerBuild|x64
{53663A03-7508-4D44-9B08-79BEB6AAB47E}.DockerBuild|x64.Build.0 = DockerBuild|x64
{53663A03-7508-4D44-9B08-79BEB6AAB47E}.Release|x64.ActiveCfg = Release|x64
{53663A03-7508-4D44-9B08-79BEB6AAB47E}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {39A7E333-AFC1-4BA8-9EBC-9298791FDC78}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Teleport/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xfer/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

View file

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<MSBuildGitHashCommand>git describe --match=NeVeRmAtCh --always --abbrev=7</MSBuildGitHashCommand>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<StartupObject>BetterSecondBot.Program</StartupObject>
<Configurations>Debug;Release;DockerBuild;Windows</Configurations>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DockerBuild|x64'">
<ErrorReport>none</ErrorReport>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<PublishTrimmed>false</PublishTrimmed>
<PublishSingleFile>false</PublishSingleFile>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<None Remove="WikiMake\wiki_footer.txt" />
<None Remove="WikiMake\wiki_header.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.7.82" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.9.10" />
<PackageReference Include="MSBuildGitHash" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="WikiMake\wiki_footer.txt" />
<EmbeddedResource Include="WikiMake\wiki_header.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\BSB.csproj" />
</ItemGroup>
</Project>

80
CLI/Cli.cs Normal file
View file

@ -0,0 +1,80 @@
using System;
using CommandLine;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using Newtonsoft.Json;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
using BSB;
using BSB.bottypes;
using BetterSecondBot.HttpServer;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.IO;
using BetterSecondBotShared.API;
using BetterSecondBotShared.Static;
using BetterSecondBotShared.logs;
using BetterSecondBot.WikiMake;
namespace BetterSecondBot
{
class Program
{
static void Main(string[] args)
{
if (helpers.notempty(Environment.GetEnvironmentVariable("BotRunningInDocker")) == true)
{
new CliDocker();
}
else
{
new CliHardware(args);
}
ConsoleLog.Status("- Exiting in 5 secs -");
Thread.Sleep(5000);
}
public static class AssemblyInfo
{
/// <summary> Gets the git hash value from the assembly
/// or null if it cannot be found. </summary>
public static string GetGitHash()
{
var asm = typeof(AssemblyInfo).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
return attrs.FirstOrDefault(a => a.Key == "GitHash")?.Value;
}
}
}
class Options : API_supported_interface
{
[Option('j', "json", Required = false, HelpText = "see api")]
public string Json { get; set; }
public override int ApiCommandsCount { get { return 1; } }
public override string[] GetCommandsList()
{
return new string[] { "j"};
}
public override string[] GetCommandArgTypes(string cmd)
{
return new string[] { "Text" };
}
public override string[] GetCommandArgHints(string cmd)
{
return new string[] { "\"Bot4.json\"" };
}
public override string GetCommandHelp(string cmd)
{
return "json config file [Defaults to: mybot.json] <br/>Example: \"BetterSecondBot.exe -j=otherbot.json\"";
}
public override int GetCommandArgs(string cmd)
{
return 1;
}
}
}

30
CLI/CliDocker.cs Normal file
View file

@ -0,0 +1,30 @@
using BetterSecondBotShared.IO;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using BetterSecondBotShared.Static;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace BetterSecondBot
{
public class CliDocker
{
public CliDocker()
{
Thread.Sleep(3000);
ConsoleLog.Status("-> Getting config from Docker");
JsonConfig Config = MakeJsonConfig.FromENV();
Config = MakeJsonConfig.http_config_check(Config);
if (Config.HttpAsCnC == true)
{
new HttpCnC(Config);
}
else
{
new CliExitOnLogout(Config);
}
}
}
}

49
CLI/CliExitOnLogout.cs Normal file
View file

@ -0,0 +1,49 @@
using BetterSecondBot.HttpServer;
using BetterSecondBotShared.IO;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using BetterSecondBotShared.Static;
using BSB.bottypes;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using static BetterSecondBot.Program;
namespace BetterSecondBot
{
public class CliExitOnLogout
{
public CliExitOnLogout(JsonConfig Config)
{
ConsoleLog.Status("Mode: Cli [Basic]");
if (helpers.botRequired(Config) == true)
{
SecondBot Bot = new SecondBot();
Bot.Setup(Config, AssemblyInfo.GetGitHash());
Bot.Start();
if (Config.EnableHttp == true)
{
http_server my_http_server = null;
my_http_server = new http_server();
my_http_server.start_http_server(Bot,Config);
}
while (Bot.KillMe == false)
{
string NewStatusMessage = Bot.GetStatus();
if (NewStatusMessage != Bot.LastStatusMessage)
{
Bot.LastStatusMessage = NewStatusMessage;
ConsoleLog.Status(Bot.LastStatusMessage);
}
Thread.Sleep(5000);
}
Bot.GetClient.Network.Logout();
}
else
{
ConsoleLog.Warn("Required settings missing");
}
}
}
}

93
CLI/CliHardware.cs Normal file
View file

@ -0,0 +1,93 @@
using BetterSecondBot.WikiMake;
using BetterSecondBotShared.IO;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.Static;
using CommandLine;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using static BetterSecondBot.Program;
using BetterSecondBotShared.logs;
namespace BetterSecondBot
{
public class CliHardware
{
public CliHardware(string[] args)
{
SimpleIO io = new SimpleIO();
JsonConfig Config;
string json_file = "";
#if DEBUG
ConsoleLog.Status("Hardware/Debug version");
json_file = "debug.json";
#else
ConsoleLog.Status("Hardware/Live version");
Parser.Default.ParseArguments<Options>(args).WithParsed<Options>(o =>
{
json_file = o.Json;
});
#endif
if (SimpleIO.dir_exists("wiki") == false)
{
ConsoleLog.Info("Basic Wiki [Creating]");
new DebugModeCreateWiki(AssemblyInfo.GetGitHash(), io);
ConsoleLog.Info("Basic Wiki [Ready]");
io = new SimpleIO();
}
Config = MakeJsonConfig.GetDefault();
if (helpers.notempty(json_file) == false)
{
json_file = "mybot.json";
ConsoleLog.Warn("Using: mybot.json as the config");
}
bool ok_to_try_start = false;
if (SimpleIO.FileType(json_file, "json") == true)
{
if (io.Exists(json_file))
{
string json = io.ReadFile(json_file);
if (json.Length > 0)
{
try
{
Config = JsonConvert.DeserializeObject<JsonConfig>(json);
ok_to_try_start = true;
}
catch (Exception e)
{
ConsoleLog.Warn("Unable to read config file\n moving config to " + json_file + ".old and creating a empty config\nError was: " + e.Message + "");
io.makeOld(json_file);
Config = new JsonConfig();
io.WriteJsonConfig(MakeJsonConfig.GetDefault(), json_file);
}
}
else
{
ConsoleLog.Warn("Json config is empty creating an empty one for you");
io.WriteJsonConfig(MakeJsonConfig.GetDefault(), json_file);
}
}
else
{
ConsoleLog.Warn("Json config does not Exist creating it for you");
io.WriteJsonConfig(MakeJsonConfig.GetDefault(), json_file);
}
}
else ConsoleLog.Crit("you must select a .json file for config!");
if(ok_to_try_start == true)
{
Config = MakeJsonConfig.http_config_check(Config);
if (Config.HttpAsCnC == true)
{
new HttpCnC(Config);
}
else
{
new CliExitOnLogout(Config);
}
}
}
}
}

50
CLI/Dockerfile-ForDebug Normal file
View file

@ -0,0 +1,50 @@
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["CLI/BetterSecondBot.csproj", "CLI/"]
COPY ["Core/BSB.csproj", "Core/"]
COPY ["BSBshared/BSBshared.csproj", "BSBshared/"]
COPY ["libremetaverse-core/Libremetaversetypes/LibreMetaverse.Types.csproj", "libremetaverse-core/Libremetaversetypes/"]
COPY ["libremetaverse-core/Libremetaverse/LibreMetaverse.csproj", "libremetaverse-core/Libremetaverse/"]
COPY ["libremetaverse-core/Libremetaverse.structureddata/LibreMetaverse.StructuredData.csproj", "libremetaverse-core/Libremetaverse.structureddata/"]
RUN dotnet restore "CLI/BetterSecondBot.csproj"
COPY . .
WORKDIR "/src/CLI"
RUN dotnet build "BetterSecondBot.csproj" -c DockerBuild -o /app/build
FROM build AS publish
RUN dotnet publish "BetterSecondBot.csproj" -c DockerBuild -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV BotRunningInDocker='REQUIRED'
ENV userName=''
ENV password=''
ENV master='Madpeter Zond'
ENV code='DontTellAnyoneThis'
ENV discord=''
ENV discordGroupTarget=''
ENV allowRLV=''
ENV homeRegion=''
ENV EnableHttp=''
ENV Httpport=''
ENV Httpkey=''
ENV HttpRequireSigned=''
ENV HttpHost=''
ENV HttpAsCnC=''
ENV DiscordFullServer=''
ENV DiscordClientToken=''
ENV DiscordServerID=''
ENV DiscordServerImHistoryHours=''
ENV DefaultSitUUID='00000000-0000-0000-0000-000000000000'
EXPOSE 8080
ENV ASPNETCORE_URLS http://+:8080
ENTRYPOINT ["dotnet", "BetterSecondBot.dll"]

39
CLI/HttpCnC.cs Normal file
View file

@ -0,0 +1,39 @@
using BetterSecondBot.HttpServer;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace BetterSecondBot
{
class HttpCnC
{
protected http_server my_http_server;
public HttpCnC(JsonConfig BotConfig)
{
ConsoleLog.Status("Mode: HTTP C&C");
my_http_server = new http_server();
my_http_server.HTTPCnCmode = true;
my_http_server.ShutdownHTTP = false;
my_http_server.start_http_server(BotConfig);
string last_cnc_status = "";
while (my_http_server.ShutdownHTTP == false)
{
string NewStatusMessage = my_http_server.GetStatus();
if (NewStatusMessage != last_cnc_status)
{
last_cnc_status = NewStatusMessage;
ConsoleLog.Status(last_cnc_status);
}
Thread.Sleep(5000);
}
ConsoleLog.Status("Shutting down now");
if (my_http_server.GetBot != null)
{
my_http_server.GetBot.GetClient.Network.Logout();
}
}
}
}

View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterSecondBot.HttpServer.Control.Actions
{
public class Logout : HTTP_commands_post
{
public override string Helpfile { get { return "Makes the bot logout<br/> Returns OK,I dont want to go if accepted"; } }
public override string[] CallFunction(string[] args)
{
if (bot != null)
{
if (httpserver.HTTPCnCmode == true)
{
httpserver.KillBot();
return new string[] { "OK", "Bot Shutting down" };
}
else
{
bot.KillMePlease();
return new string[] { "OK", "I dont want to go" };
}
}
else
{
return Failed("No bot logged in");
}
}
}
}

View file

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterSecondBot.HttpServer.Control.Interface
{
public class CoreCMD : HTTP_commands_post
{
public override string[] ArgTypes { get { return new string[] { "Text","Text" }; } }
public override string[] ArgHints { get { return new string[] { "Command","Args (~#~ split)" }; } }
public override string Helpfile { get { return "Calls the Commands interface via HTTP (returns OK if not rejected)"; } }
public override int MinArgs { get { return 2; } }
public override string[] CallFunction(string[] args)
{
if (bot != null)
{
if (ArgsCheck(args) == true)
{
if (bot.GetCommandsInterface.Call(args[0], args[1]) == true)
{
return new string[] { "OK" };
}
else
{
return Failed("Something went wrong with the call");
}
}
else
{
return Failed("Incorrect number of args");
}
}
else
{
return Failed("No bot");
}
}
}
}

View file

@ -0,0 +1,28 @@
using BSB.bottypes;
using System;
using System.Collections.Generic;
using System.Text;
using static BetterSecondBot.Program;
namespace BetterSecondBot.HttpServer.Control.CNC
{
public class CNCStartBot : HTTP_commands_post
{
public override string Helpfile { get { return "[C&C Mode only]<br/>Starts bot"; } }
public override string[] CallFunction(string[] args)
{
if (httpserver.HTTPCnCmode == true)
{
if (bot == null)
{
httpserver.NewBot();
httpserver.GetBot.Setup(httpserver.GetConfig, AssemblyInfo.GetGitHash());
httpserver.GetBot.Start();
return new string[] { "OK","Starting bot now" };
}
return Failed("Unable to Login the bot (it might already be running)");
}
return Failed("Not running as CNC");
}
}
}

View file

@ -0,0 +1,22 @@
using BSB.bottypes;
using System;
using System.Collections.Generic;
using System.Text;
using static BetterSecondBot.Program;
namespace BetterSecondBot.HttpServer.Control.CNC
{
public class ShutdownCNC : HTTP_commands_post
{
public override string Helpfile { get { return "[C&C Mode only]<br/>Shutsdown the C&C process"; } }
public override string[] CallFunction(string[] args)
{
if (httpserver.HTTPCnCmode == true)
{
httpserver.ShutdownHTTP = true;
return new string[] { "OK", "Shutting down" };
}
return Failed("Not running as CNC");
}
}
}

View file

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
using BSB;
using BSB.bottypes;
using BetterSecondBotShared.API;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
namespace BetterSecondBot.HttpServer
{
public abstract class HTTPCommandsInterface : API_supported_interface
{
protected SecondBot bot;
protected http_server httpserver;
protected HTTPCommandsInterface(SecondBot linktobot, http_server sethttpserver)
{
bot = linktobot;
httpserver = sethttpserver;
}
public string[] Call(string command, string arg)
{
command = command.ToLowerInvariant();
HTTP_commands cmd = (HTTP_commands)GetCommand(command);
if (cmd != null)
{
cmd.Setup(bot,httpserver);
return cmd.CallFunction(arg.Split(new[] { "~#~" }, StringSplitOptions.None));
}
else
{
ConsoleLog.Debug("" + command + " [Failed]: I have no fucking idea what you are talking about");
return new[] { "Failed", "I have no fucking idea what you are talking about" };
}
}
}
public class HTTPCommandsInterfaceGet : HTTPCommandsInterface
{
public HTTPCommandsInterfaceGet(SecondBot linktobot, http_server sethttpserver) : base(linktobot, sethttpserver)
{
API_type = typeof(HTTP_commands_get);
LoadCommandsList();
}
}
public class HTTPCommandsInterfacePost : HTTPCommandsInterface
{
public HTTPCommandsInterfacePost(SecondBot linktobot, http_server sethttpserver) : base(linktobot, sethttpserver)
{
API_type = typeof(HTTP_commands_post);
LoadCommandsList();
}
}
public abstract class HTTP_commands_get : HTTP_commands
{
}
public abstract class HTTP_commands_post : HTTP_commands
{
}
public abstract class HTTP_commands : API_interface
{
protected SecondBot bot;
protected http_server httpserver;
public void Setup(SecondBot setBot,http_server Sethttpserver)
{
bot = setBot;
httpserver = Sethttpserver;
}
public virtual string[] CallFunction(string[] args)
{
if (ArgsCheck(args) == true)
{
return new[] { "OK", "No override" };
}
return Failed("Incorrect number of args");
}
protected bool ArgsCheck(string[] args)
{
if (args.Length >= MinArgs)
{
return true;
}
return false;
}
protected string[] Failed(string why_failed)
{
ConsoleLog.Warn("HTTP: " + CommandName + " [Failed]: " + why_failed);
return new string[] {"Failed",why_failed};
}
}
}

View file

@ -0,0 +1,200 @@
using BetterSecondBotShared.IO;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using BetterSecondBotShared.Static;
using BSB;
using BSB.bottypes;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BetterSecondBot.HttpServer
{
public class http_server
{
protected HttpListener listener;
protected SecondBot Bot;
public SecondBot GetBot { get { return Bot; } }
protected JsonConfig Config;
public JsonConfig GetConfig { get { return Config; } }
protected HTTPCommandsInterfaceGet get_controler;
protected HTTPCommandsInterfacePost post_controler;
protected string url = "";
protected bool require_signed_commands;
public bool ShutdownHTTP { get; set; }
public bool HTTPCnCmode { get; set; }
public string GetStatus()
{
string reply = "";
if(Bot == null)
{
reply += "- No bot";
}
else
{
Bot.LastStatusMessage = Bot.GetStatus();
reply += Bot.LastStatusMessage;
}
if(listener == null)
{
reply += " - No HTTP listener";
}
return reply;
}
public void NewBot()
{
Bot = new SecondBot();
}
public void KillBot()
{
Bot.GetClient.Network.Logout();
Bot.GetClient.Network.Shutdown(OpenMetaverse.NetworkManager.DisconnectType.ClientInitiated);
Bot = null;
}
public void start_http_server(JsonConfig setConfig)
{
start_http_server(setConfig, null);
}
public void start_http_server(SecondBot LinkBot, JsonConfig setConfig)
{
start_http_server(setConfig, LinkBot);
}
protected void start_http_server(JsonConfig setConfig, SecondBot LinkBot)
{
Config = setConfig;
require_signed_commands = Config.HttpRequireSigned;
Bot = LinkBot;
int port = Math.Abs(Config.Httpport);
if (Config.Httpkey.Length < 12)
{
Config.Httpkey = helpers.GetSHA1("" + helpers.UnixTimeNow().ToString() + "" + new Random().Next(4, 9999).ToString()).Substring(0, 12);
}
if (Config.HttpHost == "docker")
{
url = "http://*:" + port.ToString() + "/";
}
else
{
if (url.StartsWith("http://") == false)
{
url = "http://localhost:" + port.ToString() + "";
}
if (url.EndsWith("/") == false)
{
url = "" + url + "/";
}
}
open_http_service();
}
protected async Task HandleIncomingConnections()
{
while(Bot.KillMe == false)
{
HttpListenerContext ctx = await listener.GetContextAsync();
HttpListenerRequest req = ctx.Request;
HttpListenerResponse resp = ctx.Response;
string test = req.Url.AbsolutePath.Substring(1);
string[] http_args = test.Split('/', StringSplitOptions.RemoveEmptyEntries);
byte[] data = Encoding.UTF8.GetBytes("totaly fucked");
resp.StatusCode = 404;
if (req.HttpMethod == "GET")
{
resp.StatusCode = 200;
string command = "status";
string arg = "";
if (helpers.notempty(http_args) == true)
{
if (http_args.Length >= 1)
{
command = http_args[0];
}
if (http_args.Length == 2)
{
arg = http_args[1];
}
}
string content = String.Join("{@}", get_controler.Call(command, arg));
data = Encoding.UTF8.GetBytes(content);
}
else if (req.HttpMethod == "POST")
{
string text;
using (var reader = new StreamReader(req.InputStream,req.ContentEncoding))
{
text = reader.ReadToEnd();
}
//"command = logout & args = &passkey = Kp83VZaEhcy"
string[] pairs = text.Split('&', StringSplitOptions.RemoveEmptyEntries);
Dictionary<string, string> post_args = new Dictionary<string, string>();
foreach(string B in pairs)
{
string[] bits = B.Split('=', StringSplitOptions.RemoveEmptyEntries);
if(bits.Length == 2) post_args.Add(bits[0].Trim(), bits[1].Trim());
}
if (post_args.ContainsKey("command") == true)
{
if (post_args.ContainsKey("passkey") == true)
{
if (Config.Httpkey == post_args["passkey"])
{
string command = post_args["command"];
resp.StatusCode = 200;
string arg = "";
if (post_args.ContainsKey("args") == true)
{
arg = post_args["args"];
}
data = Encoding.UTF8.GetBytes(String.Join("###", post_controler.Call(command, arg)));
}
else
{
data = Encoding.UTF8.GetBytes("totaly fucked");
}
}
else
{
data = Encoding.UTF8.GetBytes("post request missing command");
}
}
else
{
data = Encoding.UTF8.GetBytes("post request missing command");
}
}
resp.ContentType = "text/html";
resp.ContentEncoding = Encoding.UTF8;
resp.ContentLength64 = data.LongLength;
await resp.OutputStream.WriteAsync(data, 0, data.Length);
resp.Close();
}
}
protected void open_http_service()
{
get_controler = new HTTPCommandsInterfaceGet(Bot,this);
post_controler = new HTTPCommandsInterfacePost(Bot,this);
Thread t = new Thread(delegate (object unused)
{
listener = new HttpListener();
listener.Prefixes.Add(url);
listener.Start();
ConsoleLog.Status("Listening for connections on " + url + "");
Task listenTask = HandleIncomingConnections();
listenTask.GetAwaiter().GetResult();
listener.Close();
})
{
Name = "http thead",
IsBackground = true
};
t.Start();
}
}
}

View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterSecondBot.HttpServer.View.Basic
{
public class Status : HTTP_commands_get
{
public override int MinArgs { get { return 0; } }
public override string Helpfile { get { return "returns the status of the bot"; } }
public override string[] CallFunction(string[] args)
{
if (bot != null)
{
if (ArgsCheck(args) == true)
{
return new string[] { bot.GetStatus() };
}
else
{
return Failed("Incorrect number of args");
}
}
else
{
return Failed("No Bot");
}
}
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterSecondBot.HttpServer.view.Inventory
{
class getfolders : HTTP_commands_get
{
public override int MinArgs { get { return 0; } }
public override string Helpfile { get { return "returns the status of the bot"; } }
public override string[] CallFunction(string[] args)
{
if (bot != null)
{
if (ArgsCheck(args) == true)
{
return new string[] { bot.GetStatus() };
}
else
{
return Failed("Incorrect number of args");
}
}
else
{
return Failed("No Bot");
}
}
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration>
<Platform>x64</Platform>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PublishDir>..\Publish\linux-x64</PublishDir>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>True</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
<PublishTrimmed>False</PublishTrimmed>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration>
<Platform>x64</Platform>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PublishDir>..\Publish\windows_x64</PublishDir>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>True</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
<PublishTrimmed>False</PublishTrimmed>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,10 @@
{
"profiles": {
"BetterSecondBot": {
"commandName": "Project"
},
"Docker": {
"commandName": "Docker"
}
}
}

View file

@ -0,0 +1,11 @@
</div>
<script src="https://code.jquery.com/jquery-3.3.1.js" crossorigin="anonymous"></script>
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js" crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
$('.datatable').DataTable();
} );
</script>
</body>
</html>

View file

@ -0,0 +1,30 @@
<html>
<head>
<title>Secondbot Wiki [[AREA]]</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous">
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-3">
<div class="container-fluid">
<a href="[[RETURNROOT]]index.html" class="navbar-brand mr-3">SecondBot Wiki</a>
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#navbarCollapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<div class="navbar-nav">
<a href="[[SUBFOLDER]]Core.html" class="nav-item nav-link [[CoreActive]]">Main commands [Core]</a>
<a href="[[SUBFOLDER]]CLI.html" class="nav-item nav-link [[CLIActive]]">Command Line [Parameters]</a>
<a href="[[SUBFOLDER]]JSON.html" class="nav-item nav-link [[JSONActive]]">JSON [Bot Config file]</a>
<a href="[[SUBFOLDER]]RLVapi.html" class="nav-item nav-link [[RLVapiActive]]">Restrained Love API [RLV]</a>
<a href="[[SUBFOLDER]]HTTPget.html" class="nav-item nav-link [[HTTPgetActive]]">Get [HTTP]</a>
<a href="[[SUBFOLDER]]HTTPpost.html" class="nav-item nav-link [[HTTPpostActive]]">Post [HTTP]</a>
</div>
<div class="navbar-nav ml-auto">
<a href="https://bitbucket.org/streamadmin/secondbot/src/master/" target="_BLANK" class="nav-item nav-link">Get Source</a>
<a href="secondlife:///app/group/cbda0f35-1c0b-2a45-e693-62a63a8f9d88/about" target="_BLANK" class="nav-item nav-link">SL group</a>
</div>
</div>
</div>
</nav>
<div class="container-fluid">

340
CLI/WikiMake/wikimake.cs Normal file
View file

@ -0,0 +1,340 @@
using BetterSecondBotShared.API;
using BetterSecondBotShared.IO;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using BSB;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace BetterSecondBot.WikiMake
{
class DebugModeCreateWiki
{
protected Dictionary<string, KeyValuePair<int, string>> api_reports = new Dictionary<string, KeyValuePair<int, string>>();
protected string html_header = "";
protected string html_footer = "";
protected string buildVersion = "";
protected SimpleIO io;
protected Dictionary<string,string> seen_command_names = new Dictionary<string, string>();
public static string ReadResourceFile(string filename)
{
try
{
var assembly = Assembly.GetExecutingAssembly();
string resourceName = assembly.GetManifestResourceNames().Single(str => str.EndsWith(filename));
string result = "";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
}
return result;
}
catch (Exception e)
{
return e.Message;
}
}
public string CreateRoot()
{
StringBuilder sb = new StringBuilder();
sb.Append(html_header);
sb.Append("<h3>Build: ");
sb.Append(buildVersion);
sb.Append("</h3><br/>");
sb.Append("<table class='table table-striped table-bordered'><thead><tr><th>Interface</th><th>Commands</th><th>Status</th></tr></thead><tbody>");
foreach (KeyValuePair<string,KeyValuePair<int, string>> entry in api_reports)
{
sb.Append("<tr>");
sb.Append("<td><a href='[[SUBFOLDER]]");
sb.Append(entry.Key);
sb.Append(".html'>");
sb.Append(entry.Key);
sb.Append("</a></td>");
sb.Append("<td>");
sb.Append(entry.Value.Key.ToString());
sb.Append("</td><td>");
sb.Append(entry.Value.Value);
sb.Append("</td></tr>");
}
sb.Append("</tbody></table>");
sb.Replace("[[AREA]]", "Home");
sb.Replace("[[SUBFOLDER]]", "files/");
sb.Replace("[[RETURNROOT]]", "");
sb = MenuActive(sb, "Home");
sb.Append(html_footer);
return sb.ToString();
}
protected StringBuilder MenuActive(StringBuilder index,string area)
{
Dictionary<string, string> active_swaps = new Dictionary<string, string>
{
{ "CoreActive", "" },
{ "CLIActive", "" },
{ "JSONActive", "" },
{ "RLVapiActive", "" },
{ "HTTPgetActive", "" },
{ "HTTPpostActive", "" }
};
active_swaps["" + area + "Active"] = "Active";
foreach(KeyValuePair<string,string> pair in active_swaps)
{
index = index.Replace("[["+ pair.Key+"]]", pair.Value);
}
return index;
}
protected void InterfaceCommands(string area, API_supported_interface shared_interface,bool track_commands=false)
{
string[] cmds = shared_interface.GetCommandsList();
string interface_name = shared_interface.GetType().Name;
foreach (string c in cmds)
{
if (track_commands == true)
{
if (seen_command_names.ContainsKey(c) == false)
{
seen_command_names.Add(c, interface_name);
}
else
{
ConsoleLog.Debug("command " + c + " from " + interface_name + " overlaps an ready loaded command from "+ seen_command_names[c]+"");
}
}
string workspace = shared_interface.GetCommandWorkspace(c);
StringBuilder sb = new StringBuilder();
sb.Append(html_header);
sb.Append("<h3>Build: ");
sb.Append(buildVersion);
sb.Append("</h3><br/>");
sb.Append("<h4>Interface: <a href='[[AREA]].html'>[[AREA]]</a>");
if(workspace != "")
{
sb.Append(" / <a href='[[AREA]][[WORKSPACE]].html'>[[WORKSPACE]]</a>");
}
sb.Append("</h4><hr/><h3>[[COMMAND]]</h3>");
int loop = 0;
int minargs = shared_interface.GetCommandArgs(c);
string[] arg_types = shared_interface.GetCommandArgTypes(c);
string[] arg_hints = shared_interface.GetCommandArgHints(c);
if(area == "Core")
{
string example_call = "Example: [[COMMAND]]";
string preaddon = "|||";
string addon = "";
while (loop < minargs)
{
example_call += preaddon;
example_call += addon;
preaddon = "";
addon = "~#~";
string hint_value = "";
if (arg_hints.Length > loop)
{
if (arg_hints[loop] != null)
{
string[] bits = arg_hints[loop].Split("<br/>", StringSplitOptions.RemoveEmptyEntries);
if (bits.Length >= 1)
{
hint_value = bits[0];
}
}
}
if (hint_value != "")
{
example_call += hint_value;
}
else
{
example_call += "Not sure";
ConsoleLog.Debug("[WikiMake] " + area + " Command " + c + " missing some required hint values");
}
loop++;
}
sb.Append(example_call);
sb.Append("<hr style='border-top: 1px dashed #dcdcdc;'>");
}
sb.Append("[[HELP]]");
if (arg_types.Length > 0)
{
sb.Append("<hr/><h4>Args helper</h4>");
loop = 0;
sb.Append("<table class='table table-striped table-bordered'><thead><tr><td>Num</td><th>Type</th><th>Required</th><th>Hint</th></tr></thead><tbody>");
while (loop < arg_types.Length)
{
string hint = "";
if (arg_hints.Length > loop)
{
hint = arg_hints[loop];
}
string Required = "X";
if ((loop + 1) > minargs)
{
Required = "";
}
sb.Append("<tr><td>" + (loop + 1).ToString() + "</td><td>" + arg_types[loop] + "</td><td>" + Required + "</td><td>" + hint + "</td></tr>");
loop++;
}
sb.Append("</tbody></table>");
}
sb.Append(html_footer);
sb.Replace("[[COMMAND]]", c);
sb.Replace("[[HELP]]", shared_interface.GetCommandHelp(c));
sb.Replace("[[MINARGS]]", minargs.ToString());
sb.Replace("[[WORKSPACE]]", workspace);
sb.Replace("[[AREA]]", area);
sb.Replace("[[SUBFOLDER]]", "");
sb.Replace("[[RETURNROOT]]", "../");
sb = MenuActive(sb, area);
string target_file = "" + area + "" + workspace + "" + c + ".html";
io.writefile(target_file, sb.ToString());
}
}
protected void InterfaceWorkspaces(string area, API_supported_interface shared_interface)
{
string[] cmds = shared_interface.GetCommandsList();
foreach (string workspace in shared_interface.GetAllWorkspaces())
{
StringBuilder sb = new StringBuilder();
sb.Append(html_header);
sb.Append("<h3>Interface: <a href='[[AREA]].html'>[[AREA]]</a>");
if (workspace != "")
{
sb.Append(" / [[WORKSPACE]]");
}
sb.Append("</h3><br/>");
sb.Append("<h4>Build: ");
sb.Append(buildVersion);
sb.Append("</h4><br/>");
sb.Append("<table class='datatable table table-striped table-bordered'><thead><tr><th>Command</th><th>Min args</th></tr></thead><tbody>");
foreach(string c in cmds)
{
if(shared_interface.GetCommandWorkspace(c) == workspace)
{
int args = shared_interface.GetCommandArgs(c);
sb.Append("<tr><td><a href='[[AREA]]");
sb.Append(workspace);
sb.Append(c);
sb.Append(".html'>");
sb.Append(c);
sb.Append("</a></td><td>");
sb.Append(args.ToString());
sb.Append("</td></tr>");
}
}
sb.Append("</tbody></table>");
sb.Replace("[[SUBFOLDER]]", "");
sb.Replace("[[RETURNROOT]]", "../");
sb.Replace("[[WORKSPACE]]", workspace);
sb.Replace("[[AREA]]", area);
sb.Append(html_footer);
sb = MenuActive(sb, area);
string target_file = "" + area + "" + workspace + ".html";
io.writefile(target_file, sb.ToString());
}
}
protected void InterfaceIndex(string area, API_supported_interface shared_interface)
{
StringBuilder sb = new StringBuilder();
sb.Append(html_header);
sb.Append("<h3>Interface:");
sb.Append(area);
sb.Append("</h3><br/>");
sb.Append("<h4>Build: ");
sb.Append(buildVersion);
sb.Append("</h4><br/>");
string[] cmds = shared_interface.GetCommandsList();
sb.Append("<table class='datatable table table-striped table-bordered'><thead><tr><th>Workspace</th><th>Commands</th></tr></thead><tbody>");
foreach (string workspace in shared_interface.GetAllWorkspaces())
{
int commands_count = 0;
foreach (string c in cmds)
{
if (shared_interface.GetCommandWorkspace(c) == workspace)
{
commands_count++;
}
}
string workspace_link = "<a href='[[AREA]]" + workspace + ".html'>" + workspace + "</a>";
sb.Append("<tr><td>");
sb.Append(workspace_link);
sb.Append("</td><td>");
sb.Append(commands_count.ToString());
sb.Append("</td></tr>");
}
sb.Append("</tbody></table>");
sb.Replace("[[SUBFOLDER]]", "");
sb.Replace("[[RETURNROOT]]", "../");
sb.Replace("[[AREA]]", area);
sb.Append(html_footer);
sb = MenuActive(sb, area);
io.writefile(""+ area+".html", sb.ToString());
}
protected void InterfaceWiki(string area,API_supported_interface shared_interface, bool track_commands = false)
{
ConsoleLog.Info("[WIKI] Starting area " + area + "");
// create index
InterfaceIndex(area, shared_interface);
// create workspaces
InterfaceWorkspaces(area, shared_interface);
// create commands
InterfaceCommands(area, shared_interface, track_commands);
ConsoleLog.Info("[WIKI] Done with area");
}
public DebugModeCreateWiki(string Version, SimpleIO shareio)
{
io = shareio;
buildVersion = Version;
html_header = ReadResourceFile("wiki_header.txt");
html_footer = ReadResourceFile("wiki_footer.txt");
io.ChangeRoot("wiki");
io.ChangeRoot("wiki/files");
// CLI
Options cli = new Options();
api_reports.Add("CLI", new KeyValuePair<int, string>(cli.ApiCommandsCount, "Ready"));
InterfaceWiki("CLI", cli);
// JSONcfg
MakeJsonConfig defaultJson = new MakeJsonConfig();
api_reports.Add("JSON", new KeyValuePair<int, string>(defaultJson.ApiCommandsCount, "Ready"));
InterfaceWiki("JSON", defaultJson);
// CMD
BSB.Commands.CoreCommandsInterface cmd = new BSB.Commands.CoreCommandsInterface(null);
api_reports.Add("Core", new KeyValuePair<int, string>(cmd.ApiCommandsCount, "Basic"));
InterfaceWiki("Core", cmd,true);
// RLVapi
BSB.RLV.RLVcontrol RLVapi = new BSB.RLV.RLVcontrol(null);
api_reports.Add("RLVapi", new KeyValuePair<int, string>(RLVapi.ApiCommandsCount, "Basic"));
InterfaceWiki("RLVapi", RLVapi, true);
// HTTP get
BetterSecondBot.HttpServer.HTTPCommandsInterfaceGet http_get = new BetterSecondBot.HttpServer.HTTPCommandsInterfaceGet(null,null);
api_reports.Add("HTTPget", new KeyValuePair<int, string>(http_get.ApiCommandsCount, "Limited"));
InterfaceWiki("HTTPget", http_get);
// HTTP post
BetterSecondBot.HttpServer.HTTPCommandsInterfacePost http_post = new BetterSecondBot.HttpServer.HTTPCommandsInterfacePost(null,null);
api_reports.Add("HTTPpost", new KeyValuePair<int, string>(http_post.ApiCommandsCount, "Limited"));
InterfaceWiki("HTTPpost", http_post);
io.ChangeRoot("wiki");
io.writefile("index.html", CreateRoot());
}
}
}

25
Core/BSB.csproj Normal file
View file

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Configurations>Debug;Release;DockerBuild</Configurations>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DockerBuild|x64'">
<ErrorReport>none</ErrorReport>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<PublishTrimmed>false</PublishTrimmed>
<PublishSingleFile>false</PublishSingleFile>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup> <ItemGroup>
<PackageReference Include="Discord.Net.Webhook" Version="2.1.1" />
<PackageReference Include="Discord.Net.WebSocket" Version="2.1.1" />
<PackageReference Include="RestSharp" Version="106.10.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BSBshared\BSBshared.csproj" />
</ItemGroup>
</Project>

32
Core/Commands/Chat/im.cs Normal file
View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Chat
{
class IM : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Avatar", "Text" }; } }
public override string[] ArgHints { get { return new[] { "Avatar [UUID or Firstname Lastname]","Message" }; } }
public override string Helpfile { get { return "Makes the bot send a IM to an avatar<br/>Example: IM|||289c3e36-69b3-40c5-9229-0c6a5d230766~#~Hello Mad I am a bot"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID target_av) == true)
{
bot.GetClient.Self.InstantMessage(target_av, args[1]);
return true;
}
else
{
return Failed("Not a vaild UUID");
}
}
return false;
}
}
}

32
Core/Commands/Chat/say.cs Normal file
View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Chat
{
class Say : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Mixed","Text" }; } }
public override string[] ArgHints { get { return new[] { "Message or channel","Message" }; } }
public override string Helpfile { get { return "Makes the bot talk via chat<br/>Example: say|||Hello<br/> Example: say|||12~#~Goodbye"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if(base.CallFunction(args) == true)
{
string message = args[0];
int channel = 0;
if (args.Count() == 2)
{
message = args[1];
int.TryParse(args[0], out channel);
}
bot.GetClient.Self.Chat(message, channel, ChatType.Normal);
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,47 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Dialogs
{
class DialogRelay : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Smart" }; } }
public override string[] ArgHints { get { return new[] { "Channel (Any number),Avatar UUID,Avatar name,HTTPurl<br/>Clear" }; } }
public override string Helpfile { get { return "Updates the relay target (you can have 1 of each type)<br/>Clear will disable them all"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if(args[0] != "Clear")
{
if (UUID.TryParse(args[0], out UUID avatar) == true)
{
bot.SetRelayDialogsAvatar(avatar);
}
else if (args[0].StartsWith("http") == true)
{
bot.SetRelayDialogsHTTP(args[0]);
}
else if (int.TryParse(args[0], out int channel) == true)
{
bot.SetRelayDialogsChannel(channel);
}
else return Failed("Not a vaild option");
return true;
}
else
{
bot.SetRelayDialogsAvatar(UUID.Zero);
bot.SetRelayDialogsChannel(0);
bot.SetRelayDialogsHTTP("");
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,28 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Dialogs
{
class DialogResponce : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Number","Text" }; } }
public override string[] ArgHints { get { return new[] { "DialogID","Button to click" }; } }
public override string Helpfile { get { return "Makes the bot interact with the dialog [ARG 1] with the button [ARG 2]"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (int.TryParse(args[0], out int dialogid) == true)
{
return bot.DialogReply(dialogid, args[1]);
}
else return Failed("Invaild dialog id");
}
return false;
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Dialogs
{
class DialogTrack : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "True|False" }; } }
public override string[] ArgHints { get { return new[] { "Status" }; } }
public override string Helpfile { get { return "Should the bot track dialogs and send them to the relays setup?"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if(bool.TryParse(args[0],out bool status) == true)
{
bot.SetTrackDialogs(status);
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenMetaverse;
using BetterSecondBotShared.logs;
namespace BSB.Commands.Group
{
public class GroupInvite : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "UUID", "Avatar", "UUID" }; } }
public override string[] ArgHints { get { return new[] { "GROUP", "Avatar [UUID or Firstname Lastname]", "Role" }; } }
public override string Helpfile { get { return "Invites the avatar [ARG 2] to the Group [ARG 1] {optional with the Role [ARG 3]} otherwise to role Everyone"; } }
public override int MinArgs { get { return 2; } }
protected void ForRealGroupInvite(string[] args,UUID group,UUID avatar)
{
UUID target_role = UUID.Zero;
if (args.Length == 3)
{
UUID.TryParse(args[2], out target_role);
}
List<UUID> invite_roles = new List<UUID>();
if (target_role != UUID.Zero) invite_roles.Add(target_role);
else invite_roles.Add(UUID.Zero);
bot.GetClient.Groups.Invite(group, invite_roles, avatar);
}
public override void Callback(string[] args, EventArgs e)
{
GroupMembersReplyEventArgs reply = (GroupMembersReplyEventArgs)e;
if (UUID.TryParse(args[0], out UUID target_group) == true)
{
if (UUID.TryParse(args[1], out UUID target_avatar) == true)
{
if (reply.GroupID == target_group)
{
if (reply.Members.ContainsKey(target_avatar) == false)
{
ForRealGroupInvite(args, target_group, target_avatar);
}
}
else
{
ConsoleLog.Crit("Callback sent for the wrong group as part of group invite!");
}
}
}
}
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID target_group) == true)
{
if (UUID.TryParse(args[1], out UUID target_avatar) == true)
{
if (bot.MyGroups.ContainsKey(target_group) == true)
{
if (bot.GetAllowGroupInvite(target_avatar) == true)
{
if (bot.FastCheckInGroup(target_group, target_avatar) == false)
{
if (bot.NeedReloadGroupData(target_group) == true)
{
if (bot.CreateAwaitEventReply("groupmembersreply", this, args) == true)
{
bot.GroupInviteLockoutArm(target_avatar); // enable 120 sec cooldown
bot.GetClient.Groups.RequestGroupMembers(target_group);
return true;
}
else
{
return Failed("Unable to await reply");
}
}
else
{
bot.GroupInviteLockoutArm(target_avatar); // enable 120 sec cooldown
ForRealGroupInvite(args, target_group, target_avatar);
return true;
}
}
else
{
return true; // they are in the group (or we have old data) retry in like 2 mins
}
}
else
{
return Failed("Group invite to this avatar is on 120sec cooldown");
}
}
else
{
return Failed("I am not a member of that group");
}
}
else
{
return Failed("Invaild avatar UUID");
}
}
else
{
return Failed("Invaild group UUID");
}
}
return false;
}
}
}

View file

@ -0,0 +1,50 @@
using BetterSecondBotShared.Static;
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Group
{
class Groupnotice : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "UUID", "TEXT","TEXT" }; } }
public override string[] ArgHints { get { return new[] { "Group", "Message/Title", "Message" }; } }
public override string Helpfile { get { return "Sends a group notice to [ARG 1] with the message [ARG 2]<hr/>if given 3 Args: Sends a group notice to [ARG 1] with the title [ARG 2] and the message [ARG 3]"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID targetgroup) == true)
{
if (bot.MyGroups.ContainsKey(targetgroup) == true)
{
string Noticetitle = "GroupNotice@"+ helpers.UnixTimeNow().ToString()+"";
string Noticemessage = "";
if (args.Length == 3)
{
Noticetitle = args[1];
}
Noticemessage = args[^1];
GroupNotice NewNotice = new GroupNotice();
NewNotice.Subject = Noticetitle;
NewNotice.Message = Noticemessage;
NewNotice.OwnerID = bot.GetClient.Self.AgentID;
bot.GetClient.Groups.SendGroupNotice(targetgroup, NewNotice);
return true;
}
else
{
return Failed("Unkown group");
}
}
else
{
return Failed("Invaild UUID");
}
}
return false;
}
}
}

View file

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenMetaverse;
namespace BSB.Commands.Group
{
public class GetGroupList : CoreGroupCommand_SmartReply_auto
{
public override string Helpfile { get { return "Returns a csv of known groups NAME=UUID,NAME=UUID... "; } }
protected override string RunFunction()
{
string reply = "";
string addon = "";
foreach (KeyValuePair<UUID, OpenMetaverse.Group> groupdata in bot.MyGroups)
{
reply += addon;
reply += groupdata.Value.Name;
reply += "=";
reply += groupdata.Key;
addon = ",";
}
return reply;
}
}
}

View file

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenMetaverse;
namespace BSB.Commands.Group
{
public class GetGroupRoles : CoreGroupCommand_SmartReply_Group
{
public override string Helpfile { get { return "Returns group=UUID@@@rolename=UUID,rolename=UUID ... "; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[1], out _) == true)
{
if (bot.GetCommandsInterface.SmartCommandReply(args[0], "Vaildate", CommandName, true) == true)
{
if (bot.CreateAwaitEventReply("grouproles", this, args) == true)
{
return true;
}
else
{
return Failed("Unable to await reply");
}
}
else
{
return Failed("Smart reply [ARG 1] is not vaild");
}
}
else
{
return Failed("Invaild group UUID");
}
}
return false;
}
public override void Callback(string[] args, EventArgs e)
{
GroupRolesDataReplyEventArgs rolesData = (GroupRolesDataReplyEventArgs)e;
string reply = "group=" + args[1] + "@@@";
string addon = "";
foreach(KeyValuePair<UUID,GroupRole> data in rolesData.Roles)
{
if (data.Key != UUID.Zero)
{
reply += addon;
reply += data.Value.Name;
reply += "=";
reply += data.Key.ToString();
addon = ",";
}
}
bot.GetCommandsInterface.SmartCommandReply(args[0], reply, CommandName);
}
}
}

View file

@ -0,0 +1,66 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Group
{
public abstract class CoreGroupCommand_SmartReply : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Smart" }; } }
public override string[] ArgHints { get { return new[] { "Mixed [Channel|Avatar uuid|Avatar name|http url]"}; } }
public override int MinArgs { get { return 1; } }
}
public abstract class CoreGroupCommand_SmartReply_auto : CoreGroupCommand_SmartReply
{
protected virtual string RunFunction()
{
return "";
}
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
return bot.GetCommandsInterface.SmartCommandReply(args[0], RunFunction(), CommandName);
}
return false;
}
}
public abstract class CoreGroupCommand_SmartReply_Group : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Mixed", "UUID" }; } }
public override string[] ArgHints { get { return new[] { "Smart reply [Channel|IM uuid|http url]", "Group" }; } }
public override int MinArgs { get { return 2; } }
}
public abstract class CoreGroupCommand_SmartReply_Group_auto : CoreGroupCommand_SmartReply_Group
{
protected virtual string RunFunction(UUID group)
{
return "";
}
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[1], out UUID targetgroup) == true)
{
if (bot.MyGroups.ContainsKey(targetgroup) == true)
{
return bot.GetCommandsInterface.SmartCommandReply(args[0], RunFunction(targetgroup), CommandName);
}
else
{
return Failed("Unknown group");
}
}
else
{
return Failed("Invaild UUID");
}
}
return false;
}
}
}

View file

@ -0,0 +1,60 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Group
{
class Groupchat : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "UUID","TEXT" }; } }
public override string[] ArgHints { get { return new[] { "Group","Message" }; } }
public override string Helpfile { get { return "Sends a message [ARG 2] to the group [ARG 1]"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID targetgroup) == true)
{
if (bot.MyGroups.ContainsKey(targetgroup) == true)
{
if (bot.GetActiveGroupchatSessions.Contains(targetgroup) == true)
{
Callback(args, null);
return true;
}
else
{
if (bot.CreateAwaitEventReply("groupchatjoin", this, args) == true)
{
bot.GetClient.Self.RequestJoinGroupChat(targetgroup);
return true;
}
else
{
return Failed("Unable to await reply");
}
}
}
else
{
return Failed("Unkown group");
}
}
else
{
return Failed("Invaild UUID");
}
}
return false;
}
public override void Callback(string[] args, EventArgs e)
{
if (UUID.TryParse(args[0], out UUID targetgroup) == true)
{
bot.GetClient.Self.InstantMessageGroup(targetgroup, args[1]);
}
}
}
}

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Group
{
class GroupchatAnyUnread : CoreGroupCommand_SmartReply_auto
{
public override string Helpfile { get { return "Returns True|False via the smart reply target if there are any unread group ims"; } }
protected override string RunFunction()
{
return bot.HasUnreadGroupchats().ToString();
}
}
}

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Group
{
class GroupchatClearAll : CoreCommand
{
public override string Helpfile { get { return "Clears all group chat buffers at once"; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
bot.ClearAllGroupchat();
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,18 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Group
{
class GroupchatGroupHasUnread : CoreGroupCommand_SmartReply_Group_auto
{
public override string Helpfile { get { return "Returns True|False via the smart reply target if there are any unread group ims for the group [ARG 2]"; } }
protected override string RunFunction(UUID targetgroup)
{
return bot.GroupHasUnread(targetgroup).ToString();
}
}
}

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Group
{
class GroupchatHistory : CoreGroupCommand_SmartReply_Group_auto
{
public override string Helpfile { get { return "Returns the chat history for the selected group [ARG 2] via smart reply [ARG 1]"; } }
protected override string RunFunction(UUID targetgroup)
{
return String.Join("###", bot.GetGroupchat(targetgroup));
}
}
}

View file

@ -0,0 +1,25 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Group
{
public class GroupchatListAllUnreadGroups : CoreGroupCommand_SmartReply_auto
{
public override string Helpfile { get { return "Returns a csv of groups with unread messages: UUID,UUID... "; } }
protected override string RunFunction()
{
string reply = "";
string addon = "";
foreach (UUID group in bot.UnreadGroupchatGroups())
{
reply += addon;
reply += group;
addon = ",";
}
return reply;
}
}
}

View file

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Home
{
public class GoHome : CoreCommand
{
public override string Helpfile { get { return "Makes the bot teleport to its home region"; } }
public override int MinArgs { get { return 0; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
bot.GotoNextHomeRegion();
}
return false;
}
}
}

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Home
{
public class ResetAtHome : CoreCommand
{
public override string Helpfile { get { return "Resets the at home lockouts"; } }
public override int MinArgs { get { return 0; } }
public override bool CallFunction(string[] args)
{
if(base.CallFunction(args) == true)
{
bot.ResetAtHome();
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
using BSB.Static;
using BetterSecondBotShared.Static;
namespace BSB.Commands.Inventory
{
public class InventoryPurgeNotecards : CoreCommand
{
public override string Helpfile { get { return "Searchs the notecards folder for notecards, any older than 31 days are deleted.<br/>Depending on the number of notecards this might require multiple calls!"; } }
public override int MinArgs { get { return 0; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
List<InventoryBase> T = bot.GetClient.Inventory.Store.GetContents(bot.GetClient.Inventory.Store.RootFolder);
InventoryBase NotecardFolder = null;
foreach (InventoryBase R in T)
{
if(R.Name == "Notecards")
{
NotecardFolder = R;
break;
}
}
if(NotecardFolder != null)
{
DateTime Now = DateTime.Now;
List<UUID> purge_notecards = new List<UUID>();
List<InventoryBase> contents = bot.GetClient.Inventory.FolderContents(NotecardFolder.UUID, bot.GetClient.Self.AgentID, true, true, InventorySortOrder.ByDate, 40 * 1000);
foreach(InventoryBase C in contents)
{
InventoryItem A = (InventoryItem)C;
if(A.AssetType == AssetType.Notecard)
{
TimeSpan Dif = DateTime.Now - A.CreationDate;
if(Dif.Days >= 31)
{
purge_notecards.Add(C.UUID);
}
}
}
if(purge_notecards.Count() > 0)
{
bot.GetClient.Inventory.Remove(purge_notecards, new List<UUID>());
}
return true;
}
else
{
return Failed("Unable to find notecard folder");
}
}
return false;
}
}
}

View file

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Inventory
{
public class Attach : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Text", "Text" }; } }
public override string[] ArgHints { get { return new[] { "Sub folder", "Item name" }; } }
public override string Helpfile { get { return "Attachs a item found inside the attachments / [ARG1] folder<br/>that matchs the item name [ARG2]"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
// attachments/[arg1]/[arg2]
// arg1 = sub folder
// arg2 = itemname
InventoryFolder AA = bot.GetClient.Inventory.Store.RootFolder;
List<InventoryBase> T = bot.GetClient.Inventory.Store.GetContents(AA);
AA = null;
foreach (InventoryBase R in T)
{
if (R.Name == "attachments")
{
AA = (InventoryFolder)R;
break;
}
}
if (AA != null)
{
T = bot.GetClient.Inventory.Store.GetContents(AA);
AA = null;
foreach (InventoryBase R in T)
{
if (R.Name == args[0])
{
AA = (InventoryFolder)R;
break;
}
}
if (AA != null)
{
List<InventoryItem> items = new List<InventoryItem>();
List<InventoryBase> contents = bot.GetClient.Inventory.FolderContents(AA.UUID, bot.GetClient.Self.AgentID, true, true, InventorySortOrder.ByName, 20 * 1000);
foreach (InventoryBase item in contents)
{
if ((item is InventoryWearable) || (item is InventoryObject))
{
if (item.Name == args[1])
{
items.Add((InventoryItem)item);
break;
}
}
}
if (items.Count() > 0)
{
bot.GetClient.Appearance.AddAttachments(items, false, false);
return true;
}
else
{
return Failed("Unable to find attachment: " + args[1] + " in sub folder " + args[0] + "");
}
}
else
{
return Failed("Unable to find subfolder " + args[0] + "");
}
}
else
{
return Failed("Unable to find attachements folder");
}
}
return false;
}
}
}

View file

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
using BSB.Static;
using BetterSecondBotShared.Static;
namespace BSB.Commands.Inventory
{
public class InventoryContents : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Smart","UUID" }; } }
public override string[] ArgHints { get { return new[] { "Smart reply target: Channel,Avatar,HTTP","Folder" }; } }
public override string Helpfile { get { return "Maps the given inventory folder [ARG 2] for contents<br/>" +
"Arg 1 *Avatar UUID* sends it via notecard to [ARG 1]<hr/>" +
"Arg 1 *Anything else*<br/>as csv packed as follows folderinventory=UUID~|~entry~|~entry..." +
"entry is formated as follows: itemname###uuid###type<br/>" +
"sent to smart reply target"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[1], out UUID targetfolder) == true)
{
string[] content = HelperInventory.MapFolderInventory(bot, targetfolder);
if (UUID.TryParse(args[0], out UUID targetavatar) == true)
{
string notecardname = "InventoryContents" + helpers.UnixTimeNow().ToString() + "";
if (content != null)
{
bot.SendNotecard(notecardname, String.Join("\n\r", content), targetavatar);
}
else
{
bot.GetClient.Self.InstantMessage(targetavatar, "Unable to load folder or it is empty");
}
return true;
}
else
{
return bot.GetCommandsInterface.SmartCommandReply(args[0], String.Join("~|~", content), CommandName);
}
}
else
{
return Failed("Invaild folder UUID");
}
}
return false;
}
}
}

View file

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
using BSB.Static;
using BetterSecondBotShared.Static;
namespace BSB.Commands.Inventory
{
public class InventoryFolders : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Smart" }; } }
public override string[] ArgHints { get { return new[] { "Smart reply target: Channel,Avatar,HTTP url" }; } }
public override string Helpfile { get { return "Fully maps the avatars inventory folders<br/>" +
"*[Arg 1 UUID] sends you a notecard with it<br/>" +
"*[Arg 1 Anything else] Sends it as a CSV via the smart target<br/>" +
"Formated as follows<br>" +
"entry,entry...<br/>" +
"Where entry is formated foldername###level###UUID"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID targetavatar) == true)
{
bot.GetClient.Self.InstantMessage(targetavatar, "Building inventory folder map now [This might take a while]");
string reply = HelperInventory.MapFolder(bot, false);
bot.SendNotecard("Inventory Folders", reply, targetavatar);
return true;
}
else
{
string reply = HelperInventory.MapFolder(bot, true);
return bot.GetCommandsInterface.SmartCommandReply(args[0], reply, CommandName);
}
}
return false;
}
}
}

View file

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Inventory
{
public class Outfit : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Text" }; } }
public override string[] ArgHints { get { return new[] { "Folder name" }; } }
public override string Helpfile { get { return "Replaces the current avatar outfit with the Clothing/[ARG1] folder<br/>Please note: This does not use the outfits folder!"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
// uses the Clothing folder
// must be a full outfit (shapes/eyes ect)
InventoryFolder AA = bot.GetClient.Inventory.Store.RootFolder;
List<InventoryBase> T = bot.GetClient.Inventory.Store.GetContents(AA);
AA = null;
foreach (InventoryBase R in T)
{
if (R.Name == "Clothing")
{
AA = (InventoryFolder)R;
break;
}
}
if (AA != null)
{
T = bot.GetClient.Inventory.Store.GetContents(AA);
AA = null;
foreach (InventoryBase R in T)
{
if (R.Name == args[0])
{
AA = (InventoryFolder)R;
break;
}
}
if (AA != null)
{
List<InventoryBase> contents = bot.GetClient.Inventory.FolderContents(AA.UUID, bot.GetClient.Self.AgentID, true, true, InventorySortOrder.ByName, 20 * 1000);
List<InventoryItem> wareables = new List<InventoryItem>();
if (contents != null)
{
foreach (InventoryBase item in contents)
{
if ((item is InventoryWearable) || (item is InventoryObject))
{
wareables.Add((InventoryItem)item);
}
}
bot.GetClient.Appearance.ReplaceOutfit(wareables, false);
return true;
}
else
{
return Failed("target folder is empty or so full I cant get it in 20 secs...");
}
}
else
{
return Failed("Cant find target folder");
}
}
else
{
return Failed("Cant find Clothing folder");
}
}
return false;
}
}
}

View file

@ -0,0 +1,47 @@
using BSB.Static;
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Inventory
{
public class SendFolder : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Avatar","UUID" }; } }
public override string[] ArgHints { get { return new[] { "Avatar [UUID or Firstname Lastname]", "FOLDER" }; } }
public override string Helpfile { get { return "Sends a folder [ARG 2] to an avatar [ARG 1]"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID targetavatar) == true)
{
if (UUID.TryParse(args[1], out UUID targetfolder) == true)
{
InventoryBase FindFolderHelper = HelperInventory.FindFolder(bot, bot.GetClient.Inventory.Store.RootFolder, targetfolder);
if (FindFolderHelper != null)
{
bot.GetClient.Inventory.GiveFolder(FindFolderHelper.UUID, FindFolderHelper.Name, targetavatar, false);
return true;
}
else
{
return Failed("Unable to find folder");
}
}
else
{
return Failed("Invaild folder UUID");
}
}
else
{
return Failed("Invaild avatar UUID");
}
}
return false;
}
}
}

View file

@ -0,0 +1,52 @@
using BSB.Static;
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.Inventory
{
public class SendItem : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Avatar", "UUID", "Number" }; } }
public override string[] ArgHints { get { return new[] { "Avatar [UUID or Firstname Lastname]", "Item","Time to find item" }; } }
public override string Helpfile { get { return "Sends a item [ARG 2] to an avatar [ARG 1]. (Optional arg 3: How long to wait to find the item in secs, Defaults to 6)"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
int search_delay_time = 6;
if(args.Length == 3)
{
int.TryParse(args[2], out search_delay_time);
}
if (UUID.TryParse(args[0], out UUID targetavatar) == true)
{
if (UUID.TryParse(args[1], out UUID targetitem) == true)
{
InventoryItem itm = bot.GetClient.Inventory.FetchItem(targetitem, bot.GetClient.Self.AgentID, (search_delay_time * 1000));
if (itm != null)
{
bot.GetClient.Inventory.GiveItem(itm.AssetUUID, itm.Name, itm.AssetType, targetavatar, false);
return true;
}
else
{
return Failed("Unable to find item");
}
}
else
{
return Failed("Invaild item UUID");
}
}
else
{
return Failed("Invaild avatar UUID");
}
}
return false;
}
}
}

View file

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenMetaverse;
namespace BSB.Commands.Movement
{
public class RotateTo : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Number" }; } }
public override string[] ArgHints { get { return new[] { "0 to 360" }; } }
public override string Helpfile { get { return "Rotates the avatar to face a rotation from north in Degrees"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (float.TryParse(args[0], out float target_yaw) == true)
{
float yaw = target_yaw;
bot.GetClient.Self.Movement.BodyRotation.GetEulerAngles(out float roll, out float pitch, out _);
roll *= 57.2958f;
pitch *= 57.2958f;
yaw *= 57.2958f;
float rollOver2 = roll * 0.5f;
float sinRollOver2 = (float)Math.Sin((double)rollOver2);
float cosRollOver2 = (float)Math.Cos((double)rollOver2);
float pitchOver2 = pitch * 0.5f;
float sinPitchOver2 = (float)Math.Sin((double)pitchOver2);
float cosPitchOver2 = (float)Math.Cos((double)pitchOver2);
float yawOver2 = yaw * 0.5f;
float sinYawOver2 = (float)Math.Sin((double)yawOver2);
float cosYawOver2 = (float)Math.Cos((double)yawOver2);
Quaternion result;
result.W = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2;
result.X = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2;
result.Y = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
result.Z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
bot.GetClient.Self.Movement.BodyRotation = result;
bot.GetClient.Self.Movement.SendUpdate();
return true;
}
else
{
return Failed("Unable to process rotation");
}
}
return false;
}
}
}

View file

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Movement
{
public class Teleport : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Mixed" }; } }
public override string[] ArgHints { get { return new[] { "SLurl<br/>Region<br/>X/Y/Z<br/>Region/X/Y/Z" }; } }
public override string Helpfile { get { return "Teleports the avatar<br/> " +
"SLurl: http://maps.secondlife.com/secondlife/Bombalai/216/51/33 <br/> " +
"Region: Bombalai [Defaults to 128,128,0]<br>" +
"X/Y/Z: Stays in the sim just changes location <br/> " +
"Region/X/Y/Z: Teleports to the given region and trys to goto X/Y/Z if no teleport hub (or has access)";
} }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if(base.CallFunction(args) == true)
{
if (args[0].Contains("http://maps.secondlife.com/secondlife/") == true)
{
bot.TeleportWithSLurl(args[0]);
return true;
}
else
{
float posX = 128;
float posY = 128;
float posZ = 0;
string regionName = bot.GetClient.Network.CurrentSim.Name;
bool ok = true;
int offset = 0;
string[] tp_args = args[0].Split('/');
if ((tp_args.Length == 4) || (tp_args.Length == 1))
{
regionName = tp_args[0];
offset = 1;
}
if (tp_args.Length >= 3)
{
float.TryParse(tp_args[offset + 0], out posX);
float.TryParse(tp_args[offset + 1], out posY);
float.TryParse(tp_args[offset + 2], out posZ);
}
else if (tp_args.Length == 2) ok = false;
if (ok == true)
{
bot.SetTeleported();
bool status = bot.GetClient.Self.Teleport(regionName, new Vector3(posX, posY, posZ));
bot.ResetAnimations();
return status;
}
else
{
return Failed("Required args [Region or x/y/z or region/x/y/z not sent]");
}
}
}
return false;
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenMetaverse;
namespace BSB.Commands.Notecard
{
class NotecardAdd : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Text","Text" }; } }
public override string[] ArgHints { get { return new[] { "Collection","Content" }; } }
public override string Helpfile { get { return "Adds [ARG 2] to the Collection [ARG 1]<br/> Also creates the collection if it does not exist"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
bot.NotecardAddContent(args[0], args[1]);
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenMetaverse;
namespace BSB.Commands.Notecard
{
class NotecardClear : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Text" }; } }
public override string[] ArgHints { get { return new[] { "Collection" }; } }
public override string Helpfile { get { return "Clears Collection [ARG 1]"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
bot.ClearNotecardStorage(args[0]);
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Text;
using BetterSecondBotShared.Static;
using OpenMetaverse;
namespace BSB.Commands.Notecard
{
class NotecardSend : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Avatar","Text","Text" }; } }
public override string[] ArgHints { get { return new[] { "Avatar [UUID or Firstname Lastname]", "Collection","Notecard name" }; } }
public override string Helpfile { get { return "Creats and send a notecard to [ARG 1]<br/>" +
" using the collection [ARG 2] as the content <br/>" +
" Optional: You can name the notecard using [ARG 3]"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
// target UUID,notecard storage id,["notecard title" <unixtime> or defaults to: New notecard for <UUID> <unixtime>]
if (UUID.TryParse(args[0], out UUID target) == true)
{
string content = bot.GetNotecardContent(args[1]);
if (content != null)
{
bot.ClearNotecardStorage(args[1]);
string notecard_name = "New notecard for " + args[0] + " " + helpers.UnixTimeNow().ToString() + "";
if (args.Length == 3) notecard_name = args[2] + " " + helpers.UnixTimeNow().ToString() + "";
return bot.SendNotecard(notecard_name, content, target);
}
else
{
return Failed("No content in notecard storage " + args[1]);
}
}
else
{
return Failed("Arg 0 not a UUID");
}
}
return false;
}
}
}

View file

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Text;
using BetterSecondBotShared.logs;
using BetterSecondBotShared.Static;
using OpenMetaverse;
namespace BSB.Commands.CMD_Parcel
{
public class GetParcelFlags : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Mixed","String" }; } }
public override string[] ArgHints { get { return new[] { "Smart reply [Channel|IM uuid|http url]", "[Repeatable] flag name" }; } }
public override int MinArgs { get { return 1; } }
public override string Helpfile { get { return "Returns the value of the parcel flags (At the parcel on the bot is currently on)<br/>" +
"requested on [ARG 2+] via [ARG 1] smart reply target<br/>" +
"If you request multiple Flags they are split with CSV<br/>You can also get all the flags skipping [ARG 2]<br/><br/>" +
"" + helpers.create_dirty_table(parcel_static.get_flag_names()) + "<br/><br/>Example: getparcelflag|||0~#~ForSale<br/>Example: getparcelflag|||12"; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
Dictionary<string, ParcelFlags> flags = parcel_static.get_flags_list();
List<string> get_flags = new List<string>();
List<string> otherargs = new List<string>(args);
string target = otherargs[0];
otherargs.RemoveAt(0);
if (otherargs.Count == 0)
{
otherargs.Add("ALL");
}
if (otherargs[0] == "ALL")
{
foreach (string A in flags.Keys)
{
get_flags.Add(A);
}
}
else
{
foreach (string a in otherargs)
{
if (flags.ContainsKey(a) == true)
{
get_flags.Add(a);
}
else
{
ConsoleLog.Warn("Flag: " + a + " is unknown");
}
}
}
if (get_flags.Count > 0)
{
int localid = bot.GetClient.Parcels.GetParcelLocalID(bot.GetClient.Network.CurrentSim, bot.GetClient.Self.SimPosition);
Parcel p = bot.GetClient.Network.CurrentSim.Parcels[localid];
StringBuilder reply = new StringBuilder();
string addon = "";
foreach (string cfg in get_flags)
{
if (flags.ContainsKey(cfg) == true)
{
reply.Append(addon);
reply.Append(cfg);
reply.Append("=");
reply.Append(p.Flags.HasFlag(flags[cfg]).ToString());
if(addon != ",") addon = ",";
}
}
return bot.GetCommandsInterface.SmartCommandReply(target, reply.ToString(), CommandName);
}
else
{
return Failed("No accepted flags");
}
}
return false;
}
}
}

View file

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Text;
using BetterSecondBotShared.logs;
using BetterSecondBotShared.Static;
using OpenMetaverse;
namespace BSB.Commands.CMD_Parcel
{
public class SetParcelFlag : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Text=True|False" }; } }
public override string[] ArgHints { get { return new[] { "[Repeatable] flag args" }; } }
public override int MinArgs { get { return 1; } }
public override string Helpfile { get { return "Sets the parcel flags of the parcel the bot is currently standing on<br/>" +
"Example:AllowFly=False~#~AllowDamage=True<br/>" + helpers.create_dirty_table(parcel_static.get_flag_names()) +"<br/>"; } }
private void Dosetflag(ParcelFlags F, Parcel p, bool set)
{
ConsoleLog.Warn("Setting flag: " + F.ToString() + " To " + set.ToString() + " at " + p.Name + "");
if (p.Flags.HasFlag(F))
{
p.Flags -= F;
}
if (set == true)
{
p.Flags |= F;
}
}
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
List<string> acceptablewords = new List<string>();
Dictionary<string, ParcelFlags> flags = parcel_static.get_flags_list();
acceptablewords.AddRange(new[] { "True", "False" });
Dictionary<string, bool> setflags = new Dictionary<string, bool>();
foreach (string a in args)
{
string[] parts = a.Split('=');
if (flags.ContainsKey(parts[0]) == true)
{
if (acceptablewords.Contains(parts[1]))
{
setflags.Add(parts[0], Convert.ToBoolean(parts[1]));
}
else
{
ConsoleLog.Crit("Unable to set flag " + parts[0] + " to : " + parts[1] + "");
}
}
else
{
ConsoleLog.Warn("Flag: " + parts[0] + " is unknown");
}
}
if (setflags.Count > 0)
{
int localid = bot.GetClient.Parcels.GetParcelLocalID(bot.GetClient.Network.CurrentSim, bot.GetClient.Self.SimPosition);
Parcel p = bot.GetClient.Network.CurrentSim.Parcels[localid];
if (p.OwnerID != bot.GetClient.Self.AgentID)
{
if (p.IsGroupOwned)
{
bot.GetClient.Groups.ActivateGroup(p.GroupID);
}
}
foreach (KeyValuePair<string, bool> cfg in setflags)
{
if (flags.ContainsKey(cfg.Key) == true)
{
Dosetflag(flags[cfg.Key], p, cfg.Value);
}
}
p.Update(bot.GetClient.Network.CurrentSim, false);
return true;
}
else
{
return Failed("No accepted flags");
}
}
return false;
}
}
}

View file

@ -0,0 +1,36 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.Commands.CMD_Parcel
{
public class ParcelEject : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Avatar", "True|False" }; } }
public override string[] ArgHints { get { return new[] { "Avatar [UUID or Firstname Lastname]", "Also Ban" }; } }
public override string Helpfile { get { return "Ejects an avatar [ARG 1] from a parcel<br/>You can also ban them at the same time if you wish"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID target) == true)
{
bool banuser = false;
if (args.Length == 2)
{
if (args[1] == "True") banuser = true;
}
bot.GetClient.Parcels.EjectUser(target, banuser);
return true;
}
else
{
return Failed("Arg 0 requires UUID");
}
}
return false;
}
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Self
{
public class PayAvatar : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Avatar", "Number" }; } }
public override string[] ArgHints { get { return new[] { "Avatar [UUID or Firstname Lastname]", "Amount to pay"}; } }
public override string Helpfile { get { return "Makes the bot pay a avatar"; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID avataruuid) == true)
{
if (int.TryParse(args[1], out int amount) == true)
{
if (amount > 0)
{
bot.GetClient.Self.GiveAvatarMoney(avataruuid, amount);
return true;
}
}
return Failed("Invaild amount");
}
return Failed("Invaild avatar UUID");
}
return false;
}
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Self
{
public class PayObject : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "UUID","Number","Text" }; } }
public override string[] ArgHints { get { return new[] { "Object UUID","Amount to pay","Object Name" }; } }
public override string Helpfile { get { return "Makes the bot pay a object"; } }
public override int MinArgs { get { return 3; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID objectuuid) == true)
{
if (int.TryParse(args[1], out int amount) == true)
{
if (amount > 0)
{
if (args[2].Length > 0)
{
bot.GetClient.Self.GiveObjectMoney(objectuuid, amount, args[2]);
return true;
}
return Failed("Invaild object name");
}
}
return Failed("Invaild amount");
}
return Failed("Invaild object UUID");
}
return false;
}
}
}

View file

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Self
{
public class AddToAllowAnimations : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Text" }; } }
public override string[] ArgHints { get { return new[] { "Avatar name"}; } }
public override string Helpfile { get { return "Toggles if animation requests from this avatar (used for remote poseballs) are accepted<br/>Case sensitive also requires Lastname"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if(base.CallFunction(args) == true)
{
bot.ToggleAutoAcceptAnimations(args[0]);
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Self
{
class BotVersion : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Mixed" }; } }
public override string[] ArgHints { get { return new[] { "Smart reply target: Channel,UUID,HTTP" }; } }
public override string Helpfile { get { return "Returns the bots build version"; } }
public override int MinArgs { get { return 0; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
string target = "0";
if (args.Length > 0)
{
target = args[0];
}
return bot.GetCommandsInterface.SmartCommandReply(target, bot.MyVersion, CommandName);
}
return false;
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Self
{
public class Logout : CoreCommand
{
public override string Helpfile { get { return "Makes the bot kill itself you monster"; } }
public override int MinArgs { get { return 0; } }
public override bool CallFunction(string[] args)
{
bot.GetClient.Self.Chat("I dont want to go :( ", 0, ChatType.Normal);
bot.KillMePlease();
return true;
}
}
}

View file

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Self
{
public class PointAt : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Avatar" }; } }
public override string[] ArgHints { get { return new[] { "Avatar [UUID or Firstname Lastname]" }; } }
public override string Helpfile { get { return "Makes the bot turn to face [ARG 1] and point at them (if found)"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
if (UUID.TryParse(args[0], out UUID testavatar) == true)
{
if (bot.GetClient.Network.CurrentSim.AvatarPositions.ContainsKey(testavatar) == true)
{
bot.ResetAnimations();
bot.GetClient.Self.Stand();
bot.GetClient.Self.Movement.TurnToward(bot.GetClient.Network.CurrentSim.AvatarPositions[testavatar]);
bot.GetClient.Self.Movement.SendUpdate();
bot.GetClient.Self.AnimationStart(Animations.POINT_YOU, true);
bot.GetClient.Self.PointAtEffect(bot.GetClient.Self.AgentID, testavatar, new Vector3d(0, 0, 0), PointAtType.Select, UUID.Random());
bot.GetClient.Self.BeamEffect(bot.GetClient.Self.AgentID, testavatar, new Vector3d(0, 0, 2), new Color4(255, 255, 255, 1), (float)3.0, UUID.Random());
return true;
}
else
{
return Failed("Cant find UUID in sim");
}
}
else
{
return Failed("UUID is not vaild");
}
}
return false;
}
}
}

43
Core/Commands/Self/sit.cs Normal file
View file

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Self
{
public class BotSit : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "Mixed" }; } }
public override string[] ArgHints { get { return new[] { "Text \"ground\" or a object UUID" }; } }
public override string Helpfile { get { return "Makes the bot sit on the ground or on a object if it can see it"; } }
public override int MinArgs { get { return 1; } }
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
bot.ResetAnimations();
bot.GetClient.Self.Stand();
if (args[0] == "ground")
{
bot.GetClient.Self.SitOnGround();
return true;
}
else
{
if (UUID.TryParse(args[0], out UUID arg_is_uuid) == true)
{
bot.GetClient.Self.RequestSit(arg_is_uuid, Vector3.Zero);
return true;
}
else
{
return Failed("UUID is not vaild");
}
}
}
return false;
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
namespace BSB.Commands.Self
{
public class Stand : CoreCommand
{
public override string Helpfile { get { return "Makes the bot stand up if sitting (also resets animations)"; } }
public override int MinArgs { get { return 0; } }
public override bool CallFunction(string[] args)
{
bot.GetClient.Self.Stand();
bot.ResetAnimations();
return true;
}
}
}

View file

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
using BetterSecondBotShared.Static;
using RestSharp;
using Newtonsoft.Json;
namespace BSB.Commands.StreamAdmin
{
public class FetchNextNotecard : CoreCommand
{
public override string[] ArgTypes { get { return new[] { "TEXT","TEXT" }; } }
public override string[] ArgHints { get { return new[] { "Server endpoint","Server endpoint code" }; } }
public override string Helpfile { get { return "[StreamAdmin]\nThis command is part of the streamadmin commands set\n No end user support will be given."; } }
public override int MinArgs { get { return 2; } }
public override bool CallFunction(string[] args)
{
if(base.CallFunction(args) == true)
{
string attempt_endpoint = "" + args[0] + "endpoint.php";
string token = helpers.GetSHA1(helpers.UnixTimeNow().ToString()+"notecardnext" + args[1]);
var client = new RestClient("" + args[0] + "endpoint.php");
var request = new RestRequest("notecard/next", Method.POST);
request.AddParameter("token", token);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse endpoint_checks = client.Post(request);
if (endpoint_checks.StatusCode == System.Net.HttpStatusCode.OK)
{
notecardendpoint server_reply = new notecardendpoint();
bool server_reply_ok = true;
try
{
server_reply = JsonConvert.DeserializeObject<notecardendpoint>(endpoint_checks.Content);
}
catch
{
server_reply_ok = false;
}
if (server_reply_ok == true)
{
if (server_reply.status == true)
{
if (server_reply.NotecardTitle.Length > 3)
{
return bot.SendNotecard(server_reply.NotecardTitle, server_reply.NotecardContent, (UUID)server_reply.AvatarUUID);
}
else Failed("Notecard title is to short");
}
return Failed(server_reply.message);
}
else
{
return Failed("FetchNextNotecard - HTTP error: " + endpoint_checks.StatusCode.ToString() + " " + endpoint_checks.Content + "");
}
}
else
{
return Failed("FetchNextNotecard - HTTP error DataObject broken Status:"+ endpoint_checks.StatusCode.ToString()+" @ "+ attempt_endpoint+"");
}
}
return false;
}
}
public class notecardendpoint : endpoint
{
public string AvatarUUID { get; set; }
public string NotecardTitle { get; set; }
public string NotecardContent { get; set; }
}
public class endpoint
{
public bool status { get; set; }
public string message { get; set; }
}
}

287
Core/Commands/commandlib.cs Normal file
View file

@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using OpenMetaverse;
using System.Net.Http;
using BSB.bottypes;
using BetterSecondBotShared.API;
using BetterSecondBotShared.Static;
using BetterSecondBotShared.logs;
namespace BSB.Commands
{
public class pending_avatar_lookup
{
public pending_avatar_lookup(CoreCommandsInterface set_master,CoreCommand set_cmd,string[] set_args)
{
Master = set_master;
Command = set_cmd;
args = set_args.ToList();
}
public bool Callnow()
{
return Command.CallFunction(args.ToArray());
}
public bool needs_lookup()
{
List<int> need_lookup_on_indexs = new List<int>();
int loop = 0;
foreach (string atype in Command.ArgTypes)
{
if (loop < args.Count)
{
if (atype == "Avatar")
{
need_lookup_on_indexs.Add(loop);
}
else if(atype == "Smart")
{
if(args[loop].StartsWith("http") == false)
{
// not a url request
if (int.TryParse(args[loop],out _) == false)
{
// Not a channel
need_lookup_on_indexs.Add(loop);
}
}
}
loop++;
}
else
{
break;
}
}
bool has_required_lookups = false;
foreach (int A in need_lookup_on_indexs)
{
string value = args[A];
if(UUID.TryParse(args[A],out _) == false)
{
// not a uuid
string get_or_request_uuid = Master.GetBot.FindAvatarName2Key(args[A]);
if(get_or_request_uuid == "lookup")
{
has_required_lookups = true;
}
else
{
args[A] = get_or_request_uuid;
}
}
}
return has_required_lookups;
}
protected CoreCommand Command;
protected List<string> args;
protected CoreCommandsInterface Master;
}
public class CoreCommandsInterface : API_supported_interface
{
protected CommandsBot bot;
public CommandsBot GetBot { get { return bot; } }
protected long giveup_waiting_for_lookup = 240;
protected int next_lookup_id = 0;
protected Dictionary<int, pending_avatar_lookup> commands_pending_avatar_lookups = new Dictionary<int, pending_avatar_lookup>();
protected Dictionary<int, long> commands_pending_avatar_lookups_ages = new Dictionary<int, long>();
public void StatusTick()
{
List<int> purge_indexs = new List<int>();
List<int> call_indexs = new List<int>();
long now = helpers.UnixTimeNow();
foreach (KeyValuePair<int,long> pair in commands_pending_avatar_lookups_ages)
{
if (commands_pending_avatar_lookups[pair.Key].needs_lookup() == true)
{
long dif = now - pair.Value;
if (dif > giveup_waiting_for_lookup)
{
purge_indexs.Add(pair.Key);
}
}
else
{
call_indexs.Add(pair.Key);
}
}
foreach(int a in purge_indexs)
{
commands_pending_avatar_lookups_ages.Remove(a);
commands_pending_avatar_lookups.Remove(a);
}
foreach(int a in call_indexs)
{
commands_pending_avatar_lookups[a].Callnow();
commands_pending_avatar_lookups_ages.Remove(a);
commands_pending_avatar_lookups.Remove(a);
}
}
public CoreCommandsInterface(CommandsBot linktobot)
{
bot = linktobot;
API_type = typeof(CoreCommand);
LoadCommandsList();
}
protected HttpClient HTTPclient = new HttpClient();
public bool SmartCommandReply(string target, string output)
{
return SmartCommandReply(target, output, "", false);
}
public bool SmartCommandReply(string target, string output, string command)
{
return SmartCommandReply(target, output, command, false);
}
public bool SmartCommandReply(string target,string output,string command,bool vaildate_only)
{
string mode = "CHAT";
UUID target_avatar = UUID.Zero;
int target_channel = 0;
if (target.StartsWith("http://"))
{
mode = "HTTP";
}
else if (UUID.TryParse(target, out target_avatar) == true)
{
mode = "IM";
}
else
{
int.TryParse(target, out target_channel);
}
if (vaildate_only == false)
{
if (mode == "CHAT")
{
bot.GetClient.Self.Chat(output, target_channel, ChatType.Normal);
}
else if (mode == "IM")
{
bot.GetClient.Self.InstantMessage(target_avatar, output);
}
else if (mode == "HTTP")
{
Dictionary<string, string> values = new Dictionary<string, string>
{
{ "reply", output },
{ "unixtime", helpers.UnixTimeNow().ToString() }
};
if (command != "") values.Add("command", command);
var content = new FormUrlEncodedContent(values);
try
{
HTTPclient.PostAsync(target, content);
}
catch (Exception e)
{
ConsoleLog.Crit("[SmartReply] HTTP failed: " + e.Message + "");
}
}
else
{
return false;
}
return true;
}
else
{
if ((mode == "CHAT") || (mode == "IM") || (mode == "HTTP"))
{
return true;
}
return false;
}
}
protected List<string> suppress_warnings = new List<string>();
public bool Call(string command)
{
return Call(command, "");
}
public bool Call(string command,string arg)
{
command = command.ToLowerInvariant();
CoreCommand cmd = (CoreCommand)GetCommand(command);
if(cmd != null)
{
cmd.Setup(bot);
if ((cmd.ArgTypes.Contains("Avatar") == true) || (cmd.ArgTypes.Contains("Smart") == true))
{
string[] args = arg.Split(new string[] { "~#~" }, StringSplitOptions.None);
if (cmd.MinArgs <= args.Length)
{
pending_avatar_lookup pending = new pending_avatar_lookup(this, cmd, args);
if (pending.needs_lookup() == true)
{
commands_pending_avatar_lookups.Add(next_lookup_id, pending);
commands_pending_avatar_lookups_ages.Add(next_lookup_id, helpers.UnixTimeNow());
next_lookup_id++;
if (next_lookup_id == 13000)
{
next_lookup_id = 0;
}
return true;
}
else
{
return pending.Callnow();
}
}
else
{
return false;
}
}
else
{
return cmd.CallFunction(arg.Split(new string[] { "~#~" }, StringSplitOptions.None));
}
}
else
{
if (suppress_warnings.Contains(command) == false)
{
suppress_warnings.Add(command);
ConsoleLog.Debug("[CMD/Failed] " + command + " : I have no fucking idea what you are talking about");
}
return false;
}
}
}
public abstract class CoreCommand : API_interface
{
protected CommandsBot bot = null;
public void Setup(CommandsBot setBot)
{
bot = setBot;
}
public virtual void Callback(string[] args, EventArgs e)
{
}
public virtual bool CallFunction(string[] args)
{
if (args.Length < MinArgs)
{
return Failed("Incorrect number of args");
}
else
{
return true;
}
}
public bool Failed(string why_failed)
{
ConsoleLog.Warn("[CMD/Failed] " + CommandName + ": " + why_failed);
return false;
}
}
}

View file

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Blacklist
{
public class GetBlacklist : RLV_command_1arg
{
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
int channel;
string filter = "";
if(args.Length == 2)
{
if(int.TryParse(args[1], out channel) == true)
{
filter = args[0];
}
}
else
{
int.TryParse(args[0], out channel);
}
if (channel >= 0)
{
string sendim = "";
string recvim = "";
if (filter == "")
{
sendim = String.Join('|', bot.Getblacklist_sendim);
recvim = String.Join('|', bot.Getblacklist_recvim);
}
else
{
string addon = "";
foreach (string a in bot.Getblacklist_sendim)
{
if (a.Contains(filter) == true)
{
sendim = "" + sendim + "" + addon + "" + a + "";
addon = "|";
}
}
addon = "";
foreach (string a in bot.Getblacklist_recvim)
{
if (a.Contains(filter) == true)
{
recvim = "" + recvim + "" + addon + "" + a + "";
addon = "|";
}
}
}
if (sendim == "") sendim = "#";
if (recvim == "") recvim = "#";
bot.GetClient.Self.Chat("" + sendim + "," + recvim + "", channel, OpenMetaverse.ChatType.Normal);
return true;
}
else
{
return Failed("Invaild channel given");
}
}
return false;
}
}
}

View file

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Blacklist
{
public class VersionNumBL : RLV_command_1arg
{
public override bool CallFunction(string[] args)
{
if (base.CallFunction(args) == true)
{
_ = int.TryParse(args[0], out int channel);
if (channel >= 0)
{
string sendim = String.Join('|', bot.Getblacklist_sendim);
string recvim = String.Join('|', bot.Getblacklist_recvim);
if (sendim == "") { sendim = "#"; }
if (recvim == "") { recvim = "#"; }
bot.GetClient.Self.Chat("209000," + sendim + "," + recvim + "", channel, OpenMetaverse.ChatType.Normal);
return true;
}
else
{
return Failed("Invaild channel given");
}
}
return false;
}
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamAvDist : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamDistMax : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamDistMin : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamDrawAlphaMax : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamDrawAlphaMin : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamDrawColor : RLV_UUID_flag_4arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamDrawMax : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamDrawMin : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamTextures : RLV_UUID_flag_optional_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamUnlock : RLV_UUID_flag_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamZoomMax : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class CamZoomMin : RLV_UUID_flag_arg_yn
{
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class GetCam_AvDistMax : RLV_UUID_flag_get
{
public override string LookupFlag { get { return "camavdistmax"; } }
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class GetCam_AvDistMin : RLV_UUID_flag_get
{
public override string LookupFlag { get { return "camavdistmin"; } }
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class GetCam_Fov : RLV_UUID_flag_get
{
public override string LookupFlag { get { return "camfov"; } }
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class GetCam_FovMax : RLV_UUID_flag_get
{
public override string LookupFlag { get { return "camfovmax"; } }
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class GetCam_FovMin : RLV_UUID_flag_get
{
public override string LookupFlag { get { return "camfovmin"; } }
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class GetCam_ZoomMin : RLV_UUID_flag_get
{
public override string LookupFlag { get { return "camzoommin"; } }
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class SetCam_AvDistMax : RLV_UUID_flag_arg_yn
{
public override string SetFlagName { get { return "camavdistmax"; } }
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class SetCam_AvDistMin : RLV_UUID_flag_arg_yn
{
public override string SetFlagName { get { return "camavdistmin"; } }
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BSB.RLV.Camera
{
public class SetCam_Fov : RLV_UUID_flag_arg_yn
{
public override string SetFlagName { get { return "camfov"; } }
}
}

Some files were not shown because too many files have changed in this diff Show more