From 6eaa112f8188573aeb08e2c088bd664c680691e7 Mon Sep 17 00:00:00 2001 From: Madpeter <57852827+Madpeterz@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:27:07 +0100 Subject: [PATCH] wip --- SecondBotEvents/Config/OnEventConfig.cs | 129 ++++ SecondBotEvents/Services/EventsService.cs | 18 + .../Services/InteractionService.cs | 1 - .../Services/TriggerOnEventService.cs | 550 ++++++++++++++++++ 4 files changed, 697 insertions(+), 1 deletion(-) create mode 100644 SecondBotEvents/Config/OnEventConfig.cs create mode 100644 SecondBotEvents/Services/TriggerOnEventService.cs diff --git a/SecondBotEvents/Config/OnEventConfig.cs b/SecondBotEvents/Config/OnEventConfig.cs new file mode 100644 index 0000000..ee6c89d --- /dev/null +++ b/SecondBotEvents/Config/OnEventConfig.cs @@ -0,0 +1,129 @@ +using OpenMetaverse; +using SecondBotEvents.Services; +using System; +using System.Collections.Generic; +using System.Text; + +namespace SecondBotEvents.Config +{ + public class OnEventConfig : Config + { + public OnEventConfig(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { } + protected override void MakeSettings() + { + filename = "onevent"; + settings.Add("Enabled"); + settings.Add("Count"); + int loop = 1; + while (loop <= GetCount()) + { + settings.Add("Event" + loop.ToString() + "Enabled"); + if(GetEventEnabled(loop) == false) + { + loop++; + continue; + } + settings.Add("Event" + loop.ToString() + "Source"); + settings.Add("Event" + loop.ToString() + "Monitor"); + settings.Add("Event" + loop.ToString() + "WhereCount"); + int loop2 = 1; + while (loop2 <= GetWhereCount(loop)) + { + settings.Add("Event" + loop.ToString() + "Where" + loop2.ToString()); + loop2++; + } + settings.Add("Event" + loop.ToString() + "ActionCount"); + loop2 = 1; + while (loop2 <= GetActionCount(loop)) + { + settings.Add("Event" + loop.ToString() + "Action" + loop2.ToString()); + loop2++; + } + loop++; + } + settings.Add("HideStatusOutput"); + return; + } + + public void unloadEvents() + { + int loop = 1; + while (loop <= GetCount()) + { + if (GetEventEnabled(loop) == false) + { + loop++; + continue; + } + RemoveSetting("Event" + loop.ToString() + "Enabled"); + RemoveSetting("Event" + loop.ToString() + "Source"); + RemoveSetting("Event" + loop.ToString() + "Monitor"); + int loop2 = 1; + while (loop2 <= GetWhereCount(loop)) + { + RemoveSetting("Event" + loop.ToString() + "Where" + loop2.ToString()); + loop2++; + } + RemoveSetting("Event" + loop.ToString() + "WhereCount"); + loop2 = 1; + while (loop2 <= GetActionCount(loop)) + { + settings.Remove("Event" + loop.ToString() + "Action" + loop2.ToString()); + loop2++; + } + RemoveSetting("Event" + loop.ToString() + "ActionCount"); + loop++; + } + } + + protected void RemoveSetting(string name) + { + mysettings.Remove(name); + settings.Remove(name); + } + + public bool GetEnabled() + { + return ReadSettingAsBool("Enabled", false); + } + + public int GetCount() + { + return ReadSettingAsInt("Count", 0); + } + + public int GetActionCount(int oneventid) + { + return ReadSettingAsInt("Event" + oneventid.ToString() + "ActionCount", 0); + } + + public string GetActionStep(int oneventid, int stepid) + { + return ReadSettingAsString("Event" + oneventid.ToString() + "Action" + stepid.ToString(), ""); + } + + public string GetWhereCheck(int oneventid, int checkid) + { + return ReadSettingAsString("Event" + oneventid.ToString() + "Where" + checkid.ToString(), ""); + } + + public int GetWhereCount(int oneventid) + { + return ReadSettingAsInt("Event" + oneventid.ToString() + "WhereCount", 0); + } + + public bool GetEventEnabled(int oneventid) + { + return ReadSettingAsBool("Event" + oneventid.ToString() + "Enabled", false); + } + + public string GetSource(int oneventid) + { + return ReadSettingAsString("Event" + oneventid.ToString() + "Source", ""); + } + public string GetSourceMonitor(int oneventid) + { + return ReadSettingAsString("Event" + oneventid.ToString() + "Monitor", ""); + } + } +} \ No newline at end of file diff --git a/SecondBotEvents/Services/EventsService.cs b/SecondBotEvents/Services/EventsService.cs index 9a7123f..e5e5dbb 100644 --- a/SecondBotEvents/Services/EventsService.cs +++ b/SecondBotEvents/Services/EventsService.cs @@ -25,6 +25,7 @@ namespace SecondBotEvents.Services protected UUID OutputAvatar = UUID.Zero; protected long lastGroupMembershipUpdate = 0; + public void InventoryUpdateEvent(Dictionary detail) { if (InventoryEvents.ContainsKey(detail["itemtype"]) == true) @@ -143,6 +144,7 @@ namespace SecondBotEvents.Services GetClient().Self.AlertMessage += BotAlertMessage; } + protected long lastAvUpdate = 0; protected string avatarhash = ""; protected void TrackAvatarsOnParcel() { @@ -152,6 +154,13 @@ namespace SecondBotEvents.Services GuestListLoaded = false; return; } + long dif = SecondbotHelpers.UnixTimeNow() - lastAvUpdate; + if(dif < 30) + { + // only check for changes every 30 secs + return; + } + lastAvUpdate = SecondbotHelpers.UnixTimeNow(); string hashstring = ""; List avs = new List(); @@ -471,4 +480,13 @@ namespace SecondBotEvents.Services Hash = SecondbotHelpers.GetSHA1(raw); } } + + public class GuestUpdateEvent + { + public string parcelName = ""; + public string regionName = ""; + public string avatarName = ""; + public UUID avatarUUID = UUID.Zero; + public bool entered = false; + } } diff --git a/SecondBotEvents/Services/InteractionService.cs b/SecondBotEvents/Services/InteractionService.cs index 284f515..dcd26d2 100644 --- a/SecondBotEvents/Services/InteractionService.cs +++ b/SecondBotEvents/Services/InteractionService.cs @@ -278,7 +278,6 @@ namespace SecondBotEvents.Services GetClient().Groups.GroupInvitation -= BotGroupInviteOffer; } } - } } diff --git a/SecondBotEvents/Services/TriggerOnEventService.cs b/SecondBotEvents/Services/TriggerOnEventService.cs new file mode 100644 index 0000000..4014f61 --- /dev/null +++ b/SecondBotEvents/Services/TriggerOnEventService.cs @@ -0,0 +1,550 @@ +using OpenMetaverse; +using SecondBotEvents.Config; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SecondBotEvents.Services +{ + public class TriggerOnEventService : BotServices + { + protected OnEventConfig myConfig; + protected bool botConnected = false; + Dictionary> MyCustomEvents = new Dictionary>(); + Dictionary lockouts = new Dictionary(); + public TriggerOnEventService(EventsSecondBot setMaster) : base(setMaster) + { + myConfig = new OnEventConfig(master.fromEnv, master.fromFolder); + int loop = 1; + string[] delimiterChars = new string[] { "{IS}", "{NOT}", "{IS_UUD}", "{MISSING}", "{CONTAINS}", "{IN_GROUP}", "{NOT_IN_GROUP}", "{LESSTHAN}", "{MORETHAN}", "{LOCKOUT}" }; + while (loop < myConfig.GetCount()) + { + if(myConfig.GetEventEnabled(loop) == false) + { + loop++; + continue; + } + CustomOnEvent Event = new CustomOnEvent(); + Event.Source = myConfig.GetSource(loop); + Event.MonitorFlags = myConfig.GetSourceMonitor(loop).Split(',').ToList(); + bool eventWhereOk = true; + int loop2 = 1; + while(loop2 < myConfig.GetWhereCount(loop)) + { + string check = myConfig.GetWhereCheck(loop, loop2); + foreach (string delimiterChar in delimiterChars) + { + if(check.Contains(delimiterChar) == false) + { + continue; + } + string[] bits = check.Split(delimiterChar, StringSplitOptions.RemoveEmptyEntries); + if (bits.Length == 2) + { + string delimiterCharU = delimiterChar.Replace("{", ""); + delimiterCharU = delimiterCharU.Replace("}", ""); + delimiterCharU = delimiterCharU.Trim(); + Event.WhereChecksLeft.Add(bits[0].Trim()); + Event.WhereChecksCenter.Add(delimiterCharU); + Event.WhereChecksRight.Add(bits[1].Trim()); + } + else + { + eventWhereOk = false; + } + break; + } + loop2++; + } + if(eventWhereOk == false) + { + continue; + } + loop2 = 1; + while (loop2 < myConfig.GetActionCount(loop)) + { + Event.Actions.Add(myConfig.GetActionStep(loop, loop2)); + loop2++; + } + if(MyCustomEvents.ContainsKey(Event.Source) == false) + { + MyCustomEvents.Add(Event.Source, new List()); + } + MyCustomEvents[Event.Source].Add(Event); + loop++; + } + myConfig.unloadEvents(); // remove unneeded copy of events from the config as we wont be using it + } + + protected void BotClientRestart(object o, BotClientNotice e) + { + botConnected = false; + LogFormater.Info("OnEvent Service [Attached to new client]"); + GetClient().Network.LoggedOut += BotLoggedOut; + GetClient().Network.SimConnected += BotLoggedIn; + } + + protected void BotLoggedOut(object o, LoggedOutEventArgs e) + { + botConnected = false; + GetClient().Network.SimConnected += BotLoggedIn; + LogFormater.Info("OnEvent Service [Standby]"); + } + + protected void BotImMessage(object o, InstantMessageEventArgs e) + { + switch (e.IM.Dialog) + { + case InstantMessageDialog.MessageFromObject: + { + break; + } + case InstantMessageDialog.MessageFromAgent: // shared with SessionSend + case InstantMessageDialog.SessionSend: + { + if (e.IM.GroupIM == true) + { + break; + } + // trigger avatar IM + TriggerEvent("AvatarIm", new Dictionary() { + { "message", e.IM.Message} , + { "avatarname", e.IM.FromAgentName}, + { "avataruuid", e.IM.FromAgentID.ToString() }, + }); + break; + } + default: + { + break; + } + } + } + + protected void RemoveEvents() + { + if (master.BotClient != null) + { + if (GetClient() != null) + { + GetClient().Self.IM -= BotImMessage; + } + } + } + + protected void AttachEvents() + { + GetClient().Self.IM += BotImMessage; + } + + protected string GetAvName(string avataruuid) + { + string avatarname = "lookup"; + if (avataruuid != null) + { + if (UUID.TryParse(avataruuid, out UUID avUUID) == true) + { + avatarname = master.DataStoreService.GetAvatarName(avUUID); + } + } + return avatarname; + } + + protected bool IsLockedout(string uuid) + { + lock (lockouts) + { + if (lockouts.ContainsKey(uuid) == false) + { + return false; + } + long now = SecondbotHelpers.UnixTimeNow(); + if(now >= lockouts[uuid]) + { + lockouts.Remove(uuid); + return false; + } + return true; + } + } + + protected void AddToLockout(string uuid, int mins) + { + lock (lockouts) + { + long newvalue = SecondbotHelpers.UnixTimeNow() + (mins * 60); + if (lockouts.ContainsKey(uuid) == true) + { + if (lockouts[uuid] < newvalue) + { + lockouts[uuid] = newvalue; + } + return; + } + lockouts.Add(uuid, newvalue); + } + } + + long lastCleanedLockouts = 0; + protected void CleanLockouts() + { + long dif = SecondbotHelpers.UnixTimeNow() - lastCleanedLockouts; + if(dif < 60) + { + return; + } + lastCleanedLockouts = SecondbotHelpers.UnixTimeNow(); + long now = SecondbotHelpers.UnixTimeNow(); + lock (lockouts) + { + List toberemoved = new List(); + foreach(KeyValuePair pair in lockouts) + { + if(pair.Value > now) + { + continue; + } + toberemoved.Add(pair.Key); + } + foreach(string A in toberemoved) + { + lockouts.Remove(A); + } + } + } + + protected void TriggerEvent(string eventName, Dictionary args) + { + if (MyCustomEvents.ContainsKey(eventName) == false) + { + return; + } + string avataruuid = null; + if (args.ContainsKey("avataruuid") == true) + { + avataruuid = args["avataruuid"]; + } + Dictionary values = new Dictionary(); + values.Add("eventype", eventName); + values.Add("avatarname", GetAvName(avataruuid)); + values.Add("avatarparcel", GetAvParcel(avataruuid)); + values.Add("botsim", GetClient().Network.CurrentSim.Name); + values.Add("botparcel", GetBotParcel()); + List AvPos = GetAvPos(avataruuid); + values.Add("avatarx", AvPos[0].ToString()); + values.Add("avatary", AvPos[1].ToString()); + values.Add("avatarz", AvPos[2].ToString()); + values.Add("avatardistance", AvPos[3].ToString()); + Vector3 A = GetClient().Self.SimPosition; + List BotPos = new List() { (int)Math.Round(A.X), (int)Math.Round(A.Y), (int)Math.Round(A.Z) }; + values.Add("botx", AvPos[0].ToString()); + values.Add("boty", AvPos[1].ToString()); + values.Add("botz", AvPos[2].ToString()); + values.Add("clockhour", DateTime.Now.ToString("HH")); + values.Add("clockmin", DateTime.Now.ToString("mm")); + values.Add("dayofweek", ((int)DateTime.Now.DayOfWeek).ToString()); + + + if (avataruuid == null) + { + avataruuid = "none"; + } + if(args.ContainsKey("message") == false) + { + args["message"] = "none"; + } + values.Add("avataruuid", avataruuid); + foreach (CustomOnEvent E in MyCustomEvents[eventName]) + { + bool canFireEvent = true; + int loop = 0; + while(loop < E.WhereChecksLeft.Count()) + { + string left = swapvalues(E.WhereChecksLeft[loop],values); + string center = E.WhereChecksCenter[loop]; + string right = swapvalues(E.WhereChecksRight[loop], values); + int leftAsInt = -1; + int rightAsInt = -1; + int.TryParse(left, out leftAsInt); + int.TryParse(right, out rightAsInt); + bool leftAsBool = false; + if(left == "true") leftAsBool = true; + bool rightAsBool = false; + if (right == "true") rightAsBool = true; + bool leftContactinsRight = left.Contains(right); + UUID leftAsUUID = UUID.Zero; + bool leftIsUUID = UUID.TryParse(left, out leftAsUUID); + UUID rightAsUUID = UUID.Zero; + bool rightIsUUID = UUID.TryParse(right, out rightAsUUID); + bool leftIsInGroup = false; + bool leftIsLockedout = IsLockedout(left); + bool leftIsEven = (leftAsInt % 2 == 0); + bool leftIsDivs = (leftAsInt % rightAsInt == 0); + if ((leftIsUUID == true) && (rightIsUUID == true)) + { + leftIsInGroup = master.DataStoreService.IsGroupMember(rightAsUUID, leftAsUUID); + } + if ((center == "IS") && (left != right)) { canFireEvent = false; } + else if ((center == "NOT") && (left == right)) { canFireEvent = false; } + else if ((center == "IS_UUID") && (leftIsUUID != rightAsBool)) { canFireEvent = false; } + else if ((center == "MISSING") && (leftContactinsRight == true)) { canFireEvent = false; } + else if ((center == "CONTAINS") && (leftContactinsRight == false)) { canFireEvent = false; } + else if ((center == "IN_GROUP") && (leftIsInGroup == false)) { canFireEvent = false; } + else if ((center == "NOT_IN_GROUP") && (leftIsUUID == true) && (rightIsUUID == true) && (leftIsInGroup == true)) { canFireEvent = false; } + else if ((center == "LESSTHAN") && (leftAsInt >= rightAsInt)) { canFireEvent = false; } + else if ((center == "MORETHAN") && (leftAsInt <= rightAsInt)) { canFireEvent = false; } + else if ((center == "LOCKOUT") && (leftIsLockedout != rightAsBool)) { canFireEvent = false; } + else if ((center == "IS_EVEN") && (leftIsEven != rightAsBool)) { canFireEvent = false; } + else if ((center == "DIVISIBLE") && (leftIsDivs == false)) { canFireEvent = false; } + if (canFireEvent == false) + { + break; + } + loop++; + } + if (canFireEvent == false) + { + continue; + } + foreach(string action in E.Actions) + { + string useaction = swapvalues(action, values); + string[] bits = useaction.Split('='); + if(bits.Length == 2) + { + if (bits[0] == "lockout") + { + bits = bits[1].Split("+"); + if(bits.Length == 2) + { + if (int.TryParse(bits[1],out int mins) == true) + { + AddToLockout(bits[0], mins); + } + } + continue; + } + } + master.CommandsService.CommandInterfaceCaller(useaction, false, false, "OnEvents"); + } + } + } + + protected string swapvalues(string input, Dictionary values) + { + foreach(KeyValuePair pair in values) + { + input = input.Replace("["+pair.Key+"]", pair.Value); + } + return input; + } + + protected List GetAvPos(string avataruuid) + { + if (UUID.TryParse(avataruuid, out UUID avUUID) == false) + { + return new List() { -1, -1, -1, -1 }; + } + foreach (Avatar A in GetClient().Network.CurrentSim.ObjectsAvatars.Copy().Values) + { + if (A.ID != avUUID) + { + continue; + } + Vector3 avpos = A.Position; + int dist = (int)Math.Round(Vector3.Distance(avpos, GetClient().Self.SimPosition)); + return new List() { (int)Math.Round(A.Position.X), (int)Math.Round(A.Position.Y), (int)Math.Round(A.Position.Z), dist }; + } + return new List() { -1, -1, -1, -1 }; + } + + protected string GetAvParcel(string avataruuid) + { + if (UUID.TryParse(avataruuid, out UUID avUUID) == false) + { + return "?"; + } + foreach (Avatar A in GetClient().Network.CurrentSim.ObjectsAvatars.Copy().Values) + { + if(A.ID != avUUID) + { + continue; + } + int localid = GetClient().Parcels.GetParcelLocalID(GetClient().Network.CurrentSim, A.Position); + if (GetClient().Network.CurrentSim.Parcels.ContainsKey(localid) == false) + { + return "?"; + } + return GetClient().Network.CurrentSim.Parcels[localid].Name; + } + return "?"; + } + + protected string GetBotParcel() + { + int localid = GetClient().Parcels.GetParcelLocalID(GetClient().Network.CurrentSim, GetClient().Self.SimPosition); + if (GetClient().Network.CurrentSim.Parcels.ContainsKey(localid) == false) + { + return "-"; + } + return GetClient().Network.CurrentSim.Parcels[localid].Name; + } + + string avatarhash = ""; + bool GuestListLoaded = false; + protected List AvatarsOnSim = new List(); + protected void TrackAvatarsOnParcel() + { + long dif = SecondbotHelpers.UnixTimeNow() - lastAvUpdate; + if (dif < 30) + { + // only check for changes every 30 secs + return; + } + lastAvUpdate = SecondbotHelpers.UnixTimeNow(); + string hashstring = ""; + List avs = new List(); + foreach (Avatar A in GetClient().Network.CurrentSim.ObjectsAvatars.Copy().Values) + { + avs.Add(A.ID); + hashstring = hashstring + A.ID.ToString(); + } + string hash = SecondbotHelpers.GetSHA1(hashstring); + if (hash == avatarhash) + { + // no changes + return; + } + avatarhash = hash; + + if (GuestListLoaded == true) + { + List joined = new List(); + List tracked = new List(); + foreach (UUID a in AvatarsOnSim) + { + tracked.Add(a); + } + foreach (UUID a in avs) + { + if (tracked.Contains(a) == true) + { + tracked.Remove(a); + continue; + } + joined.Add(a); + } + if ((tracked.Count > 0) || (joined.Count > 0)) + { + if (tracked.Count > 0) + { + foreach (UUID a in tracked) + { + TriggerEvent("GuestLeaves", new Dictionary() { { "avataruuid", a.ToString() } }); + } + } + if (joined.Count > 0) + { + foreach (UUID a in joined) + { + TriggerEvent("GuestJoins", new Dictionary() { { "avataruuid", a.ToString() } }); + } + } + } + } + AvatarsOnSim = avs; + GuestListLoaded = true; + } + + long lastClockEvent = 0; + protected void ClockEvent() + { + long dif = SecondbotHelpers.UnixTimeNow() - lastClockEvent; + if (dif < 60) + { + return; + } + lastClockEvent = SecondbotHelpers.UnixTimeNow(); + TriggerEvent("Clock", new Dictionary()); + } + + long lastAvUpdate = 0; + protected void TimedEvents() + { + TrackAvatarsOnParcel(); + CleanLockouts(); + ClockEvent(); + } + + public override string Status() + { + if (myConfig == null) + { + return "No Config"; + } + else if (myConfig.GetEnabled() == false) + { + return "- Not requested -"; + } + if (botConnected == true) + { + TimedEvents(); + } + if (myConfig.GetHideStatusOutput() == true) + { + return "hidden"; + } + else if (botConnected == false) + { + return "Waiting for bot"; + } + return "Active"; + } + + protected void BotLoggedIn(object o, SimConnectedEventArgs e) + { + GetClient().Network.SimConnected -= BotLoggedIn; + botConnected = true; + LogFormater.Info("OnEvent Service [Active]"); + AttachEvents(); + } + + public override void Start() + { + if (myConfig.GetEnabled() == false) + { + LogFormater.Info("OnEvent Service [- Not requested -]"); + return; + } + Stop(); + running = true; + master.BotClientNoticeEvent += BotClientRestart; + LogFormater.Info("OnEvent Service [Starting]"); + } + + public override void Stop() + { + if (running == true) + { + LogFormater.Info("OnEvent Service [Stopping]"); + } + running = false; + if (myConfig.GetEnabled() == false) + { + return; + } + master.BotClientNoticeEvent -= BotClientRestart; + RemoveEvents(); + } + } + + public class CustomOnEvent + { + public string Source = ""; + public List MonitorFlags = new List(); + public List WhereChecksLeft = new List(); + public List WhereChecksCenter = new List(); + public List WhereChecksRight = new List(); + public List Actions = new List(); + } +}