From 50ff8f0c5fd318e6eeaa1f35e2fd8787ccd9df37 Mon Sep 17 00:00:00 2001
From: Madpeter <57852827+Madpeterz@users.noreply.github.com>
Date: Thu, 17 Mar 2022 21:08:02 +0000
Subject: [PATCH] token mania!
---
Core/HttpService/HttpAsService.cs | 54 --
SecondBotEvents/Commands/Animation.cs | 69 ++
SecondBotEvents/Commands/Avatars.cs | 76 ++
SecondBotEvents/Commands/Chat.cs | 161 ++++
SecondBotEvents/Commands/Core.cs | 76 ++
SecondBotEvents/Commands/Dialogs.cs | 95 +++
SecondBotEvents/Commands/Discord.cs | 710 +++++++++++++++++
SecondBotEvents/Commands/Estate.cs | 184 +++++
SecondBotEvents/Commands/Friends.cs | 109 +++
SecondBotEvents/Commands/Funds.cs | 109 +++
SecondBotEvents/Commands/Group.cs | 694 ++++++++++++++++
SecondBotEvents/Commands/Info.cs | 188 +++++
SecondBotEvents/Commands/Inventory.cs | 516 ++++++++++++
SecondBotEvents/Commands/Movement.cs | 313 ++++++++
SecondBotEvents/Commands/Notecard.cs | 129 +++
SecondBotEvents/Commands/ParcelCommands.cs | 782 +++++++++++++++++++
SecondBotEvents/Commands/Self.cs | 273 +++++++
SecondBotEvents/Commands/StreamAdmin.cs | 99 +++
SecondBotEvents/Config/BasicConfig.cs | 50 ++
SecondBotEvents/Config/CommandsConfig.cs | 58 ++
SecondBotEvents/Config/HttpConfig.cs | 28 +
SecondBotEvents/Config/basic_config.cs | 50 --
SecondBotEvents/Config/config.cs | 50 +-
SecondBotEvents/Config/http_config.cs | 33 -
SecondBotEvents/EventsSecondBot.cs | 55 +-
SecondBotEvents/SecondBotEvents.csproj | 1 +
SecondBotEvents/Services/BotClient.cs | 90 ---
SecondBotEvents/Services/BotClientService.cs | 96 +++
SecondBotEvents/Services/Commands.cs | 149 ++++
SecondBotEvents/Services/HttpService.cs | 300 +++++++
SecondBotEvents/Services/Services.cs | 34 +
SecondBotEvents/Services/http.cs | 111 ---
SecondBotEvents/Services/service.cs | 29 -
33 files changed, 5363 insertions(+), 408 deletions(-)
create mode 100644 SecondBotEvents/Commands/Animation.cs
create mode 100644 SecondBotEvents/Commands/Avatars.cs
create mode 100644 SecondBotEvents/Commands/Chat.cs
create mode 100644 SecondBotEvents/Commands/Core.cs
create mode 100644 SecondBotEvents/Commands/Dialogs.cs
create mode 100644 SecondBotEvents/Commands/Discord.cs
create mode 100644 SecondBotEvents/Commands/Estate.cs
create mode 100644 SecondBotEvents/Commands/Friends.cs
create mode 100644 SecondBotEvents/Commands/Funds.cs
create mode 100644 SecondBotEvents/Commands/Group.cs
create mode 100644 SecondBotEvents/Commands/Info.cs
create mode 100644 SecondBotEvents/Commands/Inventory.cs
create mode 100644 SecondBotEvents/Commands/Movement.cs
create mode 100644 SecondBotEvents/Commands/Notecard.cs
create mode 100644 SecondBotEvents/Commands/ParcelCommands.cs
create mode 100644 SecondBotEvents/Commands/Self.cs
create mode 100644 SecondBotEvents/Commands/StreamAdmin.cs
create mode 100644 SecondBotEvents/Config/BasicConfig.cs
create mode 100644 SecondBotEvents/Config/CommandsConfig.cs
create mode 100644 SecondBotEvents/Config/HttpConfig.cs
delete mode 100644 SecondBotEvents/Config/basic_config.cs
delete mode 100644 SecondBotEvents/Config/http_config.cs
delete mode 100644 SecondBotEvents/Services/BotClient.cs
create mode 100644 SecondBotEvents/Services/BotClientService.cs
create mode 100644 SecondBotEvents/Services/Commands.cs
create mode 100644 SecondBotEvents/Services/HttpService.cs
create mode 100644 SecondBotEvents/Services/Services.cs
delete mode 100644 SecondBotEvents/Services/http.cs
delete mode 100644 SecondBotEvents/Services/service.cs
diff --git a/Core/HttpService/HttpAsService.cs b/Core/HttpService/HttpAsService.cs
index e73868b..2bbad8c 100644
--- a/Core/HttpService/HttpAsService.cs
+++ b/Core/HttpService/HttpAsService.cs
@@ -201,60 +201,6 @@ namespace BetterSecondBot.HttpService
- [AttributeUsage(AttributeTargets.Method)]
- public class About : Attribute
- {
- public string about = "";
- public About(string about)
- {
- this.about = about;
- }
- }
-
- [AttributeUsage(AttributeTargets.Method)]
- public class NeedsToken : Attribute
- {
- public bool needsToken = true;
- public NeedsToken(bool needsToken)
- {
- this.needsToken = needsToken;
- }
- }
-
- [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
- public class ReturnHints : Attribute
- {
- public string hint = "";
- public bool good = true;
- public ReturnHints(string hint)
- {
- this.hint = hint;
- }
- }
-
- [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
- public class ReturnHintsFailure : ReturnHints
- {
- public ReturnHintsFailure(string hint): base(hint)
- {
- this.good = false;
- }
- }
-
- [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
- public class ArgHints : Attribute
- {
- public string name = "";
- public string typename = "";
- public string about = "";
-
- public ArgHints(string name,string typename, string about)
- {
- this.name = name;
- this.typename = typename;
- this.about = about;
- }
- }
diff --git a/SecondBotEvents/Commands/Animation.cs b/SecondBotEvents/Commands/Animation.cs
new file mode 100644
index 0000000..461deae
--- /dev/null
+++ b/SecondBotEvents/Commands/Animation.cs
@@ -0,0 +1,69 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+
+namespace SecondBotEvents.Commands
+{
+ internal class Animation : CommandsAPI
+ {
+ public Animation(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+ [About("Toggles if animation requests from this avatar (used for remote poseballs) are accepted")]
+ [ReturnHints("Granted perm animation")]
+ [ReturnHints("Removed perm animation")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ArgHints("avatar", "URLARG", "UUID (or Firstname Lastname)")]
+ [Route(HttpVerbs.Get, "/AddToAllowAnimations/{avatar}/{token}")]
+ public object AddToAllowAnimations(string avatar, string token)
+ {
+ if(AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return BasicReply("avatar lookup", "AddToAllowAnimations", new [] { avatar });
+ }
+ // @todo accept storage
+ return Failure("@todo", "AddToAllowAnimations");
+ }
+
+ [About("Attempts to play a gesture")]
+ [ReturnHintsFailure("Error with gesture")]
+ [ReturnHints("Accepted")]
+ [ArgHints("gesture", "URLARG", "Inventory UUID of the gesture")]
+ [Route(HttpVerbs.Get, "/PlayGesture/{gesture}/{token}")]
+ public object PlayGesture(string gesture, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(gesture, out UUID gestureUUID) == false)
+ {
+ return BasicReply("Error with gesture", "PlayGesture", new [] { gesture });
+ }
+ InventoryItem itm = getClient().Inventory.FetchItem(gestureUUID, getClient().Self.AgentID, (3 * 1000));
+ getClient().Self.PlayGesture(itm.AssetUUID);
+ return BasicReply("Accepted", "PlayGesture", new [] { gesture });
+ }
+
+ [About("Resets the animation stack for the bot")]
+ [ReturnHints("Accepted")]
+ [Route(HttpVerbs.Get, "/ResetAnimations/{token}")]
+ public object ResetAnimations(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ // @todo Reset animations function from old version
+ return Failure("@todo", "ResetAnimations");
+ }
+
+ }
+}
diff --git a/SecondBotEvents/Commands/Avatars.cs b/SecondBotEvents/Commands/Avatars.cs
new file mode 100644
index 0000000..ff1fff6
--- /dev/null
+++ b/SecondBotEvents/Commands/Avatars.cs
@@ -0,0 +1,76 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using System;
+using System.Collections.Generic;
+using OpenMetaverse;
+using Newtonsoft.Json;
+using SecondBotEvents.Services;
+
+namespace SecondBotEvents.Commands
+{
+ public class Avatars : CommandsAPI
+ {
+ public Avatars(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+ [About("an improved version of near me with extra details
NearMeDetails is a object formated as follows
")]
+ [ReturnHints("array NearMeDetails")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [Route(HttpVerbs.Get, "/NearmeWithDetails/{token}")]
+ public object NearmeWithDetails(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Network.CurrentSim == null)
+ {
+ return BasicReply("Error not in a sim", "nearmewithdetails");
+ }
+ List BetterNearMe = new List();
+ Dictionary avcopy = getClient().Network.CurrentSim.ObjectsAvatars.Copy();
+ foreach (Avatar av in avcopy.Values)
+ {
+ if (av.ID != getClient().Self.AgentID)
+ {
+ NearMeDetails details = new NearMeDetails();
+ details.id = av.ID.ToString();
+ details.name = av.Name;
+ details.x = (int)Math.Round(av.Position.X);
+ details.y = (int)Math.Round(av.Position.Y);
+ details.z = (int)Math.Round(av.Position.Z);
+ details.range = (int)Math.Round(Vector3.Distance(av.Position, getClient().Self.SimPosition));
+ BetterNearMe.Add(details);
+ }
+ }
+ return BasicReply(JsonConvert.SerializeObject(BetterNearMe), "nearmewithdetails");
+ }
+
+ [About("returns a list of all known avatars nearby")]
+ [ReturnHints("array UUID = Name")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [Route(HttpVerbs.Get, "/Nearme/{token}")]
+ public object Nearme(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Network.CurrentSim == null)
+ {
+ return BasicReply("Error not in a sim", "nearme");
+ }
+ Dictionary NearMe = new Dictionary();
+ Dictionary avcopy = getClient().Network.CurrentSim.ObjectsAvatars.Copy();
+ foreach (Avatar av in avcopy.Values)
+ {
+ if (av.ID != getClient().Self.AgentID)
+ {
+ NearMe.Add(av.ID, av.Name);
+ }
+ }
+ return BasicReply(JsonConvert.SerializeObject(NearMe), "nearme");
+ }
+ }
+}
diff --git a/SecondBotEvents/Commands/Chat.cs b/SecondBotEvents/Commands/Chat.cs
new file mode 100644
index 0000000..6d57dc0
--- /dev/null
+++ b/SecondBotEvents/Commands/Chat.cs
@@ -0,0 +1,161 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+
+
+namespace SecondBotEvents.Commands
+{
+ public class Chat : CommandsAPI
+ {
+ public Chat(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+ [About("fetchs the last 20 localchat messages")]
+ [ReturnHints("array string")]
+ [Route(HttpVerbs.Get, "/LocalChatHistory/{token}")]
+ public object LocalChatHistory(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(JsonConvert.SerializeObject(bot.getLocalChatHistory()), "LocalChatHistory");
+ }
+
+ [About("sends a message to localchat")]
+ [ArgHints("channel", "URLARG", "the channel to output on (>=0)")]
+ [ArgHints("message", "Text", "the message to send")]
+ [ReturnHints("array string")]
+ [ReturnHintsFailure("Message empty")]
+ [ReturnHintsFailure("Invaild channel")]
+ [Route(HttpVerbs.Post, "/Say/{channel}/{token}")]
+ public object Say(string channel,[FormField] string message,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.isempty(message) == true)
+ {
+ return Failure("Message empty", "Say", new [] { channel, message });
+ }
+ if(int.TryParse(channel,out int channelnum) == false)
+ {
+ return Failure("Invaild channel", "Say", new [] { channel, message });
+ }
+ if(channelnum < 0)
+ {
+ return Failure("Invaild channel", "Say", new [] { channel, message });
+ }
+ getClient().Self.Chat(message, channelnum, ChatType.Normal);
+ if (channelnum == 0)
+ {
+ bot.AddToLocalChat(getClient().Self.Name, message);
+ }
+ return BasicReply(JsonConvert.SerializeObject(bot.getLocalChatHistory()), "Say", new [] { channel, message });
+
+ }
+
+ [About("sends a im to the selected avatar")]
+ [ArgHints("avatar", "URLARG", "a UUID or Firstname Lastname")]
+ [ArgHints("message", "Text", "the message to send")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Message empty")]
+ [ReturnHintsFailure("avatar lookup")]
+ [Route(HttpVerbs.Post, "/IM/{avatar}/{token}")]
+ public object IM(string avatar, [FormField] string message, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if(avataruuid == UUID.Zero)
+ {
+ return Failure("avatar lookup", "IM", new [] { avatar, message });
+ }
+ if (SecondbotHelpers.isempty(message) == true)
+ {
+ return Failure("Message empty", "IM", new [] { avatar, message });
+ }
+ bot.SendIM(avataruuid, message);
+ return BasicReply("ok", "IM", new [] { avatar, message });
+ }
+
+ [About("gets a full list of all chat windows")]
+ [ReturnHints("array UUID = Name")]
+ [Route(HttpVerbs.Get, "/chatwindows/{token}")]
+ public object chatwindows(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(JsonConvert.SerializeObject(bot.GetIMChatWindowKeyNames()), "chatwindows");
+ }
+
+ [About("gets a list of chat windows with unread messages")]
+ [ReturnHints("array of UUID")]
+ [Route(HttpVerbs.Get, "/listwithunread/{token}")]
+ public object listwithunread(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ List unreadimwindows = new List();
+ foreach (UUID window in bot.IMChatWindows())
+ {
+ if (bot.ImChatWindowHasUnread(window) == true)
+ {
+ unreadimwindows.Add(window);
+ }
+ }
+ return BasicReply(JsonConvert.SerializeObject(unreadimwindows), "listwithunread");
+ }
+
+ [About("gets if there are any unread im messages at all")]
+ [ReturnHints("True|False")]
+ [Route(HttpVerbs.Get, "/haveunreadims/{token}")]
+ public object haveunreadims(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ bool reply = false;
+ foreach (UUID window in bot.IMChatWindows())
+ {
+ if (bot.ImChatWindowHasUnread(window) == true)
+ {
+ reply = true;
+ break;
+ }
+ }
+ return BasicReply(reply.ToString(), "haveunreadims");
+ }
+
+ [About("gets the chat from the selected window")]
+ [ArgHints("window", "URLARG", "the UUID of the chat window")]
+ [ReturnHintsFailure("Window UUID invaild")]
+ [ReturnHints("Array of text")]
+ [Route(HttpVerbs.Get, "/getimchat/{window}/{token}")]
+ public object getimchat(string window, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(window, out UUID windowUUID) == false)
+ {
+ return Failure("Window UUID invaild", "getimchat", new[] { window });
+ }
+ return BasicReply(JsonConvert.SerializeObject(bot.GetIMChatWindow(windowUUID)), "getimchat", new[] { window });
+ }
+
+ }
+
+
+}
diff --git a/SecondBotEvents/Commands/Core.cs b/SecondBotEvents/Commands/Core.cs
new file mode 100644
index 0000000..e5be4e1
--- /dev/null
+++ b/SecondBotEvents/Commands/Core.cs
@@ -0,0 +1,76 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+using System.Threading;
+
+namespace SecondBotEvents.Commands
+{
+ public class Core : CommandsAPI
+ {
+ public Core(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+ [About("Used to check HTTP connections")]
+ [ReturnHints("world")]
+ [NeedsToken(false)]
+ [Route(HttpVerbs.Get, "/Hello")]
+ public object Hello()
+ {
+ return BasicReply("world", "Hello");
+ }
+
+ [About("Delays a thead by X ms
Mostly pointless but good if your doing custom commands")]
+ [ReturnHintsFailure("Invaild amount")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/Delay/{amount}/{token}")]
+ public object Delay(string amount,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (int.TryParse(amount,out int amountvalue) == false)
+ {
+ return Failure("Invaild amount", "Delay", new [] { amount });
+ }
+ Thread.Sleep(amountvalue);
+ return BasicReply("Ok", "Delay", new [] { amount });
+ }
+
+ [About("Removes the given token from the accepted token pool")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/LogoutUI/{token}")]
+ public object LogoutUI(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ SuccessNoReturn("LogoutUI");
+ // @todo remove token from stoage
+ return BasicReply("ok");
+ }
+ }
+
+ public class CommandLibCall
+ {
+ public string Command { get; set; }
+ public string[] Args { get; set; }
+ public string AuthCode { get; set; }
+ }
+
+ public class NearMeDetails
+ {
+ public string id { get; set; }
+ public string name { get; set; }
+ public int x { get; set; }
+ public int y { get; set; }
+ public int z { get; set; }
+ public int range { get; set; }
+
+ }
+
+
+}
diff --git a/SecondBotEvents/Commands/Dialogs.cs b/SecondBotEvents/Commands/Dialogs.cs
new file mode 100644
index 0000000..0c5434b
--- /dev/null
+++ b/SecondBotEvents/Commands/Dialogs.cs
@@ -0,0 +1,95 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+
+
+namespace SecondBotEvents.Commands
+{
+ public class Dialogs : CommandsAPI
+ {
+ public Dialogs(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+ [About("Updates the relay target (you can have 1 of each type)
Clear will disable them all")]
+ [ReturnHints("cleared")]
+ [ReturnHints("set/avatar [ok]")]
+ [ReturnHints("set/http [ok]")]
+ [ReturnHints("set/channel [ok]")]
+ [ReturnHintsFailure("Not a vaild option")]
+ [ArgHints("target","URLARG", "Options: Channel (Any number),Avatar UUID,HTTPurl
Clear")]
+ [Route(HttpVerbs.Get, "/DialogRelay/{target}/{token}")]
+ public object DialogRelay(string target,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (target == "clear")
+ {
+ bot.SetRelayDialogsAvatar(UUID.Zero);
+ bot.SetRelayDialogsChannel(0);
+ bot.SetRelayDialogsHTTP("");
+ return BasicReply("cleared", "DialogRelay", new [] { target });
+ }
+ ProcessAvatar(target);
+ if (avataruuid != UUID.Zero)
+ {
+ bot.SetRelayDialogsAvatar(avataruuid);
+ return BasicReply("set/avatar [ok]", "DialogRelay", new [] { target });
+ }
+ if (target.StartsWith("http") == true)
+ {
+ bot.SetRelayDialogsHTTP(target);
+ return BasicReply("set/http [ok]", "DialogRelay", new [] { target });
+ }
+ if (int.TryParse(target, out int channel) == true)
+ {
+ bot.SetRelayDialogsChannel(channel);
+ return BasicReply("set/channel [ok]", "DialogRelay", new [] { target });
+ }
+ return Failure("Not a vaild option", "DialogRelay", new [] { target });
+
+ }
+
+ [About("Makes the bot interact with the dialog [dialogid] with the button [buttontext]")]
+ [ReturnHints("true")]
+ [ReturnHints("false")]
+ [ReturnHintsFailure("bad dialog id")]
+ [ArgHints("dialogid", "URLARG", "The ID for the dialog")]
+ [ArgHints("buttontext", "URLARG", "The button text to push")]
+ [Route(HttpVerbs.Get, "/DialogResponce/{dialogid}/{buttontext}/{token}")]
+ public object DialogResponce(string dialogid, string buttontext, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (int.TryParse(dialogid, out int dialogidnum) == false)
+ {
+ return Failure("bad dialog id", "DialogResponce", new [] { dialogid, buttontext });
+ }
+ return BasicReply(bot.DialogReply(dialogidnum, buttontext).ToString(), "DialogResponce", new [] { dialogid, buttontext });
+ }
+
+ [About("Should the bot track dialogs and send them to the relays setup?")]
+ [ReturnHints("updated")]
+ [ReturnHintsFailure("bad status")]
+ [ArgHints("status", "URLARG", "true or false")]
+ [Route(HttpVerbs.Get, "/DialogTrack/{status}/{token}")]
+ public object DialogTrack(string status, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bool.TryParse(status, out bool statuscode) == false)
+ {
+ return Failure("bad status", "DialogTrack", new [] { status });
+ }
+ bot.SetTrackDialogs(statuscode);
+ return BasicReply("updated", "DialogTrack", new [] { status });
+ }
+
+ }
+}
diff --git a/SecondBotEvents/Commands/Discord.cs b/SecondBotEvents/Commands/Discord.cs
new file mode 100644
index 0000000..a5b04fc
--- /dev/null
+++ b/SecondBotEvents/Commands/Discord.cs
@@ -0,0 +1,710 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+using System;
+using System.Collections.Generic;
+
+namespace SecondBotEvents.Commands
+{
+ public class Discord : CommandsAPI
+ {
+ public Discord(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+ [About("Adds a discord server role to the selected member")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ReturnHints("true|false")]
+ [ArgHints("serverid","URLARG","the server id to apply this action to")]
+ [ArgHints("roleid", "URLARG", "the role id we are giving")]
+ [ArgHints("memberid", "URLARG", "who we are giving it to")]
+ [Route(HttpVerbs.Get, "/Discord_AddRole/{serverid}/{roleid}/{memberid}/{token}")]
+ public object Discord_AddRole(string serverid,string roleid,string memberid,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_AddRole", new [] { serverid, roleid, memberid });
+ }
+ return BasicReply(addRoleToMember(serverid, roleid, memberid).Result.ToString(), "Discord_AddRole", new [] { serverid, roleid, memberid });
+ }
+
+ [About("Adds the selected user to the ban list - Disallows rejoining untill they are removed from the list")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ReturnHintsFailure("Why empty")]
+ [ReturnHints("true|false")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [ArgHints("memberid", "URLARG", "who we are giving it to")]
+ [ArgHints("why", "string", "why they are being banned")]
+ [Route(HttpVerbs.Post, "/Discord_BanMember/{serverid}/{memberid}/{token}")]
+ public object Discord_BanMember(string serverid, string memberid, [FormField] string why, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_BanMember", new [] { serverid, memberid, why });
+ }
+ if (SecondbotHelpers.notempty(why) == false)
+ {
+ return Failure("Why empty", "Discord_BanMember", new [] { serverid, memberid, why });
+ }
+ return BasicReply(BanMember(serverid, memberid, why).Result.ToString(), "Discord_BanMember", new [] { serverid, memberid, why });
+ }
+
+ [About("Clears messages on the server sent by the member in the last 13 days, 22hours 59mins")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ReturnHints("true|false")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [ArgHints("memberid", "URLARG", "who we are giving it to")]
+ [Route(HttpVerbs.Get, "/Discord_BulkClear_Messages/{serverid}/{memberid}/{token}")]
+ public object Discord_BulkClear_Messages(string serverid, string memberid, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_BulkClear_Messages", new [] { serverid, memberid });
+ }
+ SuccessNoReturn("Discord_BulkClear_Messages", new [] { serverid, memberid });
+ return ClearMessages(serverid, memberid).Result;
+ }
+
+ [About("Sends a message directly to the user [They must be in the server]\n This command requires the SERVER MEMBERS INTENT found in discord app dev")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ReturnHints("true|false")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [ArgHints("memberid", "URLARG", "who we are giving it to")]
+ [ArgHints("message", "Text", "what we are sending")]
+ [Route(HttpVerbs.Post, "/Discord_Dm_Member/{serverid}/{memberid}/{token}")]
+ public object Discord_Dm_Member(string serverid, string memberid, [FormField] string message, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_Dm_Member", new [] { serverid, memberid, message });
+ }
+ return BasicReply(MessageMember(serverid, memberid, message).Result.ToString(), "Discord_Dm_Member", new [] { serverid, memberid, message });
+ }
+
+ [About("Returns a list of members in a server \n collection is userid: username \n if the user has set a nickname: userid: nickname|username \n" +
+ " This command requires Discord full client mode enabled and connected\n !!!! This command also requires: Privileged Gateway Intents / " +
+ "SERVER MEMBERS INTENT set to true on the discord bot api area !!!")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ReturnHints("mixed array of userid: nickname|username or userid:username")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [Route(HttpVerbs.Get, "/Discord_MembersList/{serverid}/{token}")]
+ public object Discord_MembersList(string serverid, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_MembersList", new [] { serverid });
+ }
+ SuccessNoReturn("Discord_MembersList", new [] { serverid });
+ return ListMembers(serverid).Result;
+ }
+
+ [About("Sends a message to the selected channel - Optional TTS usage")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ReturnHintsFailure("message empty")]
+ [ReturnHints("true|false")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [ArgHints("channelid", "URLARG", "the channel id to apply this action to")]
+ [ArgHints("tts", "URLARG", "shoud tts be enabled true or false")]
+ [ArgHints("message", "Text", "what we are sending")]
+ [Route(HttpVerbs.Post, "/Discord_MembersList/{serverid}/{tts}/{token}")]
+ public object Discord_MessageChannel(string serverid, string channelid, string tts, [FormField] string message, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_MessageChannel", new [] { serverid, channelid, tts, message });
+ }
+ if (SecondbotHelpers.notempty(message) == false)
+ {
+ return Failure("message empty", "Discord_MessageChannel", new [] { serverid, channelid, tts, message });
+ }
+ return BasicReply(MessageChannel(serverid, channelid, message, tts).Result.ToString(), "Discord_MessageChannel", new [] { serverid, channelid, tts, message });
+ }
+
+ [About("Sends a message to the selected channel - Optional TTS usage")]
+ [ReturnHints("mixed array of userid: nickname|username or userid:username")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [ArgHints("memberid", "URLARG", "who we are giving it to")]
+ [ArgHints("mode", "URLARG", "should we mute them \"true\" or unmute \"false\"")]
+ [Route(HttpVerbs.Get, "/Discord_MuteMember/{serverid}/{memberid}/{mode}/{token}")]
+ public object Discord_MuteMember(string serverid, string memberid, string mode, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_MuteMember", new [] { serverid, memberid, mode });
+ }
+ return BasicReply(MuteMember(serverid, memberid, mode).Result.ToString(), "Discord_MuteMember", new [] { serverid, memberid, mode });
+ }
+
+ [About("returns a collection of settings for the given role \n This command requires Discord full client mode enabled and connected")]
+ [ReturnHints("KeyPair of status: KeyPair[] item = value")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [ArgHints("memberid", "URLARG", "who we are giving it to")]
+ [ArgHints("mode", "URLARG", "should we mute them \"true\" or unmute \"false\"")]
+ [Route(HttpVerbs.Get, "/Discord_Role_GetSettings/{serverid}/{roleid}/{token}")]
+ public object Discord_Role_GetSettings(string serverid, string roleid, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_Role_GetSettings", new [] { serverid, roleid });
+ }
+ SuccessNoReturn("Discord_Role_GetSettings", new [] { serverid, roleid });
+ return RoleSettings(serverid,roleid);
+ }
+
+ [About("Updates perm flags for the selected role \n example CSV format: Speak=True,SendMessages=False \n for a full list of perms see output of Discord_Role_GetSettings \n" +
+ " This command requires Discord full client mode enabled and connected")]
+ [ReturnHints("true|false")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [ArgHints("roleid", "URLARG", "who we are giving it to")]
+ [ArgHints("flagscsv", "Text", "what we are setting")]
+ [Route(HttpVerbs.Post, "/Discord_Role_UpdatePerms/{serverid}/{roleid}/{token}")]
+ public object Discord_Role_UpdatePerms(string serverid, string roleid, [FormField] string flagscsv, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_Role_UpdatePerms", new [] { serverid, roleid, flagscsv });
+ }
+ return BasicReply(UpdateRolePerms(serverid, roleid, flagscsv).Result.ToString(), "Discord_Role_UpdatePerms", new [] { serverid, roleid, flagscsv });
+ }
+
+ [About("Updates perm flags for the selected role \n example CSV format: Speak=True,SendMessages=False \n for a full list of perms see output of Discord_Role_GetSettings \n" +
+ " This command requires Discord full client mode enabled and connected")]
+ [ReturnHints("KeyPair of statusmessage=roleid or 0")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [ArgHints("role", "URLARG", "the name of the role we are creating")]
+ [Route(HttpVerbs.Get, "/Discord_RoleCreate/{serverid}/{role}/{token}")]
+ public object Discord_RoleCreate(string serverid, string role, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_RoleCreate", new [] { serverid, role });
+ }
+ SuccessNoReturn("Discord_RoleCreate", new [] { serverid, role });
+ return CreateRole(serverid,role).Result;
+ }
+
+ [About("Returns a list of roles and their ids in collection \n This command requires Discord full client mode enabled and connected")]
+ [ReturnHints("KeyPair of status: KeyPair of roleid: rolename")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [Route(HttpVerbs.Get, "/Discord_RoleList/{serverid}/{token}")]
+ public object Discord_RoleList(string serverid, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_RoleList", new [] { serverid });
+ }
+ return BasicReply(ListRoles(serverid).ToString(), "Discord_RoleList", new [] { serverid });
+ }
+
+ [About("Remove a role from a server \n This command requires Discord full client mode enabled and connected")]
+ [ReturnHints("true|false")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [Route(HttpVerbs.Get, "/Discord_RoleRemove/{serverid}/{roleid}/{token}")]
+ public object Discord_RoleRemove(string serverid, string roleid, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_RoleRemove", new [] { serverid, roleid });
+ }
+ return BasicReply(RemoveRole(serverid, roleid).Result.ToString(), "Discord_RoleRemove", new [] { serverid, roleid });
+ }
+
+ [About("Returns a list of text channels in a server")]
+ [ReturnHints("array of channelid: name")]
+ [ReturnHintsFailure("Discord client not ready")]
+ [ArgHints("serverid", "URLARG", "the server id to apply this action to")]
+ [Route(HttpVerbs.Get, "/Discord_TextChannels_List/{serverid}/{roleid}/{token}")]
+ public object Discord_TextChannels_List(string serverid, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.discordReady() == false)
+ {
+ return Failure("Discord client not ready", "Discord_TextChannels_List", new [] { serverid });
+ }
+ return ListTextChannels(serverid);
+ }
+
+
+ #region discord helper functions
+
+ protected KeyValuePair> ListTextChannels(string givenserverid)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == true)
+ {
+ return new KeyValuePair>(false, new Dictionary());
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ Dictionary textChannels = new Dictionary();
+ foreach (SocketTextChannel channel in server.TextChannels)
+ {
+ textChannels.Add(channel.Id.ToString(), channel.Name);
+ }
+ return new KeyValuePair>(true, textChannels);
+ }
+
+ protected async Task RemoveRole(string givenserverid, string givenroleid)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return false;
+ }
+ if (ulong.TryParse(givenroleid, out ulong roleid) == false)
+ {
+ return false;
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ SocketRole role = server.GetRole(roleid);
+ await role.DeleteAsync().ConfigureAwait(true);
+ return true;
+ }
+
+ protected KeyValuePair> ListRoles(string givenserverid)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return new KeyValuePair>(false, new Dictionary());
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ Dictionary rolesList = new Dictionary();
+ foreach (SocketRole role in server.Roles)
+ {
+ rolesList.Add(role.Id.ToString(), role.Name);
+ }
+ return new KeyValuePair>(true, rolesList);
+ }
+
+ protected async Task> CreateRole(string givenserverid, string givenrole)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == true)
+ {
+ return new KeyValuePair("Unable to process server id", 0);
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ RestRole role = await server.CreateRoleAsync(givenrole, new GuildPermissions(), Color.DarkRed, false, null).ConfigureAwait(true);
+ return new KeyValuePair("ok", role.Id);
+ }
+
+ protected async Task UpdateRolePerms(string givenserverid,string givenroleid,string givenflagscsv)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return false;
+ }
+ if (ulong.TryParse(givenroleid, out ulong roleid) == true)
+ {
+ return false;
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ SocketRole role = server.GetRole(roleid);
+ string[] bits = givenflagscsv.Split(',');
+ GuildPermissions NewPerms = role.Permissions;
+ foreach (String bit in bits)
+ {
+ string[] subbits = bit.Split('=');
+ if (subbits.Length == 2)
+ {
+ if (bool.TryParse(subbits[1], out bool flagValue) == true)
+ {
+ NewPerms = UpdateFlag(NewPerms, subbits[0], flagValue);
+ }
+ }
+ }
+ await role.ModifyAsync(Rp =>
+ {
+ Rp.Permissions = NewPerms;
+ }).ConfigureAwait(true);
+ return true;
+ }
+
+ protected GuildPermissions UpdateFlag(GuildPermissions NewPerms, string flag, bool flagValue)
+ {
+ switch (flag)
+ {
+ case "CreateInstantInvite":
+ NewPerms = NewPerms.Modify(flagValue);
+ break;
+ case "KickMembers":
+ NewPerms = NewPerms.Modify(null, flagValue);
+ break;
+ case "BanMembers":
+ NewPerms = NewPerms.Modify(null, null, flagValue);
+ break;
+ case "Administrator":
+ NewPerms = NewPerms.Modify(null, null, null, flagValue);
+ break;
+ case "ManageChannels":
+ NewPerms = NewPerms.Modify(null, null, null, null, flagValue);
+ break;
+ case "ManageGuild":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, flagValue);
+ break;
+ case "AddReactions":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, flagValue);
+ break;
+ case "ViewAuditLog":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, flagValue);
+ break;
+ case "SendTTSMessages":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "AttachFiles":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "ReadMessageHistory":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "MentionEveryone":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "UseExternalEmojis":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "Connect":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "Speak":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "MuteMembers":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "UseVAD":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "MoveMembers":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "EmbedLinks":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "PrioritySpeaker":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "Stream":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "ChangeNickname":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "ManageNicknames":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "ManageRoles":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "DeafenMembers":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "ManageMessages":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "ViewChannel":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "SendMessages":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "ManageWebhooks":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ case "ManageEmojis":
+ NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
+ break;
+ default:
+ break;
+ }
+ return NewPerms;
+ }
+
+ protected KeyValuePair> RoleSettings(string givenserverid, string givenroleid)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return new KeyValuePair>(false, new Dictionary());
+ }
+ if (ulong.TryParse(givenroleid, out ulong roleid) == false)
+ {
+ return new KeyValuePair>(false, new Dictionary());
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ SocketRole role = server.GetRole(roleid);
+ Dictionary settings = new Dictionary
+ {
+ { "Config|Color", String.Join(",", new byte[3] { role.Color.R, role.Color.G, role.Color.B }) },
+ { "Config|Name", role.Name },
+ { "Info|AssignedCount", role.Members.ToString() },
+ { "Perm|AttachFiles", role.Permissions.AttachFiles.ToString() },
+ { "Perm|ReadMessageHistory", role.Permissions.ReadMessageHistory.ToString() },
+ { "Perm|MentionEveryone", role.Permissions.MentionEveryone.ToString() },
+ { "Perm|UseExternalEmojis", role.Permissions.UseExternalEmojis.ToString() },
+ { "Perm|Connect", role.Permissions.Connect.ToString() },
+ { "Perm|Speak", role.Permissions.Speak.ToString() },
+ { "Perm|MuteMembers", role.Permissions.MuteMembers.ToString() },
+ { "Perm|UseVAD", role.Permissions.UseVAD.ToString() },
+ { "Perm|MoveMembers", role.Permissions.MoveMembers.ToString() },
+ { "Perm|EmbedLinks", role.Permissions.EmbedLinks.ToString() },
+ { "Perm|PrioritySpeaker", role.Permissions.PrioritySpeaker.ToString() },
+ { "Perm|Stream", role.Permissions.Stream.ToString() },
+ { "Perm|ChangeNickname", role.Permissions.ChangeNickname.ToString() },
+ { "Perm|ManageNicknames", role.Permissions.ManageNicknames.ToString() },
+ { "Perm|ManageRoles", role.Permissions.ManageRoles.ToString() },
+ { "Perm|DeafenMembers", role.Permissions.DeafenMembers.ToString() },
+ { "Perm|ManageMessages", role.Permissions.ManageMessages.ToString() },
+ { "Perm|ViewChannel", role.Permissions.ViewChannel.ToString() },
+ { "Perm|SendMessages", role.Permissions.SendMessages.ToString() },
+ { "Perm|CreateInstantInvite", role.Permissions.CreateInstantInvite.ToString() },
+ { "Perm|BanMembers", role.Permissions.BanMembers.ToString() },
+ { "Perm|SendTTSMessages", role.Permissions.SendTTSMessages.ToString() },
+ { "Perm|Administrator", role.Permissions.Administrator.ToString() },
+ { "Perm|ManageChannels", role.Permissions.ManageChannels.ToString() },
+ { "Perm|KickMembers", role.Permissions.KickMembers.ToString() },
+ { "Perm|AddReactions", role.Permissions.AddReactions.ToString() },
+ { "Perm|ViewAuditLog", role.Permissions.ViewAuditLog.ToString() },
+ { "Perm|ManageWebhooks", role.Permissions.ManageWebhooks.ToString() },
+ { "Perm|ManageGuild", role.Permissions.ManageGuild.ToString() },
+ { "Perm|ManageEmojis", role.Permissions.ManageEmojis.ToString() }
+ };
+ return new KeyValuePair>(true, settings);
+ }
+
+ protected async Task MuteMember(string givenserverid, string givenmemberid, string mode)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == true)
+ {
+ return false;
+ }
+ if (ulong.TryParse(givenmemberid, out ulong memberid) == true)
+ {
+ return false;
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ SocketGuildUser user = server.GetUser(memberid);
+ bool.TryParse(mode, out bool status);
+ await user.ModifyAsync(pr =>
+ {
+ pr.Mute = status;
+ });
+ return true;
+ }
+
+ protected async Task MessageChannel(string givenserverid, string givenchannelid, string givenmessage, string tts)
+ {
+ if (bool.TryParse(tts, out bool useTTS) == false)
+ {
+ return false;
+ }
+ return await bot.SendMessageToDiscord(givenserverid, givenchannelid, givenmessage, useTTS);
+ }
+
+
+ protected async Task>> ListMembers(string givenserverid)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return new KeyValuePair>(false, new Dictionary());
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ IEnumerable users = await server.GetUsersAsync().FlattenAsync().ConfigureAwait(true);
+ Dictionary membersList = new Dictionary();
+
+ foreach (IGuildUser user in users)
+ {
+ if (user.Nickname != null)
+ {
+ membersList.Add(user.Id.ToString(), user.Nickname + "|" + user.Username);
+ }
+ else
+ {
+ membersList.Add(user.Id.ToString(), user.Username);
+ }
+ }
+ return new KeyValuePair>(true, membersList);
+ }
+
+
+ protected async Task MessageMember(string givenserverid, string givenmemberid, string givenmessage)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return false;
+ }
+ if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
+ {
+ return false;
+ }
+ try
+ {
+ SocketGuild server = Discord.GetGuild(serverid);
+ IEnumerable users = await server.GetUsersAsync().FlattenAsync().ConfigureAwait(true);
+ bool sent = false;
+ foreach (IGuildUser user in users)
+ {
+ if (user.Id == memberid)
+ {
+ sent = true;
+ await user.SendMessageAsync(givenmessage);
+ break;
+ }
+ }
+ return sent;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ protected async Task> ClearMessages(string givenserverid, string givenmemberid)
+ {
+ int messagesDeleted = 0;
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return new KeyValuePair(false, 0);
+ }
+ if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
+ {
+ return new KeyValuePair(false, 0);
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ SocketGuildUser user = server.GetUser(memberid);
+ TimeSpan somedays = new TimeSpan(13, 22, 59, 59, 0);
+ DateTimeOffset justundertwosweeks = DateTimeOffset.Now;
+ justundertwosweeks = justundertwosweeks.Subtract(somedays);
+ long unixtimelimit = justundertwosweeks.ToUnixTimeSeconds();
+ foreach (SocketTextChannel channel in server.TextChannels)
+ {
+ List DeleteMessages = new List();
+ IEnumerable messages = await channel.GetMessagesAsync(100).FlattenAsync();
+ foreach (IMessage message in messages)
+ {
+ if (message.CreatedAt.ToUnixTimeSeconds() > unixtimelimit)
+ {
+ if (message.Author.Id == user.Id)
+ {
+ DeleteMessages.Add(message);
+ }
+ }
+ }
+ if (DeleteMessages.Count > 0)
+ {
+ messagesDeleted += DeleteMessages.Count;
+ await channel.DeleteMessagesAsync(DeleteMessages);
+ }
+ }
+ return new KeyValuePair(true, messagesDeleted);
+ }
+
+
+
+ protected async Task BanMember(string givenserverid, string givenmemberid, string why)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return false;
+ }
+ if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
+ {
+ return false;
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ SocketGuildUser user = server.GetUser(memberid);
+ await user.BanAsync(7, why);
+ return true;
+ }
+
+
+ protected async Task addRoleToMember(string givenserverid, string givenroleid, string givenmemberid)
+ {
+ DiscordSocketClient Discord = bot.getDiscordClient();
+ if (ulong.TryParse(givenserverid, out ulong serverid) == false)
+ {
+ return false;
+ }
+ if (ulong.TryParse(givenroleid, out ulong roleid) == false)
+ {
+ return false;
+ }
+ if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
+ {
+ return false;
+ }
+ SocketGuild server = Discord.GetGuild(serverid);
+ SocketRole role = server.GetRole(roleid);
+ SocketGuildUser user = server.GetUser(memberid);
+ await user.AddRoleAsync(role); // ? Irole seems to accept SocketRole ?
+ return true;
+ }
+
+
+ #endregion
+ }
+}
diff --git a/SecondBotEvents/Commands/Estate.cs b/SecondBotEvents/Commands/Estate.cs
new file mode 100644
index 0000000..5f913d9
--- /dev/null
+++ b/SecondBotEvents/Commands/Estate.cs
@@ -0,0 +1,184 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+
+
+namespace SecondBotEvents.Commands
+{
+ public class Estate : CommandsAPI
+ {
+ public Estate(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+ [About("Sends the message to the current sim")]
+ [ReturnHints("restarting")]
+ [ReturnHintsFailure("Not an estate manager here")]
+ [ReturnHintsFailure("canceled")]
+ [ArgHints("delay", "URLARG", "How long to delay the restart for (30 to 240 secs) - defaults to 240 if out of bounds \n" +
+ "set to 0 if your canceling!")]
+ [ArgHints("mode", "URLARG", "true to start a restart, false to cancel")]
+ [Route(HttpVerbs.Get, "/SimRestart/{delay}/{mode}/{token}")]
+ public object SimRestart(string delay, string mode, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Network.CurrentSim.IsEstateManager == false)
+ {
+ return Failure("Not an estate manager here", "SimRestart", new [] { delay, mode });
+ }
+ bool.TryParse(mode, out bool modeflag);
+ if (modeflag == false)
+ {
+ getClient().Estate.CancelRestart();
+ return BasicReply("canceled", "SimRestart", new [] { delay, mode });
+ }
+ int delay_restart = 60;
+ int.TryParse(delay, out delay_restart);
+ if((delay_restart < 30) || (delay_restart > 240))
+ {
+ delay_restart = 240;
+ }
+ getClient().Estate.RestartRegion(delay_restart);
+ return BasicReply("restarting", "SimRestart", new [] { delay, mode });
+ }
+
+ [About("Sends the message to the current sim")]
+ [ReturnHintsFailure("Not an estate manager here")]
+ [ReturnHintsFailure("Message empty")]
+ [ReturnHints("ok")]
+ [ArgHints("message", "Text", "What the message is")]
+ [Route(HttpVerbs.Post, "/SimMessage/{token}")]
+ public object SimMessage([FormField] string message, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.notempty(message) == false)
+ {
+ return Failure("Message empty", "SimMessage", new [] { message });
+ }
+ if (getClient().Network.CurrentSim.IsEstateManager == false)
+ {
+ return Failure("Not an estate manager here", "SimMessage", new [] { message });
+ }
+ getClient().Estate.SimulatorMessage(message);
+ return BasicReply("ok", "SimMessage", new [] { message });
+ }
+
+ [About("Fetchs the regions map tile")]
+ [ReturnHintsFailure("Unable to find region")]
+ [ReturnHints("Texture UUID")]
+ [ArgHints("regionname", "URLARG", "the name of the region we are fetching")]
+ [Route(HttpVerbs.Get, "/GetSimTexture/{regionname}/{token}")]
+ public object GetSimTexture(string regionname, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Grid.GetGridRegion(regionname, GridLayerType.Objects, out GridRegion region) == false)
+ {
+ return Failure("Unable to find region", "GetSimTexture", new [] { regionname });
+ }
+ return BasicReply(region.MapImageID.ToString(), "GetSimTexture", new [] { regionname });
+ }
+
+ [About("Reclaims ownership of the current parcel")]
+ [ReturnHintsFailure("Not an estate manager here")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/EstateParcelReclaim/{token}")]
+ public object EstateParcelReclaim(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Network.CurrentSim.IsEstateManager == false)
+ {
+ return Failure("Not an estate manager here", "EstateParcelReclaim");
+ }
+ int localid = getClient().Parcels.GetParcelLocalID(getClient().Network.CurrentSim, getClient().Self.SimPosition);
+ getClient().Parcels.Reclaim(getClient().Network.CurrentSim, localid);
+ return BasicReply("ok", "EstateParcelReclaim");
+ }
+
+ [About("Reclaims ownership of the current parcel")]
+ [ReturnHintsFailure("Not an estate manager here")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/GetSimGlobalPos/{regionname}/{token}")]
+ public object GetSimGlobalPos(string regionname, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Grid.GetGridRegion(regionname, GridLayerType.Objects, out GridRegion region) == false)
+ {
+ return Failure("Unable to find region", "GetSimGlobalPos", new [] { regionname });
+ }
+ Dictionary reply = new Dictionary();
+ reply.Add("region", regionname);
+ reply.Add("X", region.X.ToString());
+ reply.Add("Y", region.Y.ToString());
+ SuccessNoReturn("GetSimGlobalPos", new [] { regionname });
+ return reply;
+ }
+
+ [About("Requests the estate banlist")]
+ [ReturnHints("ban list json")]
+ [Route(HttpVerbs.Get, "/GetEstateBanList/{token}")]
+ public object GetEstateBanList(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(JsonConvert.SerializeObject(bot._lastUpdatedSimBlacklist), "banlist");
+ }
+
+ [About("Attempts to add/remove the avatar to/from the Estate banlist")]
+ [ReturnHints("Unban request accepted")]
+ [ReturnHints("Ban request accepted")]
+ [ReturnHintsFailure("Unable to find avatar UUID")]
+ [ReturnHintsFailure("Unable to process global value please use true or false")]
+ [ReturnHintsFailure("Not an estate manager on region {REGIONNAME}")]
+ [ArgHints("avatar", "URLARG", "the uuid avatar you wish to ban")]
+ [ArgHints("mode", "URLARG", "What action would you like to take
Defaults to remove if not given \"add\"")]
+ [ArgHints("global", "URLARG", "if true this the ban/unban will be applyed to all estates the bot has access to")]
+ [Route(HttpVerbs.Get, "/UpdateEstateBanlist/{avatar}/{mode}/{global}/{token}")]
+
+ public object UpdateEstateBanlist(string avatar, string mode, string global, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Network.CurrentSim.IsEstateManager == false)
+ {
+ return Failure("Not an estate manager on region " + getClient().Network.CurrentSim.Name, "UpdateEstateBanlist", new [] { avatar, mode, global });
+ }
+ UUID avataruuid = UUID.Zero;
+ if (UUID.TryParse(avatar, out avataruuid) == false)
+ {
+ return Failure("Unable to find avatar UUID", "UpdateEstateBanlist", new [] { avatar, mode, global });
+ }
+ bool globalban = false;
+ if (bool.TryParse(global, out globalban) == false)
+ {
+ return Failure("Unable to process global value please use true or false", "UpdateEstateBanlist", new [] { avatar, mode, global });
+ }
+ if (mode != "add")
+ {
+ getClient().Estate.UnbanUser(avataruuid, globalban);
+ return BasicReply("Unban request accepted", "UpdateEstateBanlist", new [] { avatar, mode, global });
+ }
+ getClient().Estate.BanUser(avataruuid, globalban);
+ return BasicReply("Ban request accepted", "UpdateEstateBanlist", new [] { avatar, mode, global });
+ }
+ }
+}
diff --git a/SecondBotEvents/Commands/Friends.cs b/SecondBotEvents/Commands/Friends.cs
new file mode 100644
index 0000000..6f6c015
--- /dev/null
+++ b/SecondBotEvents/Commands/Friends.cs
@@ -0,0 +1,109 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+
+
+namespace SecondBotEvents.Commands
+{
+ public class Friends : CommandsAPI
+ {
+ public Friends(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+ [About("Gets the friendslist
Formated as follows
friendreplyobject
- name: String
- id: String
- online: bool
")]
+ [ReturnHints("array UUID = friendreplyobject")]
+ [Route(HttpVerbs.Get, "/Friendslist/{token}")]
+ public object Friendslist(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(bot.getJsonFriendlist(), "Friendslist");
+ }
+
+ [About("Updates the friend perms for avatar avatar to State \n if true grants (Online/Map/Modify) perms")]
+ [ArgHints("avatar", "URLARG", "A avatar uuid or Firstname Lastname")]
+ [ArgHints("state", "URLARG", "true: Grant perms, false: Remove perms")]
+ [ReturnHints("granted")]
+ [ReturnHints("removed")]
+ [ReturnHintsFailure("Not A friend")]
+ [ReturnHintsFailure("state invaild")]
+ [ReturnHintsFailure("avatar lookup")]
+ [Route(HttpVerbs.Get, "/FriendFullPerms/{avatar}/{state}/{token}")]
+ public object FriendFullPerms(string avatar, string state, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("avatar lookup", "FriendFullPerms", new [] { avatar, state });
+ }
+ bool status = false;
+ if (bool.TryParse(state, out status) == false)
+ {
+ return Failure("state invaild", "FriendFullPerms", new [] { avatar, state });
+ }
+ if (getClient().Friends.FriendList.ContainsKey(avataruuid) == false)
+ {
+ return Failure("Not A friend", "FriendFullPerms", new [] { avatar, state });
+ }
+ if (status == true)
+ {
+ getClient().Friends.GrantRights(avataruuid, FriendRights.CanSeeOnline | FriendRights.CanSeeOnMap | FriendRights.CanModifyObjects);
+ return BasicReply("granted", "FriendFullPerms", new [] { avatar, state });
+ }
+ getClient().Friends.GrantRights(avataruuid, FriendRights.None);
+ return BasicReply("removed", "FriendFullPerms", new [] { avatar, state });
+ }
+
+ [About("Updates the friend perms for avatar avatar to State \n if true grants (Online/Map/Modify) perms")]
+ [ReturnHints("Request sent")]
+ [ReturnHints("Removed")]
+ [ReturnHintsFailure("Already a friend")]
+ [ReturnHintsFailure("Not in friendslist")]
+ [ReturnHintsFailure("state invaild")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ArgHints("avatar", "URLARG", "A avatar uuid or Firstname Lastname")]
+ [ArgHints("state", "URLARG", "true: Send invite, false: Remove from friendslist")]
+ [Route(HttpVerbs.Get, "/FriendRequest/{avatar}/{state}/{token}")]
+ public object FriendRequest(string avatar, string state, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("avatar lookup", "FriendRequest", new [] { avatar, state });
+ }
+ bool status = false;
+ if (bool.TryParse(state, out status) == false)
+ {
+ return Failure("state invaild", "FriendRequest", new [] { avatar, state });
+ }
+ if (getClient().Friends.FriendList.ContainsKey(avataruuid) == true)
+ {
+ if (status == false)
+ {
+ getClient().Friends.TerminateFriendship(avataruuid);
+ return BasicReply("Removed", "FriendRequest", new [] { avatar, state });
+ }
+ }
+ if (status == false)
+ {
+ return Failure("Not in friendslist", "FriendRequest", new [] { avatar, state });
+ }
+ getClient().Friends.OfferFriendship(avataruuid);
+ return BasicReply("Request sent", "FriendRequest", new [] { avatar, state });
+
+ }
+ }
+
+
+}
diff --git a/SecondBotEvents/Commands/Funds.cs b/SecondBotEvents/Commands/Funds.cs
new file mode 100644
index 0000000..4a0078a
--- /dev/null
+++ b/SecondBotEvents/Commands/Funds.cs
@@ -0,0 +1,109 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+
+
+namespace SecondBotEvents.Commands
+{
+ public class Funds : CommandsAPI
+ {
+ public Funds(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+ [About("Requests the current balance and requests the balance to update.")]
+ [ReturnHints("Current fund level")]
+ [ReturnHintsFailure("Funds commands are disabled")]
+ [Route(HttpVerbs.Get, "/Balance/{token}")]
+ public object Balance(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.GetAllowFunds == false)
+ {
+ return Failure("Funds commands are disabled", "Balance");
+ }
+ getClient().Self.RequestBalance();
+ return BasicReply(getClient().Self.Balance.ToString(), "Balance");
+ }
+
+ [About("Makes the bot pay a avatar")]
+ [ReturnHints("Accepted")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ReturnHintsFailure("Amount out of range")]
+ [ReturnHintsFailure("Invaild amount")]
+ [ReturnHintsFailure("Transfer funds to avatars disabled")]
+ [ArgHints("avatar", "URLARG", "the avatars UUID or Firstname Lastname")]
+ [ArgHints("amount", "URLARG", "the amount to pay (from 1 to current balance)")]
+ [Route(HttpVerbs.Get, "/PayAvatar/{avatar}/{amount}/{token}")]
+ public object PayAvatar(string avatar, string amount, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.GetAllowFunds == false)
+ {
+ return Failure("Transfer funds to avatars disabled", "PayAvatar", new [] { avatar, amount });
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return BasicReply("avatar lookup", "PayAvatar", new [] { avatar, amount });
+ }
+ if (int.TryParse(amount, out int amountvalue) == false)
+ {
+ return Failure("Invaild amount", "PayAvatar", new [] { avatar, amount });
+ }
+ if ((amountvalue < 0) || (amountvalue > getClient().Self.Balance))
+ {
+ return Failure("Amount out of range", "PayAvatar", new [] { avatar, amount });
+ }
+ getClient().Self.GiveAvatarMoney(avataruuid, amountvalue);
+ return BasicReply("Accepted", "PayAvatar", new [] { avatar, amount });
+ }
+
+ [About("Makes the bot pay a object")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Primname is empty")]
+ [ReturnHintsFailure("Invaild object UUID")]
+ [ReturnHintsFailure("Invaild amount")]
+ [ReturnHintsFailure("Amount out of range")]
+ [ReturnHintsFailure("Funds commands are disabled")]
+ [ArgHints("object", "URLARG", "UUID of the object to pay")]
+ [ArgHints("primname", "URLARG", "The name of the prim on the object to pay")]
+ [ArgHints("amount", "URLARG", "the amount to pay (from 1 to current balance)")]
+ [Route(HttpVerbs.Get, "/PayObject/{object}/{primname}/{amount}/{token}")]
+ public object PayObject(string objectuuid,string primname,string amount,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.GetAllowFunds == false)
+ {
+ return Failure("Funds commands are disabled", "PayObject", new [] { objectuuid, primname, amount });
+ }
+ if (UUID.TryParse(objectuuid, out UUID objectUUID) == false)
+ {
+ return Failure("Invaild object UUID", "PayObject", new [] { objectuuid, primname, amount });
+ }
+ if (int.TryParse(amount, out int amountvalue) == false)
+ {
+ return Failure("Invaild amount", "PayObject", new [] { objectuuid, primname, amount });
+ }
+ if((amountvalue < 0) || (amountvalue > getClient().Self.Balance))
+ {
+ return Failure("Amount out of range", "PayObject", new [] { objectuuid, primname, amount });
+ }
+ if(SecondbotHelpers.notempty(primname) == false)
+ {
+ return Failure("Primname is empty", "PayObject", new [] { objectuuid, primname, amount });
+ }
+ getClient().Self.GiveObjectMoney(objectUUID, amountvalue, primname);
+ return BasicReply("ok", "PayObject", new [] { objectuuid, primname, amount });
+ }
+ }
+}
diff --git a/SecondBotEvents/Commands/Group.cs b/SecondBotEvents/Commands/Group.cs
new file mode 100644
index 0000000..c317149
--- /dev/null
+++ b/SecondBotEvents/Commands/Group.cs
@@ -0,0 +1,694 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using Newtonsoft.Json;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace SecondBotEvents.Commands
+{
+ public class Group : CommandsAPI
+ {
+ public Group(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+ [About("Checks if the given UUID is in the given group
Note: if group membership data is more than 60 secs old this will return Updating
Please wait and retry later")]
+ [ReturnHints("Membership reply with [membershipStatus,AvatarUUID,AvatarnameIfKnown,GroupUUID]")]
+ [ReturnHintsFailure("Updating")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [ArgHints("avatar", "URLARG", "the UUID of the avatar you wish to check with")]
+ [Route(HttpVerbs.Get, "/IsGroupMember/{group}/{avatar}/{token}")]
+ public object IsGroupMember(string group, string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "IsGroupMember", new [] { group, avatar });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "IsGroupMember", new [] { group, avatar });
+ }
+ if (bot.NeedReloadGroupData(groupuuid) == true)
+ {
+ getClient().Groups.RequestGroupMembers(groupuuid);
+ }
+ ProcessAvatar(avatar);
+ KeyValuePair reply = waitForReady(true, true, groupuuid);
+ if (reply.Key == false)
+ {
+ return Failure(reply.Value, "IsGroupMember", new [] { group, avatar });
+ }
+ IsGroupMemberReply replyobject = new IsGroupMemberReply();
+ replyobject.membershipStatus = bot.FastCheckInGroup(groupuuid, avataruuid);
+ replyobject.AvatarUUID = avataruuid.ToString();
+ replyobject.AvatarnameIfKnown = bot.FindAvatarKey2Name(avataruuid);
+ replyobject.GroupUUID = groupuuid.ToString();
+ return BasicReply(JsonConvert.SerializeObject(replyobject), "IsGroupMember", new [] { group, avatar });
+ }
+
+ [About("Gets membership of a group")]
+ [ReturnHints("list of UUIDS of group members")]
+ [ReturnHintsFailure("Updating")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [Route(HttpVerbs.Get, "/GetGroupMembers/{group}/{token}")]
+ public object GetGroupMembers(string group, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GetGroupMembers", new [] { group });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GetGroupMembers", new [] { group });
+ }
+ if (bot.NeedReloadGroupData(groupuuid) == true)
+ {
+ getClient().Groups.RequestGroupMembers(groupuuid);
+ }
+ KeyValuePair reply = waitForReady(false, true, groupuuid);
+ if (reply.Key == false)
+ {
+ return Failure(reply.Value, "GetGroupMembers", new [] { group });
+ }
+ SuccessNoReturn("GetGroupMembers", new [] { group });
+ return bot.GetGroupMembers(groupuuid);
+ }
+
+ [About("Attempts to ban/unban a given avatar from a group")]
+ [ReturnHints("? request accepted")]
+ [ReturnHintsFailure("Updating")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ReturnHintsFailure("Missing group GroupBanAccess power")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [ArgHints("avatar", "URLARG", "the UUID of the avatar or Firstname Lastname")]
+ [ArgHints("state", "URLARG", "true to ban false to unban")]
+ [Route(HttpVerbs.Get, "/GroupBan/{group}/{avatar}/{state}/{token}")]
+ public object GroupBan(string group, string avatar, string state, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GroupBan", new [] { group, avatar, state });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GroupBan", new [] { group, avatar, state });
+ }
+ Group G = bot.MyGroups[groupuuid];
+ if (G.Powers.HasFlag(GroupPowers.GroupBanAccess) == false)
+ {
+ return Failure("Missing group GroupBanAccess power", "GroupBan", new [] { group, avatar, state });
+ }
+ if (bool.TryParse(state, out bool banstate) == false)
+ {
+ return Failure("Invaild ban state", "GroupBan", new [] { group, avatar, state });
+ }
+ ProcessAvatar(avatar);
+ if(avataruuid == UUID.Zero)
+ {
+ return Failure("avatar lookup", "GroupBan", new [] { group, avatar, state });
+ }
+ GroupBanAction action = GroupBanAction.Unban;
+ string statename = "Unban";
+ if (banstate == true)
+ {
+ action = GroupBanAction.Ban;
+ statename = "Ban";
+ }
+ getClient().Groups.RequestBanAction(groupuuid, action, new UUID[] { avataruuid });
+ return BasicReply(statename+" request accepted", "GroupBan", new [] { group, avatar, state });
+ }
+
+
+
+ [About("Eject selected avatar from group")]
+ [ReturnHints("Requested")]
+ [ReturnHintsFailure("Updating")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("Not in group")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ReturnHintsFailure("Missing group Eject power")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [ArgHints("avatar", "URLARG", "the UUID of the avatar you wish to check with")]
+ [Route(HttpVerbs.Get, "/GroupEject/{group}/{avatar}/{token}")]
+ public object GroupEject(string group, string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GroupEject", new [] { group, avatar });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GroupEject", new [] { group, avatar });
+ }
+ Group G = bot.MyGroups[groupuuid];
+ if (G.Powers.HasFlag(GroupPowers.Eject) == false)
+ {
+ return Failure("Missing group Eject power", "GroupEject", new [] { group, avatar });
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("avatar lookup", "GroupEject", new [] { group, avatar });
+ }
+ getClient().Groups.EjectUser(groupuuid, avataruuid);
+ return BasicReply("Requested", "GroupEject", new [] { group, avatar });
+ }
+
+ [About("Adds the avatar to the Group with the role \n if they are not in the group then it invites them at that role")]
+ [ReturnHints("Roles updated")]
+ [ReturnHints("Invite sent")]
+ [ReturnHintsFailure("Updating")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("Invaild role UUID")]
+ [ReturnHintsFailure("Not in group")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [ArgHints("avatar", "URLARG", "the UUID of the avatar you wish to check with")]
+ [Route(HttpVerbs.Get, "/GroupAddRole/{group}/{avatar}/{role}/{token}")]
+ public object GroupAddRole(string group, string avatar, string role, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GroupAddRole", new [] { group, avatar });
+ }
+ if (UUID.TryParse(role, out UUID roleuuid) == false)
+ {
+ return Failure("Invaild role UUID", "GroupAddRole", new [] { group, avatar });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GroupAddRole", new [] { group, avatar });
+ }
+ if (bot.NeedReloadGroupData(groupuuid) == true)
+ {
+ getClient().Groups.RequestGroupMembers(groupuuid);
+ }
+ ProcessAvatar(avatar);
+ KeyValuePair reply = waitForReady(true, true, groupuuid);
+ if (reply.Key == false)
+ {
+ return Failure(reply.Value, "GroupAddRole", new [] { group, avatar });
+ }
+ bool status = bot.FastCheckInGroup(groupuuid, avataruuid);
+ if (status == false)
+ {
+ getClient().Groups.Invite(groupuuid, new List() { roleuuid }, avataruuid);
+ return BasicReply("Invite sent", "GroupAddRole", new [] { group, avatar });
+ }
+ getClient().Groups.AddToRole(groupuuid, roleuuid, avataruuid);
+ return BasicReply("Roles updated", "GroupAddRole", new [] { group, avatar });
+ }
+
+ [About("Invites selected avatar to the group with the selected role")]
+ [ReturnHints("Invite sent")]
+ [ReturnHints("Already in group")]
+ [ReturnHintsFailure("Updating")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ReturnHintsFailure("Missing group Invite power")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [ArgHints("avatar", "URLARG", "the UUID of the avatar you wish to check with")]
+ [ArgHints("role", "URLARG", "the UUID of the role to invite them at the word \"everyone\"")]
+ [Route(HttpVerbs.Get, "/GroupInvite/{group}/{avatar}/{role}/{token}")]
+ public object GroupInvite(string group, string avatar, string role, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GroupInvite", new [] { group, avatar, role });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GroupInvite", new [] { group, avatar, role });
+ }
+ if (bot.NeedReloadGroupData(groupuuid) == true)
+ {
+ getClient().Groups.RequestGroupMembers(groupuuid);
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("avatar lookup", "GroupInvite", new [] { group, avatar, role });
+ }
+ bool status = bot.FastCheckInGroup(groupuuid, avataruuid);
+ if (status == true)
+ {
+ return BasicReply("Already in group", "GroupInvite", new[] { group, avatar, role });
+ }
+ if(role == "everyone")
+ {
+ role = UUID.Zero.ToString();
+ }
+ if(UUID.TryParse(role,out UUID roleuuid) == false)
+ {
+ return Failure("Unable to process role UUID", "GroupInvite", new [] { group, avatar, role });
+ }
+ Group G = bot.MyGroups[groupuuid];
+ if(G.Powers.HasFlag(GroupPowers.Invite) == false)
+ {
+ return Failure("Missing group invite power", "GroupInvite", new [] { group, avatar, role });
+ }
+ getClient().Groups.Invite(groupuuid, new List() { roleuuid }, avataruuid);
+ return BasicReply("Invite sent", "GroupInvite", new [] { group, avatar, role });
+ }
+
+ [About("Sends a group notice (No attachments please use GroupnoticeWithAttachment to attach items!)")]
+ [ReturnHints("Sending notice")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("Title empty")]
+ [ReturnHintsFailure("Message empty")]
+ [ReturnHintsFailure("Missing group Notice power")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [ArgHints("title", "Text", "The title of the group notice")]
+ [ArgHints("message", "Text", "The body of the group notice")]
+ [Route(HttpVerbs.Post, "/Groupnotice/{group}/{token}")]
+ public object Groupnotice(string group, [FormField] string title, [FormField] string message, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "Groupnotice", new [] { group, title, message });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "Groupnotice", new [] { group, title, message });
+ }
+ if (SecondbotHelpers.notempty(title) == false)
+ {
+ return Failure("Title empty", "Groupnotice", new [] { group, title, message });
+ }
+ if (SecondbotHelpers.notempty(message) == false)
+ {
+ return Failure("Message empty", "Groupnotice", new [] { group, title, message });
+ }
+ Group G = bot.MyGroups[groupuuid];
+ if (G.Powers.HasFlag(GroupPowers.SendNotices) == false)
+ {
+ return Failure("Missing group notice power", "Groupnotice", new [] { group, title, message });
+ }
+ GroupNotice NewNotice = new GroupNotice();
+ NewNotice.Subject = title;
+ NewNotice.Message = message;
+ NewNotice.OwnerID = getClient().Self.AgentID;
+ getClient().Groups.SendGroupNotice(groupuuid, NewNotice);
+ return BasicReply("Sending notice", "Groupnotice", new [] { group, title, message });
+ }
+
+ [About("Activates the selected title")]
+ [ReturnHints("Switching title")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("Invaild role UUID")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [ArgHints("role", "URLARG", "tje UUID of the role")]
+ [Route(HttpVerbs.Get, "/GroupActiveTitle/{group}/{token}")]
+ public object GroupActiveTitle(string group, string role, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GroupActiveTitle", new [] { group, role });
+ }
+ if (UUID.TryParse(role, out UUID roleuuid) == false)
+ {
+ return Failure("Invaild role UUID", "GroupActiveTitle", new [] { group, role });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GroupActiveTitle", new [] { group, role });
+ }
+ getClient().Groups.ActivateTitle(groupuuid, roleuuid);
+ return BasicReply("Switching title", "GroupActiveTitle", new [] { group, role });
+ }
+
+ [About("Sets the selected group to the active group")]
+ [ReturnHints("Switching active group")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [Route(HttpVerbs.Get, "/GroupActiveGroup/{group}/{token}")]
+ public object GroupActiveGroup(string group, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GroupActiveGroup", new [] { group });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GroupActiveGroup", new [] { group });
+ }
+ getClient().Groups.ActivateGroup(groupuuid);
+ return BasicReply("Switching active group", "GroupActiveGroup", new [] { group });
+ }
+
+ [About("Sends a group notice with an attachment")]
+ [ReturnHints("Sending notice with attachment")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("Invaild inventory UUID")]
+ [ReturnHintsFailure("Title empty")]
+ [ReturnHintsFailure("Message empty")]
+ [ReturnHintsFailure("Missing group Notice power")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [ArgHints("title", "Text", "The title of the group notice")]
+ [ArgHints("message", "Text", "The body of the group notice")]
+ [ArgHints("attachment", "URLARG", "the UUID of inventory you wish to attach")]
+ [Route(HttpVerbs.Post, "/Groupnotice/{group}/{attachment}/{token}")]
+ public object GroupnoticeWithAttachment(string group, [FormField] string title, [FormField] string message, string attachment, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GroupnoticeWithAttachment", new [] { group, title, message, attachment });
+ }
+ if (UUID.TryParse(attachment, out UUID inventoryuuid) == false)
+ {
+ return Failure("Invaild inventory UUID", "GroupnoticeWithAttachment", new [] { group, title, message, attachment });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GroupnoticeWithAttachment", new [] { group, title, message, attachment });
+ }
+ if (SecondbotHelpers.notempty(title) == false)
+ {
+ return Failure("Title empty", "GroupnoticeWithAttachment", new [] { group, title, message, attachment });
+ }
+ if (SecondbotHelpers.notempty(message) == false)
+ {
+ return Failure("Message empty", "GroupnoticeWithAttachment", new [] { group, title, message, attachment });
+ }
+ Group G = bot.MyGroups[groupuuid];
+ if (G.Powers.HasFlag(GroupPowers.SendNotices) == false)
+ {
+ return Failure("Missing group notice power", "GroupnoticeWithAttachment", new [] { group, title, message, attachment });
+ }
+ GroupNotice NewNotice = new GroupNotice();
+ NewNotice.Subject = title;
+ NewNotice.Message = message;
+ NewNotice.OwnerID = getClient().Self.AgentID;
+ NewNotice.AttachmentID = inventoryuuid;
+ getClient().Groups.SendGroupNotice(groupuuid, NewNotice);
+ return BasicReply("Sending notice with attachment", "GroupnoticeWithAttachment", new [] { group, title, message, attachment });
+ }
+
+
+ [About("fetchs a list of all groups known to the bot")]
+ [ReturnHints("array UUID=name")]
+ [Route(HttpVerbs.Get, "/GetGroupList/{token}")]
+ public object GetGroupList(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ Dictionary grouppackage = new Dictionary();
+ foreach (KeyValuePair entry in bot.MyGroups)
+ {
+ grouppackage.Add(entry.Value.ID.ToString(), entry.Value.Name);
+ }
+ return BasicReply(JsonConvert.SerializeObject(grouppackage), "GetGroupList");
+ }
+
+ [About("Requests the roles for the selected group
Replys with GroupRoleDetails object formated as follows - UpdateUnderway (Bool)
- RoleDataAge (Int) [default -1]
- Roles (KeyPair array of UUID=Name)
")]
+ [ReturnHints("GroupRoleDetails object")]
+ [ReturnHintsFailure("Group is not currently known")]
+ [ReturnHintsFailure("Invaild group UUID")]
+ [ReturnHintsFailure("Updating")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [Route(HttpVerbs.Get, "/GetGroupRoles/{group}/{token}")]
+ public object GetGroupRoles(string group, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group UUID", "GetGroupRoles", new [] { group });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Unknown group", "GetGroupRoles", new [] { group });
+ }
+ GroupRoleDetails reply = new GroupRoleDetails();
+ reply.UpdateUnderway = false;
+ reply.RoleDataAge = -1;
+ reply.Roles = new Dictionary();
+ if (bot.MyGroupRolesStorage.ContainsKey(groupuuid) == false)
+ {
+ reply.UpdateUnderway = true;
+ }
+ else
+ {
+ long dif = SecondbotHelpers.UnixTimeNow() - bot.MyGroupRolesStorage[groupuuid].Key;
+ reply.RoleDataAge = dif;
+ if (dif >= 120)
+ {
+ reply.UpdateUnderway = true;
+ }
+ foreach (GroupRole gr in bot.MyGroupRolesStorage[groupuuid].Value)
+ {
+ reply.Roles.Add(gr.ID.ToString(), gr.Name);
+ }
+ }
+ if (reply.UpdateUnderway == true)
+ {
+ getClient().Groups.RequestGroupRoles(groupuuid);
+ if (reply.Roles.Count == 0)
+ {
+ return Failure("Updating", "GetGroupRoles", new [] { group });
+ }
+ }
+ return BasicReply(JsonConvert.SerializeObject(reply), "GetGroupRoles", new [] { group });
+ }
+
+ [About("fetchs a list of all groups with unread messages")]
+ [ReturnHints("array UUID")]
+ [Route(HttpVerbs.Get, "/GroupchatListAllUnreadGroups/{token}")]
+ public object GroupchatListAllUnreadGroups(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(JsonConvert.SerializeObject(bot.UnreadGroupchatGroups()), "GroupchatListAllUnreadGroups");
+ }
+
+ [About("fetchs a list of all groups with unread messages")]
+ [ReturnHints("true|false")]
+ [ReturnHintsFailure("Unknown group")]
+ [ReturnHintsFailure("group value is invaild")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [Route(HttpVerbs.Get, "/GroupchatGroupHasUnread/{group}/{token}")]
+ public object GroupchatGroupHasUnread(string group,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ UUID groupUUID = UUID.Zero;
+ if(UUID.TryParse(group,out groupUUID) == false)
+ {
+ return Failure("group value is invaild", "GroupchatGroupHasUnread", new [] { group });
+ }
+ if(bot.MyGroups.ContainsKey(groupUUID) == false)
+ {
+ return Failure("Unknown group", "GroupchatGroupHasUnread", new [] { group });
+ }
+ return BasicReply(bot.GroupHasUnread(groupUUID).ToString(), "GroupchatGroupHasUnread", new [] { group });
+ }
+
+
+ [About("checks if there are any groups with unread messages")]
+ [ReturnHints("True|False")]
+ [Route(HttpVerbs.Get, "/GroupchatAnyUnread/{token}")]
+ public object GroupchatAnyUnread(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(bot.HasUnreadGroupchats().ToString(), "GroupchatAnyUnread");
+ }
+
+ [About("Clears all group chat buffers at once")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/GroupchatClearAll/{token}")]
+ public object GroupchatClearAll(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ bot.ClearAllGroupchat();
+ return BasicReply("ok", "GroupchatClearAll");
+ }
+
+ [About("fetchs the groupchat history")]
+ [ReturnHintsFailure("Group UUID invaild")]
+ [ReturnHints("Group Chat")]
+ [ArgHints("group", "URLARG", "the UUID of the group")]
+ [Route(HttpVerbs.Get, "/GroupchatHistory/{group}/{token}")]
+ public object GroupchatHistory(string group, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(group, out UUID groupUUID) == false)
+ {
+ return Failure("Group UUID invaild", "GroupchatHistory", new [] { group });
+ }
+ return BasicReply(JsonConvert.SerializeObject(bot.GetGroupchat(groupUUID)), "GroupchatHistory", new [] { group });
+ }
+
+ [About("sends a message to the groupchat")]
+ [ArgHints("group", "URLARG", "UUID of the group")]
+ [ArgHints("message", "Text", "the message to send")]
+ [ReturnHints("Sending")]
+ [ReturnHintsFailure("Group UUID invaild")]
+ [ReturnHintsFailure("Opening groupchat - Please retry later")]
+ [ReturnHintsFailure("Missing group JoinChat power")]
+ [Route(HttpVerbs.Post, "/Groupchat/{group}/{token}")]
+ public object Groupchat(string group, [FormField] string message, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ UUID groupUUID = UUID.Zero;
+ if (UUID.TryParse(group, out groupUUID) == false)
+ {
+ return Failure("group value is invaild", "Groupchat", new [] { group, message });
+ }
+ if (bot.MyGroups.ContainsKey(groupUUID) == false)
+ {
+ return Failure("Unknown group", "Groupchat", new [] { group, message });
+ }
+ Group G = bot.MyGroups[groupUUID];
+ if (G.Powers.HasFlag(GroupPowers.JoinChat) == false)
+ {
+ return Failure("Missing group JoinChat power", "Groupchat", new [] { group, message });
+ }
+ if (bot.GetActiveGroupchatSessions.Contains(groupUUID) == false)
+ {
+ getClient().Self.RequestJoinGroupChat(groupUUID);
+ return Failure("Opening groupchat - Please retry later", "Groupchat", new [] { group, message });
+ }
+ getClient().Self.InstantMessageGroup(groupUUID, message);
+ return BasicReply("Sending", "Groupchat", new [] { group, message });
+ }
+
+ protected KeyValuePair waitForReady(bool avatar, bool group, UUID groupuuid)
+ {
+ bool exit = false;
+ int sleeps = 0;
+ string failed_on = "";
+ bool allok = true;
+ while (exit == false)
+ {
+ allok = true;
+ failed_on = "";
+ if (avatar == true)
+ {
+ if (avataruuid == UUID.Zero)
+ {
+ allok = false;
+ failed_on = "avatar lookup";
+ }
+ }
+ if ((group == true) && (allok == true))
+ {
+ allok = !bot.NeedReloadGroupData(groupuuid);
+ failed_on = "Updating";
+ }
+ if (allok == false)
+ {
+ if (sleeps > 3)
+ {
+ exit = true;
+ }
+ }
+ if (allok == true)
+ {
+ exit = true;
+ }
+ else
+ {
+ if (exit == false)
+ {
+ Thread.Sleep(1000);
+ sleeps++;
+ }
+ }
+ }
+ return new KeyValuePair(allok, failed_on);
+ }
+ }
+
+ public class GroupRoleDetails
+ {
+ public bool UpdateUnderway { get; set; }
+ public Dictionary Roles { get; set; }
+ public long RoleDataAge = 0;
+ }
+
+ public class IsGroupMemberReply
+ {
+ public bool membershipStatus { get; set; }
+ public string AvatarUUID { get; set; }
+ public string GroupUUID { get; set; }
+ public string AvatarnameIfKnown { get; set; }
+ }
+}
diff --git a/SecondBotEvents/Commands/Info.cs b/SecondBotEvents/Commands/Info.cs
new file mode 100644
index 0000000..fd6ab86
--- /dev/null
+++ b/SecondBotEvents/Commands/Info.cs
@@ -0,0 +1,188 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using Newtonsoft.Json;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+using System;
+using System.Collections.Generic;
+
+namespace SecondBotEvents.Commands
+{
+ public class Info : CommandsAPI
+ {
+ public Info(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+ [About("Lists objects that are sculpty type in the current sim that the bot can see")]
+ [ReturnHints("A json object")]
+ [Route(HttpVerbs.Get, "/ListSculptys/{token}")]
+ public object ListSculptys(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ Dictionary objects_copy = getClient().Network.CurrentSim.ObjectsPrimitives.Copy();
+
+ Dictionary mapLocalID = new Dictionary();
+ Dictionary sculpts = new Dictionary();
+ foreach (KeyValuePair Obj in objects_copy)
+ {
+ mapLocalID.Add(Obj.Value.LocalID, Obj.Key);
+ if (PrimType.Sculpt.Equals(Obj.Value.Type) == true)
+ {
+ sculpts.Add(Obj.Key, Obj.Value);
+ }
+ }
+ List reply = new List();
+ foreach (KeyValuePair Obj in sculpts)
+ {
+ Vector3 pos = Obj.Value.Position;
+ string name = "?";
+ if (Obj.Value.NameValues != null)
+ {
+ name = Obj.Value.NameValues[0].Value.ToString();
+ }
+ UUID key = Obj.Value.ID;
+ UUID owner = Obj.Value.OwnerID;
+ bool asLink = false;
+ if (Obj.Value.ParentID > 0)
+ {
+ if(mapLocalID.ContainsKey(Obj.Value.ParentID) == true)
+ {
+ Primitive wip = objects_copy[mapLocalID[Obj.Value.ParentID]];
+ pos = wip.Position;
+ asLink = true;
+ owner = wip.OwnerID;
+ if (wip.NameValues != null)
+ {
+ name = wip.NameValues[0].Value.ToString();
+ }
+ key = wip.ID;
+ }
+ }
+ SculptysInfo A = new SculptysInfo();
+ A.isLinked = asLink.ToString();
+ A.name = name.ToString();
+ A.owner = owner.ToString();
+ A.uuid = key.ToString();
+ A.pos = pos.ToString();
+ reply.Add(A);
+ }
+ if (reply.Count == 0)
+ {
+ return Failure("No objects found", "ListSculptys");
+ }
+ return BasicReply(JsonConvert.SerializeObject(reply), "ListSculptys",new string[] { });
+ }
+
+ [About("Fetchs the current bot")]
+ [ReturnHints("The build ID of the bot")]
+ [Route(HttpVerbs.Get, "/Version/{token}")]
+ public object Version(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(bot.MyVersion, "Version");
+
+ }
+
+ [About("Fetchs the name of the bot")]
+ [ReturnHints("Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/Name/{token}")]
+ public object Name(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(getClient().Self.FirstName + " " + getClient().Self.LastName, "Name");
+ }
+
+ [About("Fetchs the current parcels name")]
+ [ReturnHints("Parcelname")]
+ [ReturnHintsFailure("Error parcel not found")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [Route(HttpVerbs.Get, "/ParcelName/{token}")]
+ public object ParcelName(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Network.CurrentSim == null)
+ {
+ return Failure("Error not in a sim", "ParcelName");
+ }
+ int localid = getClient().Parcels.GetParcelLocalID(getClient().Network.CurrentSim, getClient().Self.SimPosition);
+ if (getClient().Network.CurrentSim.Parcels.ContainsKey(localid) == false)
+ {
+ return Failure("Error parcel not found", "ParcelName");
+ }
+ return BasicReply(getClient().Network.CurrentSim.Parcels[localid].Name, "ParcelName");
+ }
+
+ [About("Requests the current unixtime at the bot")]
+ [ReturnHints("Unixtime")]
+ [Route(HttpVerbs.Get, "/UnixTimeNow/{token}")]
+ public object UnixTimeNow(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(SecondbotHelpers.UnixTimeNow().ToString(), "UnixTimeNow");
+ }
+
+ [About("Fetchs the current region name")]
+ [ReturnHints("Regionname")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [Route(HttpVerbs.Get, "/SimName/{token}")]
+ public object SimName(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Network.CurrentSim == null)
+ {
+ return Failure("Error not in a sim", "SimName");
+ }
+ return BasicReply(getClient().Network.CurrentSim.Name, "SimName");
+ }
+
+ [About("Fetchs the current location of the bot")]
+ [ReturnHints("array of X,Y,Z values")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [Route(HttpVerbs.Get, "/GetPosition/{token}")]
+ public object GetPosition(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (getClient().Network.CurrentSim == null)
+ {
+ return Failure("Error not in a sim", "GetPosition");
+ }
+ Dictionary pos = new Dictionary();
+ pos.Add("x", (int)Math.Round(getClient().Self.SimPosition.X));
+ pos.Add("y", (int)Math.Round(getClient().Self.SimPosition.Y));
+ pos.Add("z", (int)Math.Round(getClient().Self.SimPosition.Z));
+ return BasicReply(JsonConvert.SerializeObject(pos), "GetPosition");
+ }
+ }
+
+ public class SculptysInfo
+ {
+ public string name = "";
+ public string isLinked = "False";
+ public string owner = "?";
+ public string uuid = "?";
+ public string pos = "<0,0,0>";
+
+ }
+}
diff --git a/SecondBotEvents/Commands/Inventory.cs b/SecondBotEvents/Commands/Inventory.cs
new file mode 100644
index 0000000..86e86d0
--- /dev/null
+++ b/SecondBotEvents/Commands/Inventory.cs
@@ -0,0 +1,516 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using OpenMetaverse.Assets;
+using SecondBotEvents.Services;
+using System.Collections.Generic;
+using System.IO;
+
+namespace SecondBotEvents.Commands
+{
+ public class Inventory : CommandsAPI
+ {
+ public Inventory(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+ [About("Attachs an event for inventory changes")]
+ [ReturnHints("cleared")]
+ [ReturnHints("No action")]
+ [ReturnHints("Event added")]
+ [ArgHints("inventoryType", "URLARG", "Types: texture,sound,callcard,landmark,clothing,object,notecard,lsltext,lslbyte,animatn,gesture,mesh")]
+ [ArgHints("outputTarget", "text", "HTTP url, channel, avatar UUID or clear to remove all events for the selected type")]
+ [Route(HttpVerbs.Post, "/SetInventoryUpdate/{inventoryType}/{token}")]
+ public object SetInventoryUpdate(string inventoryType, [FormField] string outputTarget, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ return BasicReply(bot.addInventoryUpdateEvent(inventoryType, outputTarget), "SetInventoryUpdate");
+ }
+
+ [About("Uploads a new sound file to inventory")]
+ [ReturnHints("ok")]
+ [ArgHints("sourcePath", "Text", "accepts a file path to a wave PCM file @ 44100")]
+ [ArgHints("inventoryName", "URLARG", "the name in secondlife")]
+ [Route(HttpVerbs.Post, "/UploadMediaWave/{inventoryName}/{token}")]
+ public object UploadMediaWave([FormField] string sourcePath, string inventoryName, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ byte[] audioData = File.ReadAllBytes(sourcePath);
+ InventoryFolder AA = getClient().Inventory.Store.RootFolder;
+ List T = getClient().Inventory.Store.GetContents(AA);
+ AA = null;
+ foreach (InventoryBase R in T)
+ {
+ if (R.Name == "Sounds")
+ {
+ AA = (InventoryFolder)R;
+ break;
+ }
+ }
+ AssetSound S = new AssetSound();
+ S.AssetData = audioData;
+ S.Encode();
+
+ getClient().Inventory.RequestCreateItemFromAsset(S.AssetData, inventoryName, "", AssetType.Sound, InventoryType.Sound, AA.UUID, null);
+ return BasicReply("ok", "UploadMediaWave");
+ }
+
+
+ [About("rezs the item at the bots current location")]
+ [ReturnHints("UUID of rezzed item")]
+ [ReturnHints("Invaild item UUID")]
+ [ReturnHints("Unable to find item")]
+ [ArgHints("item", "URLARG", "UUID of item to rez")]
+ [Route(HttpVerbs.Get, "/RezObject/{item}/{token}")]
+ public object RezObject(string item, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(item, out UUID targetitem) == false)
+ {
+ return Failure("Invaild item UUID: "+ item, "RezObject", new [] { item });
+ }
+ InventoryItem itm = getClient().Inventory.FetchItem(targetitem, getClient().Self.AgentID, (5 * 1000));
+ if (itm == null)
+ {
+ return Failure("Unable to find item: " + item, "RezObject", new [] { item });
+ }
+ UUID rezedobject = getClient().Inventory.RequestRezFromInventory(getClient().Network.CurrentSim, getClient().Self.SimRotation, getClient().Self.RelativePosition, itm);
+ return BasicReply(rezedobject.ToString(), "RezObject", new [] { item });
+ }
+
+
+ [About("renames a folder or inventory item")]
+ [ReturnHints("ok")]
+ [ReturnHints("invaild item uuid")]
+ [ReturnHints("Item name is to short")]
+ [ReturnHints("Unable to find inventory item")]
+ [ArgHints("item", "URLARG", "UUID of item/folder to name")]
+ [ArgHints("newname", "Text", "What we are changing it to")]
+ [Route(HttpVerbs.Post, "/RenameInventory/{item}/{token}")]
+ public object RenameInventory(string item, [FormField] string newname, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(item, out UUID target) == false)
+ {
+ return Failure("invaild item uuid", "RenameInventory", new [] { item, newname });
+ }
+ if (newname.Length < 3)
+ {
+ return Failure("Item name is to short", "RenameInventory", new [] { item, newname });
+ }
+ InventoryItem realitem = HelperInventory.getItemByInventoryUUID(bot, target);
+ if (realitem == null)
+ {
+ return Failure("Unable to find inventory item", "RenameInventory", new [] { item, newname });
+ }
+ realitem.Name = newname;
+ getClient().Inventory.RequestUpdateItem(realitem);
+ return BasicReply("Ok", "RenameInventory", new [] { item, newname });
+ }
+
+ [About("Attempts to Remove the given inventory item")]
+ [ReturnHints("ok")]
+ [ReturnHints("invaild item uuid")]
+ [ArgHints("item", "URLARG", "UUID of item")]
+ [Route(HttpVerbs.Get, "/DeleteInventoryItem/{item}/{token}")]
+ public object DeleteInventoryItem(string item, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(item, out UUID target) == false)
+ {
+ return Failure("invaild item uuid", "DeleteInventoryItem", new [] { item });
+ }
+ getClient().Inventory.Remove(new List() { target }, new List());
+ return BasicReply("Ok", "DeleteInventoryItem", new [] { item });
+ }
+
+ [About("Attempts to Remove the given inventory folder")]
+ [ReturnHints("ok")]
+ [ReturnHints("invaild folder uuid")]
+ [ArgHints("folder", "URLARG", "UUID of folder")]
+ [Route(HttpVerbs.Get, "/DeleteInventoryFolder/{folder}/{token}")]
+ public object DeleteInventoryFolder(string folder, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(folder, out UUID target) == false)
+ {
+ return Failure("invaild folder uuid", "DeleteInventoryFolder", new [] { folder });
+ }
+ getClient().Inventory.Remove(new List(), new List() { target });
+ return BasicReply("Ok", "DeleteInventoryFolder", new [] { folder });
+ }
+
+ [About("Attempts to attach the given inventory item")]
+ [ReturnHints("ok")]
+ [ReturnHints("invaild item uuid")]
+ [ArgHints("item", "URLARG", "UUID of item")]
+ [Route(HttpVerbs.Get, "/Attach/{item}/{token}")]
+ public object Attach(string item, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(item,out UUID itemuuid) == false)
+ {
+ return Failure("invaild item uuid", "Attach", new [] { item });
+ }
+ InventoryItem realitem = HelperInventory.getItemByInventoryUUID(bot, itemuuid);
+ getClient().Appearance.AddAttachments(new List() { realitem }, false, false);
+ return BasicReply("Ok", "Attach", new [] { item });
+ }
+
+ [About("Attempts to Remove the given inventory folder")]
+ [ReturnHints("ok")]
+ [ReturnHints("invaild item uuid")]
+ [ArgHints("item", "URLARG", "UUID of item")]
+ [Route(HttpVerbs.Get, "/Detach/{item}/{token}")]
+ public object Detach(string item, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(item, out UUID itemuuid) == false)
+ {
+ return Failure("invaild item uuid", "Detach", new [] { item });
+ }
+ InventoryItem realitem = HelperInventory.getItemByInventoryUUID(bot, itemuuid);
+ getClient().Appearance.RemoveFromOutfit(realitem);
+ return BasicReply("Ok", "Detach", new [] { item });
+ }
+
+ [About("Replaces the current avatar outfit with the Clothing/[NAME] folder
Please note: This does not use the outfits folder!
Please do not use links in the folder!")]
+ [ReturnHints("ok")]
+ [ReturnHints("Named folder value is empty")]
+ [ReturnHints("Cant find Clothing folder")]
+ [ReturnHints("Cant find target folder")]
+ [ReturnHints("target folder is empty or so full I cant get it in 5 secs...")]
+ [ArgHints("name", "URLARG", "Name of the folder")]
+ [Route(HttpVerbs.Get, "/Outfit/{name}/{token}")]
+ public object Outfit(string name, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.notempty(name) == false)
+ {
+ return Failure("Named folder value is empty", "Outfit", new [] { name });
+ }
+ // uses the Clothing folder
+ // must be a full outfit (shapes/eyes ect)
+ InventoryFolder AA = getClient().Inventory.Store.RootFolder;
+ List T = getClient().Inventory.Store.GetContents(AA);
+ AA = null;
+ foreach (InventoryBase R in T)
+ {
+ if (R.Name == "Clothing")
+ {
+ AA = (InventoryFolder)R;
+ break;
+ }
+ }
+ if (AA == null)
+ {
+ return Failure("Cant find Clothing folder", "Outfit", new [] { name });
+ }
+ T = getClient().Inventory.Store.GetContents(AA);
+ AA = null;
+ foreach (InventoryBase R in T)
+ {
+ if (R.Name == name)
+ {
+ AA = (InventoryFolder)R;
+ break;
+ }
+ }
+ if (AA == null)
+ {
+ return Failure("Cant find target folder", "Outfit", new [] { name });
+ }
+ List contents = getClient().Inventory.FolderContents(AA.UUID, getClient().Self.AgentID, true, true, InventorySortOrder.ByName, 5 * 1000);
+ List wareables = new List();
+ if (contents == null)
+ {
+ return Failure("target folder is empty or so full I cant get it in 5 secs...", "Outfit", new [] { name });
+ }
+ foreach (InventoryBase item in contents)
+ {
+ if ((item is InventoryWearable) || (item is InventoryObject))
+ {
+ wareables.Add((InventoryItem)item);
+ }
+ }
+ getClient().Appearance.ReplaceOutfit(wareables, false);
+ return BasicReply("ok", "Outfit", new [] { name });
+ }
+
+
+
+ [About("Searchs the notecards folder for notecards, any older than 31 days are deleted.
Depending on the number of notecards this might require multiple calls!")]
+ [ReturnHints("ok")]
+ [ReturnHints("Unable to find notecard folder")]
+ [Route(HttpVerbs.Get, "/InventoryPurgeNotecards/{token}")]
+ public object InventoryPurgeNotecards(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ List T = getClient().Inventory.Store.GetContents(getClient().Inventory.Store.RootFolder);
+ InventoryBase NotecardFolder = null;
+ foreach (InventoryBase R in T)
+ {
+ if (R.Name == "Notecards")
+ {
+ NotecardFolder = R;
+ break;
+ }
+ }
+ if (NotecardFolder != null)
+ {
+ return Failure("Unable to find notecard folder", "InventoryPurgeNotecards");
+ }
+ List purge_notecards = new List();
+ List contents = getClient().Inventory.FolderContents(NotecardFolder.UUID, 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)
+ {
+ getClient().Inventory.Remove(purge_notecards, new List());
+ }
+ return BasicReply("Ok", "InventoryPurgeNotecards");
+ }
+
+ [About("converts a inventory uuid to a realworld uuid
Needed for texture preview")]
+ [ReturnHints("Asset UUID or UUID zero")]
+ [ReturnHints("Invaild item uuid")]
+ [ArgHints("item", "URLARG", "inventory level UUID of item")]
+ [Route(HttpVerbs.Get, "/getRealUUID/{item}/{token}")]
+ public object getRealUUID(string item, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(item, out UUID itemUUID) == false)
+ {
+ return Failure("Invaild item uuid", "getRealUUID", new [] { item });
+ }
+ UUID reply = HelperInventory.GetAssetUUID(bot, itemUUID);
+ return BasicReply(reply.ToString(), "getRealUUID", new [] { item });
+ }
+
+ [About("sends a item to an avatar")]
+ [ReturnHints("ok")]
+ [ReturnHints("Failed")]
+ [ReturnHints("Invaild avatar uuid")]
+ [ReturnHints("Invaild item uuid")]
+ [ReturnHints("Unable to find item")]
+ [ArgHints("item", "URLARG", "UUID of item")]
+ [ArgHints("avatar", "URLARG", "a UUID or Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/SendItem/{item}/{avatar}/{token}")]
+ public object SendItem(string item, string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if(avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar uuid", "SendItem", new [] { item, avatar });
+ }
+ if (UUID.TryParse(item, out UUID targetitem) == false)
+ {
+ return Failure("Invaild item uuid", "SendItem", new [] { item, avatar });
+ }
+ InventoryItem itm = getClient().Inventory.FetchItem(targetitem, getClient().Self.AgentID, (3 * 1000));
+ if (itm == null)
+ {
+ return Failure("Unable to find item", "SendItem", new [] { item, avatar });
+ }
+ getClient().Inventory.GiveItem(itm.UUID, itm.Name, itm.AssetType, avataruuid, false);
+ return BasicReply("ok", "SendItem", new [] { item, avatar });
+ }
+
+ [About("Sends a folder to an avatar")]
+ [ReturnHints("ok")]
+ [ReturnHints("Failed")]
+ [ReturnHints("Invaild avatar uuid")]
+ [ReturnHints("Invaild folter uuid")]
+ [ReturnHints("Unable to find folder")]
+ [ArgHints("item", "URLARG", "UUID of item")]
+ [ArgHints("avatar", "URLARG", "a UUID or Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/SendFolder/{folder}/{avatar}/{token}")]
+ public object SendFolder(string folder, string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar uuid", "SendFolder", new [] { folder, avatar });
+ }
+ if (UUID.TryParse(folder, out UUID targetfolder) == true)
+ {
+ return Failure("Invaild folder uuid", "SendFolder", new [] { folder, avatar });
+ }
+ InventoryBase FindFolderHelper = HelperInventory.FindFolder(bot, getClient().Inventory.Store.RootFolder, targetfolder);
+ if (FindFolderHelper == null)
+ {
+ return Failure("Unable to find folder", "SendFolder", new [] { folder, avatar });
+ }
+ getClient().Inventory.GiveFolder(FindFolderHelper.UUID, FindFolderHelper.Name, avataruuid, false);
+ return BasicReply("ok", "SendFolder", new [] { folder, avatar });
+ }
+
+
+
+ [About("Transfers a item [ARG 2] to a objects inventory [ARG 1] (And if set with the script running state [ARG 3])")]
+ [ReturnHints("Transfering running script")]
+ [ReturnHints("Transfering inventory")]
+ [ReturnHints("Invaild item uuid")]
+ [ReturnHints("Invaild object uuid")]
+ [ReturnHints("Unable to find inventory")]
+ [ReturnHints("Unable to find object")]
+ [ReturnHints("Invaild running")]
+ [ArgHints("item", "URLARG", "UUID of item")]
+ [ArgHints("object", "URLARG", "the uuid of the object")]
+ [ArgHints("running", "URLARG", "true if you wish the transfered script to be running otherwise false")]
+
+ [Route(HttpVerbs.Get, "/TransferInventoryToObject/{item}/{objectuuid}/{running}/{token}")]
+ public object TransferInventoryToObject(string item, string objectuuid, string running, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(item, out UUID itemuuid) == false)
+ {
+ return Failure("Invaild item uuid", "TransferInventoryToObject", new [] { item, objectuuid, running });
+ }
+ if (UUID.TryParse(objectuuid, out UUID objectUUID) == false)
+ {
+ return Failure("Invaild object uuid", "TransferInventoryToObject", new [] { item, objectuuid, running });
+ }
+ if (bool.TryParse(running, out bool runscript) == false)
+ {
+ return Failure("Invaild running", "TransferInventoryToObject", new [] { item, objectuuid, running });
+ }
+
+ InventoryItem itm = getClient().Inventory.FetchItem(itemuuid, getClient().Self.AgentID, (3 * 1000));
+ if (itm == null)
+ {
+ return Failure("Unable to find inventory", "TransferInventoryToObject", new [] { item, objectuuid, running });
+ }
+ Dictionary objects_copy = getClient().Network.CurrentSim.ObjectsPrimitives.Copy();
+ KeyValuePair RealObject = new KeyValuePair(0,null);
+ foreach (KeyValuePair Obj in objects_copy)
+ {
+ if (Obj.Value.ID == objectUUID)
+ {
+ RealObject = Obj;
+ break;
+ }
+ }
+ if (RealObject.Value == null)
+ {
+ return Failure("Unable to find object", "TransferInventoryToObject", new [] { item, objectuuid, running });
+ }
+ bool scriptState = runscript;
+ if (itm.AssetType != AssetType.LSLText)
+ {
+ scriptState = false;
+ }
+ if (itm.AssetType == AssetType.LSLText)
+ {
+ getClient().Inventory.CopyScriptToTask(RealObject.Key, itm, scriptState);
+ return BasicReply("Transfering script [state: "+scriptState.ToString()+"]", "TransferInventoryToObject", new [] { item, objectuuid, running });
+ }
+ getClient().Inventory.UpdateTaskInventory(RealObject.Key, itm);
+ return BasicReply("Transfering inventory", "TransferInventoryToObject", new [] { item, objectuuid, running });
+ }
+
+ [About("Requests the inventory folder layout as a json object InventoryMapFolder
Formated as follows
InventoryMapItem
- id: UUID
- name: String
- subfolders: InventoryMapFolder[]
")]
+ [ReturnHints("array of InventoryMapFolder")]
+ [ReturnHints("Error")]
+ [Route(HttpVerbs.Get, "/InventoryFolders/{token}")]
+ public object InventoryFolders(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ string reply = HelperInventory.MapFolderJson(bot);
+ if (reply != null) return BasicReply(reply, "InventoryFolders");
+ return Failure("Error", "InventoryFolders");
+ }
+
+ [About("Requests folders limited to selected folder")]
+ [ArgHints("targetfolder", "URLARG", "the UUID of the folder or root")]
+ [ReturnHints("single InventoryMapFolder")]
+ [ReturnHints("Error")]
+ [Route(HttpVerbs.Get, "/InventoryFoldersLimited/{targetfolder}/{token}")]
+ public object InventoryFoldersLimited(string targetfolder,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ string reply = HelperInventory.MapFolderJson(bot, targetfolder,false);
+ if (reply != null) return BasicReply(reply, "InventoryFoldersLimited");
+ return Failure("Error", "InventoryFoldersLimited");
+ }
+
+ [About("Requests the contents of a folder as an array of InventoryMapItem
Formated as follows
InventoryMapItem
- id: UUID
- name: String
- typename: String
")]
+ [ReturnHints("array of InventoryMapItem")]
+ [ReturnHints("Invaild folder UUID")]
+ [ArgHints("folderUUID", "URLARG", "the folder to fetch (Found via: inventory/folders)")]
+ [Route(HttpVerbs.Get, "/InventoryContents/{folderUUID}/{token}")]
+ public object InventoryContents(string folderUUID, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(folderUUID, out UUID folder) == false)
+ {
+ return Failure("Invaild folder UUID", "InventoryContents", new [] { folderUUID });
+ }
+ return BasicReply(HelperInventory.MapFolderInventoryJson(bot, folder), "InventoryContents", new [] { folderUUID });
+ }
+ }
+}
diff --git a/SecondBotEvents/Commands/Movement.cs b/SecondBotEvents/Commands/Movement.cs
new file mode 100644
index 0000000..b566748
--- /dev/null
+++ b/SecondBotEvents/Commands/Movement.cs
@@ -0,0 +1,313 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace SecondBotEvents.Commands
+{
+ public class Movement : CommandsAPI
+ {
+ public Movement(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+
+ [About("uses the AutoPilot to move to a location")]
+ [ReturnHints("Error Unable to AutoPilot to location")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Convert to vector has failed")]
+ [ReturnHintsFailure("? value out of range 0-?")]
+ [ArgHints("x", "URLARG", "X location to AutoPilot to")]
+ [ArgHints("y", "URLARG", "y location to AutoPilot to")]
+ [ArgHints("z", "URLARG", "z location to AutoPilot to")]
+ [Route(HttpVerbs.Get, "/AutoPilot/{x}/{y}/{z}/{token}")]
+ public object AutoPilot(string x, string y, string z, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (Vector3.TryParse("<" + x + "," + y + "," + z + ">", out Vector3 pos) == false)
+ {
+ return Failure("Convert to vector has failed", "AutoPilot", new [] { x, y, z });
+ }
+ if (SecondbotHelpers.inrange(pos.X, 0, 255) == false)
+ {
+ return Failure("x value out of range 0-255", "AutoPilot", new [] { x, y, z });
+ }
+ if (SecondbotHelpers.inrange(pos.Y, 0, 255) == false)
+ {
+ return Failure("y value out of range 0-255", "AutoPilot", new [] { x, y, z });
+ }
+ if (SecondbotHelpers.inrange(pos.Z, 0, 5000) == false)
+ {
+ return Failure("z value out of range 0-5000", "AutoPilot", new [] { x, y, z });
+ }
+ getClient().Self.AutoPilotCancel();
+ getClient().Self.Movement.TurnToward(pos, true);
+ Thread.Sleep(500);
+ uint Globalx, Globaly;
+ Utils.LongToUInts(getClient().Network.CurrentSim.Handle, out Globalx, out Globaly);
+ getClient().Self.AutoPilot((ulong)(Globalx + pos.X), (ulong)(Globaly + pos.Y), pos.Z);
+ return BasicReply("ok", "AutoPilot", new [] { x, y, z });
+ }
+
+ [About("Attempt to teleport to a new region")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/AutoPilotStop/{token}")]
+ public object AutoPilotStop(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ getClient().Self.AutoPilotCancel();
+ return BasicReply("ok", "AutoPilotStop");
+ }
+
+ [About("Make the bot request the target avatar teleport to the bot")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Invaild avatar UUID")]
+ [ArgHints("avatar", "URLARG", "Avatar UUID or Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/SendTeleportLure/{avatar}/{token}")]
+ public object SendTeleportLure(string avatar,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if(avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar UUID", "SendTeleportLure", new [] { avatar });
+ }
+ getClient().Self.SendTeleportLure(avataruuid);
+ return BasicReply("ok", "SendTeleportLure", new [] { avatar });
+ }
+
+ [About("Sends a teleport request (Move the bot to the avatar)")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Invaild avatar UUID")]
+ [ArgHints("avatar", "URLARG", "Avatar UUID or Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/RequestTeleport/{avatar}/{token}")]
+ public object RequestTeleport(string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar UUID", "RequestTeleport", new [] { avatar });
+ }
+ bot.Add_action_from("teleport", avataruuid, bot.FindAvatarKey2Name(avataruuid));
+ getClient().Self.SendTeleportLureRequest(avataruuid, "I would like to teleport to you");
+ return BasicReply("ok", "RequestTeleport", new [] { avatar });
+ }
+
+ [About("Makes the bot fly (or not)")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Invaild mode")]
+ [ArgHints("mode", "URLARG", "true: Start flying, false: stop flying (super fun at height)")]
+ [Route(HttpVerbs.Get, "/Fly/{mode}/{token}")]
+ public object Fly(string mode, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bool.TryParse(mode, out bool flymode) == false)
+ {
+ return Failure("Invaild mode", "Fly", new [] { mode });
+ }
+ getClient().Self.Movement.Fly = flymode;
+ getClient().Self.Movement.SendUpdate();
+ return BasicReply("ok", "Fly", new [] { mode });
+ }
+
+
+
+ [About("Rotates the bot to face a vector from its current location")]
+ [ReturnHints("true|false")]
+ [ReturnHintsFailure("Invaild vector")]
+ [ReturnHintsFailure("Vector ? value is out of range 0-?")]
+ [ArgHints("vector", "Text", "a vector to face eg <123,45,44>")]
+ [Route(HttpVerbs.Post, "/RotateToFaceVector/{token}")]
+ public object RotateToFaceVector([FormField] string vector, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (Vector3.TryParse(vector, out Vector3 pos) == false)
+ {
+ return Failure("Invaild vector", "RotateToFaceVector", new [] { vector });
+ }
+ if (SecondbotHelpers.inrange(pos.X, 0, 255) == false)
+ {
+ return Failure("Vector x value is out of range 0-255", "RotateToFaceVector", new [] { vector });
+ }
+ if (SecondbotHelpers.inrange(pos.Y, 0, 255) == false)
+ {
+ return Failure("Vector y value is out of range 0-255", "RotateToFaceVector", new [] { vector });
+ }
+ if (SecondbotHelpers.inrange(pos.Z, 0, 5000) == false)
+ {
+ return Failure("Vector z value is out of range 0-5000", "RotateToFaceVector", new [] { vector });
+ }
+ return BasicReply(getClient().Self.Movement.TurnToward(pos, true).ToString(), "RotateToFaceVector", new [] { vector });
+ }
+
+ [About("Rotates the bot to face a avatar")]
+ [ReturnHints("true|false")]
+ [ReturnHintsFailure("Invaild avatar UUID")]
+ [ReturnHintsFailure("Unable to see avatar")]
+ [ArgHints("avatar", "URLARG", "An avatar UUID or Firstname Lastname")]
+ [Route(HttpVerbs.Post, "/RotateToFace/{avatar}/{token}")]
+ public object RotateToFace(string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if(avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar UUID", "RotateToFace", new [] { avatar });
+ }
+ if (getClient().Network.CurrentSim.AvatarPositions.ContainsKey(avataruuid) == false)
+ {
+ return Failure("Unable to see avatar", "RotateToFace", new [] { avatar });
+ }
+ return BasicReply(getClient().Self.Movement.TurnToward(getClient().Network.CurrentSim.AvatarPositions[avataruuid], true).ToString(), "RotateToFace", new [] { avatar });
+ }
+
+
+ [About("Rotates the avatar to face a rotation from north in Degrees")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Unable to process rotation")]
+ [ArgHints("deg", "URLARG", "0 to 360")]
+ [Route(HttpVerbs.Post, "/RotateTo/{deg}/{token}")]
+ public object RotateTo(string deg, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (float.TryParse(deg, out float target_yaw) == true)
+ {
+ return Failure("Unable to process rotation", "RotateTo", new [] { deg });
+ }
+ float yaw = target_yaw;
+ 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;
+ getClient().Self.Movement.BodyRotation = result;
+ getClient().Self.Movement.SendUpdate();
+ return BasicReply("ok", "RotateTo", new [] { deg });
+ }
+
+
+
+ [About("Attempt to teleport to a new region")]
+ [ReturnHintsFailure("Error Unable to Teleport to location")]
+ [ReturnHints("Accepted")]
+ [ArgHints("region", "URLARG", "the name of the region we are going to")]
+ [ArgHints("x", "URLARG", "X location to teleport to")]
+ [ArgHints("y", "URLARG", "y location to teleport to")]
+ [ArgHints("z", "URLARG", "z location to teleport to")]
+ [Route(HttpVerbs.Get, "/Teleport/{region}/{x}/{y}/{z}/{token}")]
+ public object Teleport(string region, string x, string y, string z, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ bool status = TeleportRequest(new [] { region, x, y, z });
+ if (status == false)
+ {
+ return Failure("Error Unable to Teleport to location", "Teleport", new [] { region, x, y, z });
+ }
+ bot.SetTeleported();
+ return BasicReply("Accepted", "Teleport", new [] { region, x, y, z });
+ }
+
+
+ [About("Attempt to teleport to a new region via a SL url")]
+ [ReturnHintsFailure("slurl is empty")]
+ [ReturnHints("True|False")]
+ [ArgHints("slurl", "Text", "a full SLurl")]
+ [Route(HttpVerbs.Post, "/TeleportSLURL/{token}")]
+ public object TeleportSLURL([FormField] string slurl, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.notempty(slurl) == false)
+ {
+ return Failure("slurl is empty", "TeleportSLURL", new [] { slurl });
+ }
+ bot.SetTeleported();
+ return BasicReply(TeleportRequest(new [] { slurl }).ToString(), "TeleportSLURL", new [] { slurl });
+ }
+
+ protected bool TeleportRequest(string[] args)
+ {
+ getClient().Self.AutoPilotCancel();
+ if (args[0].Contains("http://maps.secondlife.com/secondlife/") == true)
+ {
+ if (bot.TeleportWithSLurl(args[0]) == "ok")
+ {
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ List argvalues = new List(args);
+ if(argvalues.Count == 3)
+ {
+ string regionName = getClient().Network.CurrentSim.Name;
+ argvalues = new List() { regionName };
+ argvalues.AddRange(args);
+ }
+ if (argvalues.Count == 4)
+ {
+ string regionName = argvalues[0];
+ float.TryParse(argvalues[1], out float posX);
+ float.TryParse(argvalues[2], out float posY);
+ float.TryParse(argvalues[3], out float posZ);
+ bot.SetTeleported();
+ bool status = getClient().Self.Teleport(regionName, new Vector3(posX, posY, posZ));
+ bot.ResetAnimations();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ }
+}
diff --git a/SecondBotEvents/Commands/Notecard.cs b/SecondBotEvents/Commands/Notecard.cs
new file mode 100644
index 0000000..ee982f5
--- /dev/null
+++ b/SecondBotEvents/Commands/Notecard.cs
@@ -0,0 +1,129 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+
+
+namespace SecondBotEvents.Commands
+{
+ public class Notecard : CommandsAPI
+ {
+ public Notecard(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+ [About("Adds content to the Collection
Also creates the Collection if it does not exist")]
+ [ReturnHintsFailure("Collection value is empty")]
+ [ReturnHintsFailure("Content value is empty")]
+ [ReturnHints("ok")]
+ [ArgHints("collection", "URLARG", "The name of the collection")]
+ [ArgHints("content", "String", "The text to add to the collection")]
+ [Route(HttpVerbs.Post, "/NotecardAdd/{collection}/{token}")]
+ public object NotecardAdd(string collection, [FormField] string content, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.notempty(collection) == false)
+ {
+ return Failure("Collection value is empty", "NotecardAdd", new [] { collection, content });
+ }
+ if (SecondbotHelpers.notempty(content) == false)
+ {
+ return Failure("Content value is empty", "NotecardAdd", new [] { collection, content });
+ }
+ bot.NotecardAddContent(collection, content);
+ return BasicReply("ok", "NotecardAdd", new [] { collection, content });
+ }
+
+ [About("Clears the contents of a collection")]
+ [ReturnHintsFailure("Collection value is empty")]
+ [ReturnHints("ok")]
+ [ArgHints("collection", "URLARG", "The name of the collection")]
+ [Route(HttpVerbs.Get, "/NotecardClear/{collection}/{token}")]
+ public object NotecardClear(string collection, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.notempty(collection) == false)
+ {
+ return Failure("Collection value is empty", "NotecardClear", new [] { collection });
+ }
+ bot.ClearNotecardStorage(collection);
+ return BasicReply("ok", "NotecardClear", new [] { collection });
+ }
+
+ [About("Sends a notecard to a avatar using the text in the prebuilt collection [see NotecardAdd] and also clears the collection just before sending [see NotecardClear]")]
+ [ReturnHintsFailure("Collection value is empty")]
+ [ReturnHintsFailure("Notecardname value is empty")]
+ [ReturnHintsFailure("Invaild avatar uuid")]
+ [ReturnHintsFailure("No content in notecard storage ?")]
+ [ReturnHints("ok")]
+ [ArgHints("avatar", "URLARG", "The UUID or Name of an avatar")]
+ [ArgHints("collection", "URLARG", "The name of the collection")]
+ [ArgHints("notecardname", "URLARG", "What to call the created notecard")]
+ [Route(HttpVerbs.Get, "/NotecardSend/{avatar}/{collection}/{notecardname}/{token}")]
+ public object NotecardSend(string avatar, string collection, string notecardname, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.notempty(collection) == false)
+ {
+ return Failure("Collection value is empty", "NotecardSend", new [] { avatar, collection, notecardname });
+ }
+ if (SecondbotHelpers.notempty(notecardname) == false)
+ {
+ return Failure("Notecardname value is empty", "NotecardSend", new [] { avatar, collection, notecardname });
+ }
+ ProcessAvatar(avatar);
+ if(avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar uuid", "NotecardSend", new [] { avatar, collection, notecardname });
+ }
+ string content = bot.GetNotecardContent(collection);
+ if (content == null)
+ {
+ return Failure("No content in notecard storage " + collection, "NotecardSend", new [] { avatar, collection, notecardname });
+ }
+ bot.ClearNotecardStorage(collection);
+ return BasicReply(bot.SendNotecard(notecardname, content, avataruuid).ToString(), "NotecardSend", new [] { avatar, collection, notecardname });
+ }
+
+ [About("Creates and sends a notecard in one command good if you are using HTTP otherwise see [NotecardSend]")]
+ [ReturnHintsFailure("notecardname value is empty")]
+ [ReturnHintsFailure("Content value is empty")]
+ [ReturnHintsFailure("Invaild avatar uuid")]
+ [ReturnHints("ok")]
+ [ArgHints("avatar", "URLARG", "The UUID or Name of an avatar")]
+ [ArgHints("content", "String", "The text to add to the collection")]
+ [ArgHints("notecardname", "URLARG", "What to call the created notecard")]
+ [Route(HttpVerbs.Post, "/NotecardDirectSend/{avatar}/{notecardname}/{token}")]
+ public object NotecardDirectSend(string avatar, [FormField] string content, string notecardname, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.notempty(notecardname) == false)
+ {
+ return Failure("notecardname value is empty", "NotecardDirectSend", new[] { avatar, content, notecardname });
+ }
+ if (SecondbotHelpers.notempty(content) == false)
+ {
+ return Failure("content value is empty", "NotecardDirectSend", new[] { avatar, content, notecardname });
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar uuid", "NotecardDirectSend", new[] { avatar, content, notecardname });
+ }
+ return BasicReply(bot.SendNotecard(notecardname, content, avataruuid).ToString(), "NotecardDirectSend", new[] { avatar, content, notecardname });
+ }
+ }
+}
diff --git a/SecondBotEvents/Commands/ParcelCommands.cs b/SecondBotEvents/Commands/ParcelCommands.cs
new file mode 100644
index 0000000..61abbee
--- /dev/null
+++ b/SecondBotEvents/Commands/ParcelCommands.cs
@@ -0,0 +1,782 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+using System.Collections.Generic;
+
+namespace SecondBotEvents.Commands
+{
+ public partial class ParcelCommands : CommandsAPI
+ {
+ public ParcelCommands(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+ [About("Sets the current parcel for sale Also marks the parcel for sale")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Invaild amount")]
+ [ArgHints("amount", "URLARG", "The amount to sell the parcel for from 1 to 9999999")]
+ [ArgHints("avatar", "URLARG", "Avatar uuid or Firstname Lastname or \"none\" who we are locking the sale to")]
+ [Route(HttpVerbs.Get, "/SetParcelSale/{amount}/{avatar}/{token}")]
+ public object SetParcelSale(string amount,string avatar,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if(tests.Key == false)
+ {
+ return Failure(tests.Value, "SetParcelSale", new [] { amount, avatar });
+ }
+ if (int.TryParse(amount,out int amountvalue) == false)
+ {
+ return Failure("Invaild amount", "SetParcelSale", new [] { amount, avatar });
+ }
+
+ int minAmount = 1;
+ avataruuid = UUID.Zero;
+ if (avatar != "none")
+ {
+ ProcessAvatar(avatar);
+ if(avataruuid != UUID.Zero)
+ {
+ minAmount = 0;
+ }
+ }
+ if ((amountvalue < minAmount) || (amountvalue > 9999999))
+ {
+ return Failure("Invaild amount", "SetParcelSale", new [] { amount, avatar });
+ }
+ targetparcel.SalePrice = amountvalue;
+ targetparcel.AuthBuyerID = avataruuid;
+ parcel_static.ParcelSetFlag(ParcelFlags.ForSale, targetparcel, true);
+ parcel_static.ParcelSetFlag(ParcelFlags.ForSaleObjects, targetparcel, false);
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ return Failure("ok", "SetParcelSale", new [] { amount, avatar });
+ }
+
+ [About("Gets the parcel Dwell (Traffic) value and returns it via the reply target")]
+ [ReturnHints("traffic value")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [Route(HttpVerbs.Get, "/GetParcelTraffic/{token}")]
+ public object GetParcelTraffic(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "GetParcelTraffic");
+ }
+ return BasicReply(targetparcel.Dwell.ToString(), "GetParcelTraffic");
+ }
+
+ [About("Changes the parcel landing mode to point and sets the landing point")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Invaild amount")]
+ [ArgHints("x", "URLARG", "X point for landing")]
+ [ArgHints("y", "URLARG", "Y point for landing")]
+ [ArgHints("z", "URLARG", "Z point for landing")]
+ [Route(HttpVerbs.Get, "/SetParcelLandingZone/{x}/{y}/{z}/{token}")]
+ public object SetParcelLandingZone(string x, string y, string z, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "SetParcelLandingZone", new [] { x, y, z });
+ }
+ if(int.TryParse(x,out int X) == false)
+ {
+ return Failure("Unable to process landing point x value", "SetParcelLandingZone", new [] { x, y, z });
+ }
+ if (int.TryParse(y, out int Y) == false)
+ {
+ return Failure("Unable to process landing point y value", "SetParcelLandingZone", new [] { x, y, z });
+ }
+ if (int.TryParse(z, out int Z) == false)
+ {
+ return Failure("Unable to process landing point z value", "SetParcelLandingZone", new [] { x, y, z });
+ }
+ targetparcel.Landing = LandingType.LandingPoint;
+ targetparcel.UserLocation = new Vector3(X, Y, Z);
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ return BasicReply("ok", "SetParcelLandingZone", new [] { x, y, z });
+ }
+
+
+ [About("Updates the current parcels name")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Parcel name is empty")]
+ [ArgHints("name", "URLARG", "The new name of the parcel")]
+ [Route(HttpVerbs.Get, "/SetParcelName/{name}/{token}")]
+ public object SetParcelName(string name, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "SetParcelName", new [] { name });
+ }
+ if (SecondbotHelpers.notempty(name) == false)
+ {
+ return Failure("Parcel name is empty", "SetParcelName", new [] { name });
+ }
+ targetparcel.Name = name;
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ return BasicReply("ok", "SetParcelName", new [] { name });
+ }
+
+ [About("Updates the current parcels description")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ArgHints("desc", "Text", "The new desc of the parcel")]
+ [Route(HttpVerbs.Post, "/SetParcelDesc/{token}")]
+ public object SetParcelDesc([FormField] string desc, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "SetParcelDesc", new [] { desc });
+ }
+ targetparcel.Desc = desc;
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ return BasicReply("ok", "SetParcelDesc", new [] { desc });
+ }
+
+ [About("Fetchs the current parcels desc")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [Route(HttpVerbs.Get, "/GetParcelDesc/{token}")]
+ public object GetParcelDesc(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "GetParcelDesc");
+ }
+ return BasicReply(targetparcel.Desc, "GetParcelDesc");
+ }
+
+ [About("gets the flags for the parcel")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [Route(HttpVerbs.Get, "/GetParcelFlags/{token}")]
+ public object GetParcelFlags(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "GetParcelFlags");
+ }
+ Dictionary flags = parcel_static.get_flags_list();
+ Dictionary collection = new Dictionary();
+ foreach (ParcelFlags cfg in flags.Values)
+ {
+ collection.Add(cfg.ToString(), targetparcel.Flags.HasFlag(cfg).ToString());
+ }
+ SuccessNoReturn("GetParcelFlags");
+ return collection;
+ }
+
+ [About("Ejects an avatar")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Invaild avatar")]
+ [ArgHints("avatar", "URLARG", "uuid of the avatar or Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/ParcelEject/{avatar}/{token}")]
+ public object ParcelEject(string avatar,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if(avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar", "ParcelEject", new [] { avatar });
+ }
+ getClient().Parcels.EjectUser(avataruuid, false);
+ return BasicReply("ok", "ParcelEject", new [] { avatar });
+ }
+
+ [About("Abandons the parcel the bot is currently on, returning it to Linden's or Estate owner")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/AbandonLand/{token}")]
+ public object AbandonLand(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "AbandonLand");
+ }
+ getClient().Parcels.ReleaseParcel(getClient().Network.CurrentSim, targetparcel.LocalID);
+ return BasicReply("ok", "AbandonLand");
+ }
+
+
+ [About("Bans an avatar from a parcel")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Invaild avatar")]
+ [ReturnHintsFailure("Avatar is in the blacklist")]
+ [ArgHints("avatar", "URLARG", "uuid of the avatar or Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/ParcelBan/{avatar}/{token}")]
+ public object ParcelBan(string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "ParcelBan", new [] { avatar });
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar", "ParcelBan", new [] { avatar });
+ }
+ bool alreadyBanned = false;
+ foreach (ParcelAccessEntry E in targetparcel.AccessBlackList)
+ {
+ if (E.AgentID == avataruuid)
+ {
+ alreadyBanned = true;
+ break;
+ }
+ }
+ if (alreadyBanned == true)
+ {
+ return BasicReply("Avatar is in the blacklist", "ParcelBan", new [] { avatar });
+ }
+ ParcelAccessEntry entry = new ParcelAccessEntry();
+ entry.AgentID = avataruuid;
+ entry.Flags = AccessList.Ban;
+ entry.Time = new System.DateTime(3030, 03, 03);
+ targetparcel.AccessBlackList.Add(entry);
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ return BasicReply("ok", "ParcelBan", new [] { avatar });
+ }
+
+ [About("Unbans an avatar from a parcel")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Invaild avatar")]
+ [ReturnHintsFailure("Avatar is already unbanned")]
+ [ArgHints("avatar", "URLARG", "uuid of the avatar or Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/ParcelUnBan/{avatar}/{token}")]
+ public object ParcelUnBan(string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "ParcelUnBan", new [] { avatar });
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar", "ParcelUnBan", new [] { avatar });
+ }
+ bool alreadyBanned = false;
+ ParcelAccessEntry removeentry = new ParcelAccessEntry();
+ foreach (ParcelAccessEntry E in targetparcel.AccessBlackList)
+ {
+ if (E.AgentID == avataruuid)
+ {
+ alreadyBanned = true;
+ removeentry = E;
+ break;
+ }
+ }
+ if (alreadyBanned == false)
+ {
+ return BasicReply("Avatar is already unbanned", "ParcelUnBan", new [] { avatar });
+ }
+ targetparcel.AccessBlackList.Remove(removeentry);
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ return BasicReply("ok", "ParcelUnBan", new [] { avatar });
+ }
+
+
+ [About("Updates the current parcels name")]
+ [ReturnHints("true|false")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ArgHints("musicurl", "URLARG", "The new name of the parcel")]
+ [Route(HttpVerbs.Get, "/SetParcelMusic/{musicurl}/{token}")]
+ public object SetParcelMusic(string musicurl, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "SetParcelMusic", new [] { musicurl });
+ }
+ bool status = parcel_static.set_parcel_music(bot, targetparcel, musicurl);
+ return BasicReply(status.ToString(), "SetParcelMusic", new [] { musicurl });
+ }
+
+ [About("Updates the current parcels name")]
+ [ReturnHints("true|false")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Incorrect perms to control parcel")]
+ [ReturnHintsFailure("No accepted flags")]
+ [ReturnHintsFailure("Unable to set flag ...")]
+ [ReturnHintsFailure("Flag: ? is unknown")]
+ [ReturnHintsFailure("Flag: ? missing \"=\"")]
+ [ArgHints("escapedflagdata", "Text", "repeatable flag data split by ::: formated Flag=True|False")]
+ [Route(HttpVerbs.Post, "/SetParcelFlag/{escapedflagdata}/{token}")]
+ public object SetParcelFlag([FormField] string escapedflagdata, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "SetParcelFlag", new [] { escapedflagdata });
+ }
+ List acceptablewords = new List();
+ Dictionary flags = parcel_static.get_flags_list();
+ acceptablewords.AddRange(new[] { "True", "False" });
+
+ Dictionary setflags = new Dictionary();
+ string[] args = escapedflagdata.Split(":::");
+ foreach (string a in args)
+ {
+ string[] parts = a.Split('=', StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length != 2)
+ {
+ LogFormater.Warn("Flag: " + a + " missing \"=\"");
+ continue;
+ }
+ if (flags.ContainsKey(parts[0]) == false)
+ {
+ LogFormater.Warn("Flag: " + parts[0] + " is unknown");
+ continue;
+ }
+ if (acceptablewords.Contains(parts[1]) == false)
+ {
+ LogFormater.Crit("Unable to set flag " + parts[0] + " to : " + parts[1] + "");
+ }
+ setflags.Add(parts[0], Convert.ToBoolean(parts[1]));
+ }
+ if (setflags.Count == 0)
+ {
+ return Failure("No accepted flags", "SetParcelFlag", new [] { escapedflagdata });
+ }
+ if (parcel_static.has_parcel_perm(targetparcel, bot) == false)
+ {
+ return Failure("Incorrect perms to control parcel", "SetParcelFlag", new [] { escapedflagdata });
+ }
+ foreach (KeyValuePair cfg in setflags)
+ {
+ if (flags.ContainsKey(cfg.Key) == true)
+ {
+ parcel_static.ParcelSetFlag(flags[cfg.Key], targetparcel, cfg.Value);
+ }
+ }
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ return BasicReply("Applying perms", "SetParcelFlag", new [] { escapedflagdata });
+ }
+
+ [About("Returns all objects from the current parcel for the selected avatar")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Invaild avatar UUID")]
+ [ArgHints("avatar", "URLARG", "avatar uuid or Firstname Lastname")]
+ [Route(HttpVerbs.Get, "/ParcelReturnTargeted/{avatar}/{token}")]
+ public object ParcelReturnTargeted(string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "ParcelReturnTargeted", new [] { avatar });
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar UUID", "ParcelReturnTargeted", new [] { avatar });
+ }
+ getClient().Parcels.ReturnObjects(getClient().Network.CurrentSim, targetparcel.LocalID, ObjectReturnType.None, new List() { avataruuid });
+ return BasicReply("ok", "ParcelReturnTargeted", new [] { avatar });
+ }
+
+
+ [About("transfers the current parcel ownership to the assigned group")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Invaild group uuid")]
+ [ReturnHintsFailure("Not in group")]
+ [Route(HttpVerbs.Get, "/ParcelDeedToGroup/{group}/{token}")]
+ public object ParcelDeedToGroup(string group, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "ParcelDeedToGroup", new [] { group });
+ }
+ if (UUID.TryParse(group, out UUID groupuuid) == false)
+ {
+ return Failure("Invaild group uuid", "ParcelDeedToGroup", new [] { group });
+ }
+ if (bot.MyGroups.ContainsKey(groupuuid) == false)
+ {
+ return Failure("Not in group", "ParcelDeedToGroup", new [] { group });
+ }
+ targetparcel.GroupID = groupuuid;
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ Thread.Sleep(500);
+ getClient().Parcels.DeedToGroup(getClient().Network.CurrentSim, targetparcel.LocalID, groupuuid);
+ return BasicReply("ok", "ParcelDeedToGroup", new [] { group });
+ }
+
+ [About("Attempts to buy the parcel the bot is standing on, the amount must match the sale price for the land!")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ReturnHintsFailure("Parcel not for sale")]
+ [ReturnHintsFailure("Parcel not for sale")]
+ [ReturnHintsFailure("Parcel sale locked to other avatars")]
+ [ReturnHintsFailure("Parcel sale price and amount do not match")]
+ [ReturnHintsFailure("Invaild amount")]
+ [ArgHints("amount", "URLARG", "amount to pay for the parcel (min 1)")]
+ [Route(HttpVerbs.Get, "/ParcelBuy/{amount}/{token}")]
+ public object ParcelBuy(string amount, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "ParcelBuy", new [] { amount });
+ }
+ if ((targetparcel.AuthBuyerID != UUID.Zero) && (targetparcel.AuthBuyerID != getClient().Self.AgentID))
+ {
+ return Failure("Parcel sale locked to other avatars", "ParcelBuy", new[] { amount });
+ }
+ int minAmount = 1;
+ if (targetparcel.AuthBuyerID == getClient().Self.AgentID)
+ {
+ minAmount = 0;
+ }
+ if (int.TryParse(amount, out int amountvalue) == false)
+ {
+ return Failure("Invaild amount", "ParcelBuy", new [] { amount });
+ }
+ if (amountvalue < minAmount)
+ {
+ return Failure("Invaild amount", "ParcelBuy", new [] { amount });
+ };
+ if (targetparcel.Flags.HasFlag(ParcelFlags.ForSale) == false)
+ {
+ return Failure("Parcel not for sale", "ParcelBuy", new [] { amount });
+ }
+ if (targetparcel.SalePrice != amountvalue)
+ {
+ return Failure("Parcel sale price and amount do not match", "ParcelBuy", new [] { amount });
+ }
+ getClient().Parcels.Buy(getClient().Network.CurrentSim, targetparcel.LocalID, false, UUID.Zero, false, targetparcel.Area, amountvalue);
+ return BasicReply("ok", "ParcelBuy", new [] { amount });
+ }
+
+ [About("Freezes an avatar")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Invaild avatar UUID")]
+ [ReturnHintsFailure("Invaild state")]
+ [ArgHints("avatar", "URLARG", "avatar uuid or Firstname Lastname")]
+ [ArgHints("state", "URLARG", "setting state to false will unfreeze or true to freeze")]
+ [Route(HttpVerbs.Get, "/ParcelFreeze/{avatar}/{state}/{token}")]
+ public object ParcelFreeze(string avatar, string state, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bool.TryParse(state, out bool freezestate) == false)
+ {
+ return Failure("Invaild state", "ParcelFreeze", new [] { avatar, state });
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("Invaild avatar UUID", "ParcelFreeze", new [] { avatar, state });
+ }
+ getClient().Parcels.FreezeUser(avataruuid, freezestate);
+ return BasicReply("ok", "ParcelFreeze", new [] { avatar, state });
+ }
+
+
+ [About("Fetchs the parcel ban list of the parcel the bot is currently on
If the name returned is lookup the bot is currently requesting the avatar name")]
+ [ReturnHintsFailure("json object: GetParcelBanlistObject")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [Route(HttpVerbs.Get, "/GetParcelBanlist/{token}")]
+ public object GetParcelBanlist(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "GetParcelBanlist");
+ }
+ GetParcelBanlistObject reply = new GetParcelBanlistObject();
+ int delays = 0;
+ bool haslookup = true;
+ while ((haslookup == true) && (delays < 3))
+ {
+ haslookup = false;
+ foreach (ParcelAccessEntry e in targetparcel.AccessBlackList)
+ {
+ if (e.AgentID != UUID.Zero)
+ {
+ if (bot.FindAvatarKey2Name(e.AgentID) == "lookup")
+ {
+ haslookup = true;
+ }
+ }
+ }
+ if(haslookup == true)
+ {
+ Thread.Sleep(1000);
+ delays++;
+ }
+ }
+ foreach (ParcelAccessEntry e in targetparcel.AccessBlackList)
+ {
+ string name = bot.FindAvatarKey2Name(e.AgentID);
+ reply.entrys.Add(e.AgentID, name);
+ }
+ reply.reportedEntrys = targetparcel.AccessBlackList.Count;
+ reply.delay = delays * 1000;
+ reply.parcelName = targetparcel.Name;
+ reply.regionName = getClient().Network.CurrentSim.Name;
+ return BasicReply(JsonConvert.SerializeObject(reply), "GetParcelBanlist");
+ }
+
+ [About("Returns a rezzed object")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Invaild object uuid")]
+ [ReturnHintsFailure("Unable to find object")]
+ [ArgHints("objectuuid", "URLARG", "object UUID to unrez")]
+ [Route(HttpVerbs.Get, "/UnRezObject/{objectuuid}/{token}")]
+ public object UnRezObject(string objectuuid,string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(objectuuid, out UUID targetobject) == false)
+ {
+ return Failure("Invaild object uuid", "UnRezObject", new [] { objectuuid });
+ }
+ bool found = false;
+ Dictionary objects_copy = getClient().Network.CurrentSim.ObjectsPrimitives.Copy();
+ foreach (KeyValuePair Obj in objects_copy)
+ {
+ if (Obj.Value.ID == targetobject)
+ {
+ getClient().Inventory.RequestDeRezToInventory(Obj.Key);
+ found = true;
+ break;
+ }
+ }
+ if(found == false)
+ {
+ return Failure("Unable to find object", "UnRezObject", new [] { objectuuid });
+ }
+ return BasicReply("ok", "UnRezObject", new [] { objectuuid });
+ }
+
+ [About("Updates the current parcels media settings \n" +
+ "MediaAutoScale=Bool (True|False)\n" +
+ "MediaLoop=Bool (True|False)\n" +
+ "MediaID=UUID (Texture)\n" +
+ "MediaURL=String\n" +
+ "MediaDesc=String\n" +
+ "MediaHeight=Int (256 to 1024)\n" +
+ "MediaWidth=Int (256 to 1024)\n" +
+ "MediaType=String [\"IMG-PNG\",\"IMG-JPG\",\"VID-MP4\",\"VID-AVI\" or \"Custom-MIME_TYPE_CODE\"]"
+ )]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Error not in a sim")]
+ [ReturnHintsFailure("Parcel data not ready")]
+ [ArgHints("escapedflagdata", "Text", "repeatable flag data split by ::: formated Flag=True|False")]
+ [Route(HttpVerbs.Get, "/ParcelSetMedia/{token}")]
+ public object ParcelSetMedia([FormField] string escapedflagdata, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ KeyValuePair tests = SetupCurrentParcel();
+ if (tests.Key == false)
+ {
+ return Failure(tests.Value, "ParcelSetMedia", new [] { escapedflagdata });
+ }
+ string[] args = escapedflagdata.Split(":::");
+ foreach (string A in args)
+ {
+ string[] bits = A.Split('=');
+ if (bits.Length == 2)
+ {
+ if (bits[0] == "MediaType")
+ {
+ UpdateParcel_MediaType(targetparcel, bits[1]);
+ }
+ else if ((bits[0] == "MediaWidth") || (bits[0] == "MediaHeight"))
+ {
+ if (int.TryParse(bits[1], out int size) == true)
+ {
+ if ((size >= 256) || (size <= 1024))
+ {
+ if (bits[0] == "MediaHeight")
+ {
+ targetparcel.Media.MediaHeight = size;
+ }
+ else if (bits[0] == "MediaWidth")
+ {
+ targetparcel.Media.MediaWidth = size;
+ }
+ }
+ }
+ }
+ else if (bits[0] == "MediaID")
+ {
+ if (UUID.TryParse(bits[1], out UUID texture) == true)
+ {
+ targetparcel.Media.MediaID = texture;
+ }
+ }
+ else if (bits[0] == "MediaURL")
+ {
+ targetparcel.Media.MediaURL = bits[1];
+ }
+ else if (bits[0] == "MediaDesc")
+ {
+ targetparcel.Media.MediaDesc = bits[1];
+ }
+ else if (bits[0] == "MediaAutoScale")
+ {
+ if (bool.TryParse(bits[1], out bool output) == true)
+ {
+ targetparcel.Media.MediaAutoScale = output;
+ }
+ }
+ else if (bits[0] == "MediaLoop")
+ {
+ if (bool.TryParse(bits[1], out bool output) == true)
+ {
+ targetparcel.Media.MediaLoop = output;
+ }
+ }
+ }
+ }
+ targetparcel.Update(getClient().Network.CurrentSim, false);
+ return BasicReply("ok", "ParcelSetMedia", new [] { escapedflagdata });
+ }
+
+ protected bool UpdateParcel_MediaType(Parcel targetparcel, string value)
+ {
+ if (value == "IMG-PNG")
+ {
+ targetparcel.Media.MediaType = "image/png";
+ }
+ else if (value == "IMG-JPG")
+ {
+ targetparcel.Media.MediaType = "image/jpeg";
+ }
+ else if (value == "VID-MP4")
+ {
+ targetparcel.Media.MediaType = "video/mp4";
+ }
+ else if (value == "VID-AVI")
+ {
+ targetparcel.Media.MediaType = "video/x-msvideo";
+ }
+ else if (value.StartsWith("Custom-") == true)
+ {
+ string mime = value;
+ mime = mime.Replace("Custom-", "");
+ targetparcel.Media.MediaType = mime;
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public class GetParcelBanlistObject
+ {
+ public Dictionary entrys = new Dictionary();
+ public string parcelName = "";
+ public string regionName = "";
+ public int delay = 0;
+ public int reportedEntrys = 0;
+ }
+}
diff --git a/SecondBotEvents/Commands/Self.cs b/SecondBotEvents/Commands/Self.cs
new file mode 100644
index 0000000..f4351ec
--- /dev/null
+++ b/SecondBotEvents/Commands/Self.cs
@@ -0,0 +1,273 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Services;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SecondBotEvents.Commands
+{
+ public class Self : CommandsAPI
+ {
+ public Self(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+ [About("Makes the bot teleport to its home region")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/GoHome/{token}")]
+ public object GoHome(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ bot.GotoNextHomeRegion();
+ return BasicReply("ok", "GoHome");
+ }
+
+ [About("Makes the bot turn to face avatar and point at them (if found)")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Cant find UUID in sim")]
+ [Route(HttpVerbs.Get, "/PointAt/{avatar}/{token}")]
+ public object PointAt(string avatar, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if (getClient().Network.CurrentSim.AvatarPositions.ContainsKey(avataruuid) == false)
+ {
+ return Failure("Cant find UUID in sim", "PointAt", new [] { avatar });
+ }
+ bot.ResetAnimations();
+ getClient().Self.Stand();
+ getClient().Self.Movement.TurnToward(getClient().Network.CurrentSim.AvatarPositions[avataruuid]);
+ getClient().Self.Movement.SendUpdate();
+ getClient().Self.AnimationStart(Animations.POINT_YOU, true);
+ getClient().Self.PointAtEffect(getClient().Self.AgentID, avataruuid, new Vector3d(0, 0, 0), PointAtType.Select, UUID.Random());
+ getClient().Self.BeamEffect(getClient().Self.AgentID, avataruuid, new Vector3d(0, 0, 2), new Color4(255, 255, 255, 1), (float)3.0, UUID.Random());
+ return BasicReply("ok", "PointAt", new [] { avatar });
+ }
+
+ [About("Reads a value from the KeyValue storage (temp unless SQL is enabled)")]
+ [ReturnHints("value")]
+ [ReturnHintsFailure("Unknown Key: KeyName")]
+ [ArgHints("Key", "URLARG", "the key we are trying to read from")]
+ [Route(HttpVerbs.Get, "/ReadKeyValue/{Key}/{token}")]
+ public object ReadKeyValue(string Key, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.HaveLocalKeyStorage(Key) == true)
+ {
+ return BasicReply(bot.ReadLocalKeyStorage(Key), "ReadKeyValue");
+ }
+ return Failure("Unknown Key: "+Key, "ReadKeyValue");
+ }
+
+ [About("sets a value for KeyValue storage (temp unless SQL is enabled)")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Key is empty")]
+ [ReturnHintsFailure("Value is empty")]
+ [ArgHints("Key", "URLARG", "the key we are trying to set")]
+ [ArgHints("Value", "string", "the value we are tring to put on the key")]
+ [Route(HttpVerbs.Post, "/SetKeyValue/{Key}/{token}")]
+ public object SetKeyValue(string Key, [FormField] string Value, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.isempty(Key) == false)
+ {
+ return Failure("Key is empty", "SetKeyValue", new[] { Key, Value });
+ }
+ if (SecondbotHelpers.isempty(Value) == false)
+ {
+ return Failure("Value is empty", "SetKeyValue", new[] { Key, Value });
+ }
+ bot.setLocalKeyStorage(Key, Value);
+ return BasicReply("ok", "SetKeyValue");
+ }
+
+ [About("Reads a value from the KeyValue storage (temp unless SQL is enabled)")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Key is empty")]
+ [ArgHints("Key", "URLARG", "the key we are trying to clear")]
+ [Route(HttpVerbs.Get, "/ClearKeyValue/{Key}/{token}")]
+ public object ClearKeyValue(string Key, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.isempty(Key) == false)
+ {
+ return Failure("Key is empty", "SetKeyValue", new[] { Key });
+ }
+ bot.clearLocalKeyStorage(Key);
+ return BasicReply("ok", "SetKeyValue");
+ }
+
+ [About("Makes the bot sit on the ground or on a object if it can see it")]
+ [ReturnHints("ok")]
+ [ReturnHintsFailure("Invaild object UUID")]
+ [ArgHints("target", "URLARG", "ground or a object UUID")]
+ [Route(HttpVerbs.Get, "/Sit/{target}/{token}")]
+ public object Sit(string target, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (target == "ground")
+ {
+ getClient().Self.SitOnGround();
+ return BasicReply("ok", "Sit", new [] { target });
+ }
+ if(UUID.TryParse(target,out UUID objectuuid) == false)
+ {
+ return Failure("Invaild object UUID", "Sit", new [] { target });
+ }
+ getClient().Self.RequestSit(objectuuid, Vector3.Zero);
+ return BasicReply("ok", "Sit", new [] { target });
+ }
+
+ [About("Makes the bot stand up if sitting (also resets animations)")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/Stand/{token}")]
+ public object Stand(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ getClient().Self.Stand();
+ bot.ResetAnimations();
+ return BasicReply("ok", "Stand");
+ }
+
+ [About("Makes the bot sit on the ground or on a object if it can see it")]
+ [ReturnHints("true|false")]
+ [ReturnHintsFailure("Invaild object UUID")]
+ [ReturnHintsFailure("Unable to see object")]
+ [ArgHints("target", "URLARG", "object UUID")]
+ [Route(HttpVerbs.Get, "/ClickObject/{object}/{token}")]
+ public object ClickObject(string target, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (UUID.TryParse(target, out UUID objectuuid) == false)
+ {
+ return Failure("Invaild object UUID", "ClickObject", new [] { target });
+ }
+
+ getClient().Self.PointAtEffect(getClient().Self.AgentID, objectuuid, new Vector3d(0, 0, 0), PointAtType.Select, new UUID("1df9eb92-62fa-15e5-4bfb-5931f1525274"));
+
+ Dictionary objectsentrys = getClient().Network.CurrentSim.ObjectsPrimitives.Copy();
+
+ bool found_object = false;
+ foreach (KeyValuePair entry in objectsentrys)
+ {
+ if (entry.Value.ID == objectuuid)
+ {
+ getClient().Objects.ClickObject(getClient().Network.CurrentSim, entry.Key);
+ found_object = true;
+ break;
+ }
+ }
+ return BasicReply(found_object.ToString(), "ClickObject", new [] { target });
+ }
+
+ [About("Makes the bot kill itself you monster")]
+ [ReturnHints("ok")]
+ [Route(HttpVerbs.Get, "/Logoff/{token}")]
+ public object Logoff(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (bot.KillMe == false)
+ {
+ getClient().Self.Chat("Laters im out", 0, ChatType.Normal);
+ bot.KillMePlease();
+ }
+ return BasicReply("ok", "Logoff");
+ }
+
+ [About("Gets the last 5 commands issued to the bot")]
+ [ReturnHints("list of commands")]
+ [Route(HttpVerbs.Get, "/GetLastCommands/{token}")]
+ public object GetLastCommands(string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ SuccessNoReturn("GetLastCommands");
+ return bot.GetLastCommands(5);
+ }
+
+ [About("Sets the bot to accept a request type from the avatar (or a object owned by the avatar)\n " +
+ "friend: friend request \n " +
+ "group: group invite \n " +
+ "animation: trigger animation request [from a object]\n" +
+ "teleport: teleport lure\n" +
+ "inventory: Inventory transfer\n" +
+ "command: A non signed command")]
+ [ReturnHintsFailure("avatar lookup")]
+ [ReturnHintsFailure("Invaild state")]
+ [ReturnHintsFailure("Invaild sticky")]
+ [ReturnHintsFailure("Invaild flag")]
+ [ArgHints("avatar", "URLARG", "avatar uuid or Firstname Lastname")]
+ [ArgHints("flag", "URLARG", "friend, group, animation, teleport, inventory or command")]
+ [ArgHints("state", "URLARG", "State to set the flag to true or false")]
+ [ArgHints("sticky", "URLARG", "if true the permissing will not expire after the first use otherwise false")]
+ [Route(HttpVerbs.Get, "/SetPermFlag/{avatar}/{flag}/{state}/{sticky}/{token}")]
+ public object SetPermFlag(string avatar, string flag, string state, string sticky, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ ProcessAvatar(avatar);
+ if (avataruuid == UUID.Zero)
+ {
+ return Failure("avatar lookup", "SetPermFlag", new [] { avatar, flag, state, sticky });
+ }
+ bool stateflag = false;
+ bool stickyflag = false;
+ if(bool.TryParse(state,out stateflag) == false)
+ {
+ return Failure("Invaild state", "SetPermFlag", new [] { avatar, flag, state, sticky });
+ }
+ if (bool.TryParse(sticky, out stickyflag) == false)
+ {
+ return Failure("Invaild sticky", "SetPermFlag", new [] { avatar, flag, state, sticky });
+ }
+ string[] AcceptedFlags = new [] { "friend", "group", "animation", "teleport", "command", "inventory" };
+ if (AcceptedFlags.Contains(flag) == false)
+ {
+ return Failure("Invaild flag", "SetPermFlag", new [] { avatar, flag, state, sticky });
+ }
+ if (stateflag == true)
+ {
+ bot.Add_action_from(flag, avataruuid, bot.FindAvatarKey2Name(avataruuid), stickyflag);
+ return BasicReply("Added perm: " + flag + " Sticky: " + stickyflag.ToString(), "SetPermFlag", new [] { avatar, flag, state, sticky });
+ }
+ bot.Remove_action_from(flag, avataruuid, stickyflag);
+ return BasicReply("Removed perm: " + flag + " Sticky: " + stickyflag.ToString(), "SetPermFlag", new [] { avatar, flag, state, sticky });
+ }
+
+
+
+ }
+}
diff --git a/SecondBotEvents/Commands/StreamAdmin.cs b/SecondBotEvents/Commands/StreamAdmin.cs
new file mode 100644
index 0000000..036ce0b
--- /dev/null
+++ b/SecondBotEvents/Commands/StreamAdmin.cs
@@ -0,0 +1,99 @@
+using EmbedIO;
+using EmbedIO.Routing;
+using EmbedIO.WebApi;
+using Newtonsoft.Json;
+using OpenMetaverse;
+using RestSharp;
+using SecondBotEvents.Services;
+using System;
+
+namespace SecondBotEvents.Commands
+{
+ public class StreamAdmin : CommandsAPI
+ {
+ public StreamAdmin(EventsSecondBot setmaster) : base(setmaster)
+ {
+ }
+
+
+ [About("A streamadin command")]
+ [ReturnHints("True|False")]
+ [ReturnHintsFailure("Bad reply: ...")]
+ [ReturnHintsFailure("Endpoint is empty")]
+ [ReturnHintsFailure("Endpointcode is empty")]
+ [ReturnHintsFailure("HTTP status code: ...")]
+ [ReturnHintsFailure("Error: ...")]
+ [ReturnHintsFailure("Notecard title is to short")]
+ [ArgHints("endpoint","Text","The end point")]
+ [ArgHints("endpointcode", "Text", "The end point code")]
+ [Route(HttpVerbs.Post, "/FetchNextNotecard/{token}")]
+ public object FetchNextNotecard([FormField] string endpoint, [FormField] string endpointcode, string token)
+ {
+ if (AllowToken(token) == false)
+ {
+ return Failure("Token not accepted");
+ }
+ if (SecondbotHelpers.notempty(endpoint) == false)
+ {
+ return Failure("Endpoint is empty", "FetchNextNotecard", new [] { endpoint, endpointcode });
+ }
+ if (SecondbotHelpers.notempty(endpointcode) == false)
+ {
+ return Failure("Endpointcode is empty", "FetchNextNotecard", new [] { endpoint, endpointcode });
+ }
+
+ string attempt_endpoint = endpoint + "sys.php";
+ token = SecondbotHelpers.GetSHA1(SecondbotHelpers.UnixTimeNow().ToString() + "NotecardNext" + endpointcode);
+ var client = new RestClient(attempt_endpoint);
+ var request = new RestRequest("Notecard/Next", Method.Post);
+ string unixtime = SecondbotHelpers.UnixTimeNow().ToString();
+ request.AddParameter("token", token);
+ request.AddParameter("unixtime", unixtime);
+ request.AddParameter("method", "Notecard");
+ request.AddParameter("action", "Next");
+ request.AddHeader("content-type", "application/x-www-form-urlencoded");
+ RestResponse endpoint_checks = client.ExecutePostAsync(request).Result;
+ if (endpoint_checks.StatusCode != System.Net.HttpStatusCode.OK)
+ {
+ return Failure("HTTP status code: " + endpoint_checks.StatusCode.ToString(), "FetchNextNotecard", new [] { endpoint, endpointcode });
+ }
+ try
+ {
+ NotecardEndpoint server_reply = JsonConvert.DeserializeObject(endpoint_checks.Content);
+ if (server_reply.status == false)
+ {
+ return Failure("Bad reply: " + server_reply.message, "FetchNextNotecard", new [] { endpoint, endpointcode });
+ }
+ if (server_reply.NotecardTitle.Length < 3)
+ {
+ return Failure("Notecard title is to short", "FetchNextNotecard", new [] { endpoint, endpointcode });
+ }
+ return BasicReply
+ (
+ bot.SendNotecard(server_reply.NotecardTitle,
+ server_reply.NotecardContent,
+ (UUID)server_reply.AvatarUUID).ToString(),
+ "FetchNextNotecard",
+ new [] { endpoint, endpointcode }
+ );
+ }
+ catch (Exception e)
+ {
+ return Failure("Error: " + e.Message + "", "FetchNextNotecard", new [] { endpoint, endpointcode });
+ }
+ }
+
+ public class NotecardEndpoint : BasicEndpoint
+ {
+ public string AvatarUUID { get; set; }
+ public string NotecardTitle { get; set; }
+ public string NotecardContent { get; set; }
+ }
+
+ public class BasicEndpoint
+ {
+ public bool status { get; set; }
+ public string message { get; set; }
+ }
+ }
+}
diff --git a/SecondBotEvents/Config/BasicConfig.cs b/SecondBotEvents/Config/BasicConfig.cs
new file mode 100644
index 0000000..998ff23
--- /dev/null
+++ b/SecondBotEvents/Config/BasicConfig.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SecondBotEvents.Config
+{
+ public class BasicConfig : Config
+ {
+ public BasicConfig(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { }
+ protected override void MakeSettings()
+ {
+ filename = "basic";
+ settings.Add("username");
+ settings.Add("password");
+ settings.Add("loginURI");
+ }
+
+ public string GetUsername()
+ {
+ return ReadSettingAsString("username","Firstname Lastname");
+ }
+
+ public string GetPassword()
+ {
+ return ReadSettingAsString("password", "passwordHere");
+ }
+
+ public string GetFirstName()
+ {
+
+ string[] bits = ReadSettingAsString("username", "Firstname Lastname").Split(" ");
+ return bits[0];
+ }
+
+ public string GetLastName()
+ {
+ string[] bits = ReadSettingAsString("username", "Firstname Lastname").Split(" ");
+ if(bits.Length == 2)
+ {
+ return bits[1];
+ }
+ return "Resident";
+ }
+
+ public string GetLoginURI()
+ {
+ return ReadSettingAsString("loginURI", "secondlife");
+ }
+ }
+}
diff --git a/SecondBotEvents/Config/CommandsConfig.cs b/SecondBotEvents/Config/CommandsConfig.cs
new file mode 100644
index 0000000..84a211a
--- /dev/null
+++ b/SecondBotEvents/Config/CommandsConfig.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Linq;
+
+namespace SecondBotEvents.Config
+{
+ public class CommandsConfig : Config
+ {
+ public CommandsConfig(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { }
+ protected override void MakeSettings()
+ {
+ filename = "commands";
+ settings.Add("allowFundsCommands");
+ settings.Add("allowIMcontrol");
+ settings.Add("sharedSecret");
+ settings.Add("onlySelectedAvs");
+ settings.Add("avsCSV");
+ settings.Add("enforceTimeWindow");
+ settings.Add("timeWindowSecs");
+ }
+
+ public bool GetEnforceTimeWindow()
+ {
+ return ReadSettingAsBool("enforceTimeWindow", false);
+ }
+
+ public int GetTimeWindowSecs()
+ {
+ return ReadSettingAsInt("timeWindowSecs", 35);
+ }
+
+ public bool GetOnlySelectedAvs()
+ {
+ return ReadSettingAsBool("onlySelectedAvs", false);
+ }
+
+ public List GetAvsCSV()
+ {
+ return ReadSettingAsString("avsCSV", "Madpeter Zond,Madpeter Zond,Madpeter Zond").Split(",").ToList();
+ }
+
+ public bool GetAllowFundsCommands()
+ {
+ return ReadSettingAsBool("allowFundsCommands", false);
+ }
+
+ public bool GetAllowIMcontrol()
+ {
+ return ReadSettingAsBool("allowIMcontrol", false);
+ }
+
+ public string GetSharedSecret()
+ {
+ return ReadSettingAsString("sharedSecret", "ThisIsMySecret");
+ }
+ }
+}
diff --git a/SecondBotEvents/Config/HttpConfig.cs b/SecondBotEvents/Config/HttpConfig.cs
new file mode 100644
index 0000000..adfe803
--- /dev/null
+++ b/SecondBotEvents/Config/HttpConfig.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SecondBotEvents.Config
+{
+ public class HttpConfig: Config
+ {
+ public HttpConfig(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { }
+ protected override void MakeSettings()
+ {
+ filename = "http";
+ settings.Add("enabled");
+ settings.Add("host");
+ }
+
+ public string GetHost()
+ {
+ return ReadSettingAsString("host", "http://*:80");
+ }
+
+ public bool GetEnabled()
+ {
+ return ReadSettingAsBool("enabled", false);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/SecondBotEvents/Config/basic_config.cs b/SecondBotEvents/Config/basic_config.cs
deleted file mode 100644
index a46f3b8..0000000
--- a/SecondBotEvents/Config/basic_config.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace SecondBotEvents.Config
-{
- public class basic_config : config
- {
- public basic_config(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { }
- protected override void makeSettings()
- {
- filename = "basic";
- settings.Add("username");
- settings.Add("password");
- settings.Add("loginURI");
- }
-
- public string getUsername()
- {
- return readSetting("username","Firstname Lastname");
- }
-
- public string getPassword()
- {
- return readSetting("password", "passwordHere");
- }
-
- public string getFirstName()
- {
-
- string[] bits = readSetting("username", "Firstname Lastname").Split(" ");
- return bits[0];
- }
-
- public string getLastName()
- {
- string[] bits = readSetting("username", "Firstname Lastname").Split(" ");
- if(bits.Length == 2)
- {
- return bits[1];
- }
- return "Resident";
- }
-
- public string getLoginURI()
- {
- return readSetting("loginURI", "secondlife");
- }
- }
-}
diff --git a/SecondBotEvents/Config/config.cs b/SecondBotEvents/Config/config.cs
index 1142615..d1daf2f 100644
--- a/SecondBotEvents/Config/config.cs
+++ b/SecondBotEvents/Config/config.cs
@@ -8,40 +8,40 @@ using System.Text;
namespace SecondBotEvents.Config
{
- public abstract class config
+ public abstract class Config
{
protected Dictionary mysettings = new Dictionary();
protected List settings = new List();
protected string filename = "config";
protected bool loaded = false;
- public config(bool fromENV, string fromFolder="")
+ public Config(bool fromENV, string fromFolder="")
{
- makeSettings();
- loadSettings(fromENV, fromFolder);
+ MakeSettings();
+ LoadSettings(fromENV, fromFolder);
}
- public bool isLoaded()
+ public bool IsLoaded()
{
return loaded;
}
- protected void loadSettings(bool fromENV, string fromFolder = "")
+ protected void LoadSettings(bool fromENV, string fromFolder = "")
{
if(fromENV == true)
{
- loadSettingsFromEnv();
+ LoadSettingsFromEnv();
return;
}
- loadSettingsFromFile(fromFolder);
+ LoadSettingsFromFile(fromFolder);
}
- protected void makeSettingsFile(string fromFolder)
+ protected void MakeSettingsFile(string fromFolder)
{
Dictionary saveMe = new Dictionary();
Type thisType = this.GetType();
foreach (string setting in settings)
{
- string reader = "get"+ StringExtensions.FirstCharToUpper(setting);
+ string reader = "Get"+ StringExtensions.FirstCharToUpper(setting);
MethodInfo theMethod = thisType.GetMethod(reader);
string value = theMethod.Invoke(this, null).ToString();
saveMe.Add(setting, value);
@@ -52,14 +52,14 @@ namespace SecondBotEvents.Config
IO.WriteFile(writeFile, JsonConvert.SerializeObject(saveMe));
}
- protected void loadSettingsFromFile(string fromFolder)
+ protected void LoadSettingsFromFile(string fromFolder)
{
SimpleIO IO = new SimpleIO();
IO.ChangeRoot(fromFolder);
string readfile = filename + ".json";
if (IO.Exists(readfile) == false)
{
- makeSettingsFile(fromFolder);
+ MakeSettingsFile(fromFolder);
return;
}
JObject result = JObject.Parse(IO.ReadFile(readfile));
@@ -80,7 +80,7 @@ namespace SecondBotEvents.Config
loaded = true;
}
- protected void loadSettingsFromEnv()
+ protected void LoadSettingsFromEnv()
{
foreach (string setting in settings)
{
@@ -96,12 +96,12 @@ namespace SecondBotEvents.Config
loaded = true;
}
- protected virtual void makeSettings()
+ protected virtual void MakeSettings()
{
}
- protected string readSetting(string key, string defaultValue)
+ protected string ReadSettingAsString(string key, string defaultValue)
{
if (mysettings.ContainsKey(key) == false)
{
@@ -109,5 +109,25 @@ namespace SecondBotEvents.Config
}
return mysettings[key];
}
+
+ protected bool ReadSettingAsBool(string key,bool defaultValue=false)
+ {
+ bool result = bool.TryParse(ReadSettingAsString(key, defaultValue.ToString()), out bool enabled);
+ if (result == false)
+ {
+ return defaultValue;
+ }
+ return enabled;
+ }
+
+ protected int ReadSettingAsInt(string key, int defaultValue = 30)
+ {
+ bool result = int.TryParse(ReadSettingAsString(key, defaultValue.ToString()), out int value);
+ if (result == false)
+ {
+ return defaultValue;
+ }
+ return value;
+ }
}
}
diff --git a/SecondBotEvents/Config/http_config.cs b/SecondBotEvents/Config/http_config.cs
deleted file mode 100644
index cafe5f1..0000000
--- a/SecondBotEvents/Config/http_config.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace SecondBotEvents.Config
-{
- public class http_config: config
- {
- public http_config(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { }
- protected override void makeSettings()
- {
- filename = "http";
- settings.Add("enabled");
- settings.Add("host");
- }
-
- public string getHost()
- {
- return readSetting("host", "http://*:80");
- }
-
- public bool getEnabled()
- {
- bool result = bool.TryParse(readSetting("enabled", "false"), out bool enabled);
- if (result == false)
- {
- return false;
- }
- return enabled;
- }
- }
-
-}
\ No newline at end of file
diff --git a/SecondBotEvents/EventsSecondBot.cs b/SecondBotEvents/EventsSecondBot.cs
index 04d6e28..2156a0e 100644
--- a/SecondBotEvents/EventsSecondBot.cs
+++ b/SecondBotEvents/EventsSecondBot.cs
@@ -13,21 +13,22 @@ namespace SecondBotEvents
Console.WriteLine("Welcome to Secondbot [Events build] version: " + AssemblyInfo.GetGitHash());
EventsSecondBot worker = new EventsSecondBot(args);
- while(worker.exit() == false)
+ while(worker.Exit() == false)
{
Thread.Sleep(1000);
}
}
}
- internal class EventsSecondBot
+ public class EventsSecondBot
{
- public http HttpService = null;
- public BotClient botClient = null;
+ public HttpService HttpService = null;
+ public CommandsService CommandsService = null;
+ public BotClientService botClient = null;
- private EventHandler botclient_eventNotices;
- public event EventHandler botClientNoticeEvent
+ private EventHandler botclient_eventNotices;
+ public event EventHandler BotClientNoticeEvent
{
add { lock (botclient_event_attach) { botclient_eventNotices += value; } }
remove { lock (botclient_event_attach) { botclient_eventNotices -= value; } }
@@ -35,13 +36,13 @@ namespace SecondBotEvents
private readonly object botclient_event_attach = new object();
- public void triggerBotClientEvent(bool asRestart)
+ public void TriggerBotClientEvent(bool asRestart)
{
- EventHandler handler = botclient_eventNotices;
- handler?.Invoke(this, new botClientNotice(asRestart));
+ EventHandler handler = botclient_eventNotices;
+ handler?.Invoke(this, new BotClientNotice(asRestart));
}
- public string getVersion()
+ public string GetVersion()
{
return AssemblyInfo.GetGitHash();
}
@@ -60,43 +61,45 @@ namespace SecondBotEvents
{
fromFolder = args[0];
}
- restartServices();
+ RestartServices();
}
- public void restartServices()
+ public void RestartServices()
{
- stopServices();
- startServices();
+ StopServices();
+ StartServices();
}
- protected void startServices()
+ protected void StartServices()
{
- HttpService = new http(this);
- HttpService.start();
- botClient = new BotClient(this);
- if(botClient.basicCfg.isLoaded() == false)
+ botClient = new BotClientService(this);
+ HttpService = new HttpService(this);
+ CommandsService = new CommandsService(this);
+ if (botClient.IsLoaded() == false)
{
Console.WriteLine("Config is not loaded :(");
exitNow = true;
return;
}
- botClient.start();
+ HttpService.Start();
+ CommandsService.Start();
+ botClient.Start();
}
- protected void stopServices()
+ protected void StopServices()
{
if (HttpService != null)
{
- HttpService.stop();
+ HttpService.Stop();
}
if(botClient != null)
{
- botClient.stop();
+ botClient.Stop();
}
HttpService = null;
botClient = null;
}
- public bool exit()
+ public bool Exit()
{
return exitNow;
}
@@ -115,10 +118,10 @@ namespace SecondBotEvents
}
}
- public class botClientNotice
+ public class BotClientNotice
{
public bool isRestart = false;
- public botClientNotice(bool asRestart=false)
+ public BotClientNotice(bool asRestart=false)
{
isRestart = asRestart;
}
diff --git a/SecondBotEvents/SecondBotEvents.csproj b/SecondBotEvents/SecondBotEvents.csproj
index cfdf9d8..157b26b 100644
--- a/SecondBotEvents/SecondBotEvents.csproj
+++ b/SecondBotEvents/SecondBotEvents.csproj
@@ -16,6 +16,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/SecondBotEvents/Services/BotClient.cs b/SecondBotEvents/Services/BotClient.cs
deleted file mode 100644
index 4da2011..0000000
--- a/SecondBotEvents/Services/BotClient.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using OpenMetaverse;
-using SecondBotEvents.Config;
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace SecondBotEvents.Services
-{
- internal class BotClient : service
- {
- public GridClient client = null;
- public basic_config basicCfg = null;
- public BotClient(EventsSecondBot setMaster) : base(setMaster)
- {
- basicCfg = new basic_config(master.fromEnv, master.fromFolder);
- }
- public override void start()
- {
- Console.WriteLine("Client service [Starting]");
- login();
- }
- public override void stop()
- {
- Console.WriteLine("Client service [Stopping]");
- resetClient();
- }
-
- protected void botLoggedOut(object o, LoggedOutEventArgs e)
- {
- Console.WriteLine("Client service ~ Logged out");
- }
-
- protected void botLoggedIn(object o, SimConnectedEventArgs e)
- {
- Console.WriteLine("Client service ~ Connected to sim: "+e.Simulator.Name);
- }
-
- protected void botLoseSim(object o, SimDisconnectedEventArgs e)
- {
- Console.WriteLine("Client service ~ Disconnected from sim: " + e.Simulator.Name);
- }
-
- protected void botDisconnected(object o, DisconnectedEventArgs e)
- {
- Console.WriteLine("Client service ~ Network lost: " + e.Message);
- }
-
- protected void botLoginStatus(object o, LoginProgressEventArgs e)
- {
- Console.WriteLine("Client service ~ Login status: " + e.Status.ToString());
- }
-
- protected void resetClient()
- {
- bool isRestart = false;
- if (client != null)
- {
- client.Network.Logout();
- isRestart = true;
- }
- client = null;
- client = new GridClient();
- master.triggerBotClientEvent(isRestart);
- client.Network.SimConnected += botLoggedIn;
- client.Network.LoggedOut += botLoggedOut;
- client.Network.Disconnected += botDisconnected;
- client.Network.SimDisconnected += botLoseSim;
- client.Network.LoginProgress += botLoginStatus;
- }
- public void login()
- {
- resetClient();
- LoginParams loginParams = new LoginParams(
- client,
- basicCfg.getFirstName(),
- basicCfg.getLastName(),
- basicCfg.getPassword(),
- "secondbot",
- master.getVersion()
- );
- if (basicCfg.getLoginURI() != "secondlife")
- {
- loginParams.URI = basicCfg.getLoginURI();
- }
- client.Network.BeginLogin(loginParams);
- }
-
-
- }
-}
diff --git a/SecondBotEvents/Services/BotClientService.cs b/SecondBotEvents/Services/BotClientService.cs
new file mode 100644
index 0000000..ad4cf0c
--- /dev/null
+++ b/SecondBotEvents/Services/BotClientService.cs
@@ -0,0 +1,96 @@
+using OpenMetaverse;
+using SecondBotEvents.Config;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SecondBotEvents.Services
+{
+ public class BotClientService : Services
+ {
+ public GridClient client = null;
+ protected BasicConfig basicCfg = null;
+ public BotClientService(EventsSecondBot setMaster) : base(setMaster)
+ {
+ basicCfg = new BasicConfig(master.fromEnv, master.fromFolder);
+ }
+
+ public bool IsLoaded()
+ {
+ return basicCfg.IsLoaded();
+ }
+
+ public override void Start()
+ {
+ Console.WriteLine("Client service [Starting]");
+ Login();
+ }
+ public override void Stop()
+ {
+ Console.WriteLine("Client service [Stopping]");
+ ResetClient();
+ }
+
+ protected void BotLoggedOut(object o, LoggedOutEventArgs e)
+ {
+ Console.WriteLine("Client service ~ Logged out");
+ }
+
+ protected void BotSimConnected(object o, SimConnectedEventArgs e)
+ {
+ Console.WriteLine("Client service ~ Connected to sim: "+e.Simulator.Name);
+ }
+
+ protected void BotSimDisconnected(object o, SimDisconnectedEventArgs e)
+ {
+ Console.WriteLine("Client service ~ Disconnected from sim: " + e.Simulator.Name);
+ }
+
+ protected void BotDisconnected(object o, DisconnectedEventArgs e)
+ {
+ Console.WriteLine("Client service ~ Network disconnected: " + e.Message);
+ }
+
+ protected void BotLoginStatus(object o, LoginProgressEventArgs e)
+ {
+ Console.WriteLine("Client service ~ Login status: " + e.Status.ToString());
+ }
+
+ protected void ResetClient()
+ {
+ bool isRestart = false;
+ if (client != null)
+ {
+ client.Network.Logout();
+ isRestart = true;
+ }
+ client = null;
+ client = new GridClient();
+ master.TriggerBotClientEvent(isRestart);
+ client.Network.SimConnected += BotSimConnected;
+ client.Network.LoggedOut += BotLoggedOut;
+ client.Network.Disconnected += BotDisconnected;
+ client.Network.SimDisconnected += BotSimDisconnected;
+ client.Network.LoginProgress += BotLoginStatus;
+ }
+ public void Login()
+ {
+ ResetClient();
+ LoginParams loginParams = new LoginParams(
+ client,
+ basicCfg.GetFirstName(),
+ basicCfg.GetLastName(),
+ basicCfg.GetPassword(),
+ "secondbot",
+ master.GetVersion()
+ );
+ if (basicCfg.GetLoginURI() != "secondlife")
+ {
+ loginParams.URI = basicCfg.GetLoginURI();
+ }
+ client.Network.BeginLogin(loginParams);
+ }
+
+
+ }
+}
diff --git a/SecondBotEvents/Services/Commands.cs b/SecondBotEvents/Services/Commands.cs
new file mode 100644
index 0000000..d4d1dd4
--- /dev/null
+++ b/SecondBotEvents/Services/Commands.cs
@@ -0,0 +1,149 @@
+using OpenMetaverse;
+using SecondBotEvents.Config;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SecondBotEvents.Services
+{
+ public class CommandsService : Services
+ {
+ public CommandsConfig myConfig = null;
+ public bool acceptNewCommands = false;
+ public CommandsService(EventsSecondBot setMaster) : base(setMaster)
+ {
+ myConfig = new CommandsConfig(master.fromEnv, master.fromFolder);
+ }
+
+ Dictionary acceptTokens = new Dictionary();
+ public bool AllowToken(string token)
+ {
+ if(acceptTokens.ContainsKey(token) == false)
+ {
+ return false;
+ }
+ if (acceptTokens[token] != -1)
+ {
+ acceptTokens[token] = acceptTokens[token] - 1;
+ if(acceptTokens[token] == 0)
+ {
+ acceptTokens.Remove(token);
+ }
+ }
+ return true;
+ }
+
+ protected string SingleUseToken()
+ {
+ bool found = false;
+ string token = "";
+ while(found == false)
+ {
+ token = SecondbotHelpers.GetSHA1(token + SecondbotHelpers.UnixTimeNow().ToString());
+ if(acceptTokens.ContainsKey(token) == false)
+ {
+ addNewToken(token, 1);
+ found = true;
+ }
+ }
+ return token;
+ }
+
+ protected void addNewToken(string token,int uses)
+ {
+ if (acceptTokens.ContainsKey(token) == false)
+ {
+ acceptTokens[token] = 0;
+ }
+ acceptTokens[token] = uses;
+ }
+
+ public override void Start()
+ {
+ master.BotClientNoticeEvent += BotClientRestart;
+ }
+
+ public override void Stop()
+ {
+ acceptNewCommands = false;
+ }
+
+ protected void BotClientRestart(object o, BotClientNotice e)
+ {
+ acceptNewCommands = false;
+ Console.WriteLine("Commands service [Attached to new client]");
+ master.botClient.client.Network.LoggedOut += BotLoggedOut;
+ master.botClient.client.Network.SimConnected += BotLoggedIn;
+ }
+
+ protected void BotLoggedOut(object o, LoggedOutEventArgs e)
+ {
+ master.botClient.client.Network.SimConnected += BotLoggedIn;
+ Console.WriteLine("Commands service [Waiting for connect]");
+ }
+
+ protected void BotImMessage(object o, InstantMessageEventArgs e)
+ {
+ if(acceptNewCommands == false)
+ {
+ return;
+ }
+ if (e.IM.FromAgentName == master.botClient.client.Self.Name)
+ {
+ return;
+ }
+ if (myConfig.GetAllowIMcontrol() == false)
+ {
+ return;
+ }
+ bool acceptMessage = true;
+ bool requireSigning = false;
+ switch (e.IM.Dialog)
+ {
+ case InstantMessageDialog.MessageFromObject:
+ {
+ requireSigning = true;
+ break;
+ }
+ case InstantMessageDialog.MessageFromAgent: // shared with SessionSend
+ case InstantMessageDialog.SessionSend:
+ {
+ if(myConfig.GetOnlySelectedAvs() == false)
+ {
+ acceptMessage = false;
+ break;
+ }
+ acceptMessage = myConfig.GetAvsCSV().Contains(e.IM.FromAgentName);
+ break;
+ }
+ default:
+ {
+ acceptMessage = false;
+ break;
+ }
+ }
+ if (requireSigning == true)
+ {
+ acceptMessage = acceptMessageSigned(e.IM.Message);
+ }
+ if (acceptMessage == false)
+ {
+ return;
+ }
+
+ }
+
+ protected bool acceptMessageSigned(string message)
+ {
+ return false;
+ }
+
+ protected void BotLoggedIn(object o, SimConnectedEventArgs e)
+ {
+ master.botClient.client.Network.SimConnected -= BotLoggedIn;
+ master.botClient.client.Self.IM += BotImMessage;
+ acceptNewCommands = true;
+ Console.WriteLine("Commands service [accepting IM commands]");
+ }
+ }
+}
diff --git a/SecondBotEvents/Services/HttpService.cs b/SecondBotEvents/Services/HttpService.cs
new file mode 100644
index 0000000..8ca0528
--- /dev/null
+++ b/SecondBotEvents/Services/HttpService.cs
@@ -0,0 +1,300 @@
+using EmbedIO;
+using EmbedIO.Actions;
+using EmbedIO.WebApi;
+using OpenMetaverse;
+using SecondBotEvents.Config;
+using Swan.Logging;
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Threading;
+
+namespace SecondBotEvents.Services
+{
+ public class HttpService: Services
+ {
+ protected WebServer HTTPendpoint = null;
+ protected HttpConfig myConfig;
+ protected bool acceptBotCommands = false;
+ public HttpService(EventsSecondBot setMaster) : base(setMaster)
+ {
+ myConfig = new HttpConfig(master.fromEnv,master.fromFolder);
+ if (myConfig.GetEnabled() == false)
+ {
+ Console.WriteLine(Status());
+ return;
+ }
+ }
+
+ public override string Status()
+ {
+ if(myConfig == null)
+ {
+ return "HTTP service [Config status broken]";
+ }
+ if (myConfig.GetEnabled() == false)
+ {
+ return "HTTP service [- Not requested -]";
+ }
+ // @todo HTTP server check here
+ if(HTTPendpoint == null)
+ {
+ return "HTTP endpoint not online";
+ }
+ if (acceptBotCommands == false)
+ {
+ return "Active [Service]";
+ }
+ return "Active [Commands]";
+ }
+
+ protected void BotClientRestart(object o, BotClientNotice e)
+ {
+ acceptBotCommands = false;
+ Console.WriteLine("HTTP service [Attached to new client]");
+ master.botClient.client.Network.LoggedOut += BotLoggedOut;
+ master.botClient.client.Network.SimConnected += BotLoggedIn;
+ }
+
+ protected void BotLoggedOut(object o, LoggedOutEventArgs e)
+ {
+ acceptBotCommands = false;
+ master.botClient.client.Network.SimConnected += BotLoggedIn;
+ Console.WriteLine("HTTP service [Bot commands disabled]");
+ }
+
+ protected void BotLoggedIn(object o, SimConnectedEventArgs e)
+ {
+ master.botClient.client.Network.SimConnected -= BotLoggedIn;
+ acceptBotCommands = true;
+ Console.WriteLine("HTTP service [Bot commands enabled]");
+ }
+
+ public override void Start()
+ {
+ if (myConfig.GetEnabled() == false)
+ {
+ Console.WriteLine("HTTP service [Disabled]");
+ return;
+ }
+ Stop();
+ master.BotClientNoticeEvent += BotClientRestart;
+ Console.WriteLine("HTTP service [Enabled]");
+ CreateWebServer();
+ }
+
+ public override void Stop()
+ {
+ if (myConfig.GetEnabled() == false)
+ {
+ return;
+ }
+ master.BotClientNoticeEvent -= BotClientRestart;
+ if (HTTPendpoint != null)
+ {
+ HTTPendpoint.Dispose();
+ HTTPendpoint = null;
+ Console.WriteLine("HTTP service [Stopping]");
+ }
+ }
+
+ private void CreateWebServer()
+ {
+ Swan.Logging.Logger.UnregisterLogger();
+ HTTPendpoint = new WebServer(o => o
+ .WithUrlPrefix(myConfig.GetHost())
+ .WithMode(HttpListenerMode.EmbedIO))
+ .WithCors()
+ .WithModule(new ActionModule("/", HttpVerbs.Any, ctx => ctx.SendDataAsync(new { Message = "Error" }))
+ );
+ HTTPendpoint.RunAsync();
+ }
+ }
+
+ public abstract class CommandsAPI : WebApiController
+ {
+ protected EventsSecondBot master;
+ public CommandsAPI(EventsSecondBot setmaster)
+ {
+ master = setmaster;
+ }
+
+ protected bool AllowToken(string token)
+ {
+ return master.CommandsService.AllowToken(token);
+ }
+
+ protected UUID avataruuid = UUID.Zero;
+ protected Parcel targetparcel = null;
+
+ protected bool disableIPchecks = false;
+ public void disableIPlockout()
+ {
+ disableIPchecks = true;
+ }
+
+ public IPAddress handleGetClientIP()
+ {
+ try
+ {
+ if (disableIPchecks == true)
+ {
+ return IPAddress.Parse("127.0.0.1");
+ }
+ else
+ {
+ return HttpContext.Request.RemoteEndPoint.Address;
+ }
+ }
+ catch
+ {
+ Console.WriteLine("Error getting client ip");
+ return IPAddress.Parse("0.0.0.0");
+ }
+ }
+
+ protected GridClient getClient()
+ {
+ return master.botClient.client;
+ }
+
+ protected KeyValuePair SetupCurrentParcel()
+ {
+ if (getClient().Network.CurrentSim == null)
+ {
+ return new KeyValuePair(false, "Error not in a sim");
+ }
+ int localid = getClient().Parcels.GetParcelLocalID(getClient().Network.CurrentSim, getClient().Self.SimPosition);
+ if (getClient().Network.CurrentSim.Parcels.ContainsKey(localid) == false)
+ {
+ Thread.Sleep(2000);
+ localid = getClient().Parcels.GetParcelLocalID(getClient().Network.CurrentSim, getClient().Self.SimPosition);
+ if (getClient().Network.CurrentSim.Parcels.ContainsKey(localid) == false)
+ {
+ return new KeyValuePair(false, "Parcel data not ready");
+ }
+ }
+ targetparcel = getClient().Network.CurrentSim.Parcels[localid];
+ return new KeyValuePair(true, "");
+ }
+
+ protected void ProcessAvatar(string avatar)
+ {
+ string[] bits = avatar.Split(' ');
+ avataruuid = UUID.Zero;
+ // @todo Avatar storage system
+ UUID.TryParse(avatar, out avataruuid);
+
+ }
+
+ protected void SuccessNoReturn(string command)
+ {
+ SuccessNoReturn(command, new string[] { });
+ }
+ protected void SuccessNoReturn(string command, string[] args)
+ {
+ // @todo Command history storage system
+ }
+
+ protected object BasicReply(string input, string command)
+ {
+ return BasicReply(input, command, new string[] { });
+ }
+
+ protected object BasicReply(string input)
+ {
+ return BasicReply(input, getCallingCommand(), new string[] { });
+ }
+
+ protected object BasicReply(string input, string command, string[] args)
+ {
+ // @todo Command history storage system
+ return new { reply = input, status = true };
+ }
+
+ protected object Failure(string input, string command, string[] args)
+ {
+ // @todo Command history storage system
+ return new { reply = input, status = false };
+ }
+
+ protected object Failure(string input)
+ {
+ // @todo Command history storage system
+ return new { reply = input, command= getCallingCommand(), status = false };
+ }
+
+ protected string getCallingCommand()
+ {
+ return (new System.Diagnostics.StackTrace()).GetFrame(2).GetMethod().Name;
+ }
+
+ protected object Failure(string input, string command, string[] args, string whyfailed)
+ {
+ // @todo Command history storage system
+ return new { reply = input, status = false };
+ }
+
+ protected object Failure(string input, string command)
+ {
+ return Failure(input, command, new string[] { });
+ }
+
+ }
+
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class About : Attribute
+ {
+ public string about = "";
+ public About(string about)
+ {
+ this.about = about;
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class NeedsToken : Attribute
+ {
+ public bool needsToken = true;
+ public NeedsToken(bool needsToken)
+ {
+ this.needsToken = needsToken;
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+ public class ReturnHints : Attribute
+ {
+ public string hint = "";
+ public bool good = true;
+ public ReturnHints(string hint)
+ {
+ this.hint = hint;
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+ public class ReturnHintsFailure : ReturnHints
+ {
+ public ReturnHintsFailure(string hint): base(hint)
+ {
+ this.good = false;
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+ public class ArgHints : Attribute
+ {
+ public string name = "";
+ public string typename = "";
+ public string about = "";
+
+ public ArgHints(string name,string typename, string about)
+ {
+ this.name = name;
+ this.typename = typename;
+ this.about = about;
+ }
+ }
+}
diff --git a/SecondBotEvents/Services/Services.cs b/SecondBotEvents/Services/Services.cs
new file mode 100644
index 0000000..ab09466
--- /dev/null
+++ b/SecondBotEvents/Services/Services.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SecondBotEvents.Services
+{
+ public abstract class Services
+ {
+ public EventsSecondBot master;
+ public Services(EventsSecondBot setMaster)
+ {
+ master = setMaster;
+ }
+
+ public virtual string Status()
+ {
+ return "";
+ }
+
+ public virtual void Start()
+ {
+
+ }
+ public virtual void Stop()
+ {
+
+ }
+ public void Restart()
+ {
+ Stop();
+ Start();
+ }
+ }
+}
diff --git a/SecondBotEvents/Services/http.cs b/SecondBotEvents/Services/http.cs
deleted file mode 100644
index ec0b536..0000000
--- a/SecondBotEvents/Services/http.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using EmbedIO;
-using EmbedIO.Actions;
-using OpenMetaverse;
-using SecondBotEvents.Config;
-using Swan.Logging;
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace SecondBotEvents.Services
-{
- internal class http: service
- {
- protected WebServer HTTPendpoint = null;
- protected http_config myConfig;
- protected bool acceptBotCommands = false;
- public http(EventsSecondBot setMaster) : base(setMaster)
- {
- myConfig = new http_config(master.fromEnv,master.fromFolder);
- if (myConfig.getEnabled() == false)
- {
- Console.WriteLine(status());
- return;
- }
- master.botClientNoticeEvent += botClientRestart;
- }
-
- public string status()
- {
- if(myConfig == null)
- {
- return "HTTP service [Config status broken]";
- }
- if (myConfig.getEnabled() == false)
- {
- return "HTTP service [- Not requested -]";
- }
- // @todo HTTP server check here
- if(HTTPendpoint == null)
- {
- return "HTTP endpoint not online";
- }
- if (acceptBotCommands == false)
- {
- return "Active [Service]";
- }
- return "Active [Commands]";
- }
-
- protected void botClientRestart(object o, botClientNotice e)
- {
- acceptBotCommands = false;
- Console.WriteLine("HTTP service [Attached to new client]");
- master.botClient.client.Network.LoggedOut += botLoggedOut;
- master.botClient.client.Network.SimConnected += botLoggedIn;
- }
-
- protected void botLoggedOut(object o, LoggedOutEventArgs e)
- {
- acceptBotCommands = false;
- master.botClient.client.Network.SimConnected += botLoggedIn;
- Console.WriteLine("HTTP service [Bot commands disabled]");
- }
-
- protected void botLoggedIn(object o, SimConnectedEventArgs e)
- {
- master.botClient.client.Network.SimConnected -= botLoggedIn;
- acceptBotCommands = true;
- Console.WriteLine("HTTP service [Bot commands enabled]");
- }
-
- public override void start()
- {
- if (myConfig.getEnabled() == false)
- {
- Console.WriteLine("HTTP service [Disabled]");
- return;
- }
- stop();
- Console.WriteLine("HTTP service [Enabled]");
- CreateWebServer();
- }
-
- public override void stop()
- {
- if (myConfig.getEnabled() == false)
- {
- return;
- }
- if(HTTPendpoint != null)
- {
- HTTPendpoint.Dispose();
- HTTPendpoint = null;
- Console.WriteLine("HTTP service [Stopping]");
- }
-
- }
-
- private void CreateWebServer()
- {
- Swan.Logging.Logger.UnregisterLogger();
- HTTPendpoint = new WebServer(o => o
- .WithUrlPrefix(myConfig.getHost())
- .WithMode(HttpListenerMode.EmbedIO))
- .WithCors()
- .WithModule(new ActionModule("/", HttpVerbs.Any, ctx => ctx.SendDataAsync(new { Message = "Error" }))
- );
- HTTPendpoint.RunAsync();
- }
- }
-}
diff --git a/SecondBotEvents/Services/service.cs b/SecondBotEvents/Services/service.cs
deleted file mode 100644
index 1a9db01..0000000
--- a/SecondBotEvents/Services/service.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace SecondBotEvents.Services
-{
- internal abstract class service
- {
- protected EventsSecondBot master;
- public service(EventsSecondBot setMaster)
- {
- master = setMaster;
- }
-
- public virtual void start()
- {
-
- }
- public virtual void stop()
- {
-
- }
- public void restart()
- {
- stop();
- start();
- }
- }
-}