From 25ebadc39bfb984a3fa01964167251b7afaaeeaa Mon Sep 17 00:00:00 2001 From: Madpeter <57852827+Madpeterz@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:20:19 +0000 Subject: [PATCH] rlv --- SecondBotEvents/Config/RlvConfig.cs | 32 ++ SecondBotEvents/Services/CommandsService.cs | 16 + SecondBotEvents/Services/DataStoreService.cs | 2 +- SecondBotEvents/Services/EventsService.cs | 1 + SecondBotEvents/Services/RlvService.cs | 486 +++++++++++++++++++ 5 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 SecondBotEvents/Config/RlvConfig.cs create mode 100644 SecondBotEvents/Services/RlvService.cs diff --git a/SecondBotEvents/Config/RlvConfig.cs b/SecondBotEvents/Config/RlvConfig.cs new file mode 100644 index 0000000..a96fefb --- /dev/null +++ b/SecondBotEvents/Config/RlvConfig.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SecondBotEvents.Config +{ + public class RlvConfig : Config + { + public RlvConfig(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { } + protected override void MakeSettings() + { + filename = "rlv"; + settings.Add("Enabled"); + settings.Add("AllowRlvViaIm"); + settings.Add("AllowRlvViaObjectOwnerSay"); + settings.Add("HideStatusOutput"); + } + public bool GetAllowRlvViaIm() + { + return ReadSettingAsBool("AllowRlvViaIm", false); + } + public bool GetAllowRlvViaObjectOwnerSay() + { + return ReadSettingAsBool("AllowRlvViaObjectOwnerSay", false); + } + + public bool GetEnabled() + { + return ReadSettingAsBool("Enabled", false); + } + } +} \ No newline at end of file diff --git a/SecondBotEvents/Services/CommandsService.cs b/SecondBotEvents/Services/CommandsService.cs index d57f451..381ccbb 100644 --- a/SecondBotEvents/Services/CommandsService.cs +++ b/SecondBotEvents/Services/CommandsService.cs @@ -168,6 +168,22 @@ namespace SecondBotEvents.Services CommandInterfaceCaller(e.IM.Message, requireSigning); } + public bool AvatarUUIDIsMaster(UUID avatar) + { + foreach(string name in myConfig.GetMastersCSV()) + { + string find = master.DataStoreService.GetAvatarUUID(name); + if (find != "lookup") + { + if(avatar.ToString() == find) + { + return true; + } + } + } + return false; + } + public KeyValuePair CommandInterfaceCaller(string message, bool requireSigning=false, bool viaCustomCommand=false) { string source = "command interface"; diff --git a/SecondBotEvents/Services/DataStoreService.cs b/SecondBotEvents/Services/DataStoreService.cs index 2467e01..dc45394 100644 --- a/SecondBotEvents/Services/DataStoreService.cs +++ b/SecondBotEvents/Services/DataStoreService.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Collections; using System.Collections.Generic; using System.Threading; +using SecondBotEvents.Commands; namespace SecondBotEvents.Services { @@ -505,7 +506,6 @@ namespace SecondBotEvents.Services * if not the bot will attempt to * lookup the avatar */ - public bool KnownAvatar(string name) { return KnownAvatar(name, UUID.Zero); diff --git a/SecondBotEvents/Services/EventsService.cs b/SecondBotEvents/Services/EventsService.cs index 611eca8..44013f3 100644 --- a/SecondBotEvents/Services/EventsService.cs +++ b/SecondBotEvents/Services/EventsService.cs @@ -73,6 +73,7 @@ namespace SecondBotEvents.Services protected void BotLoggedOut(object o, LoggedOutEventArgs e) { GetClient().Network.SimConnected += BotLoggedIn; + botConnected = false; LogFormater.Info("Events service [Bot commands disabled]"); removeEvents(); } diff --git a/SecondBotEvents/Services/RlvService.cs b/SecondBotEvents/Services/RlvService.cs new file mode 100644 index 0000000..3553696 --- /dev/null +++ b/SecondBotEvents/Services/RlvService.cs @@ -0,0 +1,486 @@ +namespace SecondBotEvents.Services +{ + using Discord; + using global::SecondBotEvents.Commands; + using global::SecondBotEvents.Config; + using OpenMetaverse; + using OpenMetaverse.ImportExport.Collada14; + using SecondBotEvents.Commands; + using System; + using System.Collections.Generic; + using System.Data; + + namespace SecondBotEvents.Services + { + public class RlvService : BotServices + { + protected bool botConnected = false; + protected RlvConfig myConfig; + protected List RlvRules = new List(); + + protected List NotifyRules = new List(); + + + public RlvService(EventsSecondBot setMaster) : base(setMaster) + { + myConfig = new RlvConfig(master.fromEnv, master.fromFolder); + } + public override string Status() + { + if (myConfig == null) + { + return "Config broken"; + } + else if(myConfig.GetEnabled() == false) + { + return "Disabled by config"; + } + else if (myConfig.GetHideStatusOutput() == true) + { + return "hidden"; + } + else if (botConnected == false) + { + return "Waiting for client"; + } + return "Active"; + } + + protected void BotClientRestart(object o, BotClientNotice e) + { + LogFormater.Info("Rlv service [Attached to new client]"); + GetClient().Network.LoggedOut += BotLoggedOut; + GetClient().Network.SimConnected += BotLoggedIn; + } + + protected void BotLoggedOut(object o, LoggedOutEventArgs e) + { + GetClient().Network.SimConnected += BotLoggedIn; + GetClient().Self.IM -= BotImMessage; + GetClient().Self.ChatFromSimulator -= LocalChat; + botConnected = false; + LogFormater.Info("Rlv service [waiting for new client]"); + } + + protected void BotLoggedIn(object o, SimConnectedEventArgs e) + { + GetClient().Network.SimConnected -= BotLoggedIn; + GetClient().Self.IM += BotImMessage; + GetClient().Self.ChatFromSimulator += LocalChat; + botConnected = true; + } + + public override void Start() + { + if (myConfig.GetEnabled() == false) + { + return; + } + running = true; + master.BotClientNoticeEvent += BotClientRestart; + } + + public override void Stop() + { + if (myConfig.GetEnabled() == false) + { + return; + } + running = false; + master.BotClientNoticeEvent -= BotClientRestart; + } + + protected int RlvoutputChannel = 0; + + protected void rlvEvent(UUID fromAvatar, string message, bool fromAv, UUID Source) + { + if((fromAvatar != GetClient().Self.AgentID) && (master.CommandsService.AvatarUUIDIsMaster(fromAvatar) == false)) + { + return; + } + if(message.StartsWith("@") == false) + { + return; + } + + string[] bits = message.Split(','); + foreach(string bit in bits) + { + string[] subbits = bit.Split("=", 2); + // if 2 subbits then command = args + // if 1 then just command + string command = subbits[0]; + string? subcommand = null; + string args = ""; + if (subbits.Length > 1) + { + args = subbits[1]; + } + subbits = command.Split(":"); + command = subbits[0]; + if (subbits.Length > 1) + { + subcommand = subbits[1]; + } + rlvCommand(command, subcommand, args, fromAv, Source); + } + } + // https://wiki.secondlife.com/wiki/LSL_Protocol/RestrainedLoveAPI#Audience + // currently to inventory + + protected void rlvCommand(string command,string? subcommand, string args,bool fromAv,UUID Source) + { + int argAsInt = RlvoutputChannel; + if(int.TryParse(args, out argAsInt) == false) + { + argAsInt = RlvoutputChannel; + } + switch(command) + { + case "version": + case "versionnew": + { + ChatOutput("RestrainedLife viewer v2.8 (Secondbot [Events] "+master.GetVersion()+")", argAsInt); + break; + } + case "versionnum": + { + ChatOutput("2080000", argAsInt); + break; + } + case "versionnumbl": + { + ChatOutput("2080000" + GetBlackListAsString(subcommand), argAsInt); + break; + } + case "getblacklist": + { + if(fromAv == false) + { + break; + } + ImChatOutput(Source, "Current RLV blacklist is "+GetBlackListAsString(subcommand)); + break; + } + case "notify": + { + string[] bits = subcommand.Split(';'); + if (bits.Length == 1) + { + bits = new string[] { "*", bits[0] }; + } + if ((args == "add") || (args == "y")) + { + NotifyRules = AddRule(Source, bits[0], bits[1], "*", NotifyRules); + break; + } + NotifyRules = RemoveRule(bits[0], bits[1],"*", NotifyRules); + break; + } + case "chatwhisper": + case "chatnormal": + case "chatshout": + case "sendchat": + case "setcam_unlock": + case "camunlock": + case "alwaysrun": + case "temprun": + case "fly": + case "recvchat_sec": + case "sendgesture": + case "emote": + case "recvemote_sec": + case "sendim_sec": + case "tplm": + case "tploc": + case "tplure_sec": + case "permissive": + { + RlvRules = RemoveRule(command, "*", "*", RlvRules); + if ((args == "add") || (args == "y")) + { + RlvRules = AddRule(Source, command, "*", "*", RlvRules); + } + break; + } + case "setrot": + { + if(args != "force") + { + break; + } + if(double.TryParse(subcommand, System.Globalization.NumberStyles.Float, Utils.EnUsCulture, out double rot) == false) + { + break; + } + GetClient().Self.Movement.UpdateFromHeading(Math.PI / 2d - rot, true); + break; + } + case "tpto": + { + if (args != "force") + { + break; + } + if (subcommand == null) + { + break; + } + string[] bits = subcommand.Split(';'); + if(bits.Length == 2) + { + // look at (ignored as docs suck) + subcommand = bits[0]; + } + bits = subcommand.Split('/'); + if(bits.Length == 3) + { + master.HomeboundService.MarkTeleport(); + Movement A = new Movement(master); + A.Teleport(GetClient().Network.CurrentSim.Name, bits[0], bits[1], bits[2]); + break; + } + else if(bits.Length == 4) + { + master.HomeboundService.MarkTeleport(); + Movement A = new Movement(master); + A.Teleport(bits[0], bits[1], bits[2], bits[3]); + break; + } + break; + } + case "getcam_fov": + case "camtextures": + case "camavdist": + case "camdrawmax": + case "camdrawmin": + case "camdistmax": + case "camzoommin": + case "camzoommax": + case "setcam_textures": + case "setcam_fov": + case "adjustheight": + case "standtp": + { + break; + } + case "redirchat": + case "camdrawalphamax": + case "camdrawalphamin": + case "setcam_avdistmin": + case "setcam_avdistmax": + case "setcam_fovmax": + case "setcam_fovmin": + case "recvchat": + case "recvchatfrom": + case "rediremote": + case "recvemotefrom": + case "recvemote": + case "sendchannel": + case "sendchannel_sec": + case "sendchannel_except": + case "sendim": + case "sendimto": + case "startim": + case "startimto": + case "recvim": + case "recvimfrom": + case "tplocal": + case "accepttp": + case "accepttprequest": + case "tprequest": + case "tplure": + { + RlvRules = RemoveRule(command, subcommand, "*", RlvRules); + if ((args == "add") || (args == "y")) + { + RlvRules = AddRule(Source, command, subcommand, "*", RlvRules); + } + break; + } + case "getcam_fovmax": + case "getcam_avdistmax": + { + GetMaxRulesetValue(command.Replace("get", "set"), argAsInt); + break; + } + case "getcam_zoommin": + case "getcam_avdistmin": + case "getcam_fovmin": + { + GetMinRulesetValue(command.Replace("get","set"), argAsInt); + break; + } + } + } + protected void GetMaxRulesetValue(string ruleName, int channelout) + { + List appliedRules = GetRules(ruleName); + int maxDis = 9999; + foreach (RLVRule rule in appliedRules) + { + if (int.TryParse(rule.Option, out int value) == true) + { + if (value < maxDis) + { + maxDis = value; + } + } + } + ChatOutput(maxDis.ToString(), channelout); + } + + protected void GetMinRulesetValue(string ruleName, int channelout) + { + List appliedRules = GetRules(ruleName); + int minDis = 0; + foreach (RLVRule rule in appliedRules) + { + if (int.TryParse(rule.Option, out int value) == true) + { + if (value > minDis) + { + minDis = value; + } + } + } + ChatOutput(minDis.ToString(), channelout); + } + + protected List GetRules(string command) + { + List reply = new List(); + foreach(RLVRule rule in RlvRules) + { + if(rule.Behaviour == command) + { + reply.Add(rule); + } + } + return reply; + + } + + protected List AddRule(UUID Source,string Behaviour, string Option, string Param, List ruleset) + { + RLVRule newRule = new RLVRule(); + newRule.Behaviour = "permissive"; + newRule.Sender = Source; + newRule.Option = "*"; + newRule.Param = "*"; + ruleset.Add(newRule); + return ruleset; + } + + protected List RemoveRule(string Behaviour, string Option,string Param,List ruleset) + { + bool foundRule = true; + while (foundRule == true) + { + foundRule = false; + int index = -1; + foreach (RLVRule rule in ruleset) + { + if ((rule.Behaviour == Behaviour) && (rule.Option == Option) && (rule.Param == Param)) + { + index = NotifyRules.IndexOf(rule); + break; + } + } + if (index != -1) + { + foundRule = true; + ruleset.RemoveAt(index); + } + } + return ruleset; + } + + protected string GetBlackListAsString(string? filter=null) + { + return ""; + } + + protected void ImChatOutput(UUID target, string message) + { + + } + + protected void ChatOutput(string message, int chan=0) + { + GetClient().Self.Chat(message, chan, ChatType.Normal); + } + + protected void LocalChat(object o, ChatEventArgs e) + { + if(myConfig.GetAllowRlvViaObjectOwnerSay() == false) + { + return; + } + switch (e.Type) + { + case ChatType.OwnerSay: + { + rlvEvent(e.OwnerID, e.Message,false, e.SourceID); + break; + } + case ChatType.Whisper: + case ChatType.Normal: + case ChatType.Shout: + case ChatType.RegionSayTo: + default: + { + break; + } + } + } + + protected void BotImMessage(object o, InstantMessageEventArgs e) + { + if (myConfig.GetAllowRlvViaIm() == false) + { + return; + } + switch (e.IM.Dialog) + { + case InstantMessageDialog.MessageFromObject: + { + // trigger object IM + rlvEvent(e.IM.FromAgentID, e.IM.Message,false, e.IM.FromAgentID); + break; + } + case InstantMessageDialog.MessageFromAgent: // shared with SessionSend + case InstantMessageDialog.SessionSend: + { + if (master.DataStoreService.GetIsGroup(e.IM.IMSessionID) == true) + { + break; + } + // trigger avatar IM + rlvEvent(e.IM.FromAgentID, e.IM.Message, true, e.IM.FromAgentID); + break; + } + default: + { + break; + } + } + } + } + } + + public class RLVRule + { + public string Behaviour { set; get; } + public string Option { set; get; } + public string Param { set; get; } + public UUID Sender { set; get; } + public string SenderName { set; get; } + + public override string ToString() + { + return string.Format("{0}: {1}:{2}={3} [{4}]", SenderName, Behaviour, Option, Param, Sender); + } + } + +}