From 4ce4c1b7695672f78d9fa49630b47ec504fef0d5 Mon Sep 17 00:00:00 2001 From: Madpeter Date: Thu, 23 Apr 2020 18:19:18 +0100 Subject: [PATCH] Port from bitbucket --- .dockerignore | 24 + .gitignore | 23 + BSBshared/API/API_interface.cs | 154 +++++ BSBshared/BSBshared.csproj | 24 + BSBshared/BotTypes/BasicBot.cs | 188 ++++++ BSBshared/IO/SimpleIO.cs | 84 +++ BSBshared/Json/JsonConfig.cs | 30 + BSBshared/Json/JsonConfigApi.cs | 315 +++++++++ BSBshared/Staic/ConsoleLog.cs | 83 +++ BSBshared/Staic/Helpers.cs | 173 +++++ BetterSecondBot.sln | 67 ++ BetterSecondBot.sln.DotSettings | 4 + CLI/BetterSecondBot.csproj | 37 ++ CLI/Cli.cs | 80 +++ CLI/CliDocker.cs | 30 + CLI/CliExitOnLogout.cs | 49 ++ CLI/CliHardware.cs | 93 +++ CLI/Dockerfile-ForDebug | 50 ++ CLI/HttpCnC.cs | 39 ++ CLI/HttpServer/control/Actions/logout.cs | 32 + CLI/HttpServer/control/Interface/cmd_lib.cs | 40 ++ CLI/HttpServer/control/Service/LoginBot.cs | 28 + CLI/HttpServer/control/Service/ShutdownCNC.cs | 22 + CLI/HttpServer/http_commands.cs | 99 +++ CLI/HttpServer/http_server.cs | 200 ++++++ CLI/HttpServer/view/Basic/status.cs | 32 + CLI/HttpServer/view/Inventory/getfolders.cs | 30 + .../PublishProfiles/Linux-x64.pubxml | 18 + .../PublishProfiles/Windows-64bit.pubxml | 18 + CLI/Properties/launchSettings.json | 10 + CLI/WikiMake/wiki_footer.txt | 11 + CLI/WikiMake/wiki_header.txt | 30 + CLI/WikiMake/wikimake.cs | 340 ++++++++++ Core/BSB.csproj | 25 + Core/Commands/Chat/im.cs | 32 + Core/Commands/Chat/say.cs | 32 + Core/Commands/Dialogs/DialogRelay.cs | 47 ++ Core/Commands/Dialogs/DialogResponce.cs | 28 + Core/Commands/Dialogs/DialogTrack.cs | 27 + Core/Commands/Group/GroupInvite.cs | 111 ++++ Core/Commands/Group/GroupNotice.cs | 50 ++ Core/Commands/Group/getGroupList.cs | 26 + Core/Commands/Group/getGroupRoles.cs | 61 ++ Core/Commands/Group/group.cs | 66 ++ Core/Commands/Group/groupchat.cs | 60 ++ Core/Commands/Group/groupchatAnyUnread.cs | 15 + Core/Commands/Group/groupchatClearAll.cs | 20 + .../Commands/Group/groupchatGroupHasUnread.cs | 18 + Core/Commands/Group/groupchatHistory.cs | 17 + .../Group/groupchatListAllUnreadGroups.cs | 25 + Core/Commands/Home/GoHome.cs | 23 + Core/Commands/Home/resetAtHome.cs | 24 + .../Inventory/InventoryPurgeNotecards.cs | 60 ++ Core/Commands/Inventory/attach.cs | 84 +++ Core/Commands/Inventory/inventoryContents.cs | 55 ++ Core/Commands/Inventory/inventoryFolders.cs | 42 ++ Core/Commands/Inventory/outfit.cs | 79 +++ Core/Commands/Inventory/sendFolder.cs | 47 ++ Core/Commands/Inventory/sendItem.cs | 52 ++ Core/Commands/Movement/rotateTo.cs | 51 ++ Core/Commands/Movement/teleport.cs | 68 ++ Core/Commands/Notecard/notecardAdd.cs | 25 + Core/Commands/Notecard/notecardClear.cs | 25 + Core/Commands/Notecard/notecardSend.cs | 46 ++ Core/Commands/Parcel/GetParcelFlags.cs | 82 +++ Core/Commands/Parcel/SetParcelFlag.cs | 89 +++ Core/Commands/Parcel/eject.cs | 36 ++ Core/Commands/Self/PayAvatar.cs | 37 ++ Core/Commands/Self/PayObject.cs | 41 ++ Core/Commands/Self/addToAnimations.cs | 26 + Core/Commands/Self/botVersion.cs | 29 + Core/Commands/Self/logout.cs | 21 + Core/Commands/Self/pointAt.cs | 46 ++ Core/Commands/Self/sit.cs | 43 ++ Core/Commands/Self/stand.cs | 21 + .../Commands/StreamAdmin/FetchNextNotecard.cs | 80 +++ Core/Commands/commandlib.cs | 287 +++++++++ Core/RLV/Blacklist/getblacklist.cs | 69 ++ Core/RLV/Blacklist/versionnumbl.cs | 31 + Core/RLV/Camera/camavdist.cs | 11 + Core/RLV/Camera/camdistmax.cs | 10 + Core/RLV/Camera/camdistmin.cs | 10 + Core/RLV/Camera/camdrawalphamax.cs | 10 + Core/RLV/Camera/camdrawalphamin.cs | 10 + Core/RLV/Camera/camdrawcolor.cs | 10 + Core/RLV/Camera/camdrawmax.cs | 10 + Core/RLV/Camera/camdrawmin.cs | 10 + Core/RLV/Camera/camtextures.cs | 10 + Core/RLV/Camera/camunlock.cs | 10 + Core/RLV/Camera/camzoommax.cs | 10 + Core/RLV/Camera/camzoommin.cs | 10 + Core/RLV/Camera/getcam_avdistmax.cs | 11 + Core/RLV/Camera/getcam_avdistmin.cs | 12 + Core/RLV/Camera/getcam_fov.cs | 12 + Core/RLV/Camera/getcam_fovmax.cs | 12 + Core/RLV/Camera/getcam_fovmin.cs | 11 + Core/RLV/Camera/getcam_zoommin.cs | 12 + Core/RLV/Camera/setcam_avdistmax.cs | 11 + Core/RLV/Camera/setcam_avdistmin.cs | 12 + Core/RLV/Camera/setcam_fov.cs | 11 + Core/RLV/Camera/setcam_fovmax.cs | 11 + Core/RLV/Camera/setcam_fovmin.cs | 11 + Core/RLV/Camera/setcam_textures.cs | 11 + Core/RLV/Camera/setcam_unlock.cs | 12 + Core/RLV/Chat/chatnormal.cs | 10 + Core/RLV/Chat/chatshout.cs | 10 + Core/RLV/Chat/chatwhisper.cs | 10 + Core/RLV/Chat/recvchat.cs | 37 ++ Core/RLV/Chat/recvchat_sec.cs | 10 + Core/RLV/Chat/recvchatfrom.cs | 10 + Core/RLV/Chat/redirchat.cs | 10 + Core/RLV/Chat/sendchat.cs | 10 + Core/RLV/Chat/sendgesture.cs | 10 + Core/RLV/Emotes/emote.cs | 10 + Core/RLV/Emotes/recvemote.cs | 38 ++ Core/RLV/Emotes/recvemote_sec.cs | 11 + Core/RLV/Emotes/rediremote.cs | 11 + Core/RLV/InstantMessages/sendim.cs | 37 ++ Core/RLV/InstantMessages/sendim_sec.cs | 18 + Core/RLV/Miscellaneous/clear.cs | 26 + Core/RLV/Miscellaneous/getstatus.cs | 51 ++ Core/RLV/Miscellaneous/getstatusall.cs | 58 ++ Core/RLV/Miscellaneous/notify.cs | 44 ++ Core/RLV/Miscellaneous/permissive.cs | 28 + Core/RLV/Movement/adjustheight.cs | 45 ++ Core/RLV/Movement/alwaysrun.cs | 10 + Core/RLV/Movement/fly.cs | 10 + Core/RLV/Movement/setrot.cs | 23 + Core/RLV/Movement/temprun.cs | 11 + Core/RLV/PrivateChannels/sendchannel.cs | 10 + .../RLV/PrivateChannels/sendchannel_except.cs | 11 + Core/RLV/PrivateChannels/sendchannel_sec.cs | 11 + Core/RLV/RLV_commandlib.cs | 218 +++++++ Core/RLV/Sitting/getsitid.cs | 31 + Core/RLV/Sitting/sit.cs | 29 + Core/RLV/Sitting/unsit.cs | 22 + Core/RLV/Version/version.cs | 20 + Core/RLV/Version/versionnew.cs | 20 + Core/RLV/Version/versionnum.cs | 20 + Core/Static/Inventory.cs | 96 +++ Core/Static/parcel.cs | 39 ++ Core/bottypes/AVstorageBot.cs | 236 +++++++ Core/bottypes/AnimationsBot.cs | 44 ++ Core/bottypes/AwaitReplyEventBot.cs | 129 ++++ Core/bottypes/CommandsBot.cs | 189 ++++++ Core/bottypes/DialogsBot.cs | 129 ++++ Core/bottypes/DiscordBot.cs | 599 ++++++++++++++++++ Core/bottypes/EventsBot.cs | 294 +++++++++ Core/bottypes/MessageSwitcherBot.cs | 149 +++++ Core/bottypes/RLVbot.cs | 288 +++++++++ Core/bottypes/SecondBot.cs | 14 + Core/bottypes/VirtualsBot.cs | 85 +++ Core/bottypes/athome.cs | 367 +++++++++++ Core/bottypes/chatrelay.cs | 88 +++ Dockerfile | 50 ++ README.md | 37 ++ 156 files changed, 8779 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 BSBshared/API/API_interface.cs create mode 100644 BSBshared/BSBshared.csproj create mode 100644 BSBshared/BotTypes/BasicBot.cs create mode 100644 BSBshared/IO/SimpleIO.cs create mode 100644 BSBshared/Json/JsonConfig.cs create mode 100644 BSBshared/Json/JsonConfigApi.cs create mode 100644 BSBshared/Staic/ConsoleLog.cs create mode 100644 BSBshared/Staic/Helpers.cs create mode 100644 BetterSecondBot.sln create mode 100644 BetterSecondBot.sln.DotSettings create mode 100644 CLI/BetterSecondBot.csproj create mode 100644 CLI/Cli.cs create mode 100644 CLI/CliDocker.cs create mode 100644 CLI/CliExitOnLogout.cs create mode 100644 CLI/CliHardware.cs create mode 100644 CLI/Dockerfile-ForDebug create mode 100644 CLI/HttpCnC.cs create mode 100644 CLI/HttpServer/control/Actions/logout.cs create mode 100644 CLI/HttpServer/control/Interface/cmd_lib.cs create mode 100644 CLI/HttpServer/control/Service/LoginBot.cs create mode 100644 CLI/HttpServer/control/Service/ShutdownCNC.cs create mode 100644 CLI/HttpServer/http_commands.cs create mode 100644 CLI/HttpServer/http_server.cs create mode 100644 CLI/HttpServer/view/Basic/status.cs create mode 100644 CLI/HttpServer/view/Inventory/getfolders.cs create mode 100644 CLI/Properties/PublishProfiles/Linux-x64.pubxml create mode 100644 CLI/Properties/PublishProfiles/Windows-64bit.pubxml create mode 100644 CLI/Properties/launchSettings.json create mode 100644 CLI/WikiMake/wiki_footer.txt create mode 100644 CLI/WikiMake/wiki_header.txt create mode 100644 CLI/WikiMake/wikimake.cs create mode 100644 Core/BSB.csproj create mode 100644 Core/Commands/Chat/im.cs create mode 100644 Core/Commands/Chat/say.cs create mode 100644 Core/Commands/Dialogs/DialogRelay.cs create mode 100644 Core/Commands/Dialogs/DialogResponce.cs create mode 100644 Core/Commands/Dialogs/DialogTrack.cs create mode 100644 Core/Commands/Group/GroupInvite.cs create mode 100644 Core/Commands/Group/GroupNotice.cs create mode 100644 Core/Commands/Group/getGroupList.cs create mode 100644 Core/Commands/Group/getGroupRoles.cs create mode 100644 Core/Commands/Group/group.cs create mode 100644 Core/Commands/Group/groupchat.cs create mode 100644 Core/Commands/Group/groupchatAnyUnread.cs create mode 100644 Core/Commands/Group/groupchatClearAll.cs create mode 100644 Core/Commands/Group/groupchatGroupHasUnread.cs create mode 100644 Core/Commands/Group/groupchatHistory.cs create mode 100644 Core/Commands/Group/groupchatListAllUnreadGroups.cs create mode 100644 Core/Commands/Home/GoHome.cs create mode 100644 Core/Commands/Home/resetAtHome.cs create mode 100644 Core/Commands/Inventory/InventoryPurgeNotecards.cs create mode 100644 Core/Commands/Inventory/attach.cs create mode 100644 Core/Commands/Inventory/inventoryContents.cs create mode 100644 Core/Commands/Inventory/inventoryFolders.cs create mode 100644 Core/Commands/Inventory/outfit.cs create mode 100644 Core/Commands/Inventory/sendFolder.cs create mode 100644 Core/Commands/Inventory/sendItem.cs create mode 100644 Core/Commands/Movement/rotateTo.cs create mode 100644 Core/Commands/Movement/teleport.cs create mode 100644 Core/Commands/Notecard/notecardAdd.cs create mode 100644 Core/Commands/Notecard/notecardClear.cs create mode 100644 Core/Commands/Notecard/notecardSend.cs create mode 100644 Core/Commands/Parcel/GetParcelFlags.cs create mode 100644 Core/Commands/Parcel/SetParcelFlag.cs create mode 100644 Core/Commands/Parcel/eject.cs create mode 100644 Core/Commands/Self/PayAvatar.cs create mode 100644 Core/Commands/Self/PayObject.cs create mode 100644 Core/Commands/Self/addToAnimations.cs create mode 100644 Core/Commands/Self/botVersion.cs create mode 100644 Core/Commands/Self/logout.cs create mode 100644 Core/Commands/Self/pointAt.cs create mode 100644 Core/Commands/Self/sit.cs create mode 100644 Core/Commands/Self/stand.cs create mode 100644 Core/Commands/StreamAdmin/FetchNextNotecard.cs create mode 100644 Core/Commands/commandlib.cs create mode 100644 Core/RLV/Blacklist/getblacklist.cs create mode 100644 Core/RLV/Blacklist/versionnumbl.cs create mode 100644 Core/RLV/Camera/camavdist.cs create mode 100644 Core/RLV/Camera/camdistmax.cs create mode 100644 Core/RLV/Camera/camdistmin.cs create mode 100644 Core/RLV/Camera/camdrawalphamax.cs create mode 100644 Core/RLV/Camera/camdrawalphamin.cs create mode 100644 Core/RLV/Camera/camdrawcolor.cs create mode 100644 Core/RLV/Camera/camdrawmax.cs create mode 100644 Core/RLV/Camera/camdrawmin.cs create mode 100644 Core/RLV/Camera/camtextures.cs create mode 100644 Core/RLV/Camera/camunlock.cs create mode 100644 Core/RLV/Camera/camzoommax.cs create mode 100644 Core/RLV/Camera/camzoommin.cs create mode 100644 Core/RLV/Camera/getcam_avdistmax.cs create mode 100644 Core/RLV/Camera/getcam_avdistmin.cs create mode 100644 Core/RLV/Camera/getcam_fov.cs create mode 100644 Core/RLV/Camera/getcam_fovmax.cs create mode 100644 Core/RLV/Camera/getcam_fovmin.cs create mode 100644 Core/RLV/Camera/getcam_zoommin.cs create mode 100644 Core/RLV/Camera/setcam_avdistmax.cs create mode 100644 Core/RLV/Camera/setcam_avdistmin.cs create mode 100644 Core/RLV/Camera/setcam_fov.cs create mode 100644 Core/RLV/Camera/setcam_fovmax.cs create mode 100644 Core/RLV/Camera/setcam_fovmin.cs create mode 100644 Core/RLV/Camera/setcam_textures.cs create mode 100644 Core/RLV/Camera/setcam_unlock.cs create mode 100644 Core/RLV/Chat/chatnormal.cs create mode 100644 Core/RLV/Chat/chatshout.cs create mode 100644 Core/RLV/Chat/chatwhisper.cs create mode 100644 Core/RLV/Chat/recvchat.cs create mode 100644 Core/RLV/Chat/recvchat_sec.cs create mode 100644 Core/RLV/Chat/recvchatfrom.cs create mode 100644 Core/RLV/Chat/redirchat.cs create mode 100644 Core/RLV/Chat/sendchat.cs create mode 100644 Core/RLV/Chat/sendgesture.cs create mode 100644 Core/RLV/Emotes/emote.cs create mode 100644 Core/RLV/Emotes/recvemote.cs create mode 100644 Core/RLV/Emotes/recvemote_sec.cs create mode 100644 Core/RLV/Emotes/rediremote.cs create mode 100644 Core/RLV/InstantMessages/sendim.cs create mode 100644 Core/RLV/InstantMessages/sendim_sec.cs create mode 100644 Core/RLV/Miscellaneous/clear.cs create mode 100644 Core/RLV/Miscellaneous/getstatus.cs create mode 100644 Core/RLV/Miscellaneous/getstatusall.cs create mode 100644 Core/RLV/Miscellaneous/notify.cs create mode 100644 Core/RLV/Miscellaneous/permissive.cs create mode 100644 Core/RLV/Movement/adjustheight.cs create mode 100644 Core/RLV/Movement/alwaysrun.cs create mode 100644 Core/RLV/Movement/fly.cs create mode 100644 Core/RLV/Movement/setrot.cs create mode 100644 Core/RLV/Movement/temprun.cs create mode 100644 Core/RLV/PrivateChannels/sendchannel.cs create mode 100644 Core/RLV/PrivateChannels/sendchannel_except.cs create mode 100644 Core/RLV/PrivateChannels/sendchannel_sec.cs create mode 100644 Core/RLV/RLV_commandlib.cs create mode 100644 Core/RLV/Sitting/getsitid.cs create mode 100644 Core/RLV/Sitting/sit.cs create mode 100644 Core/RLV/Sitting/unsit.cs create mode 100644 Core/RLV/Version/version.cs create mode 100644 Core/RLV/Version/versionnew.cs create mode 100644 Core/RLV/Version/versionnum.cs create mode 100644 Core/Static/Inventory.cs create mode 100644 Core/Static/parcel.cs create mode 100644 Core/bottypes/AVstorageBot.cs create mode 100644 Core/bottypes/AnimationsBot.cs create mode 100644 Core/bottypes/AwaitReplyEventBot.cs create mode 100644 Core/bottypes/CommandsBot.cs create mode 100644 Core/bottypes/DialogsBot.cs create mode 100644 Core/bottypes/DiscordBot.cs create mode 100644 Core/bottypes/EventsBot.cs create mode 100644 Core/bottypes/MessageSwitcherBot.cs create mode 100644 Core/bottypes/RLVbot.cs create mode 100644 Core/bottypes/SecondBot.cs create mode 100644 Core/bottypes/VirtualsBot.cs create mode 100644 Core/bottypes/athome.cs create mode 100644 Core/bottypes/chatrelay.cs create mode 100644 Dockerfile create mode 100644 README.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ce59afa --- /dev/null +++ b/.dockerignore @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0814ce --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/BSBshared/API/API_interface.cs b/BSBshared/API/API_interface.cs new file mode 100644 index 0000000..a32a256 --- /dev/null +++ b/BSBshared/API/API_interface.cs @@ -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 subtype_map = new Dictionary(); + protected Dictionary subtype_workspace_map = new Dictionary(); + 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 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(); + } + 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 GetAllWorkspaces() + { + List reply = new List(); + foreach (string c in GetCommandsList()) + { + string workspace = GetCommandWorkspace(c); + if (reply.Contains(workspace) == false) + { + reply.Add(workspace); + } + } + return reply; + } + } +} diff --git a/BSBshared/BSBshared.csproj b/BSBshared/BSBshared.csproj new file mode 100644 index 0000000..1ccdaaf --- /dev/null +++ b/BSBshared/BSBshared.csproj @@ -0,0 +1,24 @@ + + + netcoreapp3.1 + Debug;Release;DockerBuild + x64 + + + none + Linux + false + false + false + linux-x64 + + + + + + + + + + + diff --git a/BSBshared/BotTypes/BasicBot.cs b/BSBshared/BotTypes/BasicBot.cs new file mode 100644 index 0000000..b5ad22d --- /dev/null +++ b/BSBshared/BotTypes/BasicBot.cs @@ -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 mygroups = new Dictionary(); + + public Dictionary 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 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 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"); + } + } +} diff --git a/BSBshared/IO/SimpleIO.cs b/BSBshared/IO/SimpleIO.cs new file mode 100644 index 0000000..d77f344 --- /dev/null +++ b/BSBshared/IO/SimpleIO.cs @@ -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 ""; + } + } +} diff --git a/BSBshared/Json/JsonConfig.cs b/BSBshared/Json/JsonConfig.cs new file mode 100644 index 0000000..613070e --- /dev/null +++ b/BSBshared/Json/JsonConfig.cs @@ -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; } + } + +} diff --git a/BSBshared/Json/JsonConfigApi.cs b/BSBshared/Json/JsonConfigApi.cs new file mode 100644 index 0000000..2e058bc --- /dev/null +++ b/BSBshared/Json/JsonConfigApi.cs @@ -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
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
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
Example: 8080"; } + case "httpkey": { return "A known secret between used for HTTP calls
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(); + } + } +} diff --git a/BSBshared/Staic/ConsoleLog.cs b/BSBshared/Staic/ConsoleLog.cs new file mode 100644 index 0000000..ceecf67 --- /dev/null +++ b/BSBshared/Staic/ConsoleLog.cs @@ -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()); + } + } +} diff --git a/BSBshared/Staic/Helpers.cs b/BSBshared/Staic/Helpers.cs new file mode 100644 index 0000000..5492d79 --- /dev/null +++ b/BSBshared/Staic/Helpers.cs @@ -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(""); + foreach (string A in items) + { + if (row_open == false) + { + reply.Append(""); + row_open = true; + } + reply.Append(""); + col_counter++; + if (col_counter >= cols) + { + reply.Append(""); + col_counter = 0; + row_open = false; + } + } + while (row_open == true) + { + reply.Append(""); + col_counter++; + if (col_counter >= cols) + { + reply.Append(""); + row_open = false; + } + } + reply.Append("
" + A + "
"); + 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; + } + } + } +} diff --git a/BetterSecondBot.sln b/BetterSecondBot.sln new file mode 100644 index 0000000..311993b --- /dev/null +++ b/BetterSecondBot.sln @@ -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 diff --git a/BetterSecondBot.sln.DotSettings b/BetterSecondBot.sln.DotSettings new file mode 100644 index 0000000..f70c8c4 --- /dev/null +++ b/BetterSecondBot.sln.DotSettings @@ -0,0 +1,4 @@ + + True + True + \ No newline at end of file diff --git a/CLI/BetterSecondBot.csproj b/CLI/BetterSecondBot.csproj new file mode 100644 index 0000000..b06259b --- /dev/null +++ b/CLI/BetterSecondBot.csproj @@ -0,0 +1,37 @@ + + + git describe --match=NeVeRmAtCh --always --abbrev=7 + AnyCPU;x64 + + + Exe + netcoreapp3.1 + BetterSecondBot.Program + Debug;Release;DockerBuild;Windows + Linux + + + none + Linux + false + false + false + linux-x64 + + + + + + + + + + + + + + + + + + diff --git a/CLI/Cli.cs b/CLI/Cli.cs new file mode 100644 index 0000000..d3d41b8 --- /dev/null +++ b/CLI/Cli.cs @@ -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 + { + /// Gets the git hash value from the assembly + /// or null if it cannot be found. + public static string GetGitHash() + { + var asm = typeof(AssemblyInfo).Assembly; + var attrs = asm.GetCustomAttributes(); + 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]
Example: \"BetterSecondBot.exe -j=otherbot.json\""; + } + public override int GetCommandArgs(string cmd) + { + return 1; + } + } +} + + diff --git a/CLI/CliDocker.cs b/CLI/CliDocker.cs new file mode 100644 index 0000000..2087c24 --- /dev/null +++ b/CLI/CliDocker.cs @@ -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); + } + } + } +} diff --git a/CLI/CliExitOnLogout.cs b/CLI/CliExitOnLogout.cs new file mode 100644 index 0000000..4094e04 --- /dev/null +++ b/CLI/CliExitOnLogout.cs @@ -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"); + } + } + } +} diff --git a/CLI/CliHardware.cs b/CLI/CliHardware.cs new file mode 100644 index 0000000..4535935 --- /dev/null +++ b/CLI/CliHardware.cs @@ -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(args).WithParsed(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(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); + } + } + } + } +} diff --git a/CLI/Dockerfile-ForDebug b/CLI/Dockerfile-ForDebug new file mode 100644 index 0000000..c09c4a7 --- /dev/null +++ b/CLI/Dockerfile-ForDebug @@ -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"] \ No newline at end of file diff --git a/CLI/HttpCnC.cs b/CLI/HttpCnC.cs new file mode 100644 index 0000000..ede90fc --- /dev/null +++ b/CLI/HttpCnC.cs @@ -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(); + } + } + } +} diff --git a/CLI/HttpServer/control/Actions/logout.cs b/CLI/HttpServer/control/Actions/logout.cs new file mode 100644 index 0000000..e982338 --- /dev/null +++ b/CLI/HttpServer/control/Actions/logout.cs @@ -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
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"); + } + } + } +} diff --git a/CLI/HttpServer/control/Interface/cmd_lib.cs b/CLI/HttpServer/control/Interface/cmd_lib.cs new file mode 100644 index 0000000..e92c342 --- /dev/null +++ b/CLI/HttpServer/control/Interface/cmd_lib.cs @@ -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"); + } + } + } +} diff --git a/CLI/HttpServer/control/Service/LoginBot.cs b/CLI/HttpServer/control/Service/LoginBot.cs new file mode 100644 index 0000000..0c51043 --- /dev/null +++ b/CLI/HttpServer/control/Service/LoginBot.cs @@ -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]
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"); + } + } +} diff --git a/CLI/HttpServer/control/Service/ShutdownCNC.cs b/CLI/HttpServer/control/Service/ShutdownCNC.cs new file mode 100644 index 0000000..d0549bf --- /dev/null +++ b/CLI/HttpServer/control/Service/ShutdownCNC.cs @@ -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]
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"); + } + } +} diff --git a/CLI/HttpServer/http_commands.cs b/CLI/HttpServer/http_commands.cs new file mode 100644 index 0000000..7ce5bf3 --- /dev/null +++ b/CLI/HttpServer/http_commands.cs @@ -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}; + } + } + + +} diff --git a/CLI/HttpServer/http_server.cs b/CLI/HttpServer/http_server.cs new file mode 100644 index 0000000..ad7819e --- /dev/null +++ b/CLI/HttpServer/http_server.cs @@ -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 post_args = new Dictionary(); + 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(); + } + } +} diff --git a/CLI/HttpServer/view/Basic/status.cs b/CLI/HttpServer/view/Basic/status.cs new file mode 100644 index 0000000..7988f1d --- /dev/null +++ b/CLI/HttpServer/view/Basic/status.cs @@ -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"); + } + } + } +} diff --git a/CLI/HttpServer/view/Inventory/getfolders.cs b/CLI/HttpServer/view/Inventory/getfolders.cs new file mode 100644 index 0000000..de46790 --- /dev/null +++ b/CLI/HttpServer/view/Inventory/getfolders.cs @@ -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"); + } + } + } +} diff --git a/CLI/Properties/PublishProfiles/Linux-x64.pubxml b/CLI/Properties/PublishProfiles/Linux-x64.pubxml new file mode 100644 index 0000000..049c070 --- /dev/null +++ b/CLI/Properties/PublishProfiles/Linux-x64.pubxml @@ -0,0 +1,18 @@ + + + + + FileSystem + Release + x64 + netcoreapp3.1 + ..\Publish\linux-x64 + linux-x64 + true + True + False + False + + \ No newline at end of file diff --git a/CLI/Properties/PublishProfiles/Windows-64bit.pubxml b/CLI/Properties/PublishProfiles/Windows-64bit.pubxml new file mode 100644 index 0000000..2e54755 --- /dev/null +++ b/CLI/Properties/PublishProfiles/Windows-64bit.pubxml @@ -0,0 +1,18 @@ + + + + + FileSystem + Release + x64 + netcoreapp3.1 + ..\Publish\windows_x64 + win-x64 + true + True + False + False + + \ No newline at end of file diff --git a/CLI/Properties/launchSettings.json b/CLI/Properties/launchSettings.json new file mode 100644 index 0000000..1aa4c7c --- /dev/null +++ b/CLI/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "BetterSecondBot": { + "commandName": "Project" + }, + "Docker": { + "commandName": "Docker" + } + } +} \ No newline at end of file diff --git a/CLI/WikiMake/wiki_footer.txt b/CLI/WikiMake/wiki_footer.txt new file mode 100644 index 0000000..dcd1b1f --- /dev/null +++ b/CLI/WikiMake/wiki_footer.txt @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/CLI/WikiMake/wiki_header.txt b/CLI/WikiMake/wiki_header.txt new file mode 100644 index 0000000..0007e7c --- /dev/null +++ b/CLI/WikiMake/wiki_header.txt @@ -0,0 +1,30 @@ + + +Secondbot Wiki [[AREA]] + + + + + +
\ No newline at end of file diff --git a/CLI/WikiMake/wikimake.cs b/CLI/WikiMake/wikimake.cs new file mode 100644 index 0000000..8245141 --- /dev/null +++ b/CLI/WikiMake/wikimake.cs @@ -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> api_reports = new Dictionary>(); + protected string html_header = ""; + protected string html_footer = ""; + protected string buildVersion = ""; + protected SimpleIO io; + protected Dictionary seen_command_names = new Dictionary(); + + 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("

Build: "); + sb.Append(buildVersion); + sb.Append("


"); + sb.Append(""); + foreach (KeyValuePair> entry in api_reports) + { + sb.Append(""); + sb.Append(""); + sb.Append(""); + } + sb.Append("
InterfaceCommandsStatus
"); + sb.Append(entry.Key); + sb.Append(""); + sb.Append(entry.Value.Key.ToString()); + sb.Append(""); + sb.Append(entry.Value.Value); + sb.Append("
"); + 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 active_swaps = new Dictionary + { + { "CoreActive", "" }, + { "CLIActive", "" }, + { "JSONActive", "" }, + { "RLVapiActive", "" }, + { "HTTPgetActive", "" }, + { "HTTPpostActive", "" } + }; + active_swaps["" + area + "Active"] = "Active"; + foreach(KeyValuePair 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("

Build: "); + sb.Append(buildVersion); + sb.Append("


"); + sb.Append("

Interface: [[AREA]]"); + if(workspace != "") + { + sb.Append(" / [[WORKSPACE]]"); + } + sb.Append("


[[COMMAND]]

"); + 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("
", 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("
"); + } + sb.Append("[[HELP]]"); + if (arg_types.Length > 0) + { + sb.Append("

Args helper

"); + loop = 0; + + sb.Append(""); + 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(""); + loop++; + } + sb.Append("
NumTypeRequiredHint
" + (loop + 1).ToString() + "" + arg_types[loop] + "" + Required + "" + hint + "
"); + } + 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("

Interface: [[AREA]]"); + if (workspace != "") + { + sb.Append(" / [[WORKSPACE]]"); + } + sb.Append("


"); + sb.Append("

Build: "); + sb.Append(buildVersion); + sb.Append("


"); + sb.Append(""); + foreach(string c in cmds) + { + if(shared_interface.GetCommandWorkspace(c) == workspace) + { + int args = shared_interface.GetCommandArgs(c); + sb.Append(""); + } + } + sb.Append("
CommandMin args
"); + sb.Append(c); + sb.Append(""); + sb.Append(args.ToString()); + sb.Append("
"); + 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("

Interface:"); + sb.Append(area); + sb.Append("


"); + sb.Append("

Build: "); + sb.Append(buildVersion); + sb.Append("


"); + string[] cmds = shared_interface.GetCommandsList(); + sb.Append(""); + 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 = "" + workspace + ""; + sb.Append(""); + } + sb.Append("
WorkspaceCommands
"); + sb.Append(workspace_link); + sb.Append(""); + sb.Append(commands_count.ToString()); + sb.Append("
"); + 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(cli.ApiCommandsCount, "Ready")); + InterfaceWiki("CLI", cli); + + // JSONcfg + MakeJsonConfig defaultJson = new MakeJsonConfig(); + api_reports.Add("JSON", new KeyValuePair(defaultJson.ApiCommandsCount, "Ready")); + InterfaceWiki("JSON", defaultJson); + + // CMD + BSB.Commands.CoreCommandsInterface cmd = new BSB.Commands.CoreCommandsInterface(null); + api_reports.Add("Core", new KeyValuePair(cmd.ApiCommandsCount, "Basic")); + InterfaceWiki("Core", cmd,true); + + // RLVapi + BSB.RLV.RLVcontrol RLVapi = new BSB.RLV.RLVcontrol(null); + api_reports.Add("RLVapi", new KeyValuePair(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(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(http_post.ApiCommandsCount, "Limited")); + InterfaceWiki("HTTPpost", http_post); + + io.ChangeRoot("wiki"); + io.writefile("index.html", CreateRoot()); + } + } +} diff --git a/Core/BSB.csproj b/Core/BSB.csproj new file mode 100644 index 0000000..e56b52a --- /dev/null +++ b/Core/BSB.csproj @@ -0,0 +1,25 @@ + + + + netcoreapp3.1 + Debug;Release;DockerBuild + x64 + + + none + Linux + false + false + false + linux-x64 + + + + + + + + + + + diff --git a/Core/Commands/Chat/im.cs b/Core/Commands/Chat/im.cs new file mode 100644 index 0000000..8fb592d --- /dev/null +++ b/Core/Commands/Chat/im.cs @@ -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
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; + } + } +} diff --git a/Core/Commands/Chat/say.cs b/Core/Commands/Chat/say.cs new file mode 100644 index 0000000..af210e7 --- /dev/null +++ b/Core/Commands/Chat/say.cs @@ -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
Example: say|||Hello
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; + } + } +} diff --git a/Core/Commands/Dialogs/DialogRelay.cs b/Core/Commands/Dialogs/DialogRelay.cs new file mode 100644 index 0000000..7efe0ce --- /dev/null +++ b/Core/Commands/Dialogs/DialogRelay.cs @@ -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
Clear" }; } } + public override string Helpfile { get { return "Updates the relay target (you can have 1 of each type)
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; + } + } + +} diff --git a/Core/Commands/Dialogs/DialogResponce.cs b/Core/Commands/Dialogs/DialogResponce.cs new file mode 100644 index 0000000..57ff23d --- /dev/null +++ b/Core/Commands/Dialogs/DialogResponce.cs @@ -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; + } + } + +} diff --git a/Core/Commands/Dialogs/DialogTrack.cs b/Core/Commands/Dialogs/DialogTrack.cs new file mode 100644 index 0000000..64f0675 --- /dev/null +++ b/Core/Commands/Dialogs/DialogTrack.cs @@ -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; + } + } + +} diff --git a/Core/Commands/Group/GroupInvite.cs b/Core/Commands/Group/GroupInvite.cs new file mode 100644 index 0000000..aef0076 --- /dev/null +++ b/Core/Commands/Group/GroupInvite.cs @@ -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 invite_roles = new List(); + 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; + } + } +} diff --git a/Core/Commands/Group/GroupNotice.cs b/Core/Commands/Group/GroupNotice.cs new file mode 100644 index 0000000..59a4e02 --- /dev/null +++ b/Core/Commands/Group/GroupNotice.cs @@ -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]
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; + } + } +} \ No newline at end of file diff --git a/Core/Commands/Group/getGroupList.cs b/Core/Commands/Group/getGroupList.cs new file mode 100644 index 0000000..97321c4 --- /dev/null +++ b/Core/Commands/Group/getGroupList.cs @@ -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 groupdata in bot.MyGroups) + { + reply += addon; + reply += groupdata.Value.Name; + reply += "="; + reply += groupdata.Key; + addon = ","; + } + return reply; + } + } +} diff --git a/Core/Commands/Group/getGroupRoles.cs b/Core/Commands/Group/getGroupRoles.cs new file mode 100644 index 0000000..1b5dcbb --- /dev/null +++ b/Core/Commands/Group/getGroupRoles.cs @@ -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 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); + } + } +} diff --git a/Core/Commands/Group/group.cs b/Core/Commands/Group/group.cs new file mode 100644 index 0000000..991acb4 --- /dev/null +++ b/Core/Commands/Group/group.cs @@ -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; + } + } +} diff --git a/Core/Commands/Group/groupchat.cs b/Core/Commands/Group/groupchat.cs new file mode 100644 index 0000000..143a0d3 --- /dev/null +++ b/Core/Commands/Group/groupchat.cs @@ -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]); + } + } + } +} \ No newline at end of file diff --git a/Core/Commands/Group/groupchatAnyUnread.cs b/Core/Commands/Group/groupchatAnyUnread.cs new file mode 100644 index 0000000..230c173 --- /dev/null +++ b/Core/Commands/Group/groupchatAnyUnread.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/Core/Commands/Group/groupchatClearAll.cs b/Core/Commands/Group/groupchatClearAll.cs new file mode 100644 index 0000000..bf9fe6d --- /dev/null +++ b/Core/Commands/Group/groupchatClearAll.cs @@ -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; + } + } +} diff --git a/Core/Commands/Group/groupchatGroupHasUnread.cs b/Core/Commands/Group/groupchatGroupHasUnread.cs new file mode 100644 index 0000000..b47701b --- /dev/null +++ b/Core/Commands/Group/groupchatGroupHasUnread.cs @@ -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(); + } + + } +} diff --git a/Core/Commands/Group/groupchatHistory.cs b/Core/Commands/Group/groupchatHistory.cs new file mode 100644 index 0000000..d92b711 --- /dev/null +++ b/Core/Commands/Group/groupchatHistory.cs @@ -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)); + } + } +} diff --git a/Core/Commands/Group/groupchatListAllUnreadGroups.cs b/Core/Commands/Group/groupchatListAllUnreadGroups.cs new file mode 100644 index 0000000..e01cfcb --- /dev/null +++ b/Core/Commands/Group/groupchatListAllUnreadGroups.cs @@ -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; + } + } +} diff --git a/Core/Commands/Home/GoHome.cs b/Core/Commands/Home/GoHome.cs new file mode 100644 index 0000000..677c9ed --- /dev/null +++ b/Core/Commands/Home/GoHome.cs @@ -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; + } + } +} diff --git a/Core/Commands/Home/resetAtHome.cs b/Core/Commands/Home/resetAtHome.cs new file mode 100644 index 0000000..047f34d --- /dev/null +++ b/Core/Commands/Home/resetAtHome.cs @@ -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; + } + } +} diff --git a/Core/Commands/Inventory/InventoryPurgeNotecards.cs b/Core/Commands/Inventory/InventoryPurgeNotecards.cs new file mode 100644 index 0000000..67d8516 --- /dev/null +++ b/Core/Commands/Inventory/InventoryPurgeNotecards.cs @@ -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.
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 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 purge_notecards = new List(); + List 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()); + } + return true; + } + else + { + return Failed("Unable to find notecard folder"); + } + } + return false; + } + } +} diff --git a/Core/Commands/Inventory/attach.cs b/Core/Commands/Inventory/attach.cs new file mode 100644 index 0000000..707ee3e --- /dev/null +++ b/Core/Commands/Inventory/attach.cs @@ -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
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 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 items = new List(); + List 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; + } + } +} diff --git a/Core/Commands/Inventory/inventoryContents.cs b/Core/Commands/Inventory/inventoryContents.cs new file mode 100644 index 0000000..202c559 --- /dev/null +++ b/Core/Commands/Inventory/inventoryContents.cs @@ -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
" + + "Arg 1 *Avatar UUID* sends it via notecard to [ARG 1]
" + + "Arg 1 *Anything else*
as csv packed as follows folderinventory=UUID~|~entry~|~entry..." + + "entry is formated as follows: itemname###uuid###type
" + + "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; + } + } +} diff --git a/Core/Commands/Inventory/inventoryFolders.cs b/Core/Commands/Inventory/inventoryFolders.cs new file mode 100644 index 0000000..7618032 --- /dev/null +++ b/Core/Commands/Inventory/inventoryFolders.cs @@ -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
" + + "*[Arg 1 UUID] sends you a notecard with it
" + + "*[Arg 1 Anything else] Sends it as a CSV via the smart target
" + + "Formated as follows
" + + "entry,entry...
" + + "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; + } + } +} diff --git a/Core/Commands/Inventory/outfit.cs b/Core/Commands/Inventory/outfit.cs new file mode 100644 index 0000000..22950c6 --- /dev/null +++ b/Core/Commands/Inventory/outfit.cs @@ -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
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 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 contents = bot.GetClient.Inventory.FolderContents(AA.UUID, bot.GetClient.Self.AgentID, true, true, InventorySortOrder.ByName, 20 * 1000); + List wareables = new List(); + 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; + } + } +} diff --git a/Core/Commands/Inventory/sendFolder.cs b/Core/Commands/Inventory/sendFolder.cs new file mode 100644 index 0000000..a02abfd --- /dev/null +++ b/Core/Commands/Inventory/sendFolder.cs @@ -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; + } + } +} diff --git a/Core/Commands/Inventory/sendItem.cs b/Core/Commands/Inventory/sendItem.cs new file mode 100644 index 0000000..f9e5268 --- /dev/null +++ b/Core/Commands/Inventory/sendItem.cs @@ -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; + } + } +} diff --git a/Core/Commands/Movement/rotateTo.cs b/Core/Commands/Movement/rotateTo.cs new file mode 100644 index 0000000..599a1dd --- /dev/null +++ b/Core/Commands/Movement/rotateTo.cs @@ -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; + } + } + +} diff --git a/Core/Commands/Movement/teleport.cs b/Core/Commands/Movement/teleport.cs new file mode 100644 index 0000000..6cc14b6 --- /dev/null +++ b/Core/Commands/Movement/teleport.cs @@ -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
Region
X/Y/Z
Region/X/Y/Z" }; } } + public override string Helpfile { get { return "Teleports the avatar
" + + "SLurl: http://maps.secondlife.com/secondlife/Bombalai/216/51/33
" + + "Region: Bombalai [Defaults to 128,128,0]
" + + "X/Y/Z: Stays in the sim just changes location
" + + "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; + } + } +} diff --git a/Core/Commands/Notecard/notecardAdd.cs b/Core/Commands/Notecard/notecardAdd.cs new file mode 100644 index 0000000..6611a26 --- /dev/null +++ b/Core/Commands/Notecard/notecardAdd.cs @@ -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]
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; + } + } +} diff --git a/Core/Commands/Notecard/notecardClear.cs b/Core/Commands/Notecard/notecardClear.cs new file mode 100644 index 0000000..e450f12 --- /dev/null +++ b/Core/Commands/Notecard/notecardClear.cs @@ -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; + } + } +} diff --git a/Core/Commands/Notecard/notecardSend.cs b/Core/Commands/Notecard/notecardSend.cs new file mode 100644 index 0000000..e62cdb1 --- /dev/null +++ b/Core/Commands/Notecard/notecardSend.cs @@ -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]
" + + " using the collection [ARG 2] as the content
" + + " 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" or defaults to: New notecard for ] + 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; + } + } +} diff --git a/Core/Commands/Parcel/GetParcelFlags.cs b/Core/Commands/Parcel/GetParcelFlags.cs new file mode 100644 index 0000000..4353cf1 --- /dev/null +++ b/Core/Commands/Parcel/GetParcelFlags.cs @@ -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)
" + + "requested on [ARG 2+] via [ARG 1] smart reply target
" + + "If you request multiple Flags they are split with CSV
You can also get all the flags skipping [ARG 2]

" + + "" + helpers.create_dirty_table(parcel_static.get_flag_names()) + "

Example: getparcelflag|||0~#~ForSale
Example: getparcelflag|||12"; } } + + public override bool CallFunction(string[] args) + { + if (base.CallFunction(args) == true) + { + Dictionary flags = parcel_static.get_flags_list(); + List get_flags = new List(); + List otherargs = new List(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; + } + } +} diff --git a/Core/Commands/Parcel/SetParcelFlag.cs b/Core/Commands/Parcel/SetParcelFlag.cs new file mode 100644 index 0000000..4e23900 --- /dev/null +++ b/Core/Commands/Parcel/SetParcelFlag.cs @@ -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
" + + "Example:AllowFly=False~#~AllowDamage=True
" + helpers.create_dirty_table(parcel_static.get_flag_names()) +"
"; } } + + 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 acceptablewords = new List(); + Dictionary flags = parcel_static.get_flags_list(); + acceptablewords.AddRange(new[] { "True", "False" }); + + Dictionary setflags = new Dictionary(); + 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 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; + } + } +} diff --git a/Core/Commands/Parcel/eject.cs b/Core/Commands/Parcel/eject.cs new file mode 100644 index 0000000..eae6208 --- /dev/null +++ b/Core/Commands/Parcel/eject.cs @@ -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
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; + } + } +} diff --git a/Core/Commands/Self/PayAvatar.cs b/Core/Commands/Self/PayAvatar.cs new file mode 100644 index 0000000..ae5ce88 --- /dev/null +++ b/Core/Commands/Self/PayAvatar.cs @@ -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; + } + } +} diff --git a/Core/Commands/Self/PayObject.cs b/Core/Commands/Self/PayObject.cs new file mode 100644 index 0000000..50da946 --- /dev/null +++ b/Core/Commands/Self/PayObject.cs @@ -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; + } + } +} diff --git a/Core/Commands/Self/addToAnimations.cs b/Core/Commands/Self/addToAnimations.cs new file mode 100644 index 0000000..bb3b075 --- /dev/null +++ b/Core/Commands/Self/addToAnimations.cs @@ -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
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; + } + } +} diff --git a/Core/Commands/Self/botVersion.cs b/Core/Commands/Self/botVersion.cs new file mode 100644 index 0000000..a75e79a --- /dev/null +++ b/Core/Commands/Self/botVersion.cs @@ -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; + } + } +} diff --git a/Core/Commands/Self/logout.cs b/Core/Commands/Self/logout.cs new file mode 100644 index 0000000..6adb13b --- /dev/null +++ b/Core/Commands/Self/logout.cs @@ -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; + } + } +} diff --git a/Core/Commands/Self/pointAt.cs b/Core/Commands/Self/pointAt.cs new file mode 100644 index 0000000..9cc91a5 --- /dev/null +++ b/Core/Commands/Self/pointAt.cs @@ -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; + } + } +} diff --git a/Core/Commands/Self/sit.cs b/Core/Commands/Self/sit.cs new file mode 100644 index 0000000..a82fc6c --- /dev/null +++ b/Core/Commands/Self/sit.cs @@ -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; + } + } +} diff --git a/Core/Commands/Self/stand.cs b/Core/Commands/Self/stand.cs new file mode 100644 index 0000000..579c6bf --- /dev/null +++ b/Core/Commands/Self/stand.cs @@ -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; + } + } +} diff --git a/Core/Commands/StreamAdmin/FetchNextNotecard.cs b/Core/Commands/StreamAdmin/FetchNextNotecard.cs new file mode 100644 index 0000000..b580289 --- /dev/null +++ b/Core/Commands/StreamAdmin/FetchNextNotecard.cs @@ -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(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; } + } +} diff --git a/Core/Commands/commandlib.cs b/Core/Commands/commandlib.cs new file mode 100644 index 0000000..524f67a --- /dev/null +++ b/Core/Commands/commandlib.cs @@ -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 need_lookup_on_indexs = new List(); + 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 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 commands_pending_avatar_lookups = new Dictionary(); + protected Dictionary commands_pending_avatar_lookups_ages = new Dictionary(); + + public void StatusTick() + { + List purge_indexs = new List(); + List call_indexs = new List(); + long now = helpers.UnixTimeNow(); + foreach (KeyValuePair 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 values = new Dictionary + { + { "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 suppress_warnings = new List(); + 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; + } + } + + +} diff --git a/Core/RLV/Blacklist/getblacklist.cs b/Core/RLV/Blacklist/getblacklist.cs new file mode 100644 index 0000000..faaba78 --- /dev/null +++ b/Core/RLV/Blacklist/getblacklist.cs @@ -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; + } + } +} diff --git a/Core/RLV/Blacklist/versionnumbl.cs b/Core/RLV/Blacklist/versionnumbl.cs new file mode 100644 index 0000000..14a7631 --- /dev/null +++ b/Core/RLV/Blacklist/versionnumbl.cs @@ -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; + } + } +} diff --git a/Core/RLV/Camera/camavdist.cs b/Core/RLV/Camera/camavdist.cs new file mode 100644 index 0000000..24a7933 --- /dev/null +++ b/Core/RLV/Camera/camavdist.cs @@ -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 + { + + } +} diff --git a/Core/RLV/Camera/camdistmax.cs b/Core/RLV/Camera/camdistmax.cs new file mode 100644 index 0000000..8d94885 --- /dev/null +++ b/Core/RLV/Camera/camdistmax.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camdistmin.cs b/Core/RLV/Camera/camdistmin.cs new file mode 100644 index 0000000..68a6ce1 --- /dev/null +++ b/Core/RLV/Camera/camdistmin.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camdrawalphamax.cs b/Core/RLV/Camera/camdrawalphamax.cs new file mode 100644 index 0000000..90a56c1 --- /dev/null +++ b/Core/RLV/Camera/camdrawalphamax.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camdrawalphamin.cs b/Core/RLV/Camera/camdrawalphamin.cs new file mode 100644 index 0000000..ed111c6 --- /dev/null +++ b/Core/RLV/Camera/camdrawalphamin.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camdrawcolor.cs b/Core/RLV/Camera/camdrawcolor.cs new file mode 100644 index 0000000..0c84ba2 --- /dev/null +++ b/Core/RLV/Camera/camdrawcolor.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camdrawmax.cs b/Core/RLV/Camera/camdrawmax.cs new file mode 100644 index 0000000..f883355 --- /dev/null +++ b/Core/RLV/Camera/camdrawmax.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camdrawmin.cs b/Core/RLV/Camera/camdrawmin.cs new file mode 100644 index 0000000..3312f9d --- /dev/null +++ b/Core/RLV/Camera/camdrawmin.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camtextures.cs b/Core/RLV/Camera/camtextures.cs new file mode 100644 index 0000000..54c5472 --- /dev/null +++ b/Core/RLV/Camera/camtextures.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camunlock.cs b/Core/RLV/Camera/camunlock.cs new file mode 100644 index 0000000..8a72116 --- /dev/null +++ b/Core/RLV/Camera/camunlock.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Camera +{ + public class CamUnlock : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Camera/camzoommax.cs b/Core/RLV/Camera/camzoommax.cs new file mode 100644 index 0000000..79af3c5 --- /dev/null +++ b/Core/RLV/Camera/camzoommax.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/camzoommin.cs b/Core/RLV/Camera/camzoommin.cs new file mode 100644 index 0000000..e9cece5 --- /dev/null +++ b/Core/RLV/Camera/camzoommin.cs @@ -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 + { + } +} diff --git a/Core/RLV/Camera/getcam_avdistmax.cs b/Core/RLV/Camera/getcam_avdistmax.cs new file mode 100644 index 0000000..35007cb --- /dev/null +++ b/Core/RLV/Camera/getcam_avdistmax.cs @@ -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"; } } + } +} diff --git a/Core/RLV/Camera/getcam_avdistmin.cs b/Core/RLV/Camera/getcam_avdistmin.cs new file mode 100644 index 0000000..c4c1aa3 --- /dev/null +++ b/Core/RLV/Camera/getcam_avdistmin.cs @@ -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"; } } + + } +} diff --git a/Core/RLV/Camera/getcam_fov.cs b/Core/RLV/Camera/getcam_fov.cs new file mode 100644 index 0000000..ca3117b --- /dev/null +++ b/Core/RLV/Camera/getcam_fov.cs @@ -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"; } } + + } +} diff --git a/Core/RLV/Camera/getcam_fovmax.cs b/Core/RLV/Camera/getcam_fovmax.cs new file mode 100644 index 0000000..8e29a9a --- /dev/null +++ b/Core/RLV/Camera/getcam_fovmax.cs @@ -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"; } } + + } +} diff --git a/Core/RLV/Camera/getcam_fovmin.cs b/Core/RLV/Camera/getcam_fovmin.cs new file mode 100644 index 0000000..63b13e3 --- /dev/null +++ b/Core/RLV/Camera/getcam_fovmin.cs @@ -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"; } } + } +} diff --git a/Core/RLV/Camera/getcam_zoommin.cs b/Core/RLV/Camera/getcam_zoommin.cs new file mode 100644 index 0000000..3b871c2 --- /dev/null +++ b/Core/RLV/Camera/getcam_zoommin.cs @@ -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"; } } + + } +} diff --git a/Core/RLV/Camera/setcam_avdistmax.cs b/Core/RLV/Camera/setcam_avdistmax.cs new file mode 100644 index 0000000..eca16f9 --- /dev/null +++ b/Core/RLV/Camera/setcam_avdistmax.cs @@ -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"; } } + } +} diff --git a/Core/RLV/Camera/setcam_avdistmin.cs b/Core/RLV/Camera/setcam_avdistmin.cs new file mode 100644 index 0000000..d4c4138 --- /dev/null +++ b/Core/RLV/Camera/setcam_avdistmin.cs @@ -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"; } } + + } +} diff --git a/Core/RLV/Camera/setcam_fov.cs b/Core/RLV/Camera/setcam_fov.cs new file mode 100644 index 0000000..1907cf1 --- /dev/null +++ b/Core/RLV/Camera/setcam_fov.cs @@ -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"; } } + } +} diff --git a/Core/RLV/Camera/setcam_fovmax.cs b/Core/RLV/Camera/setcam_fovmax.cs new file mode 100644 index 0000000..9cd3e10 --- /dev/null +++ b/Core/RLV/Camera/setcam_fovmax.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Camera +{ + public class SetCam_FovMax : RLV_UUID_flag_arg_yn + { + public override string SetFlagName { get { return "camfovmax"; } } + } +} diff --git a/Core/RLV/Camera/setcam_fovmin.cs b/Core/RLV/Camera/setcam_fovmin.cs new file mode 100644 index 0000000..15b5abb --- /dev/null +++ b/Core/RLV/Camera/setcam_fovmin.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Camera +{ + public class SetCam_FovMin : RLV_UUID_flag_arg_yn + { + public override string SetFlagName { get { return "camfovmin"; } } + } +} diff --git a/Core/RLV/Camera/setcam_textures.cs b/Core/RLV/Camera/setcam_textures.cs new file mode 100644 index 0000000..ed90e0c --- /dev/null +++ b/Core/RLV/Camera/setcam_textures.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Camera +{ + public class SetCam_Textures : RLV_UUID_flag_optional_arg_yn + { + public override string SetFlagName { get { return "camtextures"; } } + } +} diff --git a/Core/RLV/Camera/setcam_unlock.cs b/Core/RLV/Camera/setcam_unlock.cs new file mode 100644 index 0000000..ff5cbcd --- /dev/null +++ b/Core/RLV/Camera/setcam_unlock.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Camera +{ + public class SetCam_Unlock : RLV_UUID_flag_yn + { + public override string SetFlagName { get { return "camunlock"; } } + + } +} diff --git a/Core/RLV/Chat/chatnormal.cs b/Core/RLV/Chat/chatnormal.cs new file mode 100644 index 0000000..8dea634 --- /dev/null +++ b/Core/RLV/Chat/chatnormal.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Chat +{ + public class ChatNormal : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Chat/chatshout.cs b/Core/RLV/Chat/chatshout.cs new file mode 100644 index 0000000..bcdcd05 --- /dev/null +++ b/Core/RLV/Chat/chatshout.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Chat +{ + public class ChatShout : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Chat/chatwhisper.cs b/Core/RLV/Chat/chatwhisper.cs new file mode 100644 index 0000000..1b314f3 --- /dev/null +++ b/Core/RLV/Chat/chatwhisper.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Chat +{ + public class ChatWhisper : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Chat/recvchat.cs b/Core/RLV/Chat/recvchat.cs new file mode 100644 index 0000000..9199205 --- /dev/null +++ b/Core/RLV/Chat/recvchat.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace BSB.RLV.Chat +{ + public class RecvChat : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + if (args.Length == 1) + { + if (args[0] == "y") + { + bot.SetLock("recvchat_channel_zero_lock", false); + return true; + } + else if (args[0] == "n") + { + bot.SetLock("recvchat_channel_zero_lock", true); + return true; + } + return Failed("y/n only"); + } + else if (args.Length == 2) + { + if (UUID.TryParse(args[0], out UUID rec_uuid) == true) + { + return SetFlag(args[1], "set", rec_uuid); + } + return Failed("Unable to process target UUID"); + } + return Failed("Unsupported number of args"); + } + } +} diff --git a/Core/RLV/Chat/recvchat_sec.cs b/Core/RLV/Chat/recvchat_sec.cs new file mode 100644 index 0000000..7c1d978 --- /dev/null +++ b/Core/RLV/Chat/recvchat_sec.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Chat +{ + public class RecvChat_Sec : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Chat/recvchatfrom.cs b/Core/RLV/Chat/recvchatfrom.cs new file mode 100644 index 0000000..698f738 --- /dev/null +++ b/Core/RLV/Chat/recvchatfrom.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Chat +{ + public class RecvChatFrom : RLV_UUID_flag_arg_yn + { + } +} diff --git a/Core/RLV/Chat/redirchat.cs b/Core/RLV/Chat/redirchat.cs new file mode 100644 index 0000000..c12224b --- /dev/null +++ b/Core/RLV/Chat/redirchat.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Chat +{ + public class RedirChat : RLV_UUID_flag_arg_yn + { + } +} diff --git a/Core/RLV/Chat/sendchat.cs b/Core/RLV/Chat/sendchat.cs new file mode 100644 index 0000000..247c39c --- /dev/null +++ b/Core/RLV/Chat/sendchat.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Chat +{ + public class SendChat : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Chat/sendgesture.cs b/Core/RLV/Chat/sendgesture.cs new file mode 100644 index 0000000..80b0d2a --- /dev/null +++ b/Core/RLV/Chat/sendgesture.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Chat +{ + public class SendGesture : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Emotes/emote.cs b/Core/RLV/Emotes/emote.cs new file mode 100644 index 0000000..b3de93f --- /dev/null +++ b/Core/RLV/Emotes/emote.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Emotes +{ + public class Emote : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Emotes/recvemote.cs b/Core/RLV/Emotes/recvemote.cs new file mode 100644 index 0000000..843e87b --- /dev/null +++ b/Core/RLV/Emotes/recvemote.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace BSB.RLV.Emotes +{ + public class RecvEmote : RLV_UUID_flag_optional_arg_yn + { + public override bool AsExceptionRule { get { return true; } } + public override bool CallFunction(string[] args) + { + if (args.Length == 1) + { + if (args[0] == "y") + { + bot.SetLock("recvemote_lock", false); + return true; + } + else if (args[0] == "n") + { + bot.SetLock("recvemote_lock", true); + return true; + } + return Failed("y/n only"); + } + else if (args.Length == 2) + { + if (UUID.TryParse(args[0], out UUID rec_uuid) == true) + { + return SetFlag(args[1], "set", rec_uuid); + } + return Failed("Unable to process target UUID"); + } + return Failed("Unsupported number of args"); + } + } +} diff --git a/Core/RLV/Emotes/recvemote_sec.cs b/Core/RLV/Emotes/recvemote_sec.cs new file mode 100644 index 0000000..6afa6e7 --- /dev/null +++ b/Core/RLV/Emotes/recvemote_sec.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Emotes +{ + public class RecvEmote_Sec : RLV_UUID_flag_yn + { + public override string SetFlagName { get { return "recvemote"; } } + } +} diff --git a/Core/RLV/Emotes/rediremote.cs b/Core/RLV/Emotes/rediremote.cs new file mode 100644 index 0000000..37570ab --- /dev/null +++ b/Core/RLV/Emotes/rediremote.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Emotes +{ + public class RedirEmote : RLV_UUID_flag_arg_yn + { + public override string SetFlagName { get { return CommandName; } } + } +} diff --git a/Core/RLV/InstantMessages/sendim.cs b/Core/RLV/InstantMessages/sendim.cs new file mode 100644 index 0000000..dc16a86 --- /dev/null +++ b/Core/RLV/InstantMessages/sendim.cs @@ -0,0 +1,37 @@ +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.InstantMessages +{ + public class SendIm : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + if (args.Length == 1) + { + if (args[0] == "y") + { + bot.SetLock("sendim_lock", false); + return true; + } + else if (args[0] == "n") + { + bot.SetLock("sendim_lock", true); + return true; + } + return Failed("y/n only"); + } + else if (args.Length == 2) + { + if (UUID.TryParse(args[0], out UUID rec_uuid) == true) + { + return SetFlag(args[1], "set", rec_uuid); + } + return Failed("Unable to process target UUID"); + } + return Failed("Unsupported number of args"); + } + } +} diff --git a/Core/RLV/InstantMessages/sendim_sec.cs b/Core/RLV/InstantMessages/sendim_sec.cs new file mode 100644 index 0000000..b381d58 --- /dev/null +++ b/Core/RLV/InstantMessages/sendim_sec.cs @@ -0,0 +1,18 @@ +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.InstantMessages +{ + public class SendIm_Sec : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + if (args[0] == "y") { bot.SetLock("sendim_lock", false); } + else if (args[0] == "n") { bot.SetLock("sendim_lock", true); } + else { return Failed("y/n only"); } + return true; + } + } +} \ No newline at end of file diff --git a/Core/RLV/Miscellaneous/clear.cs b/Core/RLV/Miscellaneous/clear.cs new file mode 100644 index 0000000..684586a --- /dev/null +++ b/Core/RLV/Miscellaneous/clear.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace BSB.RLV.Miscellaneous +{ + public class Clear : RLV_command + { + public override bool CallFunction(string[] args) + { + if(args.Length == 1) + { + string target = args[0]; + bot.Clearrule(false, caller_uuid); + bot.Clearrule(true, caller_uuid); + } + else + { + bot.Clearrule(false, caller_uuid); + bot.Clearrule(true, caller_uuid); + } + return true; + } + } +} diff --git a/Core/RLV/Miscellaneous/getstatus.cs b/Core/RLV/Miscellaneous/getstatus.cs new file mode 100644 index 0000000..2c44d6d --- /dev/null +++ b/Core/RLV/Miscellaneous/getstatus.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace BSB.RLV.Miscellaneous +{ + public class GetStatus : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + string part = "#"; + string split = "/"; + int channel = -1; + if (args.Length == 1) + { + _ = int.TryParse(args[0], out channel); + } + else if (args.Length == 2) + { + part = args[0]; + _ = int.TryParse(args[1], out channel); + } + else if (args.Length == 3) + { + part = args[0]; + split = args[1]; + _ = int.TryParse(args[2], out channel); + } + if (channel > -1) + { + string reply = ""; + string addon = ""; + if (bot.Getuuid_rules.ContainsKey(caller_uuid) == true) + { + foreach (string rule in bot.Getuuid_rules[caller_uuid].Keys) + { + if ((part == "#") || (rule.Contains(part) == true)) + { + reply = "" + reply + "" + addon + "" + rule + ""; + addon = split; + } + } + } + bot.GetClient.Self.Chat(reply, channel, ChatType.Normal); + return true; + } + return Failed("Invaild channel given"); + } + } +} diff --git a/Core/RLV/Miscellaneous/getstatusall.cs b/Core/RLV/Miscellaneous/getstatusall.cs new file mode 100644 index 0000000..2894aa5 --- /dev/null +++ b/Core/RLV/Miscellaneous/getstatusall.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace BSB.RLV.Miscellaneous +{ + public class GetStatusAll : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + if (base.CallFunction(args) == true) + { + string part = "#"; + string split = "/"; + int channel = -1; + if (args.Length == 1) + { + int.TryParse(args[0], out channel); + } + else if (args.Length == 2) + { + part = args[0]; + int.TryParse(args[1], out channel); + } + else if (args.Length == 3) + { + part = args[0]; + split = args[1]; + int.TryParse(args[2], out channel); + } + if (channel > -1) + { + string reply = ""; + string addon = ""; + foreach (UUID a in bot.Getuuid_rules.Keys) + { + foreach (string rule in bot.Getuuid_rules[a].Keys) + { + if ((part == "#") || (rule.Contains(part) == true)) + { + reply = "" + reply + "" + addon + "" + rule + ""; + addon = split; + } + } + } + bot.GetClient.Self.Chat(reply, channel, ChatType.Normal); + return true; + } + else + { + Failed("Invaild channel given"); + } + } + return false; + } + } +} diff --git a/Core/RLV/Miscellaneous/notify.cs b/Core/RLV/Miscellaneous/notify.cs new file mode 100644 index 0000000..8bec65a --- /dev/null +++ b/Core/RLV/Miscellaneous/notify.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Miscellaneous +{ + public class Notify : RLV_command + { + public override int MinArgs { get { return 2; } } + public override bool CallFunction(string[] args) + { + _ = int.TryParse(args[0], out int channel); + if (channel >= 0) + { + string word = "#"; + string action; + if (args.Length == 3) + { + word = args[1]; + action = args[2]; + } + else + { + action = args[1]; + } + if ((action == "add") || (action == "n")) + { + bot.NotifyUpdate(true, word, channel); + return true; + } + else if ((action == "rem") || (action == "y")) + { + bot.NotifyUpdate(false, word, channel); + return true; + } + else + { + return Failed("unknown action add/rem only! Action:" + action + " Channel:" + channel.ToString() + " Word: " + word + ""); + } + } + return Failed("Invaild channel given"); + } + } +} diff --git a/Core/RLV/Miscellaneous/permissive.cs b/Core/RLV/Miscellaneous/permissive.cs new file mode 100644 index 0000000..de87f3b --- /dev/null +++ b/Core/RLV/Miscellaneous/permissive.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace BSB.RLV.Miscellaneous +{ + public class Permissive : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + string action = args[0]; + if ((action == "y") || (action == "n")) + { + if (action == "n") + { + bot.SetPermissiveMode(caller_uuid, true); + } + else if (action == "y") + { + bot.SetPermissiveMode(caller_uuid, false); + } + return true; + } + return Failed("Permissive requires y/n!"); + } + } +} diff --git a/Core/RLV/Movement/adjustheight.cs b/Core/RLV/Movement/adjustheight.cs new file mode 100644 index 0000000..3275cef --- /dev/null +++ b/Core/RLV/Movement/adjustheight.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Movement +{ + public class AdjustHeight : RLV_command + { + public override int MinArgs { get { return 3; } } + public override bool CallFunction(string[] args) + { + int distance_pelvis_to_foot_in_meters = 0; + double factor = 0; + double offset = 0; + if (args[^1] == "force") + { + if(args.Length == 3) + { + int.TryParse(args[0], out distance_pelvis_to_foot_in_meters); + double.TryParse(args[1], out factor); + } + else if (args.Length == 4) + { + int.TryParse(args[0], out distance_pelvis_to_foot_in_meters); + double.TryParse(args[1], out factor); + double.TryParse(args[2], out offset); + } + Double hh = distance_pelvis_to_foot_in_meters * factor; + hh += offset; + if (hh > 2.0) + { + hh = 2.0; + } + else if (hh < -2.0) + { + hh = -2.0; + } + bot.GetClient.Self.SetHoverHeight(hh); + return true; + } + return Failed("Required magic word force missing"); + } + + } +} diff --git a/Core/RLV/Movement/alwaysrun.cs b/Core/RLV/Movement/alwaysrun.cs new file mode 100644 index 0000000..a92fc25 --- /dev/null +++ b/Core/RLV/Movement/alwaysrun.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Movement +{ + public class AlwaysRun : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Movement/fly.cs b/Core/RLV/Movement/fly.cs new file mode 100644 index 0000000..ac3711f --- /dev/null +++ b/Core/RLV/Movement/fly.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Movement +{ + public class Fly : RLV_UUID_flag_yn + { + } +} diff --git a/Core/RLV/Movement/setrot.cs b/Core/RLV/Movement/setrot.cs new file mode 100644 index 0000000..d7c138a --- /dev/null +++ b/Core/RLV/Movement/setrot.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Movement +{ + public class SetRot : RLV_command + { + public override int MinArgs { get { return 2; } } + public override bool CallFunction(string[] args) + { + if (base.CallFunction(args) == true) + { + if (args[1] == "force") + { + return bot.GetCommandsInterface.Call("rotateto", args[0]); + } + return Failed("magic word force is required [dont ask me]"); + } + return false; + } + } +} diff --git a/Core/RLV/Movement/temprun.cs b/Core/RLV/Movement/temprun.cs new file mode 100644 index 0000000..f46f278 --- /dev/null +++ b/Core/RLV/Movement/temprun.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Movement +{ + public class TempRun : RLV_UUID_flag_yn + { + + } +} diff --git a/Core/RLV/PrivateChannels/sendchannel.cs b/Core/RLV/PrivateChannels/sendchannel.cs new file mode 100644 index 0000000..c87ba72 --- /dev/null +++ b/Core/RLV/PrivateChannels/sendchannel.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.PrivateChannels +{ + public class SendChannel : RLV_UUID_flag_arg_yn + { + } +} diff --git a/Core/RLV/PrivateChannels/sendchannel_except.cs b/Core/RLV/PrivateChannels/sendchannel_except.cs new file mode 100644 index 0000000..9403127 --- /dev/null +++ b/Core/RLV/PrivateChannels/sendchannel_except.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.PrivateChannels +{ + public class SendChannel_Except : RLV_UUID_flag_arg_yn + { + public override string SetFlagName { get { return "sendchannel"; } } + } +} diff --git a/Core/RLV/PrivateChannels/sendchannel_sec.cs b/Core/RLV/PrivateChannels/sendchannel_sec.cs new file mode 100644 index 0000000..b43223f --- /dev/null +++ b/Core/RLV/PrivateChannels/sendchannel_sec.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.PrivateChannels +{ + public class SendChannel_Sec : RLV_UUID_flag_arg_yn + { + public override string SetFlagName { get { return "sendchannel"; } } + } +} diff --git a/Core/RLV/RLV_commandlib.cs b/Core/RLV/RLV_commandlib.cs new file mode 100644 index 0000000..8de9e34 --- /dev/null +++ b/Core/RLV/RLV_commandlib.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using OpenMetaverse; +using BSB.bottypes; +using BetterSecondBotShared.API; +using BetterSecondBotShared.logs; + +namespace BSB.RLV +{ + public class RLVcontrol : API_supported_interface + { + protected RLVbot bot; + protected UUID last_caller_uuid = UUID.Zero; + protected string last_caller_name = ""; + + public void SetCallerDetails(UUID callerUUID, string callerName) + { + last_caller_name = callerName; + last_caller_uuid = callerUUID; + } + + public RLVcontrol(RLVbot linktobot) + { + bot = linktobot; + API_type = typeof(RLV_command); + LoadCommandsList(); + } + + protected List suppress_warning = new List(); + public bool Call(string command, string[] args) + { + command = command.ToLowerInvariant(); + RLV_command cmd = (RLV_command)GetCommand(command); + if (cmd != null) + { + if (args.Length >= cmd.MinArgs) + { + cmd.Setup(bot, last_caller_uuid, last_caller_name); + return cmd.CallFunction(args); + } + else + { + ConsoleLog.Warn("[RLVapi/Failed] " + command + ": Incorrect number of args"); + } + } + else + { + if(suppress_warning.Contains(command) == false) + { + suppress_warning.Add(command); + ConsoleLog.Warn("[RLVapi/Failed] " + command + ": I have no fucking idea what you are talking about"); + } + } + return false; + } + } + public abstract class RLV_command_1arg : RLV_command + { + public override int MinArgs { get { return 1; } } + } + public abstract class RLV_command : API_interface + { + public override string Helpfile { get { return "[Class helpfile] an RLV_command."; } } + public virtual string SetFlagName { get { return CommandName.ToLowerInvariant(); } } + protected RLVbot bot = null; + protected UUID caller_uuid = UUID.Zero; + protected string caller_name = ""; + + public virtual bool AsExceptionRule { get { return false; } } + + + public void Setup(RLVbot setBot, UUID callerUUID, string callerName) + { + bot = setBot; + caller_name = callerName; + caller_uuid = callerUUID; + } + + public virtual bool CallFunction(string[] args) + { + return Failed("Function not overridden"); + } + public bool Failed(string why_failed) + { + ConsoleLog.Warn("[RLVapi/Failed] " + CommandName + ": " + why_failed); + return false; + } + + protected bool SetFlag(string signal,string arg) + { + return SetFlag(signal, arg, caller_uuid); + } + protected bool SetFlag(string signal, string arg,UUID caller) + { + if ((signal == "y") || (signal == "rem")) + { + return bot.RemoveRule(AsExceptionRule, SetFlagName, caller); + } + else if ((signal == "n") || (signal == "add")) + { + return bot.UpdateRule(AsExceptionRule, SetFlagName, arg, caller); + } + return Failed("[RLV/Debug] arg only accepts y/n or add/rem for control signaling [Sig: " + signal+" Arg: "+arg+"]"); + } + } + public abstract class RLV_1arg_force : RLV_command + { + public override string Helpfile { get { return "[Class helpfile] Requires you send =force at the end of the command..."; } } + public override string[] ArgTypes { get { return new[] { "TEXT" }; } } + public override string[] ArgHints { get { return new[] { "The word force"}; } } + public override int MinArgs { get { return 1; } } + public override bool CallFunction(string[] args) + { + if (args.Length > 0) + { + if (args[0] == "force") return true; + return Failed("Magic word force is missing"); + } + return Failed("[RLV/WARN] RLV_1arg_force invaild number of args"); + } + } + + + public abstract class RLV_UUID_flag_4arg_yn : RLV_UUID_flag_optional_arg_yn + { + public override int MinArgs { get { return 4; } } + public override string Helpfile { get { return "[Class helpfile] RLV / 4arg"; } } + public override string[] ArgTypes { get { return new[] { "Unknown", "Unknown", "Unknown", "Unknown" }; } } + public override string[] ArgHints { get { return new[] { "Unknown", "Unknown", "Unknown", "Unknown" }; } } + } + + public abstract class RLV_UUID_flag_optional_arg_yn : RLV_command + { + public override int MinArgs { get { return 1; } } + public override string Helpfile { get { return "[Class helpfile] RLV / UUID_flag_optional_arg_yn"; } } + public override string[] ArgTypes { get { return new[] { "Unknown", "Unknown"}; } } + public override string[] ArgHints { get { return new[] { "Unknown", "Unknown" }; } } + + public override bool CallFunction(string[] args) + { + if (args.Length == 2) + { + return SetFlag(args[^1], args[0]); + } + else if (args.Length > 0) + { + return SetFlag(args[^1], "set"); + } + return Failed("[RLV/WARN] RLV_UUID_flag_optional_arg_yn invaild number of args"); + } + } + + public abstract class RLV_UUID_flag_yn : RLV_command + { + public override int MinArgs { get { return 1; } } + public override string Helpfile { get { return "[Class helpfile] RLV / UUID_flag_yn "; } } + public override string[] ArgTypes { get { return new[] { "Unknown", "Unknown" }; } } + public override string[] ArgHints { get { return new[] { "Unknown", "Unknown" }; } } + public override bool CallFunction(string[] args) + { + if (args.Length >= 1) + { + return SetFlag(args[0], "set"); + } + return Failed("[RLV/WARN] RLV_UUID_flag_yn invaild number of args"); + } + } + public abstract class RLV_UUID_flag_get : RLV_command + { + public abstract string LookupFlag { get; } + public override int MinArgs { get { return 1; } } + public override string Helpfile { get { return "[Class helpfile]
Gets the flag value "+LookupFlag+" and returns it on channel [ARG 1]"; } } + public override string[] ArgTypes { get { return new[] { "Number"}; } } + public override string[] ArgHints { get { return new[] { "Channel" }; } } + public override bool CallFunction(string[] args) + { + if (args.Length == 1) + { + _ = int.TryParse(args[0], out int channel); + if (channel >= 0) + { + if (bot.Getuuid_rules.ContainsKey(caller_uuid) == true) + { + if (bot.Getuuid_rules[caller_uuid].ContainsKey(LookupFlag) == true) + { + bot.GetClient.Self.Chat(bot.Getuuid_rules[caller_uuid][LookupFlag], channel, ChatType.Normal); + } + } + return true; + } + else + { + return Failed("Channel number not vaild"); + } + } + return Failed("[RLV/WARN] RLV_UUID_flag_get invaild number of args"); + } + } + public abstract class RLV_UUID_flag_arg_yn : RLV_command + { + public override int MinArgs { get { return 2; } } + public override string Helpfile { get { return "[Class helpfile]
RLV / UUID_flag_arg_yn
When clearing please set [ARG 1] to Anything
Flags are tracked per object"; } } + public override string[] ArgTypes { get { return new[] { "Mixed", "Flag" }; } } + public override string[] ArgHints { get { return new[] { "The arg value you are setting", "[Y/N] or [Rem/Add]" }; } } + public override bool CallFunction(string[] args) + { + if (args.Length == 2) + { + return SetFlag(args[1], args[0]); + } + return Failed("[RLV/WARN] RLV_UUID_flag_arg_yn invaild number of args"); + } + } + + +} diff --git a/Core/RLV/Sitting/getsitid.cs b/Core/RLV/Sitting/getsitid.cs new file mode 100644 index 0000000..6c0907b --- /dev/null +++ b/Core/RLV/Sitting/getsitid.cs @@ -0,0 +1,31 @@ +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Sitting +{ + class GetSitId : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + _ = int.TryParse(args[0], out int channel); + if (channel > -1) + { + if(bot.GetClient.Self.SittingOn > 0) + { + if(bot.GetClient.Network.CurrentSim.ObjectsPrimitives.ContainsKey(bot.GetClient.Self.SittingOn)) + { + Primitive obj = bot.GetClient.Network.CurrentSim.ObjectsPrimitives[bot.GetClient.Self.SittingOn]; + + bot.GetClient.Self.Chat(obj.ID.ToString(), channel, OpenMetaverse.ChatType.Normal); + return true; + } + } + bot.GetClient.Self.Chat(UUID.Zero.ToString(), channel, OpenMetaverse.ChatType.Normal); + return true; + } + return Failed("Invaild channel given"); + } + } +} diff --git a/Core/RLV/Sitting/sit.cs b/Core/RLV/Sitting/sit.cs new file mode 100644 index 0000000..45098fb --- /dev/null +++ b/Core/RLV/Sitting/sit.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Sitting +{ + public class Sit : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + if (args.Length == 1) + { + return SetFlag(args[0], "set"); + } + else if (args.Length == 2) + { + if (args[1] == "force") + { + return bot.GetCommandsInterface.Call("sit", args[0]); + } + else + { + return Failed("Magic keyword force missing."); + } + } + return Failed("Badly formatted command."); + } + } +} diff --git a/Core/RLV/Sitting/unsit.cs b/Core/RLV/Sitting/unsit.cs new file mode 100644 index 0000000..06469bb --- /dev/null +++ b/Core/RLV/Sitting/unsit.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Sitting +{ + public class UnSit : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + if (args[0] == "force") + { + return bot.GetCommandsInterface.Call("stand", ""); + } + else if ((args[0] == "y") || (args[0] == "n")) + { + return SetFlag(args[0], "set"); + } + return Failed("Magic keyword force missing."); + } + } +} diff --git a/Core/RLV/Version/version.cs b/Core/RLV/Version/version.cs new file mode 100644 index 0000000..3c1d8ef --- /dev/null +++ b/Core/RLV/Version/version.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Version +{ + public class Version : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + _ = int.TryParse(args[0], out int channel); + if (channel >= 0) + { + bot.GetClient.Self.Chat("RestrainedLife BOT (RLVa 2.9)", channel, OpenMetaverse.ChatType.Normal); + return true; + } + return Failed("Invaild channel given"); + } + } +} diff --git a/Core/RLV/Version/versionnew.cs b/Core/RLV/Version/versionnew.cs new file mode 100644 index 0000000..51c6432 --- /dev/null +++ b/Core/RLV/Version/versionnew.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Version +{ + public class VersionNew : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + _ = int.TryParse(args[0], out int channel); + if (channel >= 0) + { + bot.GetClient.Self.Chat("RestrainedLove BOT (RLVa 2.9)", channel, OpenMetaverse.ChatType.Normal); + return true; + } + return Failed("Invaild channel given"); + } + } +} diff --git a/Core/RLV/Version/versionnum.cs b/Core/RLV/Version/versionnum.cs new file mode 100644 index 0000000..6a89e13 --- /dev/null +++ b/Core/RLV/Version/versionnum.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.RLV.Version +{ + public class VersionNum : RLV_command_1arg + { + public override bool CallFunction(string[] args) + { + _ = int.TryParse(args[0], out int channel); + if (channel >= 0) + { + bot.GetClient.Self.Chat("209000", channel, OpenMetaverse.ChatType.Normal); + return true; + } + return Failed("Invaild channel given"); + } + } +} diff --git a/Core/Static/Inventory.cs b/Core/Static/Inventory.cs new file mode 100644 index 0000000..d349446 --- /dev/null +++ b/Core/Static/Inventory.cs @@ -0,0 +1,96 @@ +using BSB.bottypes; +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.Static +{ + public static class HelperInventory + { + public static InventoryBase FindFolder(CommandsBot bot, InventoryFolder current,UUID target) + { + List T = bot.GetClient.Inventory.Store.GetContents(current); + InventoryBase Found = null; + foreach (InventoryBase R in T) + { + if(R.UUID == target) + { + Found = R; + break; + } + else + { + InventoryBase FoundSub = FindFolder(bot, (InventoryFolder)R, target); + if(FoundSub != null) + { + Found = FoundSub; + break; + } + } + } + return Found; + } + public static string[] MapFolderInventory(CommandsBot bot,UUID folder) + { + List entrys = new List(); + entrys.Add("folderinventory=" + folder.ToString() + ""); + List contents = bot.GetClient.Inventory.FolderContents(folder, bot.GetClient.Self.AgentID, true, true, InventorySortOrder.ByDate, 40 * 1000); + if (contents != null) + { + if (contents.Count > 0) + { + StringBuilder B = new StringBuilder(); + foreach (InventoryBase item in contents) + { + B.Append(item.Name); + B.Append("###"); + B.Append(item.UUID.ToString()); + B.Append("###"); + B.Append(item.GetType().Name); + } + entrys.Add(B.ToString()); + } + } + return entrys.ToArray(); + } + public static string MapFolder(CommandsBot bot, bool as_csv) + { + StringBuilder B = new StringBuilder(); + DoMapFolder(bot, 0, bot.GetClient.Inventory.Store.RootFolder, as_csv, "", B); + return B.ToString(); + } + public static void DoMapFolder(CommandsBot bot,int level, InventoryFolder folder,bool as_csv,string addon, StringBuilder reply) + { + List T = bot.GetClient.Inventory.Store.GetContents(folder); + foreach (InventoryBase R in T) + { + if (as_csv == false) + { + reply.Append("" + Spaces(level) + "|- " + R.Name + " [" + R.UUID.ToString() + "]\n\r"); + } + else + { + reply.Append(addon); + reply.Append(R.Name); + reply.Append("###"); + reply.Append(level.ToString()); + reply.Append("###"); + reply.Append(R.UUID.ToString()); + addon = ","; + } + DoMapFolder(bot, level + 1, (InventoryFolder)R, as_csv, addon, reply); + } + } + public static string Spaces(int counter) + { + string reply = ""; + while (counter > 0) + { + reply += " "; + counter--; + } + return reply; + } + } +} diff --git a/Core/Static/parcel.cs b/Core/Static/parcel.cs new file mode 100644 index 0000000..1d276a9 --- /dev/null +++ b/Core/Static/parcel.cs @@ -0,0 +1,39 @@ +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB +{ + public class parcel_static + { + public static string[] get_flag_names() + { + System.Type enumType = typeof(ParcelFlags); + List flagnames = new List(System.Enum.GetNames(enumType)); + flagnames.Remove("None"); + return flagnames.ToArray(); + } + public static Dictionary get_flags_list() + { + Dictionary flags = new Dictionary(); + + System.Type enumType = typeof(ParcelFlags); + System.Type enumUnderlyingType = System.Enum.GetUnderlyingType(enumType); + System.Array enumValues = System.Enum.GetValues(enumType); + System.Array enumNames = System.Enum.GetNames(enumType); + for (int i = 0; i < enumValues.Length; i++) + { + object value = enumValues.GetValue(i); + ParcelFlags realValue = (ParcelFlags)value; + if (realValue != 0) + { + string name = (string)enumNames.GetValue(i); + flags.Add(name, realValue); + } + } + return flags; + } + + } +} diff --git a/Core/bottypes/AVstorageBot.cs b/Core/bottypes/AVstorageBot.cs new file mode 100644 index 0000000..effe68b --- /dev/null +++ b/Core/bottypes/AVstorageBot.cs @@ -0,0 +1,236 @@ +using BetterSecondBotShared.Static; +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BSB.bottypes +{ + public abstract class AVstorageBot : AtHome + { + protected Dictionary AvatarStorageLastUsed = new Dictionary(); + protected Dictionary AvatarName2Key = new Dictionary(); + protected Dictionary AvatarKey2Name = new Dictionary(); + protected Dictionary> PendingAvatarFinds_vianame = new Dictionary>(); + protected Dictionary> PendingAvatarFinds_viauuid = new Dictionary>(); + + protected long max_wait_for_retry_name2key = 30; // How long to wait before we attempt a name to key lookup again for the same avatar (in secs) + protected long max_wait_for_retry_key2name = 10; // How long to wait before we attempt a key to name lookup again for the same avatar (in secs) + protected int max_retry_attempts = 2; // how many times should we attempt to get the avatar before giving up. + protected long max_db_storage_age = 240; // how long should we keep an avatar in memory before clearing (in secs) [now - last accessed] + protected bool show_av_storage_info_in_status = true; + + public override string GetStatus() + { + cleanupAvatarStorage(); + ReTriggerAvLookups(); + string reply = ""; + if (show_av_storage_info_in_status == true) + { + int count = PendingAvatarFinds_vianame.Count + PendingAvatarFinds_viauuid.Count; + if (count > 0) + { + reply = count.ToString() + " pending"; + } + if (AvatarStorageLastUsed.Count > 0) + { + if (reply != "") reply += " , "; + reply += AvatarStorageLastUsed.Count.ToString() + " loaded"; + } + if (reply != "") + { + reply = " (AVStorage: " + reply + ")"; + } + else + { + reply = " (AVStorage: Empty)"; + } + } + return base.GetStatus() + reply; + } + + protected void ReTriggerUUIDtoName() + { + long now = helpers.UnixTimeNow(); + List retrigger_key2name = new List(); + List giveup_key2name = new List(); + foreach (KeyValuePair> pair in PendingAvatarFinds_viauuid) + { + long dif = now - pair.Value.Key; + if (dif > max_wait_for_retry_key2name) + { + if (pair.Value.Value < max_retry_attempts) + { + retrigger_key2name.Add(pair.Key); + } + else + { + giveup_key2name.Add(pair.Key); + } + } + } + if(retrigger_key2name.Count > 0) + { + foreach (UUID avuuid in retrigger_key2name) + { + PendingAvatarFinds_viauuid[avuuid] = new KeyValuePair(now, PendingAvatarFinds_viauuid[avuuid].Value + 1); + } + Client.Avatars.RequestAvatarNames(retrigger_key2name); + } + foreach (UUID avuuid in giveup_key2name) + { + PendingAvatarFinds_viauuid.Remove(avuuid); + } + } + + protected void ReTriggerNameToUUID() + { + long now = helpers.UnixTimeNow(); + List retrigger_name2key = new List(); + List giveup_name2key = new List(); + foreach (KeyValuePair> pair in PendingAvatarFinds_vianame) + { + long dif = now - pair.Value.Key; + if (dif > max_wait_for_retry_key2name) + { + if (pair.Value.Value < max_retry_attempts) + { + retrigger_name2key.Add(pair.Key); + } + else + { + giveup_name2key.Add(pair.Key); + } + } + } + + foreach (string avname in retrigger_name2key) + { + Client.Directory.StartPeopleSearch(avname, 0); + PendingAvatarFinds_vianame[avname] = new KeyValuePair(now, PendingAvatarFinds_vianame[avname].Value + 1); + + } + foreach (string avname in giveup_name2key) + { + PendingAvatarFinds_vianame.Remove(avname); + } + } + + protected void ReTriggerAvLookups() + { + ReTriggerUUIDtoName(); + ReTriggerNameToUUID(); + } + + protected void cleanupAvatarStorage() + { + List PurgeAvatars = new List(); + long now = helpers.UnixTimeNow(); + foreach (KeyValuePair entry in AvatarStorageLastUsed) + { + long dif = now - entry.Value; + if(dif > max_db_storage_age) + { + PurgeAvatars.Add(entry.Key); + } + } + foreach(string purge in PurgeAvatars) + { + AvatarStorageLastUsed.Remove(purge); + UUID avuuid = AvatarName2Key[purge]; + AvatarName2Key.Remove(purge); + AvatarKey2Name.Remove(avuuid); + } + } + + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + if (reconnect == false) + { + Client.Avatars.UUIDNameReply += Key2NameEvent; + Client.Directory.DirPeopleReply += Name2KeyEvent; + } + } + + protected void Key2NameEvent(object sender,UUIDNameReplyEventArgs e) + { + foreach(KeyValuePair pair in e.Names) + { + AddAvatarToDB(pair.Key, pair.Value); + } + } + protected void Name2KeyEvent(object sender, DirPeopleReplyEventArgs e) + { + foreach (DirectoryManager.AgentSearchData pair in e.MatchedPeople) + { + AddAvatarToDB(pair.AgentID, "" + pair.FirstName + " " + pair.LastName + ""); + } + } + + protected void AddAvatarToDB(UUID av_uuid,string av_name) + { + if ((av_name != "null") && (av_name != null)) + { + if (AvatarStorageLastUsed.ContainsKey(av_name) == false) + { + AvatarName2Key.Add(av_name, av_uuid); + AvatarKey2Name.Add(av_uuid, av_name); + AvatarStorageLastUsed.Add(av_name, helpers.UnixTimeNow()); + if (PendingAvatarFinds_vianame.ContainsKey(av_name) == true) + { + PendingAvatarFinds_vianame.Remove(av_name); + } + if (PendingAvatarFinds_viauuid.ContainsKey(av_uuid) == true) + { + PendingAvatarFinds_viauuid.Remove(av_uuid); + } + } + } + } + + public string FindAvatarName2Key(string avatar_name) + { + List bits = avatar_name.Split(' ').ToList(); + if(bits.Count() == 1) + { + bits.Add("Resident"); + } + avatar_name = bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper(); + long now = helpers.UnixTimeNow(); + if (AvatarStorageLastUsed.ContainsKey(avatar_name) == true) + { + AvatarStorageLastUsed[avatar_name] = now; + return AvatarName2Key[avatar_name].ToString(); + } + else + { + if(PendingAvatarFinds_vianame.ContainsKey(avatar_name) == false) + { + PendingAvatarFinds_vianame.Add(avatar_name, new KeyValuePair(now, 0)); + Client.Directory.StartPeopleSearch(avatar_name, 0); + } + } + return "lookup"; + } + public string FindAvatarKey2Name(UUID avatar_uuid) + { + long now = helpers.UnixTimeNow(); + if (AvatarKey2Name.ContainsKey(avatar_uuid) == true) + { + AvatarStorageLastUsed[AvatarKey2Name[avatar_uuid]] = now; + return AvatarKey2Name[avatar_uuid]; + } + else + { + if (PendingAvatarFinds_viauuid.ContainsKey(avatar_uuid) == false) + { + PendingAvatarFinds_viauuid.Add(avatar_uuid, new KeyValuePair(now, 0)); + Client.Avatars.RequestAvatarName(avatar_uuid); + } + return "lookup"; + } + } + } +} diff --git a/Core/bottypes/AnimationsBot.cs b/Core/bottypes/AnimationsBot.cs new file mode 100644 index 0000000..0b1ccac --- /dev/null +++ b/Core/bottypes/AnimationsBot.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace BSB.bottypes +{ + public abstract class AnimationsBot : VirtualsBot + { + protected List active_animations = new List(); + + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + if (reconnect == false) + { + Client.Avatars.AvatarAnimation += AvatarAnimationHandler; + } + } + + public override void ResetAnimations() + { + foreach (UUID ani in active_animations) + { + Client.Self.AnimationStop(ani, true); + } + active_animations.Clear(); + } + + protected override void AvatarAnimationHandler(object sender, AvatarAnimationEventArgs e) + { + if (e.AvatarID == Client.Self.AgentID) + { + foreach (Animation startani in e.Animations) + { + if (active_animations.Contains(startani.AnimationID) == false) + { + active_animations.Add(startani.AnimationID); + } + } + } + } + } +} diff --git a/Core/bottypes/AwaitReplyEventBot.cs b/Core/bottypes/AwaitReplyEventBot.cs new file mode 100644 index 0000000..6d3ae3f --- /dev/null +++ b/Core/bottypes/AwaitReplyEventBot.cs @@ -0,0 +1,129 @@ +using BSB.Commands; +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace BSB.bottypes +{ + public abstract class AwaitReplyEventBot : CommandsBot + { + public override List GetActiveGroupchatSessions { get { return active_group_chat_sessions; } } + + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + if (reconnect == false) + { + Client.Groups.GroupRoleDataReply += GroupRolesData; + Client.Self.GroupChatJoined += GroupchatJoinHandler; + Client.Groups.GroupMembersReply += GroupMembersReplyHandler; + } + } + protected override void GroupMembersReplyHandler(object sender, GroupMembersReplyEventArgs e) + { + base.GroupMembersReplyHandler(sender, e); + if (await_events.ContainsKey("groupmembersreply") == true) + { + List PurgeAwaiters = new List(); + foreach (KeyValuePair> await_reply in await_events["groupmembersreply"]) + { + // eventid + // KeyValuePair + // Args: Group, Message + if (await_reply.Value.Value.Length == 2) + { + if (UUID.TryParse(await_reply.Value.Value[0], out UUID test_group) == true) + { + if (test_group == e.GroupID) + { + // oh shit its for me + PurgeAwaiters.Add(await_reply.Key); + await_reply.Value.Key.Callback(await_reply.Value.Value, e); + } + } + } + } + foreach (string eventid in PurgeAwaiters) + { + await_event_ages.Remove(eventid); + await_event_idtolistener.Remove(eventid); + await_events["groupmembersreply"].Remove(eventid); + } + } + } + + protected virtual void GroupchatJoinHandler(object sender, GroupChatJoinedEventArgs e) + { + if (active_group_chat_sessions.Contains(e.SessionID) == false) + { + if (e.Success == true) + { + active_group_chat_sessions.Add(e.SessionID); + } + } + if (await_events.ContainsKey("groupchatjoin") == true) + { + List PurgeAwaiters = new List(); + foreach (KeyValuePair> await_reply in await_events["groupchatjoin"]) + { + // eventid + // KeyValuePair + // Args: Group, Message + if (await_reply.Value.Value.Length == 2) + { + if (UUID.TryParse(await_reply.Value.Value[0], out UUID test_group) == true) + { + if (test_group == e.SessionID) + { + // oh shit its for me + PurgeAwaiters.Add(await_reply.Key); + await_reply.Value.Key.Callback(await_reply.Value.Value, e); + } + } + } + } + foreach (string eventid in PurgeAwaiters) + { + await_event_ages.Remove(eventid); + await_event_idtolistener.Remove(eventid); + await_events["groupchatjoin"].Remove(eventid); + } + } + } + protected void GroupRolesData(object sender,GroupRolesDataReplyEventArgs e) + { + if(await_events.ContainsKey("grouproles") == true) + { + List PurgeAwaiters = new List(); + foreach(KeyValuePair> await_reply in await_events["grouproles"]) + { + // eventid + // KeyValuePair + // Args: ReplyConfig, GroupUUID + if (await_reply.Value.Value.Length == 2) + { + if(UUID.TryParse(await_reply.Value.Value[1], out UUID test_group) == true) + { + if(test_group == e.GroupID) + { + // oh shit its for me + PurgeAwaiters.Add(await_reply.Key); + await_reply.Value.Key.Callback(await_reply.Value.Value,e); + } + } + } + } + foreach(string eventid in PurgeAwaiters) + { + await_event_ages.Remove(eventid); + await_event_idtolistener.Remove(eventid); + await_events["grouproles"].Remove(eventid); + } + + } + } + + + } +} \ No newline at end of file diff --git a/Core/bottypes/CommandsBot.cs b/Core/bottypes/CommandsBot.cs new file mode 100644 index 0000000..8f4e065 --- /dev/null +++ b/Core/bottypes/CommandsBot.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using OpenMetaverse; +using OpenMetaverse.Assets; +using BetterSecondBotShared.Static; +using BetterSecondBotShared.logs; + +namespace BSB.bottypes +{ + public abstract class CommandsBot : AVstorageBot + { + public Commands.CoreCommandsInterface GetCommandsInterface { get { return CommandsInterface; } } + protected Commands.CoreCommandsInterface CommandsInterface; + public CommandsBot() + { + CommandsInterface = new Commands.CoreCommandsInterface(this); + } + protected Dictionary notecards_content = new Dictionary(); + protected Dictionary group_invite_lockout_timer = new Dictionary(); + protected long last_cleanup_commands; + + public override string GetStatus() + { + long dif = helpers.UnixTimeNow() - last_cleanup_commands; + if (dif > 30) + { + last_cleanup_commands = helpers.UnixTimeNow(); + PurgeOldGroupinviteLockouts(); + } + CommandsInterface.StatusTick(); + return base.GetStatus(); + } + public bool GetAllowGroupInvite(UUID avatar) + { + return !group_invite_lockout_timer.ContainsKey(avatar); + } + + public void GroupInviteLockoutArm(UUID avatar) + { + if (group_invite_lockout_timer.ContainsKey(avatar) == false) + { + group_invite_lockout_timer.Add(avatar,0); + } + group_invite_lockout_timer[avatar] = helpers.UnixTimeNow(); + } + protected void PurgeOldGroupinviteLockouts() + { + long now = helpers.UnixTimeNow(); + List clear_locks = new List(); + foreach(KeyValuePair Glock in group_invite_lockout_timer) + { + long dif = now - Glock.Value; + if(dif >= 120) + { + clear_locks.Add(Glock.Key); + } + } + foreach(UUID glock in clear_locks) + { + group_invite_lockout_timer.Remove(glock); + } + } + + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + if (reconnect == false) + { + CommandsInterface = new Commands.CoreCommandsInterface(this); + } + } + + protected override void CoreCommandLib(UUID fromUUID, bool from_master, string command, string arg, string signing_code) + { + if (CommandsInterface != null) + { + if (signing_code != "") + { + string raw = "" + command + "" + arg + "" + myconfig.code + ""; + string hashcheck = helpers.GetSHA1(raw); + if (hashcheck == signing_code) + { + // signed commands + CommandsInterface.Call(command, arg); + } + } + else if (from_master == true) + { + CommandsInterface.Call(command, arg); + } + } + } + public void NotecardAddContent(string target_notecard_storage_id, string content) + { + NotecardAddContent(target_notecard_storage_id, content, true, "\n\r"); + } + public void NotecardAddContent(string target_notecard_storage_id, string content, bool add_newline) + { + NotecardAddContent(target_notecard_storage_id, content, add_newline, "\n\r"); + } + public void NotecardAddContent(string target_notecard_storage_id,string content,bool add_newline,string newlinevalue) + { + if(notecards_content.ContainsKey(target_notecard_storage_id) == false) + { + notecards_content.Add(target_notecard_storage_id, ""); + } + if (add_newline == true) + { + content = "" + newlinevalue + "" + content; + } + notecards_content[target_notecard_storage_id] = notecards_content[target_notecard_storage_id] + ""+content; + } + public string GetNotecardContent(string target_notecard_storage_id) + { + if (notecards_content.ContainsKey(target_notecard_storage_id) == true) + { + return notecards_content[target_notecard_storage_id]; + } + return null; + } + + public void ClearNotecardStorage(string target_notecard_storage_id) + { + if (notecards_content.ContainsKey(target_notecard_storage_id) == true) + { + notecards_content.Remove(target_notecard_storage_id); + } + } + + public bool SendNotecard(string name, string content, UUID sendToUUID) + { + bool returnstatus = true; + name = name + " " + DateTime.Now; + Client.Inventory.RequestCreateItem( + Client.Inventory.FindFolderForType(AssetType.Notecard), + name, + name + " Created via SecondBot notecard API", + AssetType.Notecard, + UUID.Random(), + InventoryType.Notecard, + PermissionMask.All, + (bool Success, InventoryItem item) => + { + if (Success) + { + AssetNotecard empty = new AssetNotecard { BodyText = "\n" }; + empty.Encode(); + Client.Inventory.RequestUploadNotecardAsset(empty.AssetData, item.UUID, + (bool emptySuccess, string emptyStatus, UUID emptyItemID, UUID emptyAssetID) => + { + if (emptySuccess) + { + empty.BodyText = content; + empty.Encode(); + Client.Inventory.RequestUploadNotecardAsset(empty.AssetData, emptyItemID, + (bool finalSuccess, string finalStatus, UUID finalItemID, UUID finalID) => + { + if (finalSuccess) + { + Client.Inventory.GiveItem(finalItemID, name, AssetType.Notecard, sendToUUID, false); + } + else + { + returnstatus = false; + ConsoleLog.Warn("Unable to request notecard upload"); + } + + }); + } + else + { + ConsoleLog.Crit("The fuck empty success notecard create"); + returnstatus = false; + } + }); + } + else + { + ConsoleLog.Warn("Unable to find default notecards folder"); + returnstatus = false; + } + } + ); + return returnstatus; + } + } +} diff --git a/Core/bottypes/DialogsBot.cs b/Core/bottypes/DialogsBot.cs new file mode 100644 index 0000000..0a27419 --- /dev/null +++ b/Core/bottypes/DialogsBot.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; +using Discord; +using Discord.Webhook; +using BetterSecondBotShared.Json; +using BetterSecondBotShared.Static; +using Discord.WebSocket; +using System.Threading.Tasks; +using System.Threading; + +namespace BSB.bottypes +{ + public abstract class DialogsBot : DiscordBot + { + protected bool TrackDialogs = false; + protected int RelayDialogsToChannel = 0; + protected UUID RelayDialogsToAvatar = UUID.Zero; + protected string RelayDialogsToHttp = ""; + + public override void SetRelayDialogsChannel(int channel) + { + RelayDialogsToChannel = channel; + } + public override void SetRelayDialogsAvatar(UUID avatar) + { + RelayDialogsToAvatar = avatar; + } + public override void SetRelayDialogsHTTP(string httpurl) + { + RelayDialogsToHttp = httpurl; + } + public override void SetTrackDialogs(bool status) + { + TrackDialogs = status; + } + + public override bool DialogReply(int DialogID,string button) + { + if(pending_dialogs.ContainsKey(DialogID) == true) + { + int ButtonIndex = pending_dialogs[DialogID].ButtonLabels.IndexOf(button); + if(ButtonIndex != -1) + { + Client.Self.ReplyToScriptDialog(pending_dialogs[DialogID].Channel, ButtonIndex, button, pending_dialogs[DialogID].ObjectID); + pending_dialogs.Remove(DialogID); + pending_dialogs_age.Remove(DialogID); + return true; + } + } + return false; + } + + protected Dictionary pending_dialogs = new Dictionary(); + protected Dictionary pending_dialogs_age = new Dictionary(); + + public override string GetStatus() + { + List purgeDialogs = new List(); + long now = helpers.UnixTimeNow(); + foreach(KeyValuePair Dlog in pending_dialogs_age) + { + long dif = now - Dlog.Value; + if(dif >= (60*2)) + { + purgeDialogs.Add(Dlog.Key); + } + } + foreach(int P in purgeDialogs) + { + pending_dialogs.Remove(P); + pending_dialogs_age.Remove(P); + } + return base.GetStatus(); + } + + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + if (reconnect == false) + { + Client.Self.ScriptDialog += DialogsHandler; + } + } + + protected void DialogsHandler(object sender,ScriptDialogEventArgs e) + { + if (TrackDialogs == true) + { + int dialogID = AddPendingDialog(e); + string Dmess = e.Message; + if(Dmess.Length > 30) + { + Dmess = Dmess.Substring(0, 30); + } + string DialogMessage = "NewDialog###" + dialogID.ToString() + "###"+ Dmess + "###" + String.Join("~|~",e.ButtonLabels) + ""; + if (RelayDialogsToChannel != 0) + { + CommandsInterface.SmartCommandReply(RelayDialogsToChannel.ToString(), DialogMessage,"NewDialogNotice"); + } + if(RelayDialogsToAvatar != UUID.Zero) + { + CommandsInterface.SmartCommandReply(RelayDialogsToAvatar.ToString(), DialogMessage, "NewDialogNotice"); + } + if(RelayDialogsToHttp != null) + { + if (RelayDialogsToHttp.StartsWith("http") == true) + { + CommandsInterface.SmartCommandReply(RelayDialogsToAvatar.ToString(), DialogMessage, "NewDialogNotice"); + } + } + } + } + + protected int AddPendingDialog(ScriptDialogEventArgs e) + { + int selected = new Random().Next(566678); + while (pending_dialogs.ContainsKey(selected)) + { + selected = new Random().Next(566678); + } + pending_dialogs.Add(selected, e); + pending_dialogs_age.Add(selected, helpers.UnixTimeNow()); + return selected; + } + + } +} diff --git a/Core/bottypes/DiscordBot.cs b/Core/bottypes/DiscordBot.cs new file mode 100644 index 0000000..af9929e --- /dev/null +++ b/Core/bottypes/DiscordBot.cs @@ -0,0 +1,599 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; +using Discord; +using Discord.Webhook; +using BetterSecondBotShared.Json; +using BetterSecondBotShared.Static; +using Discord.WebSocket; +using System.Threading.Tasks; +using System.Threading; +using System.Linq; + +namespace BSB.bottypes +{ + public abstract class DiscordBot : ChatRelay + { + // Full fat Discord client + protected bool discord_group_relay; + protected DiscordSocketClient DiscordClient; + protected bool DiscordClientConnected; + protected IGuild DiscordServer; + protected Dictionary catmap = new Dictionary(); + protected string LastSendDiscordStatus = ""; + protected long DiscordUnixTimeOnine; + + protected bool DiscordLock; + protected Task WaitForUnlock() + { + while (DiscordLock == true) + { + Thread.Sleep(100); + } + return Task.CompletedTask; + } + + public override string GetStatus() + { + string reply = ""; + if(myconfig.DiscordFullServer == true) + { + if (DiscordClientConnected == true) + { + reply = "[Discord: connected]"; + DiscordStatusUpdate(); + } + else + { + reply = "[Discord: Disconnected]"; + } + + } + else if(discord_group_relay == true) + { + reply = " [Discord: Group Relay]"; + } + if(reply != "") + { + reply = " " + reply; + } + return base.GetStatus() + reply; + } + + protected bool AllowNewOutbound() + { + if (DiscordUnixTimeOnine != 0) + { + long dif = helpers.UnixTimeNow() - DiscordUnixTimeOnine; + if (dif > 8) + { + return true; + } + } + return false; + } + + protected async Task SendMessageToChannelAsync(string channelname,string message,string catmapid,UUID sender_id,string sendername, string TopicType) + { + if (AllowNewOutbound() == true) + { + channelname = channelname.ToLowerInvariant(); + channelname = String.Concat(channelname.Where(char.IsLetterOrDigit)); + ITextChannel Channel = await FindTextChannel(channelname, catmap[catmapid], sender_id, sendername, TopicType).ConfigureAwait(false); + if (Channel != null) + { + await Channel.SendMessageAsync(message); + } + } + } + + protected void DiscordStatusUpdate() + { + if (AllowNewOutbound() == true) + { + if (LastSendDiscordStatus != LastStatusMessage) + { + LastSendDiscordStatus = LastStatusMessage; + _ = SendMessageToChannelAsync("status", LastSendDiscordStatus, "bot", UUID.Zero, myconfig.userName, "bot"); + } + } + } + + protected async Task CreateChannel(string channelname, string channeltopictype, string sender_id) + { + channelname = channelname.ToLowerInvariant(); + channelname = String.Concat(channelname.Where(char.IsLetterOrDigit)); + string display_topic = "" + channeltopictype + ":" + sender_id + ""; + if (channelname == "interface") + { + display_topic = "Actions -> !clear, !commands, !help command or command"; + } + else if (channelname == "status") + { + display_topic = "Actions -> !clear"; + } + else if (sender_id == UUID.Zero.ToString()) + { + display_topic = "" + myconfig.userName + " #" + MyVersion + ""; + } + else if (channeltopictype == "Group") + { + display_topic = "" + channeltopictype + ":" + sender_id + ": Actions -> !clear, !notice title|||message"; + } + else if (channeltopictype == "IM") + { + display_topic = "" + channeltopictype + ":" + sender_id + ": Actions -> !clear, !close"; + } + + IGuildChannel channel = await DiscordServer.CreateTextChannelAsync(channelname, X => DiscordGetNewChannelProperies(X, channelname, display_topic, channeltopictype.ToLowerInvariant())); + ITextChannel Txtchan = await DiscordServer.GetTextChannelAsync(channel.Id); + return Txtchan; + } + + protected async Task DiscordGroupMessage(UUID group_uuid, string sender_name, string message) + { + if ((helpers.notempty(sender_name) == true) && (helpers.notempty(message) == true)) + { + if (mygroups.ContainsKey(group_uuid) == true) + { + await SendMessageToChannelAsync(mygroups[group_uuid].Name, "" + sender_name + ": " + message + "", "group", group_uuid, sender_name, "Group").ConfigureAwait(false); + } + } + } + protected async Task DiscordIMMessage(UUID sender_uuid, string sender_name, string message) + { + if ((helpers.notempty(sender_name) == true) && (helpers.notempty(message) == true)) + { + await SendMessageToChannelAsync(sender_name, "" + sender_name + ": " + message + "", "im", sender_uuid, sender_name, "IM").ConfigureAwait(false); + } + } + + protected async Task FindTextChannel(string channelname, ICategoryChannel cat, UUID sender, string sendername, string TopicType) + { + await WaitForUnlock(); + channelname = channelname.ToLowerInvariant(); + channelname = String.Concat(channelname.Where(char.IsLetterOrDigit)); + DiscordLock = true; + IReadOnlyCollection found_chans = await DiscordServer.GetTextChannelsAsync(CacheMode.AllowDownload); + ITextChannel result = null; + foreach(ITextChannel ITC in found_chans) + { + if(ITC.CategoryId == cat.Id) + { + if(ITC.Name == channelname) + { + result = ITC; + break; + } + } + } + if(result == null) + { + result = await CreateChannel(channelname, TopicType, sender.ToString()); + } + else + { + await CleanDiscordChannel(result, myconfig.DiscordServerImHistoryHours).ConfigureAwait(false); + } + DiscordLock = false; + return result; + } + + protected async Task DiscordBotAfterLogin() + { + if (reconnect == false) + { + if (helpers.notempty(myconfig.DiscordClientToken) == false) + { + myconfig.DiscordFullServer = false; + } + else if (myconfig.DiscordServerID <= 0) + { + myconfig.DiscordFullServer = false; + } + if (myconfig.DiscordFullServer == false) + { + if (helpers.notempty(myconfig.discord) == true) + { + if (helpers.notempty(myconfig.discordGroupTarget) == true) + { + discord_group_relay = true; + } + } + } + else + { + if (myconfig.DiscordServerImHistoryHours > 48) + { + myconfig.DiscordServerImHistoryHours = 48; + } + else if (myconfig.DiscordServerImHistoryHours < 1) + { + myconfig.DiscordServerImHistoryHours = 1; + } + DiscordClient = new DiscordSocketClient(); + DiscordClient.Ready += DiscordClientReady; + DiscordClient.LoggedOut += DiscordClientLoggedOut; + DiscordClient.MessageReceived += DiscordClientMessageReceived; + await DiscordClient.LoginAsync(TokenType.Bot, myconfig.DiscordClientToken); + _ = DiscordClient.StartAsync(); + } + } + } + + + + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + _ = DiscordBotAfterLogin(); + } + + protected async Task DiscordKillMePlease() + { + DiscordUnixTimeOnine = 0; + await DiscordClient.LogoutAsync(); + } + + public override void KillMePlease() + { + base.KillMePlease(); + _ = DiscordKillMePlease(); + } + + protected void DiscordGetNewChannelProperies(TextChannelProperties C,string channelname,string channeltopic,string catname) + { + if (catname != null) + { + if(catmap.ContainsKey(catname) == true) + { + C.CategoryId = catmap[catname].Id; + } + } + C.Name = channelname; + C.Topic = channeltopic; + } + + protected async static Task CleanDiscordChannel(ITextChannel chan) + { + await CleanDiscordChannel(chan, 48).ConfigureAwait(false); + } + protected async static Task CleanDiscordChannel(ITextChannel chan, int HistoryHours) + { + await CleanDiscordChannel(chan, HistoryHours,false).ConfigureAwait(false); + } + + protected async static Task CleanDiscordChannel(ITextChannel chan,int HistoryHours, bool forceempty) + { + DateTimeOffset Now = new DateTimeOffset(new DateTime()); + IEnumerable messages; + bool empty = false; + while(empty == false) + { + empty = true; + messages = await chan.GetMessagesAsync(50).FlattenAsync(); + List deleteMessages = new List(); + foreach (IMessage mess in messages) + { + var hours = ((Now.ToUnixTimeSeconds() - mess.Timestamp.ToUnixTimeSeconds()) / 60) / 60; + if ((hours > HistoryHours) || (forceempty == true)) + { + empty = false; + deleteMessages.Add(mess.Id); + } + } + if(deleteMessages.Count > 0) + { + await chan.DeleteMessagesAsync(deleteMessages); + } + } + } + + protected async Task DiscordRebuildChannels() + { + List required_cats = new List() { "bot", "group", "im" }; + IReadOnlyCollection found_cats = await DiscordServer.GetCategoriesAsync(CacheMode.AllowDownload); + foreach(ICategoryChannel fcat in found_cats) + { + if(required_cats.Contains(fcat.Name) == true) + { + required_cats.Remove(fcat.Name); + catmap.Add(fcat.Name, fcat); + } + } + foreach (string A in required_cats) + { + ICategoryChannel newcat = await DiscordServer.CreateCategoryAsync(A).ConfigureAwait(true); + catmap.Add(A, newcat); + } + List required_channels = new List() {"status","interface" }; + IReadOnlyCollection found_chans = await DiscordServer.GetTextChannelsAsync(CacheMode.AllowDownload); + List GroupChannels = new List(); + foreach (ITextChannel chan in found_chans) + { + if (chan.CategoryId == catmap["bot"].Id) + { + required_channels.Remove(chan.Name); + } + else + { + if (chan.CategoryId == catmap["group"].Id) + { + GroupChannels.Add(chan.Name); + } + } + } + foreach (string A in required_channels) + { + _ = await FindTextChannel(A, catmap["bot"], UUID.Zero, A, "bot").ConfigureAwait(false); + } + foreach (Group G in mygroups.Values) + { + string groupname = G.Name.ToLowerInvariant(); + groupname = String.Concat(groupname.Where(char.IsLetterOrDigit)); + if (GroupChannels.Contains(groupname) == false) + { + _ = await FindTextChannel(groupname, catmap["group"], G.ID, groupname, "Group").ConfigureAwait(false); + } + } + } + protected async Task DiscordClientMessageReceived(SocketMessage message) + { + if (AllowNewOutbound() == true) + { + if (message.Author.IsBot == false) + { + if (Client.Network.Connected == false) + { + await message.DeleteAsync(); + } + else + { + ITextChannel Chan = (ITextChannel)message.Channel; + if(Chan.CategoryId == catmap["bot"].Id) + { + await message.DeleteAsync(); + if (message.Content == "!clear") + { + await CleanDiscordChannel(Chan, 0, true); + } + if (Chan.Name == "interface") + { + if (message.Content.StartsWith("!")) + { + if (message.Content == "!commands") + { + string reply = ""; + int counter = 0; + string addon = ""; + foreach(string a in CommandsInterface.GetCommandsList()) + { + reply += addon; + reply += a; + counter++; + if (counter == 5) + { + reply += "\n"; + addon = ""; + counter = 0; + } + else + { + addon = " , "; + } + } + _ = SendMessageToChannelAsync("interface", reply, "bot", UUID.Zero, myconfig.userName, "bot"); + } + else if (message.Content.StartsWith("!help") == true) + { + string[] bits = message.Content.Split(' '); + if (bits.Length == 2) + { + string command = bits[1].ToLowerInvariant(); + if (CommandsInterface.GetCommandsList().Contains(command) == true) + { + _ = SendMessageToChannelAsync("interface", "\n=========================\nCommand: " + command + "\n" + + "Workspace: " + CommandsInterface.GetCommandWorkspace(command) + "\n" + + "Min args: " + CommandsInterface.GetCommandArgs(command).ToString() + "\n" + + "Arg types" + String.Join(",", CommandsInterface.GetCommandArgTypes(command)) + "\n" + + "\n" + + "About: " + CommandsInterface.GetCommandHelp(command) + "", "bot", UUID.Zero, myconfig.userName, "bot"); + } + else + { + _ = SendMessageToChannelAsync("interface", "Unable to find command: " + command + " please use !commands for a full list", "bot", UUID.Zero, myconfig.userName, "bot"); + } + } + else + { + _ = SendMessageToChannelAsync("interface", "Please format help request as follows: !help commandname", "bot", UUID.Zero, myconfig.userName, "bot"); + } + } + else if (message.Content != "!clear") + { + _ = SendMessageToChannelAsync("interface", "Unknown request: " + message.Content + "", "bot", UUID.Zero, myconfig.userName, "bot"); + } + } + else + { + string[] bits = message.Content.Split("|||"); + string command = bits[0].ToLowerInvariant(); + if (CommandsInterface.GetCommandsList().Contains(command) == true) + { + bool status = false; + if (bits.Length == 2) + { + status = CommandsInterface.Call(command, bits[1]); + } + else + { + status = CommandsInterface.Call(command); + } + if (status == true) + { + _ = SendMessageToChannelAsync("interface", "Command running: " + message.Content + "", "bot", UUID.Zero, myconfig.userName, "bot"); + } + else + { + _ = SendMessageToChannelAsync("interface", "Command rejected: " + message.Content + "", "bot", UUID.Zero, myconfig.userName, "bot"); + } + } + else + { + _ = SendMessageToChannelAsync("interface", "Unable to find command: " + command + " please use !commands for a full list", "bot", UUID.Zero, myconfig.userName, "bot"); + } + } + } + } + else if (Chan.CategoryId == catmap["im"].Id) + { + // Avatar + if (message.Content == "!close") + { + await Chan.DeleteAsync(); + } + else if (message.Content == "!clear") + { + await CleanDiscordChannel(Chan, 0, true); + } + else + { + string[] bits = Chan.Topic.Split(':'); + if (bits.Length >= 2) + { + if (bits[0] == "IM") + { + if (message.Content == "!clear") + { + await CleanDiscordChannel(Chan, 0, true); + } + else if (UUID.TryParse(bits[1], out UUID avatar) == true) + { + Client.Self.InstantMessage(avatar, "["+message.Author.Username+"]->"+message.Content); + } + } + } + } + } + else if (Chan.CategoryId == catmap["group"].Id) + { + // Group + string[] bits = Chan.Topic.Split(':'); + if (bits.Length >= 2) + { + if (bits[0] == "Group") + { + if (UUID.TryParse(bits[1], out UUID group) == true) + { + if(mygroups.ContainsKey(group) == true) + { + if (message.Content == "!clear") + { + await CleanDiscordChannel(Chan, 0, true); + } + else if (message.Content.StartsWith("!notice") == true) + { + string Noticetitle = "Notice"; + string Noticemessage = ""; + bits = message.Content.Split("|||", StringSplitOptions.None); + if (bits.Length == 2) + { + Noticetitle = bits[0]; + Noticemessage = bits[1]; + } + else + { + Noticemessage = bits[0]; + } + Noticetitle = Noticetitle.Replace("!notice ", ""); + Noticetitle = Noticetitle.Trim(); + CommandsInterface.Call("GroupNotice", ""+ group.ToString()+"~#~" + Noticetitle + "~#~" + Noticemessage); + } + else + { + CommandsInterface.Call("Groupchat", "" + group.ToString() + "~#~" + "[" + message.Author.Username + "]->" + message.Content); + } + await message.DeleteAsync(); + } + } + } + } + } + } + } + } + return Task.CompletedTask; + } + + protected Task DiscordClientReady() + { + DiscordServer = DiscordClient.GetGuild(myconfig.DiscordServerID); + if (DiscordServer != null) + { + DiscordUnixTimeOnine = helpers.UnixTimeNow(); + DiscordClientConnected = true; + _ = DiscordRebuildChannels(); + } + else + { + DiscordClientConnected = false; + } + return Task.CompletedTask; + } + protected Task DiscordClientLoggedOut() + { + DiscordClientConnected = false; + return Task.CompletedTask; + } + + protected async override void BotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid, bool localchat, bool fromme) + { + base.BotChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme); + await DiscordBotChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme); + } + + protected async Task DiscordBotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid, bool localchat, bool fromme) + { + if (myconfig.DiscordFullServer == false) + { + if (discord_group_relay == true) + { + // GroupChat Relay only + if (group == true) + { + if (myconfig.discord != "") + { + if ((myconfig.discordGroupTarget == group_uuid.ToString()) || (myconfig.discordGroupTarget == "*") || (myconfig.discordGroupTarget == "all")) + { + Group Gr = mygroups[group_uuid]; + using (var DWHclient = new DiscordWebhookClient(myconfig.discord)) + { + string SendMessage = "(" + Gr.Name + ") @" + sender_name + ":" + message + ""; + await DWHclient.SendMessageAsync(text: SendMessage).ConfigureAwait(false); + } + } + } + } + } + } + else if (DiscordClientConnected == true) + { + if (avatar == true) + { + if (localchat == false) + { + if (group == true) + { + await DiscordGroupMessage(group_uuid, sender_name, message).ConfigureAwait(false); + } + else + { + await DiscordIMMessage(sender_uuid,sender_name, message).ConfigureAwait(false); + } + } + } + } + } + } +} diff --git a/Core/bottypes/EventsBot.cs b/Core/bottypes/EventsBot.cs new file mode 100644 index 0000000..ae62fd6 --- /dev/null +++ b/Core/bottypes/EventsBot.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Text; +using BetterSecondBotShared.Static; +using BSB.Commands; +using OpenMetaverse; +using OpenMetaverse.Packets; + +namespace BSB.bottypes +{ + public abstract class VoidEventBot : AnimationsBot + { + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + if (reconnect == false) + { + Client.Network.RegisterCallback(PacketType.SetFollowCamProperties, SetFollowCamPropertiesPacketHandler); + } + + } + + protected void SetFollowCamPropertiesPacketHandler(object sender, PacketReceivedEventArgs e) + { + // do nothing + } + + + } + public class EventsBot : VoidEventBot + { + protected List accept_animation_request_from_users = new List(); + protected bool login_auto_logout = false; + protected long delay_group_fetch = 0; + + Dictionary>> group_members_storage = new Dictionary>>(); + protected virtual void GroupMembersReplyHandler(object sender, GroupMembersReplyEventArgs e) + { + } + + public bool FastCheckInGroup(UUID group,UUID avatar) + { + if(NeedReloadGroupData(group) == false) + { + return group_members_storage[group].Value.Contains(avatar); + } + return false; + } + public bool NeedReloadGroupData(UUID group) + { + bool result = true; + lock (group_members_storage) + { + if (group_members_storage.ContainsKey(group) == true) + { + long dif = last_cleanup - group_members_storage[group].Key; + if (dif < 60) + { + result= false; + } + else + { + group_members_storage.Remove(group); + } + } + } + return result; + } + + protected void cleanup_group_member_storage() + { + lock (group_members_storage) + { + List groups_to_purge = new List(); + foreach (UUID groupid in group_members_storage.Keys) + { + long dif = last_cleanup - group_members_storage[groupid].Key; + if (dif > 60) + { + groups_to_purge.Add(groupid); + } + } + foreach (UUID group in groups_to_purge) + { + group_members_storage.Remove(group); + } + } + } + + public void ToggleAutoAcceptAnimations(string userName) + { + if(accept_animation_request_from_users.Contains(userName) == true) + { + accept_animation_request_from_users.Remove(userName); + } + else + { + accept_animation_request_from_users.Add(userName); + } + } + + protected override void BotStartHandler() + { + Client.Network.LoginProgress += new EventHandler(LoginHandler); + } + + public override string GetStatus() + { + if (Client != null) + { + if (Client.Network != null) + { + if (Client.Network.Connected) + { + long dif = helpers.UnixTimeNow() - last_cleanup; + if (dif > 30) + { + last_cleanup = helpers.UnixTimeNow(); + cleanup_await_events(); + cleanup_group_member_storage(); + } + if (delay_group_fetch > 0) + { + dif = helpers.UnixTimeNow() - delay_group_fetch; + if (dif > 0) + { + delay_group_fetch = 0; + Client.Groups.RequestCurrentGroups(); + } + } + if (Client.Network.CurrentSim != null) + { + return "Sim: " + Client.Network.CurrentSim.Name + " " + GetSimPositionAsString() + ""; + } + return "Sim: Not on sim"; + } + } + } + return ""; + } + protected string login_status = "Waiting to login"; + protected override void LoginHandler(object o, LoginProgressEventArgs e) + { + if (e.Status == LoginStatus.Success) + { + login_status = "Ok"; + AfterBotLoginHandler(); + } + else if(e.Status == LoginStatus.Failed) + { + login_status = "Logged out"; + if (e.FailReason == "presence") + { + login_auto_logout = true; + login_status = "Logged out "; + } + } + else if(e.Status == LoginStatus.Failed) + { + login_status = "Failed (Check username and password)"; + } + else if(e.Status == LoginStatus.None) + { + login_status = "None :/"; + } + else if(e.Status == LoginStatus.ConnectingToLogin) + { + login_status = "Starting login"; + } + else if(e.Status == LoginStatus.ReadingResponse) + { + login_status = "Busy"; + } + else if(e.Status == LoginStatus.ConnectingToSim) + { + login_status = "Joining sim"; + } + else if(e.Status == LoginStatus.Redirecting) + { + login_status = "Redirecting"; + } + + } + public virtual void GroupsHandler(object sender, CurrentGroupsEventArgs e) + { + mygroups = e.Groups; + foreach(KeyValuePair entry in mygroups) + { + Client.Self.RequestJoinGroupChat(entry.Value.ID); + } + } + + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + if (reconnect == false) + { + Client.Self.ChatFromSimulator += ChatInputHandler; + Client.Self.IM += MessageHandler; + Client.Groups.CurrentGroups += GroupsHandler; + Client.Self.ScriptQuestion += PermissionsHandler; + delay_group_fetch = helpers.UnixTimeNow() + 10; + } + } + + protected override void PermissionsHandler(object sender, ScriptQuestionEventArgs e) + { + if(e.ObjectOwnerName == Client.Self.Name) + { + Client.Self.ScriptQuestionReply(Client.Network.CurrentSim, e.ItemID, e.TaskID, e.Questions); + } + else if(accept_animation_request_from_users.Contains(e.ObjectOwnerName) == true) + { + Client.Self.ScriptQuestionReply(Client.Network.CurrentSim, e.ItemID, e.TaskID, ScriptPermission.TriggerAnimation); + } + else if((e.ObjectOwnerName == myconfig.master) && (myconfig.master != "")) + { + Client.Self.ScriptQuestionReply(Client.Network.CurrentSim, e.ItemID, e.TaskID, e.Questions); + } + else if(Client.Self.SittingOn > 0) + { + if(Client.Network.CurrentSim.ObjectsPrimitives.ContainsKey(Client.Self.SittingOn)) + { + Primitive obj = GetClient.Network.CurrentSim.ObjectsPrimitives[Client.Self.SittingOn]; + if(obj.ID == e.ItemID) + { + Client.Self.ScriptQuestionReply(Client.Network.CurrentSim, e.ItemID, e.TaskID, ScriptPermission.TriggerAnimation); + } + } + } + else + { + Client.Self.ScriptQuestionReply(Client.Network.CurrentSim, e.ItemID, e.TaskID, ScriptPermission.None); + } + } + + protected void cleanup_await_events() + { + long dif; + List purge_event_ids = new List(); + foreach (KeyValuePair CheckEvent in await_event_ages) + { + dif = last_cleanup - CheckEvent.Value; + if (dif > 240) + { + purge_event_ids.Add(CheckEvent.Key); + } + } + foreach (string eventid in purge_event_ids) + { + string listener = await_event_idtolistener[eventid]; + await_event_idtolistener.Remove(eventid); + await_event_ages.Remove(eventid); + await_events[listener].Remove(eventid); + } + } + + protected long last_cleanup = 0; + + protected Dictionary>> await_events = new Dictionary>>(); + protected Dictionary await_event_ages = new Dictionary(); + protected Dictionary await_event_idtolistener = new Dictionary(); + protected int next_await_id = 1; + public bool CreateAwaitEventReply(string event_listener, CoreCommand command, string[] args) + { + string eventid = "" + event_listener + "" + next_await_id.ToString() + ""; + next_await_id++; + if (await_events.ContainsKey(event_listener) == false) + { + await_events.Add(event_listener, new Dictionary>()); + } + if (await_events[event_listener].ContainsKey(eventid) == false) + { + if (await_event_ages.ContainsKey(eventid) == false) + { + if (await_event_idtolistener.ContainsKey(eventid) == false) + { + await_events[event_listener].Add(eventid, new KeyValuePair(command, args)); + await_event_ages.Add(eventid, helpers.UnixTimeNow()); + await_event_idtolistener.Add(eventid, event_listener); + return true; + } + else return false; + } + return false; + } + else return false; + } + + + } + + +} diff --git a/Core/bottypes/MessageSwitcherBot.cs b/Core/bottypes/MessageSwitcherBot.cs new file mode 100644 index 0000000..cc79a7a --- /dev/null +++ b/Core/bottypes/MessageSwitcherBot.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using OpenMetaverse; + +namespace BSB.bottypes +{ + public abstract class MessageSwitcherBot : EventsBot + { + protected override void ChatInputHandler(object sender, ChatEventArgs e) + { + bool fromme = false; + if (e.FromName == GetClient.Self.Name) + { + fromme = true; + } + if (e.Type != ChatType.StartTyping) + { + if (e.Type != ChatType.StopTyping) + { + bool av = false; + if (e.SourceType == ChatSourceType.Agent) + { + av = true; + } + BotChatControler(e.Message, e.FromName, e.SourceID, av, false, UUID.Zero, true, fromme); + } + } + } + protected virtual void BotChatControler(string message, string sender_name, UUID sender_uuid) + { + BotChatControler(message, sender_name, sender_uuid, false, false, UUID.Zero, false, false); + } + protected virtual void BotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar) + { + BotChatControler(message, sender_name, sender_uuid, avatar, false, UUID.Zero, false, false); + } + protected virtual void BotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid) + { + BotChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, false, false); + } + protected virtual void BotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid, bool localchat) + { + BotChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, false); + } + protected virtual void BotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar,bool group, UUID group_uuid, bool localchat,bool fromme) + { + if (fromme == false) + { + if (localchat == false) + { + if (avatar == false) + { + bool signed_command = false; + string signing_code = ""; + string[] input = message.Split(new[] { "@@@" }, StringSplitOptions.None); + if (input.Count() == 2) + { + signed_command = true; + message = input[0]; + signing_code = input[1]; + } + List bits = message.Split(new[] { "|||" }, StringSplitOptions.None).ToList(); + if (bits.Count == 1) + { + bits.Add(""); + } + if (signed_command == true) + { + CoreCommandLib(sender_uuid, false, bits.ElementAt(0), bits.ElementAt(1), signing_code); + } + } + else if (myconfig.master == sender_name) + { + List bits = message.Split(new[] { "|||" }, StringSplitOptions.None).ToList(); + if (bits.Count == 1) + { + bits.Add(""); + } + CoreCommandLib(sender_uuid, true, bits.ElementAt(0), bits.ElementAt(1)); + } + } + } + } + protected override void MessageHandler(object sender, InstantMessageEventArgs e) + { + bool fromme = false; + if (e.IM.FromAgentName == GetClient.Self.Name) + { + fromme = true; + } + switch (e.IM.Dialog) + { + case InstantMessageDialog.GroupInvitation: + { + GroupInvite(e); + break; + } + case InstantMessageDialog.FriendshipOffered: + { + FriendshipOffer(e.IM.IMSessionID, e.IM.FromAgentName, e.IM.FromAgentID); + break; + } + case InstantMessageDialog.RequestLure: + { + RequestLure(e.IM.IMSessionID, e.IM.FromAgentName, e.IM.FromAgentID); + break; + } + case InstantMessageDialog.RequestTeleport: + { + RequestTeleport(e.IM.IMSessionID, e.IM.FromAgentName, e.IM.FromAgentID); + break; + } + case InstantMessageDialog.InventoryOffered: + { + break; + } + case InstantMessageDialog.TaskInventoryOffered: + { + break; + } + case InstantMessageDialog.MessageFromObject: + { + BotChatControler(e.IM.Message, e.IM.FromAgentName, e.IM.FromAgentID); + break; + } + case InstantMessageDialog.MessageFromAgent: // shared with SessionSend + case InstantMessageDialog.SessionSend: + { + if (mygroups.ContainsKey(e.IM.IMSessionID) == true) + { + AddToGroupchat(e.IM.IMSessionID, e.IM.FromAgentName, e.IM.Message); + BotChatControler(e.IM.Message, e.IM.FromAgentName, e.IM.FromAgentID, true, true, e.IM.IMSessionID, false, fromme); + } + else + { + BotChatControler(e.IM.Message, e.IM.FromAgentName, e.IM.FromAgentID, true, false, UUID.Zero, false, fromme); + } + break; + } + default: + { + break; + } + } + } + } +} diff --git a/Core/bottypes/RLVbot.cs b/Core/bottypes/RLVbot.cs new file mode 100644 index 0000000..8234070 --- /dev/null +++ b/Core/bottypes/RLVbot.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Text; +using BetterSecondBotShared.Json; +using OpenMetaverse; +using BetterSecondBotShared.logs; + +namespace BSB.bottypes +{ + public abstract class RLVbot : RLVbotHelper + { + // normal bot stuff + protected bool allow_RLV = false; + protected RLV.RLVcontrol RLVinterface = null; + public RLV.RLVcontrol GetRLVinterface { get { return RLVinterface; } } + + public override void Setup(JsonConfig config, string Version) + { + base.Setup(config, Version); + if (reconnect == false) + { + allow_RLV = myconfig.allowRLV; + if (allow_RLV == true) + { + RLVinterface = new RLV.RLVcontrol(this); + } + } + } + + protected override void ChatInputHandler(object sender, ChatEventArgs e) + { + base.ChatInputHandler(sender, e); + if (e.Type == ChatType.OwnerSay) + { + if (allow_RLV == true) + { + if (e.Message.StartsWith("@") == true) + { + string message_text = e.Message.Substring(1); + bool permissive_allow = true; + if (permissive_allow == true) + { + string[] requested_commands = message_text.Split(','); + foreach (string R in requested_commands) + { + List args = new List(); + string command = ""; + string[] build = R.Split('='); + string[] build2 = build[0].Split(':'); + if (build2.Length == 2) + { + string[] build3 = build2[1].Split(';'); + foreach (string build_arg in build3) + { + args.Add(build_arg); + } + } + command = build2[0]; + if (build.Length == 2) + { + args.Add(build[1]); + } + if (blacklist_recvim.Contains(command) == false) + { + if (Notify.ContainsKey("#") == true) + { + foreach (int channel in Notify["#"]) + { + Client.Self.Chat("/" + message_text, channel, ChatType.Normal); + } + } + if (Notify.ContainsKey(command) == true) + { + foreach (int channel in Notify[command]) + { + Client.Self.Chat("/" + message_text, channel, ChatType.Normal); + } + } + RLVinterface.SetCallerDetails(e.SourceID, e.FromName); + RLVinterface.Call(command, args.ToArray()); + } + else + { + ConsoleLog.Debug("RLV API -> blacklisted [" + command + "]"); + } + } + } + } + } + } + } + } + + + public class RLVbotHelper : AwaitReplyEventBot + { + #region PermissiveMode + // permissive + protected bool permissive_mode = true; + protected UUID permissive_mode_set_by = UUID.Zero; + public UUID Getpermissive_mode_set_by { get { return permissive_mode_set_by; } } + public void SetPermissiveMode(UUID caller, bool status = false) + { + bool allow = true; + if((permissive_mode == false) && (caller != permissive_mode_set_by)) + { + allow = false; + } + if(allow == true) + { + permissive_mode = status; + if (status == false) + { + permissive_mode_set_by = UUID.Zero; + } + else + { + permissive_mode_set_by = caller; + } + } + } + #endregion + + #region blacklist + // Blacklist + protected List blacklist_sendim = new List(); + protected List blacklist_recvim = new List(); + public List Getblacklist_sendim { get { return blacklist_sendim; } } + public List Getblacklist_recvim { get { return blacklist_recvim; } } + #endregion + + #region UUID rules Exceptions + // UUID based rules / Exceptions + protected Dictionary> uuid_rules = new Dictionary>(); + protected Dictionary> uuid_exceptions = new Dictionary>(); + public Dictionary> Getuuid_rules { get { return uuid_rules; } } + public Dictionary> Getuuid_exceptions { get { return uuid_exceptions; } } + public bool Clearrule(bool as_exception, UUID soruce) + { + return RemoveRule(as_exception, null, soruce); + } + public bool UpdateRule(bool as_exception, string rule, string arg, UUID source) + { + if (as_exception == false) + { + lock (uuid_rules) + { + if (uuid_rules.ContainsKey(source) == false) + { + uuid_rules.Add(source, new Dictionary()); + } + if (uuid_rules[source].ContainsKey(rule) == true) + { + uuid_rules[source].Remove(rule); + } + uuid_rules[source].Add(rule, arg); + } + } + else + { + lock (uuid_exceptions) + { + if (uuid_exceptions.ContainsKey(source) == false) + { + uuid_exceptions.Add(source, new Dictionary()); + } + if (uuid_exceptions[source].ContainsKey(rule) == true) + { + uuid_exceptions[source].Remove(rule); + } + uuid_exceptions[source].Add(rule, arg); + } + } + return true; + } + public bool RemoveRule(bool as_exception, string rule, UUID source) + { + if (as_exception == false) + { + lock (uuid_rules) + { + if (uuid_rules.ContainsKey(source) == true) + { + if (rule != null) + { + if (uuid_rules[source].ContainsKey(rule) == true) + { + uuid_rules[source].Remove(rule); + } + if (uuid_rules[source].Count == 0) + { + uuid_rules.Remove(source); + } + } + else + { + uuid_rules.Remove(source); + } + } + } + } + else + { + lock (uuid_exceptions) + { + if (rule != null) + { + if (uuid_exceptions[source].ContainsKey(rule) == true) + { + uuid_exceptions[source].Remove(rule); + } + if (uuid_exceptions[source].Count == 0) + { + uuid_exceptions.Remove(source); + } + } + else + { + uuid_exceptions.Remove(source); + } + } + } + return true; + } + #endregion + + #region Flags + // Flags + protected bool recvchat_channel_zero_lock = true; + protected bool sendchannel_lock = false; + protected bool recvemote_lock = false; + protected bool sendim_lock = false; + public void SetLock(string name, bool status) + { + if (name == "sendim_lock") { sendim_lock = status; } + else if (name == "recvemote_lock") { recvemote_lock = status; } + else if (name == "sendchannel_lock") { sendchannel_lock = status; } + else if (name == "recvchat_channel_zero_lock") { recvchat_channel_zero_lock = status; } + } + #endregion + + #region Notify + // Notify + protected Dictionary> Notify = new Dictionary>(); + public Dictionary> Getnotify { get { return Notify; } } + public void NotifyUpdate(bool Add, string word,int channel = -1) + { + lock (Notify) + { + if (Add == true) + { + if (Notify.ContainsKey(word) == false) + { + Notify.Add(word, new List()); + } + if (channel > -1) + { + if (Notify[word].Contains(channel) == false) + { + Notify[word].Add(channel); + } + } + } + else + { + if (Notify.ContainsKey(word) == true) + { + if (Notify[word].Contains(channel) == true) + { + Notify[word].Remove(channel); + if(Notify[word].Count == 0) + { + Notify.Remove(word); + } + } + if (channel == -1) + { + Notify.Remove(word); + } + + } + } + } + } + #endregion + + } +} diff --git a/Core/bottypes/SecondBot.cs b/Core/bottypes/SecondBot.cs new file mode 100644 index 0000000..e75fc13 --- /dev/null +++ b/Core/bottypes/SecondBot.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; +using Discord; +using Discord.Webhook; +using BetterSecondBotShared.Json; + +namespace BSB.bottypes +{ + public class SecondBot : DialogsBot + { + } +} diff --git a/Core/bottypes/VirtualsBot.cs b/Core/bottypes/VirtualsBot.cs new file mode 100644 index 0000000..d30b2c7 --- /dev/null +++ b/Core/bottypes/VirtualsBot.cs @@ -0,0 +1,85 @@ +using BetterSecondBotShared.bottypes; +using BetterSecondBotShared.logs; +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BSB.bottypes +{ + public abstract class VirtualsBot : BasicBot + { + #region group + protected List active_group_chat_sessions = new List(); + public virtual List GetActiveGroupchatSessions { get { return new List(); } } + #endregion + #region GroupIMs + public virtual bool HasUnreadGroupchats() + { + return false; + } + public virtual UUID[] UnreadGroupchatGroups() + { + return new UUID[] { }; + } + public virtual string[] GetGroupchat(UUID group) + { + return new string[] { }; + } + protected virtual void AddToGroupchat(UUID group, string name, string message) + { + + } + public virtual bool GroupHasUnread(UUID group) + { + return false; + } + public virtual void ClearGroupchat(UUID group) + { + + } + public virtual void ClearAllGroupchat() + { + + } + #endregion + #region events + protected virtual void PermissionsHandler(object sender, ScriptQuestionEventArgs e) + { + ConsoleLog.Debug("PermissionsHandler proc not overridden"); + } + protected virtual void AvatarAnimationHandler(object sender, AvatarAnimationEventArgs e) + { + ConsoleLog.Debug("AvatarAnimationHandler proc not overridden"); + } + + protected virtual void SitHandler(object sender, AvatarSitResponseEventArgs e) + { + ConsoleLog.Debug("SitHandler proc not overridden"); + } + protected virtual void MessageHandler(object sender, InstantMessageEventArgs e) + { + ConsoleLog.Debug("MessageHandler proc not overridden"); + } + #endregion + #region Dialogs + public virtual void SetRelayDialogsChannel(int channel) + { + } + public virtual void SetRelayDialogsAvatar(UUID avatar) + { + } + public virtual void SetRelayDialogsHTTP(string httpurl) + { + } + public virtual void SetTrackDialogs(bool status) + { + } + public virtual bool DialogReply(int DialogID, string button) + { + return false; + } + #endregion + + } +} diff --git a/Core/bottypes/athome.cs b/Core/bottypes/athome.cs new file mode 100644 index 0000000..1894336 --- /dev/null +++ b/Core/bottypes/athome.cs @@ -0,0 +1,367 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using OpenMetaverse; +using OpenMetaverse.Assets; +using BetterSecondBotShared.Static; +using BetterSecondBotShared.logs; + +namespace BSB.bottypes +{ + public abstract class AtHome : MessageSwitcherBot + { + protected string LoggedOutNextAction() + { + string addon = ""; + long dif = helpers.UnixTimeNow() - last_reconnect_attempt; + if ((login_auto_logout == false) && (reconnect_mode == false) && (after_login_fired == true)) + { + last_tested_home_id = -1; + last_tp_attempt_unixtime = 0; + after_login_fired = false; + teleported = false; + reconnect_mode = true; + last_reconnect_attempt = helpers.UnixTimeNow(); + addon = " [@home Connection lost - switching to recovery mode]"; + } + else if ((login_auto_logout == false) && (reconnect_mode == false) && (after_login_fired == false)) + { + addon = " [@home " + login_status + "]"; + } + else if ((login_auto_logout == false) && (reconnect_mode == true) && (dif > 120)) + { + addon = " [@home Attempting reconnect]"; + last_reconnect_attempt = helpers.UnixTimeNow(); + reconnect = true; + Start(); + } + else if ((login_auto_logout == false) && (reconnect_mode == true) && (dif <= 120)) + { + addon = " [@home W4>Reconnect attempt timer]"; + } + else if ((login_auto_logout == true) && (auto_logout_login_recover == false)) + { + auto_logout_login_recover = true; + last_reconnect_attempt = helpers.UnixTimeNow(); + addon = " [@home W4>" + login_status + " (10 secs)]"; + } + else if ((login_auto_logout == true) && (auto_logout_login_recover == true) && (dif >= 10)) + { + login_auto_logout = false; + auto_logout_login_recover = false; + last_reconnect_attempt = helpers.UnixTimeNow(); + addon = " [@home restarting first login]"; + Start(); + } + else + { + addon = " [@home "+ login_status+"]"; + } + return addon; + } + protected string LoggedInNextAction() + { + string addon = ""; + if (Client.Self.SittingOn > 0) + { + AfterLoginSitDown = true; + addon = " [@home Sit-override]"; + } + else + { + long dif = helpers.UnixTimeNow() - last_tp_attempt_unixtime; + if (teleported == true) + { + addon = " [@home disabled ~ Teleported :: use: \"resetathome\" to clear]"; + } + else if (Client.Network.CurrentSim == null) + { + addon = " [@home " + login_status + "]"; + } + else if (after_login_fired == false) + { + addon = " [@home " + login_status + "]"; + } + else if (IsSimHome(Client.Network.CurrentSim.Name) == true) + { + if (UUID.TryParse(myconfig.DefaultSitUUID, out UUID arg_is_uuid) == true) + { + if (AfterLoginSitDown == false) + { + addon = " [@home HomeSim ~ Attempting to sit down]"; + Client.Self.RequestSit(arg_is_uuid, Vector3.Zero); + } + else + { + addon = " [@home HomeSim ~ Auto sit disabled :: use: \"resetathome\" to clear]"; + } + } + else + { + addon = " [@home HomeSim]"; + } + } + else if (dif <= 20) + { + addon = " [@home W4>Cooldown]"; + } + else if (myconfig.homeRegion.Length == 0) + { + addon = " [@home No home regions]"; + } + else if (Client.Network.CurrentSim.Name == last_attempted_teleport_region) + { + addon = " [@home Teleported to a known sim]"; + last_attempted_teleport_region = ""; + } + else + { + addon = GotoNextHomeRegion(); + } + } + return addon; + } + + + protected int last_tested_home_id = -1; + protected long last_tp_attempt_unixtime; + protected bool after_login_fired; + protected bool teleported; + protected long last_reconnect_attempt; + protected bool reconnect_mode; + protected string last_attempted_teleport_region = ""; + protected Dictionary avoid_sims = new Dictionary(); + protected bool SimShutdownAvoid; + protected bool auto_logout_login_recover; + protected bool AfterLoginSitDown = false; + + protected void ChangeSim(object sender,SimChangedEventArgs e) + { + if (Client.Network.CurrentSim.Name != last_attempted_teleport_region) + { + if (IsSimHome(Client.Network.CurrentSim.Name) == false) + { + SetTeleported(); + } + } + else + { + if (SimShutdownAvoid == true) + { + ConsoleLog.Status("Avoided sim shutdown will attempt to go home in 4 mins"); + SimShutdownAvoid = false; + last_tp_attempt_unixtime = helpers.UnixTimeNow() + 240; + } + } + } + protected void AlertEvent(object sender,AlertMessageEventArgs e) + { + if(e.Message.Contains("restart") == true) + { + // oh snap region is dead run away + ConsoleLog.Info("--- Sim Restarting ---"); + if (avoid_sims.ContainsKey(Client.Network.CurrentSim.Name) == false) + { + avoid_sims.Add(Client.Network.CurrentSim.Name, helpers.UnixTimeNow() + (10 * 60)); + } + GotoNextHomeRegion(true); + } + } + + public void ResetAtHome() + { + last_tested_home_id = -1; + last_tp_attempt_unixtime = 0; + teleported = false; + reconnect_mode = true; + AfterLoginSitDown = false; + } + + public void SetTeleported() + { + teleported = true; + } + + protected void AvoidSim(string simname) + { + if(avoid_sims.ContainsKey(simname) == false) + { + avoid_sims.Add(simname, helpers.UnixTimeNow() + 240); // @home will avoid that sim for the next 4 mins + } + } + + protected bool inbox(float expected, float current, float drift) + { + return inbox(expected, current, drift, true); + } + protected bool inbox(float expected,float current, float drift, bool current_value) + { + if (current > (expected + drift)) return false; + else if (current < (expected - drift)) return false; + else return current_value; + } + + + + public bool IsSimHome(string simname) + { + simname = simname.ToLowerInvariant(); + if (helpers.notempty(myconfig.homeRegion) == false) return false; + else if (myconfig.homeRegion.Length == 0) return false; + else + { + bool reply = false; + foreach (string sl_url in myconfig.homeRegion) + { + if (sl_url.ToLowerInvariant().Contains(simname) == true) + { + bool inrange = true; + string[] bits = helpers.ParseSLurl(sl_url); + if (helpers.notempty(bits) == true) + { + if (bits.Length == 4) + { + if (GetClient.Self.SittingOn == 0) + { + float.TryParse(bits[1], out float posX); + float.TryParse(bits[2], out float posY); + float.TryParse(bits[3], out float posZ); + float drift_range = 10; + inrange = inbox(Client.Self.SimPosition.X, posX, drift_range, inrange); + inrange = inbox(Client.Self.SimPosition.Y, posY, drift_range, inrange); + inrange = inbox(Client.Self.SimPosition.Z, posZ, drift_range, inrange); + } + } + } + reply = inrange; + break; + } + } + return reply; + } + + } + + public string GotoNextHomeRegion() + { + return GotoNextHomeRegion(false); + } + + protected string GotoNextHomeRegion(bool panic_mode) + { + if (panic_mode == false) + { + if (myconfig.homeRegion.Length > 1) + { + last_tp_attempt_unixtime = helpers.UnixTimeNow(); + last_tested_home_id++; + if (myconfig.homeRegion.Length >= last_tested_home_id) + { + last_tested_home_id = 0; + } + TeleportWithSLurl(myconfig.homeRegion[last_tested_home_id]); + return " [@home **** active teleport: "+ last_attempted_teleport_region+"***]"; + } + else return " [@home No home regions]"; + } + else + { + SimShutdownAvoid = true; + string UseSLurl = ""; + foreach (string Slurl in myconfig.homeRegion) + { + string simname = helpers.RegionnameFromSLurl(Slurl); + if (avoid_sims.Keys.Contains(simname) == false) + { + UseSLurl = Slurl; + break; + } + } + if (UseSLurl != "") + { + TeleportWithSLurl(UseSLurl); + ConsoleLog.Status("Attempting panic evac to: "+ last_attempted_teleport_region+""); + AvoidSim(UseSLurl); // black list that region so we dont try to go back there if we get a shutdown notice again + } + else + { + ConsoleLog.Status("No vaild SLurl found, Teleporting to backup hub"); + string[] Hubs = new[] { "https://maps.secondlife.com/secondlife/Morris/28/228/40/", "https://maps.secondlife.com/secondlife/Ahern/28/28/40/", + "https://maps.secondlife.com/secondlife/Bonifacio/228/228/40/","https://maps.secondlife.com/secondlife/Dore/228/28/40/" }; + TeleportWithSLurl(Hubs[new Random().Next(0, Hubs.Length - 1)]); + } + return "Panic mode"; + } + } + + + + public override string GetStatus() + { + if (Client.Network.Connected == true) + { + return base.GetStatus() + LoggedInNextAction(); + } + else + { + return base.GetStatus() + LoggedOutNextAction(); + } + } + + protected override void AfterBotLoginHandler() + { + base.AfterBotLoginHandler(); + last_tp_attempt_unixtime = helpers.UnixTimeNow() + 30; + if (reconnect == true) + { + last_tested_home_id = -1; + after_login_fired = false; + teleported = false; + AfterLoginSitDown = false; + reconnect_mode = false; + } + else + { + Client.Self.AlertMessage += AlertEvent; + Client.Network.SimChanged += ChangeSim; + } + after_login_fired = true; + } + + public void TeleportWithSLurl(string sl_url) + { + string[] bits = helpers.ParseSLurl(sl_url); + if (helpers.notempty(bits) == true) + { + if (bits.Length == 4) + { + float.TryParse(bits[1], out float posX); + float.TryParse(bits[2], out float posY); + float.TryParse(bits[3], out float posZ); + string regionName = bits[0]; + if (avoid_sims.ContainsKey(regionName) == false) + { + last_tp_attempt_unixtime = helpers.UnixTimeNow(); + last_attempted_teleport_region = regionName; + Client.Self.Teleport(regionName, new Vector3(posX, posY, posZ)); + } + } + } + } + + public void SetHome(string sl_url) + { + if (sl_url != null) + { + if (myconfig.homeRegion.Contains(sl_url) == false) + { + List old = myconfig.homeRegion.ToList(); + old.Add(sl_url); + myconfig.homeRegion = old.ToArray(); + } + } + last_tested_home_id = -1; + } + } +} diff --git a/Core/bottypes/chatrelay.cs b/Core/bottypes/chatrelay.cs new file mode 100644 index 0000000..0a92f37 --- /dev/null +++ b/Core/bottypes/chatrelay.cs @@ -0,0 +1,88 @@ +using OpenMetaverse; +using System; +using System.Collections.Generic; + +namespace BSB.bottypes +{ + public abstract class ChatRelay : RLVbot + { + #region GroupIMs + protected int groupchatlimit = 10; + protected Dictionary> group_chat_history = new Dictionary>(); + protected Dictionary group_chat_unread = new Dictionary(); + protected bool unread_groupchats; + public override bool HasUnreadGroupchats() + { + return unread_groupchats; + } + protected void CheckUnreadGroupchat() + { + unread_groupchats = group_chat_unread.ContainsValue(true); + } + public override bool GroupHasUnread(UUID group) + { + if (group_chat_unread.ContainsKey(group) == true) + { + return group_chat_unread[group]; + } + return false; + } + public override UUID[] UnreadGroupchatGroups() + { + List reply = new List(); + foreach(KeyValuePair test in group_chat_unread) + { + if (test.Value == true) + { + reply.Add(test.Key); + } + } + return reply.ToArray(); + } + public override string[] GetGroupchat(UUID group) + { + if (group_chat_history.ContainsKey(group) == true) + { + group_chat_unread[group] = false; + CheckUnreadGroupchat(); + return group_chat_history[group].ToArray(); + } + return new string[] { }; + } + protected override void AddToGroupchat(UUID group,string name,string message) + { + if (group_chat_history.ContainsKey(group) == false) + { + group_chat_history.Add(group, new List()); + group_chat_unread.Add(group, false); + } + var date = DateTime.Now; + group_chat_history[group].Add("[" + date.Hour.ToString() + ":" + date.Minute.ToString() + "] " + name + ":" + message + ""); + if (group_chat_history[group].Count > groupchatlimit) + { + group_chat_history[group].RemoveAt(0); + } + if (name != GetClient.Self.Name) + { + unread_groupchats = true; + group_chat_unread[group] = true; + } + } + public override void ClearGroupchat(UUID group) + { + if (group_chat_history.ContainsKey(group) == true) + { + group_chat_history.Remove(group); + group_chat_unread.Remove(group); + CheckUnreadGroupchat(); + } + } + public override void ClearAllGroupchat() + { + group_chat_history = new Dictionary>(); + group_chat_unread = new Dictionary(); + unread_groupchats = false; + } + #endregion + } +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..95d013b --- /dev/null +++ b/Dockerfile @@ -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='Firstname LastName' +ENV password='PassWord' +ENV master='Madpeter Zond' +ENV code='DontTellAnyoneThis' +ENV discord='https://discordapp.com/api/webhooks/XXXX/YYYY' +ENV discordGroupTarget='00000000-0000-0000-0000-000000000000' +ENV allowRLV='false' +ENV homeRegion='"SLurl1","SLurl2"' +ENV EnableHttp='false' +ENV Httpport='80' +ENV Httpkey='ThisIsAKeyYo' +ENV HttpRequireSigned='false' +ENV HttpHost='http://localhost' +ENV HttpAsCnC='false' +ENV DiscordFullServer='false' +ENV DiscordClientToken='https://discordapp.com/developers/ Bot' +ENV DiscordServerID='1234567890' +ENV DiscordServerImHistoryHours='24' +ENV DefaultSitUUID='00000000-0000-0000-0000-000000000000' + +EXPOSE 8080 +ENV ASPNETCORE_URLS http://+:8080 + +ENTRYPOINT ["/app/BetterSecondBot"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..2715ff0 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +``` +______ _ _ __ _____ _ _ _ +| ___ \ | | | | / / / ___| | | | | | +| |_/ / ___| |_| |_ ___ _ __ / / \ `--. ___ ___ ___ _ __ __| | |__ ___ | |_ +| ___ \/ _ \ __| __/ _ \ '__| / / `--. \/ _ \/ __/ _ \| '_ \ / _` | '_ \ / _ \| __| +| |_/ / __/ |_| || __/ | / / /\__/ / __/ (_| (_) | | | | (_| | |_) | (_) | |_ +\____/ \___|\__|\__\___|_| /_/ \____/ \___|\___\___/|_| |_|\__,_|_.__/ \___/ \__| +``` +BetterSecondbot +=============== +BetterSecondbot is a CommandLine based bot for SecondLife based on [LibreMetaverse](https://bitbucket.org/streamadmin/libremetaverse-core/src/master/) + +Status +--------------- +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/61c2eb494f4c4b1dad40ecc186aadcbe)](https://www.codacy.com/manual/madpeter/SecondBot?utm_source=Madpeter@bitbucket.org&utm_medium=referral&utm_content=streamadmin/secondbot&utm_campaign=Badge_Grade) + +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3765/badge)](https://bestpractices.coreinfrastructure.org/projects/3765) + +Docker +--------------- +(Linux): [DockerHub](https://hub.docker.com/r/madpeter/secondbot-bitbucket-auto) + +Windows and Linux builds [Updated 01/04/2020 @ 03:10 GMT] +--------------- +[Download](https://bitbucket.org/streamadmin/secondbot/downloads/) + + +Wiki +--------------- +https://secondbot.madpeter.xyz/wiki/index.html + + + + +Old R5 GUI/Mono version - EOL +--------------- +[OLD release](https://bitbucket.org/streamadmin/secondbot/src/9feb1a3e7650fe39948d18fa2fc48289740d1e82/?at=release%2Fold)