diff --git a/LibreMetaverse.StructuredData/JSON/OSDJson.cs b/LibreMetaverse.StructuredData/JSON/OSDJson.cs
index d3ec21a..0ddef3a 100644
--- a/LibreMetaverse.StructuredData/JSON/OSDJson.cs
+++ b/LibreMetaverse.StructuredData/JSON/OSDJson.cs
@@ -20,7 +20,16 @@ namespace OpenMetaverse.StructuredData
public static OSD DeserializeJson(string json)
{
- return DeserializeJson(JsonMapper.ToObject(json));
+ try
+ {
+ JsonData A = JsonMapper.ToObject(json);
+ return DeserializeJson(A);
+ }
+ catch
+ {
+ return null; // something fucky happened to the input
+ }
+
}
public static OSD DeserializeJson(JsonData json)
diff --git a/LibreMetaverse/AppearanceManager.cs b/LibreMetaverse/AppearanceManager.cs
index c660af5..54c950d 100644
--- a/LibreMetaverse/AppearanceManager.cs
+++ b/LibreMetaverse/AppearanceManager.cs
@@ -2091,30 +2091,37 @@ namespace OpenMetaverse
// TODO: create Current Outfit Folder
}
- OSDMap request = new OSDMap(1) { ["cof_version"] = currentoutfitfolder.Version };
-
- string msg = "Server side baking failed";
-
- OSD res = null;
-
- await Client.HttpCapsClient.PostRequestAsync(cap, OSDFormat.Xml, request, cancellationToken,
- (response, data, error) => res = OSDParser.Deserialize(data));
-
- if (res is OSDMap result)
+ try
{
- if (result["success"])
- {
- Logger.Log("Successfully set appearance", Helpers.LogLevel.Info, Client);
- // TODO: Set local visual params and baked textures based on the result here
- return true;
- }
- if (result.ContainsKey("error"))
- {
- msg += ": " + result["error"].AsString();
- }
- }
+ OSDMap request = new OSDMap(1) { ["cof_version"] = currentoutfitfolder.Version };
- Logger.Log(msg, Helpers.LogLevel.Error, Client);
+ string msg = "Server side baking failed";
+
+ OSD res = null;
+
+ await Client.HttpCapsClient.PostRequestAsync(cap, OSDFormat.Xml, request, cancellationToken,
+ (response, data, error) => res = OSDParser.Deserialize(data));
+
+ if (res is OSDMap result)
+ {
+ if (result["success"])
+ {
+ Logger.Log("Successfully set appearance", Helpers.LogLevel.Info, Client);
+ // TODO: Set local visual params and baked textures based on the result here
+ return true;
+ }
+ if (result.ContainsKey("error"))
+ {
+ msg += ": " + result["error"].AsString();
+ }
+ }
+
+ Logger.Log(msg, Helpers.LogLevel.Error, Client);
+ }
+ catch (Exception e)
+ {
+ Logger.Log("UpdateAvatarAppearanceAsync has failed to unpack data: "+e.Message, Helpers.LogLevel.Error, Client);
+ }
return false;
}
diff --git a/LibreMetaverse/GroupManager.cs b/LibreMetaverse/GroupManager.cs
index ad72bfb..ea38895 100644
--- a/LibreMetaverse/GroupManager.cs
+++ b/LibreMetaverse/GroupManager.cs
@@ -1056,7 +1056,12 @@ namespace OpenMetaverse
if (error != null) { return; }
OSD result = OSDParser.Deserialize(data);
- GroupMembersHandlerCaps(requestID, result);
+ if (result != null)
+ {
+ GroupMembersHandlerCaps(requestID, result);
+ return;
+ }
+ Logger.Log("Failed to json decode data from uri "+cap.AbsoluteUri+": ", Helpers.LogLevel.Error, Client);
});
}
else
diff --git a/LibreMetaverse/InventoryManager.cs b/LibreMetaverse/InventoryManager.cs
index 5bc8b49..65c116a 100644
--- a/LibreMetaverse/InventoryManager.cs
+++ b/LibreMetaverse/InventoryManager.cs
@@ -607,14 +607,17 @@ namespace OpenMetaverse
try
{
OSD result = OSDParser.Deserialize(data);
- OSDMap res = (OSDMap)result;
- OSDArray itemsOSD = (OSDArray)res["items"];
-
- foreach (var it in itemsOSD)
+ if (result != null)
{
- InventoryItem item = InventoryItem.FromOSD(it);
- _Store[item.UUID] = item;
- OnItemReceived(new ItemReceivedEventArgs(item));
+ OSDMap res = (OSDMap)result;
+ OSDArray itemsOSD = (OSDArray)res["items"];
+
+ foreach (var it in itemsOSD)
+ {
+ InventoryItem item = InventoryItem.FromOSD(it);
+ _Store[item.UUID] = item;
+ OnItemReceived(new ItemReceivedEventArgs(item));
+ }
}
}
catch (Exception ex)
diff --git a/LibreMetaverse/Settings.cs b/LibreMetaverse/Settings.cs
index 2bcdc62..1732227 100644
--- a/LibreMetaverse/Settings.cs
+++ b/LibreMetaverse/Settings.cs
@@ -106,27 +106,27 @@ namespace OpenMetaverse
/// Number of milliseconds before a CAPS call will time out
/// Setting this too low will cause web requests time out and
/// possibly retry repeatedly
- public int CAPS_TIMEOUT = 60 * 1000;
+ public int CAPS_TIMEOUT = 63 * 1000;
/// Number of milliseconds for xml-rpc to timeout
- public int LOGIN_TIMEOUT = 60 * 1000;
+ public int LOGIN_TIMEOUT = 63 * 1000;
/// Milliseconds before a packet is assumed lost and resent
public int RESEND_TIMEOUT = 4000;
/// Milliseconds without receiving a packet before the
/// connection to a simulator is assumed lost
- public int SIMULATOR_TIMEOUT = 60 * 1000;
+ public int SIMULATOR_TIMEOUT = 75 * 1000;
/// Milliseconds to wait for a simulator info request through
/// the grid interface
public int MAP_REQUEST_TIMEOUT = 5 * 1000;
/// Number of milliseconds between sending pings to each sim
- public const int PING_INTERVAL = 2200;
+ public const int PING_INTERVAL = 3000;
/// Number of milliseconds between sending camera updates
- public const int DEFAULT_AGENT_UPDATE_INTERVAL = 500;
+ public const int DEFAULT_AGENT_UPDATE_INTERVAL = 1000;
/// Number of milliseconds between updating the current
/// positions of moving, non-accelerating and non-colliding objects
@@ -134,7 +134,7 @@ namespace OpenMetaverse
/// Millisecond interval between ticks, where all ACKs are
/// sent out and the age of unACKed packets is checked
- public const int NETWORK_TICK_INTERVAL = 500;
+ public const int NETWORK_TICK_INTERVAL = 650;
#endregion
#region Sizes
diff --git a/SecondBotEvents/Commands/Self.cs b/SecondBotEvents/Commands/Self.cs
index b8e6841..baac4e9 100644
--- a/SecondBotEvents/Commands/Self.cs
+++ b/SecondBotEvents/Commands/Self.cs
@@ -145,6 +145,7 @@ namespace SecondBotEvents.Commands
[ReturnHints("ok")]
public object Logoff()
{
+ master.BotClient.flagLogoutExpected();
GetClient().Network.BeginLogout();
return BasicReply("ok");
}
diff --git a/SecondBotEvents/Config/OnEventConfig.cs b/SecondBotEvents/Config/OnEventConfig.cs
new file mode 100644
index 0000000..775a6dd
--- /dev/null
+++ b/SecondBotEvents/Config/OnEventConfig.cs
@@ -0,0 +1,101 @@
+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");
+ settings.Add("HideStatusOutput");
+ }
+
+ 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/Config/RelayConfig.cs b/SecondBotEvents/Config/RelayConfig.cs
index 7a6d390..0202f03 100644
--- a/SecondBotEvents/Config/RelayConfig.cs
+++ b/SecondBotEvents/Config/RelayConfig.cs
@@ -13,16 +13,6 @@ namespace SecondBotEvents.Config
filename = "relay";
settings.Add("count");
settings.Add("HideStatusOutput");
- int loop = 1;
- while(loop <= GetRelayCount())
- {
- settings.Add(loop.ToString() + "_enabled");
- settings.Add(loop.ToString() + "_source_type");
- settings.Add(loop.ToString() + "_source_option");
- settings.Add(loop.ToString() + "_target_type");
- settings.Add(loop.ToString() + "_target_option");
- loop++;
- }
}
public int GetRelayCount()
diff --git a/SecondBotEvents/Config/config.cs b/SecondBotEvents/Config/config.cs
index 4825d33..119e502 100644
--- a/SecondBotEvents/Config/config.cs
+++ b/SecondBotEvents/Config/config.cs
@@ -79,22 +79,26 @@ namespace SecondBotEvents.Config
MakeSettingsFile(fromFolder);
return;
}
- JObject result = JObject.Parse(IO.ReadFile(readfile));
- foreach (string setting in settings)
+ try
{
- if(result.ContainsKey(setting) == false)
+ JObject result = JObject.Parse(IO.ReadFile(readfile));
+ foreach (var x in result)
{
- continue;
+ settings.Add(x.Key);
+ string value = x.Value.ToString();
+ if (SecondbotHelpers.notempty(value) == false)
+ {
+ mysettings.Add(x.Key, "");
+ continue;
+ }
+ mysettings.Add(x.Key, value);
}
- string value = result[setting].ToString();
- if (SecondbotHelpers.notempty(value) == false)
- {
- mysettings.Add(setting, "");
- continue;
- }
- mysettings.Add(setting, value);
+ loaded = true;
+ }
+ catch (Exception ex)
+ {
+ LogFormater.Warn("Json format for " + readfile + " is broken: " + ex.Message);
}
- loaded = true;
}
protected void LoadSettingsFromEnv()
@@ -152,7 +156,8 @@ namespace SecondBotEvents.Config
protected int ReadSettingAsInt(string key, int defaultValue = 30)
{
- bool result = int.TryParse(ReadSettingAsString(key, defaultValue.ToString()), out int value);
+ string read = ReadSettingAsString(key, defaultValue.ToString());
+ bool result = int.TryParse(read, out int value);
if (result == false)
{
return defaultValue;
diff --git a/SecondBotEvents/EventsSecondBot.cs b/SecondBotEvents/EventsSecondBot.cs
index ca60329..9743909 100644
--- a/SecondBotEvents/EventsSecondBot.cs
+++ b/SecondBotEvents/EventsSecondBot.cs
@@ -42,6 +42,8 @@ namespace SecondBotEvents
public EventsService EventsService { get { return (EventsService)GetService("EventsService"); } }
public DialogService DialogService { get { return (DialogService)GetService("DialogService"); } }
+ public TriggerOnEventService TriggerOnEventService { get { return (TriggerOnEventService)GetService("TriggerOnEventService"); } }
+
public CurrentOutfitFolder CurrentOutfitFolder { get { return (CurrentOutfitFolder)GetService("CurrentOutfitFolder"); } }
public RLVService RLV { get { return (RLVService)GetService("RLVService"); } }
@@ -161,6 +163,7 @@ namespace SecondBotEvents
RegisterService("EventsService");
RegisterService("DialogService");
RegisterService("CustomCommandsService");
+ RegisterService("TriggerOnEventService");
if (BotClient.IsLoaded() == false)
{
LogFormater.Info("Config is not loaded :(");
diff --git a/SecondBotEvents/Services/BotClientService.cs b/SecondBotEvents/Services/BotClientService.cs
index 46da80a..c3809ce 100644
--- a/SecondBotEvents/Services/BotClientService.cs
+++ b/SecondBotEvents/Services/BotClientService.cs
@@ -15,6 +15,12 @@ namespace SecondBotEvents.Services
public GridClient client = null;
public BasicConfig basicCfg = null;
+ protected bool LogoutExpected = false;
+ public void flagLogoutExpected()
+ {
+ LogoutExpected = true;
+ }
+
protected bool ExitBot = false;
protected Timer AutoRestartLoginTimer;
@@ -78,6 +84,15 @@ namespace SecondBotEvents.Services
protected void BotDisconnected(object o, DisconnectedEventArgs e)
{
LogFormater.Info("Client service ~ Network disconnected: " + e.Message);
+ if (LogoutExpected == false)
+ {
+ AutoRestartLoginTimer = new Timer();
+ AutoRestartLoginTimer.Interval = 75 * 1000;
+ AutoRestartLoginTimer.AutoReset = false;
+ AutoRestartLoginTimer.Elapsed += RestartTimer;
+ LogFormater.Info("Client service ~ Unexpected disconnect (starting reconnect timer for 75s)");
+ return;
+ }
}
protected void BotLoginStatus(object o, LoginProgressEventArgs e)
@@ -163,7 +178,10 @@ namespace SecondBotEvents.Services
bool isRestart = false;
if (client != null)
{
- client.Network.Logout();
+ if (client.Network != null)
+ {
+ client.Network.Logout();
+ }
isRestart = true;
}
client = null;
diff --git a/SecondBotEvents/Services/CommandsService.cs b/SecondBotEvents/Services/CommandsService.cs
index 936cfce..d429f1c 100644
--- a/SecondBotEvents/Services/CommandsService.cs
+++ b/SecondBotEvents/Services/CommandsService.cs
@@ -153,6 +153,7 @@ namespace SecondBotEvents.Services
{
break;
}
+ master.DataStoreService.AddAvatar(e.IM.FromAgentID, e.IM.FromAgentName);
requireSigning = false;
acceptMessage = myConfig.GetMastersCSV().Contains(e.IM.FromAgentName);
break;
@@ -415,11 +416,9 @@ namespace SecondBotEvents.Services
if (requireSigning == false)
{
accepted = true; // just accept the command
- master.CommandNotice(command, source, String.Join("@@@", args), accepted);
return;
}
Vaildate(requireTimewindow, windowSize, secret);
- master.CommandNotice(command, source, String.Join("@@@", args), accepted);
}
public SignedCommand(CommandsService setMaster, string source, string setCommand, string setSigningCode, string[] setArgs,
diff --git a/SecondBotEvents/Services/DataStoreService.cs b/SecondBotEvents/Services/DataStoreService.cs
index 62bc1be..79cf00c 100644
--- a/SecondBotEvents/Services/DataStoreService.cs
+++ b/SecondBotEvents/Services/DataStoreService.cs
@@ -976,7 +976,7 @@ namespace SecondBotEvents.Services
GetClient().Groups.RequestCurrentGroups();
}
- protected void AddAvatar(UUID id, string name)
+ public void AddAvatar(UUID id, string name)
{
lock (avatarsName2Key) lock (avatarLastUsed)
{
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..21833b2 100644
--- a/SecondBotEvents/Services/InteractionService.cs
+++ b/SecondBotEvents/Services/InteractionService.cs
@@ -196,6 +196,7 @@ namespace SecondBotEvents.Services
};
if(e.FromTask == false)
{
+ master.DataStoreService.AddAvatar(e.Offer.FromAgentID, e.Offer.FromAgentName);
details.Add("itemuuid", e.ObjectID.ToString());
details.Add("itemname", "?");
InventoryItem itm = GetClient().Inventory.FetchItem(e.ObjectID, GetClient().Self.AgentID, 2000);
@@ -213,6 +214,7 @@ namespace SecondBotEvents.Services
{
return;
}
+ master.DataStoreService.AddAvatar(e.AgentID, e.AgentName);
GetClient().Friends.AcceptFriendship(e.AgentID, e.SessionID);
JsonOuputCleaner(e.AgentName, "FriendRequest");
@@ -278,7 +280,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..cf27a9e
--- /dev/null
+++ b/SecondBotEvents/Services/TriggerOnEventService.cs
@@ -0,0 +1,827 @@
+using OpenMetaverse;
+using OpenMetaverse.Rendering;
+using SecondBotEvents.Config;
+using Swan.Logging;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection.Metadata.Ecma335;
+
+namespace SecondBotEvents.Services
+{
+ public class TriggerOnEventService : BotServices
+ {
+ protected OnEventConfig myConfig;
+ protected bool botConnected = false;
+ Dictionary> MyCustomEvents = new Dictionary>();
+ Dictionary lockouts = new Dictionary();
+ List trackEventGroups = new List();
+ 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}" };
+ if(myConfig.GetCount() == 0)
+ {
+ myConfig.setEnabled(false);
+ LogFormater.Info("OnEvent Service - No events enabled stopping");
+ }
+ while (loop <= myConfig.GetCount())
+ {
+ if(myConfig.GetEventEnabled(loop) == false)
+ {
+ LogFormater.Info("OnEvent Service - Event "+loop.ToString()+" is not enabled");
+ 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)
+ {
+ LogFormater.Info("OnEvent Service - Event " + loop.ToString() + " failed where config checks");
+ loop++;
+ continue;
+ }
+ Event.Source = myConfig.GetSource(loop);
+ Event.MonitorFlags = myConfig.GetSourceMonitor(loop).Split("=#=").ToList();
+ if ((Event.Source == "GroupMemberJoin") || (Event.Source == "GroupMemberLeave"))
+ {
+ UUID groupUUID = UUID.Zero;
+ if (Event.MonitorFlags.Count != 1)
+ {
+ LogFormater.Info("OnEvent Service - Event " + loop.ToString() + " GroupMember monitor flags not vaild");
+ loop++;
+ continue;
+ }
+ if (UUID.TryParse(Event.MonitorFlags[0], out groupUUID) == false)
+ {
+ LogFormater.Info("OnEvent Service - Event " + loop.ToString() + " GroupMember monitor flags not vaild");
+ loop++;
+ continue;
+ }
+ if (trackEventGroups.Contains(groupUUID) == false)
+ {
+ trackEventGroups.Add(groupUUID);
+ }
+ }
+ else if ((Event.Source == "GuestJoins") || (Event.Source == "GuestLeaves"))
+ {
+ if(Event.MonitorFlags.Count != 2)
+ {
+ LogFormater.Info("OnEvent Service - Event " + loop.ToString() + " Guest monitor flags not vaild");
+ loop++;
+ continue;
+ }
+ }
+ else if (Event.Source == "AvatarIm")
+ {
+ if (Event.MonitorFlags.Count != 1)
+ {
+ LogFormater.Info("OnEvent Service - Event " + loop.ToString() + " AvatarIm monitor flags not vaild");
+ loop++;
+ continue;
+ }
+ UUID testUUID = UUID.Zero;
+ UUID.TryParse(Event.MonitorFlags[0],out testUUID);
+ if ((Event.MonitorFlags[0] != "Any") && (testUUID != UUID.Zero))
+ {
+ LogFormater.Info("OnEvent Service - Event " + loop.ToString() + " AvatarIm monitor flags not vaild");
+ loop++;
+ continue;
+ }
+ }
+ loop2 = 1;
+ while (loop2 <= myConfig.GetActionCount(loop))
+ {
+ Event.Actions.Add(myConfig.GetActionStep(loop, loop2));
+ loop2++;
+ }
+ if(Event.Actions.Count == 0)
+ {
+ LogFormater.Info("OnEvent Service - Event " + loop.ToString() + " Actions are not setup");
+ loop++;
+ continue;
+ }
+ 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
+ if (MyCustomEvents.Count == 0)
+ {
+ myConfig.setEnabled(false);
+ LogFormater.Info("OnEvent Service - failed to load any events stopping");
+ }
+ }
+
+
+ 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;
+ }
+ master.DataStoreService.AddAvatar(e.IM.FromAgentID, e.IM.FromAgentName);
+ // 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;
+ GetClient().Groups.GroupMembersReply -= BotGroupMembers;
+ GetClient().Groups.CurrentGroups -= BotGroupsCurrent;
+ GetClient().Network.SimChanged -= BotChangedSim;
+ GetClient().Self.MoneyBalance -= BotFundsEvent;
+ GetClient().Self.AlertMessage -= BotAlertMessage;
+ }
+ }
+ }
+
+ protected void AttachEvents()
+ {
+ GetClient().Self.IM += BotImMessage;
+ GetClient().Groups.GroupMembersReply += BotGroupMembers;
+ GetClient().Groups.CurrentGroups += BotGroupsCurrent;
+ GetClient().Network.SimChanged += BotChangedSim;
+ GetClient().Self.MoneyBalance += BotFundsEvent;
+ GetClient().Self.AlertMessage += BotAlertMessage;
+ GetClient().Groups.RequestCurrentGroups();
+ GetClient().Self.RequestBalance();
+ }
+
+ protected void BotAlertMessage(object o, AlertMessageEventArgs e)
+ {
+ TriggerEvent("SimAlert", new Dictionary() {
+ { "alertmessage", e.Message},
+ });
+ }
+
+ protected void BotFundsEvent(object o, BalanceEventArgs e)
+ {
+ TriggerEvent("BalanceUpdate", new Dictionary() {
+ { "funds", e.Balance.ToString()},
+ });
+ }
+
+ protected void BotGroupsCurrent(object o, CurrentGroupsEventArgs e)
+ {
+ lock (GroupMembership) lock (GroupMembershipUpdated) lock(GroupNames)
+ {
+ foreach(Group G in e.Groups.Values)
+ {
+ if (trackEventGroups.Contains(G.ID) == false)
+ {
+ return;
+ }
+ if (GroupNames.ContainsKey(G.ID) == true)
+ {
+ continue;
+ }
+ GroupNames.Add(G.ID, G.Name);
+ GroupMembership.Add(G.ID, new List());
+ GroupMembershipUpdated.Add(G.ID, 0);
+ }
+ }
+ }
+
+ protected Dictionary> GroupMembership = new Dictionary>();
+ protected Dictionary GroupNames = new Dictionary();
+ protected Dictionary GroupMembershipUpdated = new Dictionary();
+ protected long lastGroupMembershipUpdate = 0;
+ // request group membership updates max of once per 60 secs
+ // request membership update for group once every 15 mins
+ protected void GroupMembershipUpkeep()
+ {
+ lock (GroupMembershipUpdated)
+ {
+ long dif = SecondbotHelpers.UnixTimeNow() - lastGroupMembershipUpdate;
+ if (dif < 60)
+ {
+ return;
+ }
+ UUID updateGroup = UUID.Zero;
+ foreach (KeyValuePair entry in GroupMembershipUpdated)
+ {
+ dif = SecondbotHelpers.UnixTimeNow() - entry.Value;
+ if (dif > (15 * 60))
+ {
+ updateGroup = entry.Key;
+ break;
+ }
+ }
+ if (updateGroup != UUID.Zero)
+ {
+ lastGroupMembershipUpdate = SecondbotHelpers.UnixTimeNow();
+ GroupMembershipUpdated[updateGroup] = SecondbotHelpers.UnixTimeNow();
+ GetClient().Groups.RequestGroupMembers(updateGroup);
+ }
+ }
+ }
+
+ protected void BotGroupMembers(object sender, GroupMembersReplyEventArgs e)
+ {
+ if(trackEventGroups.Contains(e.GroupID) == false)
+ {
+ return;
+ }
+ lock (GroupMembership) lock(GroupMembershipUpdated)
+ {
+ bool GroupLoaded = false;
+ if (GroupMembership.ContainsKey(e.GroupID) == true)
+ {
+ if (GroupMembership[e.GroupID].Count > 0)
+ {
+ GroupLoaded = true;
+ }
+ }
+ if (GroupLoaded == false)
+ {
+ foreach (GroupMember a in e.Members.Values)
+ {
+ GroupMembership[e.GroupID].Add(a.ID);
+ }
+ return;
+ }
+ List joined = new List();
+ List tracked = new List();
+ foreach (UUID a in GroupMembership[e.GroupID])
+ {
+ tracked.Add(a);
+ }
+ master.DataStoreService.GetAvatarNames(tracked);
+ foreach (UUID a in e.Members.Keys)
+ {
+ if (tracked.Contains(a) == true)
+ {
+ tracked.Remove(a);
+ continue;
+ }
+ joined.Add(a);
+
+ }
+ string groupname = "none";
+ if(GroupNames.ContainsKey(e.GroupID) == true)
+ {
+ groupname = GroupNames[e.GroupID];
+ }
+ foreach (UUID a in tracked)
+ {
+ // leave
+ GroupMembership[e.GroupID].Remove(a);
+ TriggerEvent("GroupMemberLeave", new Dictionary() {
+ { "groupuuid", e.GroupID.ToString()},
+ { "groupname", groupname },
+ { "avataruuid", a.ToString() }
+ });
+ }
+ foreach (UUID a in joined)
+ {
+ // join
+ GroupMembership[e.GroupID].Add(a);
+ TriggerEvent("GroupMemberJoin", new Dictionary() {
+ { "groupuuid", e.GroupID.ToString()},
+ { "groupname", groupname },
+ { "avataruuid", a.ToString() }
+ });
+ }
+ }
+ }
+
+ 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 BotChangedSim(object sender, SimChangedEventArgs e)
+ {
+ TriggerEvent("ChangeSim", new Dictionary()
+ {
+ { "oldsimname", e.PreviousSimulator.Name },
+ { "newsimname", GetClient().Network.CurrentSim.Name },
+ });
+ }
+
+ protected string inArgsOrDefault(string arg, Dictionary args, string defaultvalue)
+ {
+ if (args.ContainsKey(arg) == true)
+ {
+ return args[arg];
+ }
+ return defaultvalue;
+ }
+ protected void TriggerEvent(string eventName, Dictionary args)
+ {
+ if (MyCustomEvents.ContainsKey(eventName) == false)
+ {
+ return;
+ }
+ string avataruuid = inArgsOrDefault("avataruuid", args, null);
+
+ List AvPos = GetAvPos(avataruuid);
+ Vector3 A = GetClient().Self.SimPosition;
+ List BotPos = new List() { (int)Math.Round(A.X), (int)Math.Round(A.Y), (int)Math.Round(A.Z) };
+ Dictionary values = new Dictionary
+ {
+ { "alertmessage", inArgsOrDefault("alertmessage", args, "none") },
+ { "message", inArgsOrDefault("message", args, "none") },
+ { "funds", inArgsOrDefault("funds", args, "-1") },
+ { "eventype", eventName },
+ { "groupuuid", inArgsOrDefault("groupuuid", args, "none") },
+ { "groupname", inArgsOrDefault("groupname", args, "none") },
+ { "oldsimname", inArgsOrDefault("oldsimname", args, "none") },
+ { "newsimname", inArgsOrDefault("newsimname", args, "none") },
+ { "avataruuid", avataruuid },
+ { "avatarname", GetAvName(avataruuid) },
+ { "avatarparcel", GetAvParcel(avataruuid) },
+ { "botsim", GetClient().Network.CurrentSim.Name },
+ { "botparcel", GetBotParcel() },
+ { "avatarx", AvPos[0].ToString() },
+ { "avatary", AvPos[1].ToString() },
+ { "avatarz", AvPos[2].ToString() },
+ { "avatardistance", AvPos[3].ToString() },
+ { "botx", AvPos[0].ToString() },
+ { "boty", AvPos[1].ToString() },
+ { "botz", AvPos[2].ToString() },
+ { "clockhour", DateTime.Now.ToString("HH") },
+ { "clockmin", DateTime.Now.ToString("mm") },
+ { "dayofweek", ((int)DateTime.Now.DayOfWeek).ToString() }
+ };
+
+ foreach (CustomOnEvent E in MyCustomEvents[eventName])
+ {
+ bool canFireEvent = true;
+ if ((E.Source == "GroupMemberJoin") || (E.Source == "GroupMemberLeave"))
+ {
+ if (E.MonitorFlags[0] != values["groupuuid"])
+ {
+ continue;
+ }
+ }
+ else if ((E.Source == "GuestJoins") || (E.Source == "GuestLeaves"))
+ {
+ if (
+ (E.MonitorFlags[0] != values["botsim"]) ||
+ (E.MonitorFlags[1] != values["botparcel"]) ||
+ (E.MonitorFlags[1] != values["avatarparcel"])
+ )
+ {
+ continue;
+ }
+ }
+ 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 = true;
+ bool leftIsDivs = true;
+ if (leftAsInt != 0)
+ {
+ leftIsEven = ((leftAsInt % 2) == 0);
+ if(rightAsInt != 0)
+ {
+ 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);
+ master.DataStoreService.AddAvatar(A.ID, A.Name);
+ 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))
+ {
+ List seen = new List();
+ if (tracked.Count > 0)
+ {
+
+ foreach (UUID a in tracked)
+ {
+ if(seen.Contains(a) == true)
+ {
+ continue;
+ }
+ seen.Add(a);
+ TriggerEvent("GuestLeaves", new Dictionary() { { "avataruuid", a.ToString() } });
+ }
+ }
+ if (joined.Count > 0)
+ {
+ foreach (UUID a in joined)
+ {
+ if (seen.Contains(a) == true)
+ {
+ continue;
+ }
+ seen.Add(a);
+ 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();
+ GroupMembershipUpkeep();
+ }
+
+ 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();
+ }
+}