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(); - } - } -}