Update SecondBot services, commands (Info, Inventory, Interaction), and DataStore enhancements
Some checks are pending
autobuild / docker (push) Waiting to run
Some checks are pending
autobuild / docker (push) Waiting to run
This commit is contained in:
parent
f9094f6fca
commit
0b081d9e1f
16 changed files with 2624 additions and 90 deletions
|
|
@ -1334,7 +1334,7 @@ namespace OpenMetaverse
|
|||
|
||||
IPEndPoint endPoint = new IPEndPoint(ip, port);
|
||||
|
||||
if (FindSimulator(endPoint) != null) return;
|
||||
if (FindSimulator(endPoint) != null) continue;
|
||||
|
||||
if (Connect(ip, port, handle, false, null, t.RegionSizeX, t.RegionSizeY) == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ namespace OpenMetaverse
|
|||
|
||||
/// <summary>Enable/disable storing terrain heightmaps in the
|
||||
/// TerrainManager</summary>
|
||||
public bool STORE_LAND_PATCHES = false;
|
||||
public bool STORE_LAND_PATCHES = true;
|
||||
|
||||
/// <summary>Enable/disable sending agent updates</summary>
|
||||
public bool SEND_AGENT_UPDATES = true;
|
||||
|
|
@ -208,7 +208,7 @@ namespace OpenMetaverse
|
|||
/// <summary>Should we connect to multiple sims? This will allow
|
||||
/// viewing into neighboring simulators and sim crossings
|
||||
/// (Experimental)</summary>
|
||||
public bool MULTIPLE_SIMS = false;
|
||||
public bool MULTIPLE_SIMS = true;
|
||||
|
||||
/// <summary>If true, all object update packets will be decoded in to
|
||||
/// native objects. If false, only updates for our own agent will be
|
||||
|
|
|
|||
|
|
@ -80,11 +80,27 @@ namespace SecondBotEvents.Commands
|
|||
List<NearMeDetails> BetterNearMe = [];
|
||||
|
||||
Utils.LongToUInts(GetClient().Network.CurrentSim.Handle, out uint mySimX, out uint mySimY);
|
||||
Vector3 myGlobalPos = new Vector3(mySimX + GetClient().Self.SimPosition.X, mySimY + GetClient().Self.SimPosition.Y, GetClient().Self.SimPosition.Z);
|
||||
Vector3 myLocalPos = GetClient().Self.SimPosition;
|
||||
|
||||
foreach (var sim in GetClient().Network.Simulators)
|
||||
List<Simulator> sims = [];
|
||||
try
|
||||
{
|
||||
lock (GetClient().Network.Simulators)
|
||||
{
|
||||
sims = [.. GetClient().Network.Simulators];
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
sims = [GetClient().Network.CurrentSim];
|
||||
}
|
||||
|
||||
foreach (var sim in sims)
|
||||
{
|
||||
if (sim == null || !sim.Connected) continue;
|
||||
Utils.LongToUInts(sim.Handle, out uint simX, out uint simY);
|
||||
float offsetX = (long)simX - (long)mySimX;
|
||||
float offsetY = (long)simY - (long)mySimY;
|
||||
HashSet<UUID> processedAvatars = [];
|
||||
|
||||
List<Avatar> avcopy = [.. sim.ObjectsAvatars.Values];
|
||||
|
|
@ -100,21 +116,20 @@ namespace SecondBotEvents.Commands
|
|||
localPos = parentObj.Position + (av.Position * parentObj.Rotation);
|
||||
}
|
||||
|
||||
Vector3 globalPos = new Vector3(simX + localPos.X, simY + localPos.Y, localPos.Z);
|
||||
double dist = Math.Round(Vector3.Distance(globalPos, myGlobalPos), 2);
|
||||
float relX = offsetX + localPos.X;
|
||||
float relY = offsetY + localPos.Y;
|
||||
Vector3 relPos = new Vector3(relX, relY, localPos.Z);
|
||||
double dist = Math.Round(Vector3.Distance(relPos, myLocalPos), 2);
|
||||
|
||||
if (dist > 150) continue;
|
||||
|
||||
float relX = (simX - mySimX) + localPos.X;
|
||||
float relY = (simY - mySimY) + localPos.Y;
|
||||
|
||||
NearMeDetails details = new()
|
||||
{
|
||||
id = av.ID.ToString(),
|
||||
name = av.Name,
|
||||
x = (int)Math.Round(relX),
|
||||
y = (int)Math.Round(relY),
|
||||
z = (int)Math.Round(globalPos.Z),
|
||||
z = (int)Math.Round(localPos.Z),
|
||||
range = dist
|
||||
};
|
||||
BetterNearMe.Add(details);
|
||||
|
|
@ -126,14 +141,13 @@ namespace SecondBotEvents.Commands
|
|||
if (avID == GetClient().Self.AgentID || processedAvatars.Contains(avID)) continue;
|
||||
|
||||
Vector3 localPos = kvp.Value;
|
||||
Vector3 globalPos = new Vector3(simX + localPos.X, simY + localPos.Y, localPos.Z);
|
||||
double dist = Math.Round(Vector3.Distance(globalPos, myGlobalPos), 2);
|
||||
float relX = offsetX + localPos.X;
|
||||
float relY = offsetY + localPos.Y;
|
||||
Vector3 relPos = new Vector3(relX, relY, localPos.Z);
|
||||
double dist = Math.Round(Vector3.Distance(relPos, myLocalPos), 2);
|
||||
|
||||
if (dist > 150) continue;
|
||||
|
||||
float relX = (simX - mySimX) + localPos.X;
|
||||
float relY = (simY - mySimY) + localPos.Y;
|
||||
|
||||
string avName = master.DataStoreService.GetAvatarName(avID);
|
||||
if (avName == "lookup" || string.IsNullOrEmpty(avName)) avName = "Unknown";
|
||||
|
||||
|
|
@ -143,7 +157,7 @@ namespace SecondBotEvents.Commands
|
|||
name = avName,
|
||||
x = (int)Math.Round(relX),
|
||||
y = (int)Math.Round(relY),
|
||||
z = (int)Math.Round(globalPos.Z),
|
||||
z = (int)Math.Round(localPos.Z),
|
||||
range = dist
|
||||
};
|
||||
BetterNearMe.Add(details);
|
||||
|
|
|
|||
|
|
@ -98,6 +98,17 @@ namespace SecondBotEvents.Commands
|
|||
}
|
||||
master.DataStoreService.OpenChatWindow(false, avataruuid, avataruuid);
|
||||
master.BotClient.SendIM(avataruuid, message);
|
||||
string senderName = "{BOT} " + GetClient().Self.Name;
|
||||
master.DataStoreService.RecordAvatarIm(avataruuid, senderName, message);
|
||||
if (master.DiscordService != null && master.DiscordService.isRunning())
|
||||
{
|
||||
string avName = master.DataStoreService.GetAvatarName(avataruuid);
|
||||
if (string.IsNullOrEmpty(avName) || avName == "lookup")
|
||||
{
|
||||
avName = avatar;
|
||||
}
|
||||
master.DiscordService.AvatarIMChat(avataruuid, avName, message, GetClient().Self.Name);
|
||||
}
|
||||
return BasicReply("ok", [avatar, message]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -495,6 +495,8 @@ namespace SecondBotEvents.Commands
|
|||
return Failure("Unknown group", [group]);
|
||||
}
|
||||
GetClient().Self.InstantMessageGroup(groupUUID, message);
|
||||
string senderName = "{BOT} " + GetClient().Self.Name;
|
||||
master.DataStoreService.RecordGroupChat(groupUUID, senderName, message);
|
||||
return BasicReply("Sending");
|
||||
}
|
||||
[About("fetchs a list of all pending group invitations")]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1301,6 +1301,184 @@ namespace SecondBotEvents.Commands
|
|||
}
|
||||
return UUID.Zero;
|
||||
}
|
||||
|
||||
[About("Gets the list of pending approval inventory items")]
|
||||
[CmdTypeGet()]
|
||||
public object GetPendingInventory()
|
||||
{
|
||||
var interaction = master.InteractionService;
|
||||
if (interaction == null)
|
||||
{
|
||||
return Failure("Interaction service not found");
|
||||
}
|
||||
UUID pendingFolderUUID = interaction.GetOrCreatePendingFolder();
|
||||
|
||||
List<InventoryBase> contents = GetClient().Inventory.FolderContents(
|
||||
pendingFolderUUID,
|
||||
GetClient().Self.AgentID,
|
||||
true,
|
||||
true,
|
||||
InventorySortOrder.ByName,
|
||||
TimeSpan.FromSeconds(15)
|
||||
);
|
||||
|
||||
var items = new List<Dictionary<string, string>>();
|
||||
if (contents != null)
|
||||
{
|
||||
foreach (var item in contents)
|
||||
{
|
||||
if (item is InventoryItem itm)
|
||||
{
|
||||
var entry = new Dictionary<string, string>
|
||||
{
|
||||
{ "uuid", itm.UUID.ToString() },
|
||||
{ "name", itm.Name },
|
||||
{ "type", itm.AssetType.ToString() },
|
||||
{ "description", itm.Description }
|
||||
};
|
||||
items.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return BasicReply(JsonSerializer.Serialize(items, JsonOptions.UnsafeRelaxed));
|
||||
}
|
||||
|
||||
[About("Resolves a pending inventory item by either accepting it or declining it")]
|
||||
[ArgHints("itemUuid", "The UUID of the item to resolve", "UUID")]
|
||||
[ArgHints("action", "Accept or Decline", "Text")]
|
||||
[CmdTypeDo()]
|
||||
public object ResolvePendingItem(string itemUuid, string action)
|
||||
{
|
||||
if (!UUID.TryParse(itemUuid, out UUID targetItemUUID))
|
||||
{
|
||||
return Failure("Invalid item UUID");
|
||||
}
|
||||
|
||||
var store = GetClient().Inventory.GetStore;
|
||||
if (!store.Items.ContainsKey(targetItemUUID))
|
||||
{
|
||||
return Failure("Item not found in inventory cache");
|
||||
}
|
||||
|
||||
InventoryItem item = store.Items[targetItemUUID].Data as InventoryItem;
|
||||
if (item == null)
|
||||
{
|
||||
return Failure("Target is not an inventory item");
|
||||
}
|
||||
|
||||
if (action.Equals("Accept", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
UUID destFolderUUID = GetClient().Inventory.FindFolderForType(item.AssetType);
|
||||
GetClient().Inventory.MoveItem(targetItemUUID, destFolderUUID);
|
||||
return BasicReply("Accepted and moved item to default folder");
|
||||
}
|
||||
else if (action.Equals("Decline", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
GetClient().Inventory.RemoveItem(targetItemUUID);
|
||||
return BasicReply("Declined and deleted item");
|
||||
}
|
||||
else
|
||||
{
|
||||
return Failure("Invalid action. Must be 'Accept' or 'Decline'");
|
||||
}
|
||||
}
|
||||
|
||||
[About("Touches/clicks a worn attachment by its inventory item UUID")]
|
||||
[ArgHints("itemUuid", "The inventory UUID of the worn attachment to touch", "UUID")]
|
||||
[CmdTypeDo()]
|
||||
public object TouchAttachment(string itemUuid)
|
||||
{
|
||||
if (!UUID.TryParse(itemUuid, out UUID targetUUID))
|
||||
{
|
||||
return Failure("Invalid inventory item UUID");
|
||||
}
|
||||
|
||||
var client = GetClient();
|
||||
UUID botID = client.Self.AgentID;
|
||||
|
||||
// Find only the BOT's own avatar
|
||||
Avatar botAvatar = null;
|
||||
foreach (var av in client.Network.CurrentSim.ObjectsAvatars.Values)
|
||||
{
|
||||
if (av.ID == botID) { botAvatar = av; break; }
|
||||
}
|
||||
if (botAvatar == null)
|
||||
{
|
||||
return Failure("Bot avatar not found in sim");
|
||||
}
|
||||
|
||||
foreach (var attachment in botAvatar.GetChildren(client))
|
||||
{
|
||||
bool matched = false;
|
||||
|
||||
// Primary: check NameValues for AttachItemID (set by SL server for attached objects)
|
||||
if (attachment.NameValues != null)
|
||||
{
|
||||
foreach (var nv in attachment.NameValues)
|
||||
{
|
||||
if (nv.Name == "AttachItemID" && nv.Value != null)
|
||||
{
|
||||
if (UUID.TryParse(nv.Value.ToString(), out UUID attachInvUUID) && attachInvUUID == targetUUID)
|
||||
{
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: direct object UUID match (rare but possible)
|
||||
if (!matched && attachment.ID == targetUUID) matched = true;
|
||||
|
||||
if (matched)
|
||||
{
|
||||
client.Objects.ClickObject(client.Network.CurrentSim, attachment.LocalID);
|
||||
return BasicReply("Touched attachment localID=" + attachment.LocalID + " objectUUID=" + attachment.ID);
|
||||
}
|
||||
}
|
||||
|
||||
return Failure("Attachment not found - use GetAttachmentDebug to inspect available attachments");
|
||||
}
|
||||
|
||||
[About("Returns debug info for all attachments on the bot avatar - use this to diagnose TouchAttachment issues")]
|
||||
[CmdTypeGet()]
|
||||
public object GetAttachmentDebug()
|
||||
{
|
||||
var client = GetClient();
|
||||
UUID botID = client.Self.AgentID;
|
||||
|
||||
Avatar botAvatar = null;
|
||||
foreach (var av in client.Network.CurrentSim.ObjectsAvatars.Values)
|
||||
{
|
||||
if (av.ID == botID) { botAvatar = av; break; }
|
||||
}
|
||||
|
||||
if (botAvatar == null) return Failure("Bot avatar not found in sim");
|
||||
|
||||
var results = new List<Dictionary<string, string>>();
|
||||
foreach (var attachment in botAvatar.GetChildren(client))
|
||||
{
|
||||
var entry = new Dictionary<string, string>
|
||||
{
|
||||
{ "objectUUID", attachment.ID.ToString() },
|
||||
{ "localID", attachment.LocalID.ToString() },
|
||||
{ "parentID", attachment.ParentID.ToString() }
|
||||
};
|
||||
|
||||
if (attachment.Properties != null)
|
||||
entry["name"] = attachment.Properties.Name ?? "";
|
||||
|
||||
if (attachment.NameValues != null)
|
||||
{
|
||||
foreach (var nv in attachment.NameValues)
|
||||
entry["nv_" + nv.Name] = nv.Value?.ToString() ?? "";
|
||||
}
|
||||
|
||||
results.Add(entry);
|
||||
}
|
||||
|
||||
return BasicReply(JsonSerializer.Serialize(results, JsonOptions.UnsafeRelaxed));
|
||||
}
|
||||
}
|
||||
|
||||
public class InventoryNodeEntry
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Text.Json;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.ImportExport.Collada14;
|
||||
using SecondBotEvents.Services;
|
||||
|
|
@ -273,6 +274,41 @@ namespace SecondBotEvents.Commands
|
|||
return BasicReply("Removed perm: " + flag + " Sticky: " + stickyflag.ToString(), [avatar, flag, state, sticky]);
|
||||
}
|
||||
|
||||
[About("Clears all active RLV restrictions on the bot")]
|
||||
[ReturnHints("ok")]
|
||||
[CmdTypeDo()]
|
||||
public object RLVClearAll()
|
||||
{
|
||||
if (master.RLV == null)
|
||||
{
|
||||
return Failure("RLV service is not running");
|
||||
}
|
||||
lock (master.RLV.Rules)
|
||||
{
|
||||
master.RLV.Rules.Clear();
|
||||
}
|
||||
return BasicReply("ok");
|
||||
}
|
||||
|
||||
[About("Clears a specific active RLV restriction behavior on the bot")]
|
||||
[ArgHints("behaviour", "The restriction behavior to clear", "Text", "unsit")]
|
||||
[ReturnHints("ok")]
|
||||
[CmdTypeDo()]
|
||||
public object RLVClearBehaviour(string behaviour)
|
||||
{
|
||||
if (master.RLV == null)
|
||||
{
|
||||
return Failure("RLV service is not running");
|
||||
}
|
||||
lock (master.RLV.Rules)
|
||||
{
|
||||
master.RLV.Rules.RemoveAll(r => r.Behaviour.Equals(behaviour, StringComparison.CurrentCultureIgnoreCase) ||
|
||||
(behaviour.Equals("stand", StringComparison.CurrentCultureIgnoreCase) && r.Behaviour.Equals("unsit", StringComparison.CurrentCultureIgnoreCase)) ||
|
||||
(behaviour.Equals("unsit", StringComparison.CurrentCultureIgnoreCase) && r.Behaviour.Equals("stand", StringComparison.CurrentCultureIgnoreCase)));
|
||||
}
|
||||
return BasicReply("ok", [behaviour]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,8 +166,12 @@ private BotRejoinConfig rejoinCfg;
|
|||
|
||||
protected void BotSimDisconnected(object o, SimDisconnectedEventArgs e)
|
||||
{
|
||||
goodticks = 0;
|
||||
LogFormater.Info("Client service ~ Disconnected from sim: " + e.Simulator.Name);
|
||||
if (client?.Network?.CurrentSim != null && e.Simulator != client.Network.CurrentSim && client.Network.CurrentSim.Connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
goodticks = 0;
|
||||
if (!getLogoutExpected())
|
||||
{
|
||||
HandleUnexpectedDisconnect("Unexpected sim disconnect");
|
||||
|
|
@ -290,6 +294,8 @@ private BotRejoinConfig rejoinCfg;
|
|||
{
|
||||
client = null;
|
||||
client = new GridClient();
|
||||
client.Settings.STORE_LAND_PATCHES = true;
|
||||
client.Settings.MULTIPLE_SIMS = true;
|
||||
client.Network.SimConnected += BotSimConnected;
|
||||
client.Network.LoggedOut += BotLoggedOut;
|
||||
client.Network.Disconnected += BotDisconnected;
|
||||
|
|
|
|||
|
|
@ -145,13 +145,13 @@ namespace SecondBotEvents.Services
|
|||
if(myConfig.GetObjectMasterOptout() == true)
|
||||
{
|
||||
string ownername = master.DataStoreService.GetAvatarName(e.IM.FromAgentID);
|
||||
requireSigning = !myConfig.GetMastersCSV().Contains(ownername);
|
||||
if ((requireSigning == false) && (myConfig.GetCheckDotNames() == true))
|
||||
requireSigning = !IsMaster(ownername);
|
||||
if (requireSigning && myConfig.GetCheckDotNames())
|
||||
{
|
||||
string[] bits = e.IM.FromAgentName.ToLower().Split('.');
|
||||
if (bits.Length == 2)
|
||||
{
|
||||
requireSigning = myConfig.GetMastersCSV().Contains(bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper());
|
||||
requireSigning = !IsMaster(bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -170,13 +170,13 @@ namespace SecondBotEvents.Services
|
|||
}
|
||||
master.DataStoreService.AddAvatar(e.IM.FromAgentID, e.IM.FromAgentName);
|
||||
requireSigning = false;
|
||||
acceptMessage = myConfig.GetMastersCSV().Contains(e.IM.FromAgentName);
|
||||
acceptMessage = IsMaster(e.IM.FromAgentName);
|
||||
if ((acceptMessage == false) && (myConfig.GetCheckDotNames() == true))
|
||||
{
|
||||
string[] bits = e.IM.FromAgentName.ToLower().Split('.');
|
||||
if (bits.Length == 2)
|
||||
{
|
||||
acceptMessage = myConfig.GetMastersCSV().Contains(bits[0].FirstCharToUpper()+" " + bits[1].FirstCharToUpper());
|
||||
acceptMessage = IsMaster(bits[0].FirstCharToUpper()+" " + bits[1].FirstCharToUpper());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -193,20 +193,26 @@ namespace SecondBotEvents.Services
|
|||
CommandInterfaceCaller(e.IM.Message, requireSigning);
|
||||
}
|
||||
|
||||
public bool AvatarUUIDIsMaster(UUID avatar)
|
||||
public bool IsMaster(string name)
|
||||
{
|
||||
foreach(string name in myConfig.GetMastersCSV())
|
||||
if (string.IsNullOrEmpty(name)) return false;
|
||||
foreach (string masterName in myConfig.GetMastersCSV())
|
||||
{
|
||||
string find = master.DataStoreService.GetAvatarUUID(name);
|
||||
if (find != "lookup")
|
||||
if (SecondbotHelpers.IsNameMatch(masterName, name))
|
||||
{
|
||||
if(avatar.ToString() == find)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool AvatarUUIDIsMaster(UUID avatar)
|
||||
{
|
||||
string name = master.DataStoreService.GetAvatarName(avatar);
|
||||
if (name != "lookup")
|
||||
{
|
||||
return IsMaster(name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -221,6 +221,10 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
continue;
|
||||
}
|
||||
if (chatWindowsOwner.ContainsValue(entry.Value) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
clearLinks.Add(entry.Value, entry.Key);
|
||||
}
|
||||
lock (avatarLastUsed) lock (avatarsName2Key) lock(avatarsKey2Displayname)
|
||||
|
|
@ -406,25 +410,167 @@ namespace SecondBotEvents.Services
|
|||
|
||||
public string GetGroupMemberRank(UUID group, UUID avatar)
|
||||
{
|
||||
bool needFetch = false;
|
||||
lock (groupMembers)
|
||||
{
|
||||
if (groupMembers.ContainsKey(group) == false || groupMembers[group].ContainsKey(avatar) == false)
|
||||
{
|
||||
LogFormater.Info($"[DEBUG] GetGroupMemberRank: Returning Guest. GroupKnown: {groupMembers.ContainsKey(group)}, AvatarKnown: {(groupMembers.ContainsKey(group) ? groupMembers[group].ContainsKey(avatar).ToString() : "N/A")} for Group: {group}, Avatar: {avatar}");
|
||||
needFetch = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needFetch)
|
||||
{
|
||||
AutoResetEvent fetchEvent = new(false);
|
||||
void callback(object sender, GroupMembersReplyEventArgs e)
|
||||
{
|
||||
if (e.GroupID == group)
|
||||
{
|
||||
fetchEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
GetClient().Groups.GroupMembersReply += callback;
|
||||
GetClient().Groups.RequestGroupMembers(group);
|
||||
fetchEvent.WaitOne(3000, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogFormater.Warn($"GetGroupMemberRank exception during RequestGroupMembers: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
GetClient().Groups.GroupMembersReply -= callback;
|
||||
}
|
||||
}
|
||||
|
||||
lock (groupMembers)
|
||||
{
|
||||
if (groupMembers.ContainsKey(group) == false || groupMembers[group].ContainsKey(avatar) == false)
|
||||
{
|
||||
LogFormater.Info($"[DEBUG] GetGroupMemberRank: Returning Guest after fetch attempt. GroupKnown: {groupMembers.ContainsKey(group)}, AvatarKnown: {(groupMembers.ContainsKey(group) ? groupMembers[group].ContainsKey(avatar).ToString() : "N/A")} for Group: {group}, Avatar: {avatar}");
|
||||
return "Guest";
|
||||
}
|
||||
|
||||
GroupMember m = groupMembers[group][avatar];
|
||||
if (m.IsOwner == true) return "Owner";
|
||||
if (m.Title != null && (m.Title.Contains("Officer") || m.Title.Contains("Officer"))) return "Officer";
|
||||
|
||||
// Also check roles if title didn't match
|
||||
if (groupRoles.ContainsKey(group))
|
||||
|
||||
// Check for Owner powers (DeleteRole, ChangeActions, ChangeIdentity)
|
||||
GroupPowers ownerPowers = GroupPowers.DeleteRole | GroupPowers.ChangeActions | GroupPowers.ChangeIdentity;
|
||||
if ((m.Powers & ownerPowers) == ownerPowers) return "Owner";
|
||||
|
||||
if (string.IsNullOrEmpty(m.Title) == false)
|
||||
{
|
||||
// This is a bit complex as we need member-to-role mapping which we don't have cached yet
|
||||
// For now, Owner and Title check is a good start.
|
||||
if (m.Title.Contains("Owner", StringComparison.OrdinalIgnoreCase) || m.Title.Contains("Founder", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "Owner";
|
||||
}
|
||||
}
|
||||
return "Member";
|
||||
}
|
||||
|
||||
// Fetch group roles and role-member mapping to check assigned group roles
|
||||
try
|
||||
{
|
||||
// Fetch group roles
|
||||
Dictionary<UUID, GroupRole> rolesDict = new();
|
||||
AutoResetEvent rolesFetchEvent = new(false);
|
||||
void rolesCallback(object sender, GroupRolesDataReplyEventArgs e)
|
||||
{
|
||||
if (e.GroupID == group)
|
||||
{
|
||||
rolesDict = e.Roles;
|
||||
rolesFetchEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
GetClient().Groups.GroupRoleDataReply += rolesCallback;
|
||||
GetClient().Groups.RequestGroupRoles(group);
|
||||
rolesFetchEvent.WaitOne(2000, false);
|
||||
GetClient().Groups.GroupRoleDataReply -= rolesCallback;
|
||||
|
||||
// Fetch group role-members map
|
||||
List<KeyValuePair<UUID, UUID>> roleMembersList = new();
|
||||
AutoResetEvent membersFetchEvent = new(false);
|
||||
void roleMembersCallback(object sender, GroupRolesMembersReplyEventArgs e)
|
||||
{
|
||||
if (e.GroupID == group)
|
||||
{
|
||||
roleMembersList = e.RolesMembers;
|
||||
membersFetchEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
GetClient().Groups.GroupRoleMembersReply += roleMembersCallback;
|
||||
GetClient().Groups.RequestGroupRolesMembers(group);
|
||||
membersFetchEvent.WaitOne(2000, false);
|
||||
GetClient().Groups.GroupRoleMembersReply -= roleMembersCallback;
|
||||
|
||||
// Check if avatar belongs to any Owner or Officer role
|
||||
HashSet<UUID> ownerRoleIDs = new();
|
||||
HashSet<UUID> officerRoleIDs = new();
|
||||
|
||||
foreach (KeyValuePair<UUID, GroupRole> r in rolesDict)
|
||||
{
|
||||
string rName = r.Value.Name ?? "";
|
||||
string rTitle = r.Value.Title ?? "";
|
||||
GroupPowers rPowers = r.Value.Powers;
|
||||
|
||||
GroupPowers ownerPowers = GroupPowers.DeleteRole | GroupPowers.ChangeActions | GroupPowers.ChangeIdentity;
|
||||
if (rName.Equals("Owners", StringComparison.OrdinalIgnoreCase) ||
|
||||
rName.Equals("Owner", StringComparison.OrdinalIgnoreCase) ||
|
||||
rTitle.Contains("Owner", StringComparison.OrdinalIgnoreCase) ||
|
||||
(rPowers & ownerPowers) == ownerPowers)
|
||||
{
|
||||
ownerRoleIDs.Add(r.Value.ID);
|
||||
}
|
||||
else if (rName.Contains("Officer", StringComparison.OrdinalIgnoreCase) || rTitle.Contains("Officer", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
officerRoleIDs.Add(r.Value.ID);
|
||||
}
|
||||
}
|
||||
|
||||
bool isOwnerRole = false;
|
||||
bool isOfficerRole = false;
|
||||
|
||||
foreach (KeyValuePair<UUID, UUID> pair in roleMembersList)
|
||||
{
|
||||
if (pair.Value == avatar) // pair.Value = MemberID, pair.Key = RoleID
|
||||
{
|
||||
if (ownerRoleIDs.Contains(pair.Key))
|
||||
{
|
||||
isOwnerRole = true;
|
||||
break;
|
||||
}
|
||||
if (officerRoleIDs.Contains(pair.Key))
|
||||
{
|
||||
isOfficerRole = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isOwnerRole) return "Owner";
|
||||
if (isOfficerRole) return "Officer";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogFormater.Warn($"GetGroupMemberRank role check exception: {ex.Message}");
|
||||
}
|
||||
|
||||
lock (groupMembers)
|
||||
{
|
||||
if (groupMembers.ContainsKey(group) && groupMembers[group].ContainsKey(avatar))
|
||||
{
|
||||
GroupMember m = groupMembers[group][avatar];
|
||||
if (string.IsNullOrEmpty(m.Title) == false && m.Title.Contains("Officer", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "Officer";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "Member";
|
||||
}
|
||||
|
||||
public string RequestLoginToken(UUID avatar)
|
||||
|
|
@ -582,6 +728,40 @@ namespace SecondBotEvents.Services
|
|||
}
|
||||
}
|
||||
|
||||
public void RecordAvatarIm(UUID avatarId, string fromName, string message)
|
||||
{
|
||||
lock (chatWindows)
|
||||
{
|
||||
UUID sessionID = UUID.Zero;
|
||||
foreach (KeyValuePair<UUID, UUID> kvp in chatWindowsOwner)
|
||||
{
|
||||
if (kvp.Value == avatarId)
|
||||
{
|
||||
sessionID = kvp.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sessionID == UUID.Zero)
|
||||
{
|
||||
sessionID = avatarId;
|
||||
OpenChatWindow(false, sessionID, avatarId);
|
||||
}
|
||||
RecordChat(sessionID, fromName, message);
|
||||
}
|
||||
}
|
||||
|
||||
public void RecordGroupChat(UUID groupId, string fromName, string message)
|
||||
{
|
||||
lock (chatWindows)
|
||||
{
|
||||
if (!chatWindows.ContainsKey(groupId))
|
||||
{
|
||||
OpenChatWindow(true, groupId, groupId);
|
||||
}
|
||||
RecordChat(groupId, fromName, message);
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseChat(UUID owner)
|
||||
{
|
||||
lock (chatWindows)
|
||||
|
|
@ -862,6 +1042,7 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
if (av.Key == avatarId)
|
||||
{
|
||||
AddAvatar(av.Key, av.Value);
|
||||
hasReply = true;
|
||||
fetchEvent.Set();
|
||||
}
|
||||
|
|
@ -918,15 +1099,11 @@ namespace SecondBotEvents.Services
|
|||
public bool KnownAvatar(string name, UUID avatarId)
|
||||
{
|
||||
SyncAvatarsHashMap();
|
||||
if (avatarId == UUID.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(KnownAvatarUUID(avatarId) == true)
|
||||
if (avatarId != UUID.Zero && KnownAvatarUUID(avatarId) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(KnownAvatarName(name) == true)
|
||||
if (!string.IsNullOrEmpty(name) && KnownAvatarName(name) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1829,21 +2006,93 @@ namespace SecondBotEvents.Services
|
|||
displayNameFinder(id);
|
||||
return;
|
||||
}
|
||||
avatarsName2Key.Add(name, id);
|
||||
avatarLastUsed.Add(id, SecondbotHelpers.UnixTimeNow());
|
||||
avatarsKey2Displayname.Add(id, "?");
|
||||
avatarsName2Key[name] = id;
|
||||
avatarLastUsed[id] = SecondbotHelpers.UnixTimeNow();
|
||||
if (avatarsKey2Displayname.ContainsKey(id) == false)
|
||||
{
|
||||
avatarsKey2Displayname.Add(id, "?");
|
||||
}
|
||||
displayNameFinder(id);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetDisplayName(UUID id)
|
||||
{
|
||||
requestDisplayName(id);
|
||||
if (avatarsKey2Displayname.ContainsKey(id) == false)
|
||||
if (avatarsKey2Displayname.ContainsKey(id) && avatarsKey2Displayname[id] != "?" && !string.IsNullOrEmpty(avatarsKey2Displayname[id]))
|
||||
{
|
||||
return "?";
|
||||
return avatarsKey2Displayname[id];
|
||||
}
|
||||
return avatarsKey2Displayname[id];
|
||||
lock (avatarsKey2Displayname)
|
||||
{
|
||||
if (!avatarsKey2Displayname.ContainsKey(id))
|
||||
{
|
||||
avatarsKey2Displayname[id] = "?";
|
||||
}
|
||||
}
|
||||
AutoResetEvent waitEvent = new(false);
|
||||
try
|
||||
{
|
||||
_ = GetClient().Avatars.GetDisplayNames(new List<UUID> { id }, (success, agents, badIds) =>
|
||||
{
|
||||
if (success && agents != null && agents.Length > 0)
|
||||
{
|
||||
foreach (var ag in agents)
|
||||
{
|
||||
if (ag.ID == id)
|
||||
{
|
||||
lock (avatarsKey2Displayname) lock (avatarsName2Key)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ag.DisplayName))
|
||||
{
|
||||
avatarsKey2Displayname[id] = ag.DisplayName;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(ag.LegacyFullName))
|
||||
{
|
||||
avatarsName2Key[ag.LegacyFullName] = id;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
waitEvent.Set();
|
||||
});
|
||||
waitEvent.WaitOne(3000);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Fallback to RequestAgentProfile if GetDisplayNames didn't resolve
|
||||
if (!avatarsKey2Displayname.ContainsKey(id) || avatarsKey2Displayname[id] == "?")
|
||||
{
|
||||
AutoResetEvent profileWait = new(false);
|
||||
try
|
||||
{
|
||||
_ = GetClient().Avatars.RequestAgentProfile(id, (status, properties) =>
|
||||
{
|
||||
if (properties != null && !string.IsNullOrEmpty(properties.DisplayName))
|
||||
{
|
||||
lock (avatarsKey2Displayname)
|
||||
{
|
||||
avatarsKey2Displayname[id] = properties.DisplayName;
|
||||
}
|
||||
}
|
||||
profileWait.Set();
|
||||
});
|
||||
profileWait.WaitOne(2000);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (avatarsKey2Displayname.ContainsKey(id) && avatarsKey2Displayname[id] != "?" && !string.IsNullOrEmpty(avatarsKey2Displayname[id]))
|
||||
{
|
||||
return avatarsKey2Displayname[id];
|
||||
}
|
||||
string avName = GetAvatarName(id);
|
||||
if (!string.IsNullOrEmpty(avName) && avName != "lookup")
|
||||
{
|
||||
return avName;
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
protected void AvatarUUIDName(object o, UUIDNameReplyEventArgs e)
|
||||
|
|
|
|||
|
|
@ -195,6 +195,18 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduplication check: ignore duplicate dialog from same object with same message if already waiting
|
||||
CleanUp();
|
||||
foreach (var existing in DialogWindows.Values)
|
||||
{
|
||||
if (existing.ObjectID == e.ObjectID && existing.Message == e.Message && existing.Channel == e.Channel)
|
||||
{
|
||||
LogFormater.Info($"[DialogService] Ignoring duplicate dialog from {e.ObjectName}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int windowid = nextDialogWindow;
|
||||
nextDialogWindow++;
|
||||
if(nextDialogWindow > 999)
|
||||
|
|
@ -251,13 +263,14 @@ namespace SecondBotEvents.Services
|
|||
return;
|
||||
}
|
||||
LogFormater.Info("Dialog service [Attached to new client]");
|
||||
GetClient().Network.LoggedOut -= BotLoggedOut;
|
||||
GetClient().Network.LoggedOut += BotLoggedOut;
|
||||
GetClient().Network.SimConnected -= BotLoggedIn;
|
||||
GetClient().Network.SimConnected += BotLoggedIn;
|
||||
}
|
||||
|
||||
protected void BotLoggedOut(object o, LoggedOutEventArgs e)
|
||||
{
|
||||
GetClient().Network.SimConnected += BotLoggedIn;
|
||||
LogFormater.Info("Dialog service [waiting for new client]");
|
||||
if (GetClient() != null)
|
||||
{
|
||||
|
|
@ -271,18 +284,24 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
GetClient().Network.SimConnected -= BotLoggedIn;
|
||||
botConnected = true;
|
||||
// attach events
|
||||
// attach events cleanly
|
||||
GetClient().Self.ScriptDialog -= DialogWindowEvent;
|
||||
GetClient().Self.ScriptDialog += DialogWindowEvent;
|
||||
GetClient().Self.LoadURL -= LoadUrlEvent;
|
||||
GetClient().Self.LoadURL += LoadUrlEvent;
|
||||
GetClient().Self.AlertMessage -= BotAlertMessage;
|
||||
GetClient().Self.AlertMessage += BotAlertMessage;
|
||||
}
|
||||
|
||||
public void Attach()
|
||||
{
|
||||
botConnected = true;
|
||||
// attach events
|
||||
// attach events cleanly
|
||||
GetClient().Self.ScriptDialog -= DialogWindowEvent;
|
||||
GetClient().Self.ScriptDialog += DialogWindowEvent;
|
||||
GetClient().Self.LoadURL -= LoadUrlEvent;
|
||||
GetClient().Self.LoadURL += LoadUrlEvent;
|
||||
GetClient().Self.AlertMessage -= BotAlertMessage;
|
||||
GetClient().Self.AlertMessage += BotAlertMessage;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ namespace SecondBotEvents.Services
|
|||
"discordapi", "GroupBan", null, [.. args], 0, "none", false, 0, "", false), message);
|
||||
return;
|
||||
}
|
||||
string groupSender = "{BOT} " + (myConfig.GethideChatterName() ? GetClient().Self.Name : message.Author.Username);
|
||||
if (myConfig.GethideChatterName() == false)
|
||||
{
|
||||
GetClient().Self.InstantMessageGroup(groupuuid, message.Author.Username + ": " + message.CleanContent);
|
||||
|
|
@ -366,6 +367,7 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
GetClient().Self.InstantMessageGroup(groupuuid, message.CleanContent);
|
||||
}
|
||||
master.DataStoreService.RecordGroupChat(groupuuid, groupSender, message.CleanContent);
|
||||
_ = message.DeleteAsync();
|
||||
}
|
||||
|
||||
|
|
@ -444,6 +446,7 @@ namespace SecondBotEvents.Services
|
|||
GetClient().Self.SendTeleportLure(avataruuid);
|
||||
return;
|
||||
}
|
||||
string botSender = "{BOT} " + (myConfig.GethideChatterName() ? GetClient().Self.Name : message.Author.Username);
|
||||
if (myConfig.GethideChatterName() == false)
|
||||
{
|
||||
master.DataStoreService.GetAvatarName(avataruuid); // add avatar to lookup service just in case.
|
||||
|
|
@ -453,6 +456,7 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
GetClient().Self.InstantMessage(avataruuid, message.CleanContent);
|
||||
}
|
||||
master.DataStoreService.RecordAvatarIm(avataruuid, botSender, message.CleanContent);
|
||||
_ = MarkMessage((IUserMessage)message, "✅");
|
||||
}
|
||||
|
||||
|
|
@ -819,9 +823,10 @@ namespace SecondBotEvents.Services
|
|||
SendMessageToChannel(groupname, GroupPrefill(group), name + ": " + message, "group");
|
||||
}
|
||||
|
||||
protected void AvatarIMChat(UUID avatar, string name, string message)
|
||||
public void AvatarIMChat(UUID avatar, string name, string message, string sender = null)
|
||||
{
|
||||
SendMessageToChannel(name.ToLower().Replace(" ",""), "avatar=" + avatar.ToString() + "||| !close !offertp !clear", name+":"+message, "im");
|
||||
string displaySender = string.IsNullOrEmpty(sender) ? name : sender;
|
||||
SendMessageToChannel(name.ToLower().Replace(" ",""), "avatar=" + avatar.ToString() + "||| !close !offertp !clear", displaySender + ": " + message, "im");
|
||||
}
|
||||
|
||||
protected void ObjectIMChat(string objectName, string message)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
using Discord.Rest;
|
||||
using Discord.Rest;
|
||||
using OpenMetaverse;
|
||||
using SecondBotEvents.Config;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace SecondBotEvents.Services
|
||||
{
|
||||
|
|
@ -25,6 +26,7 @@ namespace SecondBotEvents.Services
|
|||
protected bool softDisable = false;
|
||||
protected bool closeToHome = false;
|
||||
protected bool softDisableSit = false;
|
||||
protected Timer? _homeboundTimer = null;
|
||||
|
||||
public void MarkTeleport()
|
||||
{
|
||||
|
|
@ -334,19 +336,27 @@ namespace SecondBotEvents.Services
|
|||
return;
|
||||
}
|
||||
Stop();
|
||||
if(master.RLV.Enabled == true)
|
||||
{
|
||||
myConfig.setEnabled(false);
|
||||
LogFormater.Info("Homebound Service [Disabled RLV enabled]");
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
master.BotClientNoticeEvent += BotClientRestart;
|
||||
_homeboundTimer?.Dispose();
|
||||
_homeboundTimer = new Timer((state) =>
|
||||
{
|
||||
if (running && botConnected && softDisable == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
Tick();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}, null, 5000, 5000);
|
||||
LogFormater.Info("Homebound Service [Starting]");
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
_homeboundTimer?.Dispose();
|
||||
_homeboundTimer = null;
|
||||
if(running == true)
|
||||
{
|
||||
LogFormater.Info("Homebound Service [Stopping]");
|
||||
|
|
|
|||
|
|
@ -13,6 +13,27 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
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);
|
||||
|
|
@ -70,17 +91,22 @@ namespace SecondBotEvents.Services
|
|||
string ObjectOwnerName = master.DataStoreService.GetAvatarName(avataruuid);
|
||||
string whyAccepted = "rejected request";
|
||||
misc.Add("ObjectOwner", ObjectOwnerName);
|
||||
if (master.CommandsService.myConfig.GetMastersCSV().Contains(avatarName) == true)
|
||||
foreach (string masterName in master.CommandsService.myConfig.GetMastersCSV())
|
||||
{
|
||||
whyAccepted = "on owners list";
|
||||
accepted = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (master.CommandsService.myConfig.GetMastersCSV().Contains(ObjectOwnerName) == true)
|
||||
{
|
||||
whyAccepted = "Object Owner is on master list";
|
||||
accepted = true;
|
||||
}
|
||||
else if (ConfigAcceptLevel == "Anyone")
|
||||
if (ConfigAcceptLevel == "Anyone")
|
||||
{
|
||||
whyAccepted = "Accepting from anyone";
|
||||
accepted = true;
|
||||
|
|
@ -95,10 +121,15 @@ namespace SecondBotEvents.Services
|
|||
string[] bits = avatarName.ToLower().Split('.');
|
||||
if (bits.Length == 2)
|
||||
{
|
||||
accepted = master.CommandsService.myConfig.GetMastersCSV().Contains(bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper());
|
||||
if (accepted == true)
|
||||
string dotName = bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper();
|
||||
foreach (string masterName in master.CommandsService.myConfig.GetMastersCSV())
|
||||
{
|
||||
whyAccepted = "on owners list [dot check]";
|
||||
if (SecondbotHelpers.IsNameMatch(masterName, dotName))
|
||||
{
|
||||
accepted = true;
|
||||
whyAccepted = "on owners list [dot check]";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (accepted == false)
|
||||
|
|
@ -106,10 +137,15 @@ namespace SecondBotEvents.Services
|
|||
bits = ObjectOwnerName.ToLower().Split('.');
|
||||
if (bits.Length == 2)
|
||||
{
|
||||
accepted = master.CommandsService.myConfig.GetMastersCSV().Contains(bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper());
|
||||
if (accepted == true)
|
||||
string dotName = bits[0].FirstCharToUpper() + " " + bits[1].FirstCharToUpper();
|
||||
foreach (string masterName in master.CommandsService.myConfig.GetMastersCSV())
|
||||
{
|
||||
whyAccepted = "Object Owner is on master list [dot check]";
|
||||
if (SecondbotHelpers.IsNameMatch(masterName, dotName))
|
||||
{
|
||||
accepted = true;
|
||||
whyAccepted = "Object Owner is on master list [dot check]";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -249,6 +285,26 @@ namespace SecondBotEvents.Services
|
|||
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 },
|
||||
|
|
@ -264,17 +320,66 @@ namespace SecondBotEvents.Services
|
|||
|
||||
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() }
|
||||
{ "targetfolder", e.FolderID.ToString() },
|
||||
{ "objectid", e.Offer.FromAgentID.ToString() },
|
||||
{ "objectname", e.Offer.FromAgentName }
|
||||
};
|
||||
if (ProcessRequest(myConfig.GetAcceptInventory(),myConfig.GetInventoryTransferLevel(),e.Offer.FromAgentName, "InventoryOffer", e.Offer.FromAgentID, details) == false)
|
||||
if (ProcessRequest(myConfig.GetAcceptInventory(),myConfig.GetInventoryTransferLevel(), ownerName, "InventoryOffer", ownerID, details) == false)
|
||||
{
|
||||
e.Accept = 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;
|
||||
|
|
@ -323,6 +428,7 @@ namespace SecondBotEvents.Services
|
|||
protected async void BotLoggedIn(object o, SimConnectedEventArgs e)
|
||||
{
|
||||
await awaitstable();
|
||||
GetOrCreatePendingFolder();
|
||||
GetClient().Network.SimConnected -= BotLoggedIn;
|
||||
GetClient().Friends.FriendshipOffered += BotFriendRequested;
|
||||
GetClient().Inventory.InventoryObjectOffered += BotInventoryOffer;
|
||||
|
|
@ -364,12 +470,19 @@ namespace SecondBotEvents.Services
|
|||
master.BotClientNoticeEvent -= BotClientRestart;
|
||||
if (master.BotClient != null)
|
||||
{
|
||||
if (GetClient() != null)
|
||||
var client = GetClient();
|
||||
if (client != null)
|
||||
{
|
||||
GetClient().Friends.FriendshipOffered -= BotFriendRequested;
|
||||
GetClient().Inventory.InventoryObjectOffered -= BotInventoryOffer;
|
||||
GetClient().Inventory.Store.InventoryObjectAdded -= BotInventoryAdd;
|
||||
GetClient().Groups.GroupInvitation -= BotGroupInviteOffer;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -180,5 +180,20 @@ namespace SecondBotEvents
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsNameMatch(string configuredName, string incomingName)
|
||||
{
|
||||
if (configuredName == null || incomingName == null) return false;
|
||||
|
||||
string c = configuredName.Trim().ToLowerInvariant();
|
||||
string i = incomingName.Trim().ToLowerInvariant();
|
||||
|
||||
if (c == i) return true;
|
||||
|
||||
if (c.EndsWith(" resident")) c = c.Substring(0, c.Length - 9).Trim();
|
||||
if (i.EndsWith(" resident")) i = i.Substring(0, i.Length - 9).Trim();
|
||||
|
||||
return c == i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue