511 lines
20 KiB
C#
511 lines
20 KiB
C#
using System.Text.Json;
|
|
using OpenMetaverse;
|
|
using SecondBotEvents.Config;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace SecondBotEvents.Services
|
|
{
|
|
public class InteractionService : BotServices
|
|
{
|
|
protected new InteractionConfig myConfig;
|
|
protected bool botConnected = false;
|
|
private readonly Dictionary<UUID, (string SenderName, UUID SenderID)> _pendingOffers = [];
|
|
|
|
public UUID GetOrCreatePendingFolder()
|
|
{
|
|
var client = GetClient();
|
|
if (client == null || client.Inventory == null || client.Inventory.Store == null || client.Inventory.Store.RootFolder == null)
|
|
{
|
|
return UUID.Zero;
|
|
}
|
|
var store = client.Inventory.Store;
|
|
var rootContent = store.GetContents(store.RootFolder.UUID);
|
|
foreach (var baseItem in rootContent)
|
|
{
|
|
if (baseItem is InventoryFolder folder && folder.Name.Equals("#Pending Approval", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return folder.UUID;
|
|
}
|
|
}
|
|
return client.Inventory.CreateFolder(store.RootFolder.UUID, "#Pending Approval");
|
|
}
|
|
|
|
public InteractionService(EventsSecondBot setMaster) : base(setMaster)
|
|
{
|
|
myConfig = new InteractionConfig(master.fromEnv,master.fromFolder);
|
|
if (myConfig.GetEnabled() == false)
|
|
{
|
|
LogFormater.Info(Status());
|
|
return;
|
|
}
|
|
}
|
|
|
|
public override string Status()
|
|
{
|
|
if (myConfig == null)
|
|
{
|
|
return "No Config";
|
|
}
|
|
else if (myConfig.GetHideStatusOutput() == true)
|
|
{
|
|
return "hidden";
|
|
}
|
|
else if (myConfig.GetEnabled() == false)
|
|
{
|
|
return "- Not requested -";
|
|
}
|
|
else if (botConnected == false)
|
|
{
|
|
return "Waiting for bot";
|
|
}
|
|
return "Active";
|
|
}
|
|
|
|
protected bool ProcessRequest(bool enabledByConfig, string ConfigAcceptLevel, string avatarName, string sourceName, UUID avataruuid, Dictionary<string,string> misc = null)
|
|
{
|
|
if (misc == null)
|
|
{
|
|
misc = [];
|
|
}
|
|
if (myConfig.GetEnabled() == false)
|
|
{
|
|
JsonOuput(false, sourceName, avatarName, "Interactions hooks disabled");
|
|
return false;
|
|
}
|
|
if(enabledByConfig == false)
|
|
{
|
|
JsonOuput(false, sourceName, avatarName, "disabled by config");
|
|
return false;
|
|
}
|
|
if (master.CommandsService.myConfig.GetEnableMasterControls() == false)
|
|
{
|
|
// master avatars list is not enabled. unable to continue
|
|
JsonOuput(false, sourceName, avatarName, "masters AV list is disabled: Please set EnableMasterControls in Commands service to true");
|
|
return false;
|
|
}
|
|
bool accepted = false;
|
|
string ObjectOwnerName = master.DataStoreService.GetAvatarName(avataruuid);
|
|
string whyAccepted = "rejected request";
|
|
misc.Add("ObjectOwner", ObjectOwnerName);
|
|
foreach (string masterName in master.CommandsService.myConfig.GetMastersCSV())
|
|
{
|
|
if (SecondbotHelpers.IsNameMatch(masterName, avatarName))
|
|
{
|
|
whyAccepted = "on owners list";
|
|
accepted = true;
|
|
break;
|
|
}
|
|
if (SecondbotHelpers.IsNameMatch(masterName, ObjectOwnerName))
|
|
{
|
|
whyAccepted = "Object Owner is on master list";
|
|
accepted = true;
|
|
break;
|
|
}
|
|
}
|
|
if (ConfigAcceptLevel == "Anyone")
|
|
{
|
|
whyAccepted = "Accepting from anyone";
|
|
accepted = true;
|
|
}
|
|
else if((ConfigAcceptLevel == "Friends") && (sourceName != "FriendRequest"))
|
|
{
|
|
whyAccepted = "on friends list";
|
|
accepted = GetClient().Friends.FriendList.ContainsKey(avataruuid);
|
|
}
|
|
if ((accepted == false) && (master.CommandsService.myConfig.GetCheckDotNames() == true))
|
|
{
|
|
string[] bits = avatarName.ToLower().Split('.');
|
|
if (bits.Length == 2)
|
|
{
|
|
string dotName = bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper();
|
|
foreach (string masterName in master.CommandsService.myConfig.GetMastersCSV())
|
|
{
|
|
if (SecondbotHelpers.IsNameMatch(masterName, dotName))
|
|
{
|
|
accepted = true;
|
|
whyAccepted = "on owners list [dot check]";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (accepted == false)
|
|
{
|
|
bits = ObjectOwnerName.ToLower().Split('.');
|
|
if (bits.Length == 2)
|
|
{
|
|
string dotName = bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper();
|
|
foreach (string masterName in master.CommandsService.myConfig.GetMastersCSV())
|
|
{
|
|
if (SecondbotHelpers.IsNameMatch(masterName, dotName))
|
|
{
|
|
accepted = true;
|
|
whyAccepted = "Object Owner is on master list [dot check]";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (accepted == false)
|
|
{
|
|
// check the one time accept list
|
|
accepted = master.DataStoreService.GetNextAccept(sourceName, avataruuid);
|
|
if(accepted == true)
|
|
{
|
|
whyAccepted = "was on NextAccept list";
|
|
}
|
|
}
|
|
JsonOuput(accepted, sourceName, avatarName, whyAccepted, misc);
|
|
return accepted;
|
|
}
|
|
|
|
|
|
|
|
protected void BotClientRestart(object o, BotClientNotice e)
|
|
{
|
|
if (e.isStart == false)
|
|
{
|
|
return;
|
|
}
|
|
botConnected = false;
|
|
LogFormater.Info("Interaction Service [Attached to new client]");
|
|
GetClient().Network.LoggedOut += BotLoggedOut;
|
|
GetClient().Network.SimConnected += BotLoggedIn;
|
|
}
|
|
|
|
protected void JsonOuput(bool status, string eventype, string from, string why="", Dictionary<string, string> setMisc = null)
|
|
{
|
|
if(myConfig.GetEnableDebug() == true)
|
|
{
|
|
LogFormater.Warn("Interaction debug:" + JsonSerializer.Serialize(new InteractionEvent(from, eventype, status, why, setMisc), JsonOptions.UnsafeRelaxed));
|
|
}
|
|
if (myConfig.GetEnableJsonOutputEvents() == false)
|
|
{
|
|
return;
|
|
}
|
|
master.CommandsService.SmartCommandReply(
|
|
myConfig.GetJsonOutputEventsTarget(),
|
|
JsonSerializer.Serialize(new InteractionEvent(from, eventype, status, why, setMisc), JsonOptions.UnsafeRelaxed),
|
|
"interactions"
|
|
);
|
|
}
|
|
|
|
protected void JsonOuputCleaner(string misc, string source)
|
|
{
|
|
Dictionary<string, string> reply = new()
|
|
{
|
|
{ "value", misc }
|
|
};
|
|
JsonOuputCleaner(reply, source);
|
|
}
|
|
|
|
protected void JsonOuputCleaner(Dictionary<string, string> misc, string source)
|
|
{
|
|
if (myConfig.GetEnableDebug() == true)
|
|
{
|
|
LogFormater.Warn("Interaction debug:" + JsonSerializer.Serialize(misc, JsonOptions.UnsafeRelaxed));
|
|
}
|
|
if (myConfig.GetEnableJsonOutputEvents() == false)
|
|
{
|
|
return;
|
|
}
|
|
misc.Add("eventsource", source);
|
|
master.CommandsService.SmartCommandReply(
|
|
myConfig.GetJsonOutputEventsTarget(),
|
|
JsonSerializer.Serialize(misc, JsonOptions.UnsafeRelaxed),
|
|
"interactions"
|
|
);
|
|
}
|
|
|
|
|
|
|
|
protected void BotTeleportOffer(object o, InstantMessageEventArgs e)
|
|
{
|
|
if((e.IM.Dialog != InstantMessageDialog.RequestTeleport) && (e.IM.Dialog != InstantMessageDialog.RequestLure))
|
|
{
|
|
return;
|
|
}
|
|
string mode = "Teleport Bot to Av";
|
|
bool markTeleport = true;
|
|
if (e.IM.Dialog == InstantMessageDialog.RequestLure)
|
|
{
|
|
mode = "Teleport Av to Bot";
|
|
markTeleport = false;
|
|
}
|
|
Dictionary<string,string> misc = new()
|
|
{
|
|
{ "mode", mode }
|
|
};
|
|
if (ProcessRequest(myConfig.GetAcceptTeleports(),myConfig.GetTeleportRequestLevel(), e.IM.FromAgentName, "Teleport", e.IM.FromAgentID, misc) == false)
|
|
{
|
|
// rejected requests
|
|
if (e.IM.Dialog != InstantMessageDialog.RequestLure)
|
|
{
|
|
return;
|
|
}
|
|
GetClient().Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, false);
|
|
return;
|
|
}
|
|
if (markTeleport == true)
|
|
{
|
|
master.HomeboundService.MarkTeleport();
|
|
}
|
|
JsonOuputCleaner(e.IM.FromAgentName, mode);
|
|
if (e.IM.Dialog == InstantMessageDialog.RequestLure)
|
|
{
|
|
GetClient().Self.SendTeleportLure(e.IM.FromAgentID);
|
|
return;
|
|
}
|
|
GetClient().Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, true);
|
|
|
|
}
|
|
|
|
protected void BotGroupInviteOffer(object o, GroupInvitationEventArgs e)
|
|
{
|
|
Dictionary<string, string> reply = new()
|
|
{
|
|
{ "message", e.Message },
|
|
{ "fromuuid", e.AgentID.ToString() },
|
|
{ "groupuuid", e.GroupID.ToString() }
|
|
};
|
|
if (ProcessRequest(myConfig.GetAcceptGroupInvites(),myConfig.GetGroupInviteLevel(), e.FromName, "GroupInvite", e.AgentID, reply) == false)
|
|
{
|
|
// Not auto-accepted, record for dashboard
|
|
master.DataStoreService.AddPendingGroupInvite(e.GroupID, e.AgentID, e.FromName, e.Message);
|
|
return;
|
|
}
|
|
e.Accept = true;
|
|
JsonOuputCleaner(reply, "GroupInvite");
|
|
}
|
|
|
|
protected void BotInventoryAdd(object o, InventoryObjectAddedEventArgs e)
|
|
{
|
|
InventoryItem F = (InventoryItem)e.Obj;
|
|
UUID pendingFolder = GetOrCreatePendingFolder();
|
|
if (F.ParentUUID == pendingFolder)
|
|
{
|
|
(string SenderName, UUID SenderID) senderDetails = ("Unknown", UUID.Zero);
|
|
bool found = false;
|
|
lock (_pendingOffers)
|
|
{
|
|
if (_pendingOffers.TryGetValue(F.UUID, out senderDetails))
|
|
{
|
|
found = true;
|
|
_pendingOffers.Remove(F.UUID);
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
F.Description = $"Sent by: {senderDetails.SenderName} ({senderDetails.SenderID})";
|
|
GetClient().Inventory.RequestUpdateItem(F);
|
|
}
|
|
}
|
|
|
|
Dictionary<string, string> details = new()
|
|
{
|
|
{ "itemname", e.Obj.Name },
|
|
{ "itemuuid", e.Obj.UUID.ToString() },
|
|
{ "itemtype", F.AssetType.ToString() },
|
|
};
|
|
if (master.EventsService.isRunning() == true)
|
|
{
|
|
master.EventsService.InventoryUpdateEvent(details);
|
|
}
|
|
JsonOuputCleaner(details, "InventoryAdd");
|
|
}
|
|
|
|
protected void BotInventoryOffer(object o, InventoryObjectOfferedEventArgs e)
|
|
{
|
|
UUID ownerID = e.Offer.FromAgentID;
|
|
string ownerName = e.Offer.FromAgentName;
|
|
|
|
if (e.FromTask)
|
|
{
|
|
var sim = GetClient().Network.CurrentSim;
|
|
var prim = sim?.ObjectsPrimitives.Values.FirstOrDefault(p => p.ID == e.Offer.FromAgentID);
|
|
if (prim != null)
|
|
{
|
|
ownerID = prim.OwnerID;
|
|
}
|
|
else
|
|
{
|
|
System.Threading.AutoResetEvent propertiesEvent = new(false);
|
|
EventHandler<ObjectPropertiesFamilyEventArgs> handler = (sender, args) =>
|
|
{
|
|
if (args.Properties.ObjectID == e.Offer.FromAgentID)
|
|
{
|
|
ownerID = args.Properties.OwnerID;
|
|
propertiesEvent.Set();
|
|
}
|
|
};
|
|
GetClient().Objects.ObjectPropertiesFamily += handler;
|
|
GetClient().Objects.RequestObjectPropertiesFamily(GetClient().Network.CurrentSim, e.Offer.FromAgentID);
|
|
propertiesEvent.WaitOne(1000);
|
|
GetClient().Objects.ObjectPropertiesFamily -= handler;
|
|
}
|
|
ownerName = master.DataStoreService.GetAvatarName(ownerID);
|
|
}
|
|
|
|
Dictionary<string, string> details = new()
|
|
{
|
|
{ "transactionid", e.Offer.IMSessionID.ToString() },
|
|
{ "itemtype", e.AssetType.ToString() },
|
|
{ "message", e.Offer.Message.ToString() },
|
|
{ "fromscript", e.FromTask.ToString() },
|
|
{ "targetfolder", e.FolderID.ToString() },
|
|
{ "objectid", e.Offer.FromAgentID.ToString() },
|
|
{ "objectname", e.Offer.FromAgentName }
|
|
};
|
|
if (ProcessRequest(myConfig.GetAcceptInventory(),myConfig.GetInventoryTransferLevel(), ownerName, "InventoryOffer", ownerID, details) == false)
|
|
{
|
|
if (myConfig.GetAcceptInventory())
|
|
{
|
|
UUID pendingFolder = GetOrCreatePendingFolder();
|
|
e.FolderID = pendingFolder;
|
|
e.Accept = true;
|
|
lock (_pendingOffers)
|
|
{
|
|
_pendingOffers[e.ObjectID] = (ownerName, ownerID);
|
|
}
|
|
details.Add("pending", "true");
|
|
details.Add("itemuuid", e.ObjectID.ToString());
|
|
details.Add("itemname", "?");
|
|
JsonOuputCleaner(details, "InventoryOffer");
|
|
}
|
|
else
|
|
{
|
|
e.Accept = false;
|
|
}
|
|
return;
|
|
}
|
|
e.Accept = true;
|
|
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, TimeSpan.FromSeconds(15));
|
|
if(itm != null)
|
|
{
|
|
details["itemname"] = itm.Name;
|
|
}
|
|
}
|
|
JsonOuputCleaner(details, "InventoryOffer");
|
|
}
|
|
|
|
protected void BotFriendRequested(object o, FriendshipOfferedEventArgs e)
|
|
{
|
|
Dictionary<string, string> misc = new()
|
|
{
|
|
{ "fromName", e.AgentName }
|
|
};
|
|
if (ProcessRequest(myConfig.GetAcceptFriendRequests(),myConfig.GetFriendRequestLevel(), e.AgentName, "FriendRequest", e.AgentID, misc) == false)
|
|
{
|
|
return;
|
|
}
|
|
master.DataStoreService.AddAvatar(e.AgentID, e.AgentName);
|
|
GetClient().Friends.AcceptFriendship(e.AgentID, e.SessionID);
|
|
JsonOuputCleaner(e.AgentName, "FriendRequest");
|
|
|
|
}
|
|
|
|
protected void BotLoggedOut(object o, LoggedOutEventArgs e)
|
|
{
|
|
botConnected = false;
|
|
GetClient().Network.SimConnected += BotLoggedIn;
|
|
LogFormater.Info("Interaction Service [Standby]");
|
|
}
|
|
|
|
async Task awaitstable()
|
|
{
|
|
await Task.Delay(3000);
|
|
}
|
|
|
|
protected async void BotLoggedIn(object o, SimConnectedEventArgs e)
|
|
{
|
|
await awaitstable();
|
|
GetOrCreatePendingFolder();
|
|
GetClient().Network.SimConnected -= BotLoggedIn;
|
|
GetClient().Friends.FriendshipOffered += BotFriendRequested;
|
|
GetClient().Inventory.InventoryObjectOffered += BotInventoryOffer;
|
|
GetClient().Groups.GroupInvitation += BotGroupInviteOffer;
|
|
GetClient().Inventory.Store.InventoryObjectAdded += BotInventoryAdd;
|
|
GetClient().Self.IM += BotTeleportOffer;
|
|
botConnected = true;
|
|
LogFormater.Info("Interaction Service [Active]");
|
|
}
|
|
|
|
public override void Start(bool updateEnabled = false, bool setEnabledTo = false)
|
|
{
|
|
if (updateEnabled)
|
|
{
|
|
myConfig.setEnabled(setEnabledTo);
|
|
}
|
|
if (myConfig.GetEnabled() == false)
|
|
{
|
|
LogFormater.Info("Interaction Service [- Not requested -]");
|
|
return;
|
|
}
|
|
Stop();
|
|
running = true;
|
|
master.BotClientNoticeEvent += BotClientRestart;
|
|
LogFormater.Info("Interaction Service [Starting]");
|
|
}
|
|
|
|
public override void Stop()
|
|
{
|
|
if(running == true)
|
|
{
|
|
LogFormater.Info("Interaction Service [Stopping]");
|
|
}
|
|
running = false;
|
|
if (myConfig.GetEnabled() == false)
|
|
{
|
|
return;
|
|
}
|
|
master.BotClientNoticeEvent -= BotClientRestart;
|
|
if (master.BotClient != null)
|
|
{
|
|
var client = GetClient();
|
|
if (client != null)
|
|
{
|
|
if (client.Friends != null)
|
|
client.Friends.FriendshipOffered -= BotFriendRequested;
|
|
if (client.Inventory != null)
|
|
{
|
|
client.Inventory.InventoryObjectOffered -= BotInventoryOffer;
|
|
if (client.Inventory.Store != null)
|
|
client.Inventory.Store.InventoryObjectAdded -= BotInventoryAdd;
|
|
}
|
|
if (client.Groups != null)
|
|
client.Groups.GroupInvitation -= BotGroupInviteOffer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class InteractionEvent
|
|
{
|
|
public string from = "?";
|
|
public string eventType = "?";
|
|
public bool accepted = false;
|
|
public string info = "";
|
|
public Dictionary<string, string> misc = [];
|
|
public InteractionEvent(string setFrom, string setType, bool setAccepted, string setInfo, Dictionary<string, string> setMisc = null)
|
|
{
|
|
accepted = setAccepted;
|
|
from = setFrom;
|
|
eventType = setType;
|
|
info = setInfo;
|
|
if(setMisc != null)
|
|
{
|
|
misc = setMisc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|