Dashboard & Group Management Integration
- [LibreMetaverse] Enhanced GroupInvitationEventArgs with GroupID. - [InteractionService] Added persistence for group invites when auto-accept is disabled. - [DataStoreService] Implemented pending group invitation tracking and management. - [DataStoreService] Enhanced local chat recording with Shout/Whisper/OwnerSay type tags. - [BotClientService] Improved logout stability with explicit logout call and delay. - [RLVService] Exposed active RLV rules list for external monitoring. - [Commands] Added GroupInviteRespond, GetPendingGroupInvites, and RLVStatus API commands. - [Commands] Overhauled NearmeWithDetails to support multi-simulator tracking and better position resolution for seated avatars.
This commit is contained in:
parent
176b9d059f
commit
adf4e30cd2
10 changed files with 196 additions and 27 deletions
|
|
@ -914,7 +914,7 @@ namespace OpenMetaverse
|
|||
{
|
||||
if (m_GroupInvitation == null || e.IM.Dialog != InstantMessageDialog.GroupInvitation) return;
|
||||
|
||||
GroupInvitationEventArgs args = new GroupInvitationEventArgs(e.Simulator, e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
|
||||
GroupInvitationEventArgs args = new GroupInvitationEventArgs(e.Simulator, e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message, e.IM.IMSessionID);
|
||||
OnGroupInvitation(args);
|
||||
|
||||
if (args.Accept)
|
||||
|
|
@ -2460,15 +2460,19 @@ namespace OpenMetaverse
|
|||
/// <summary>The Simulator</summary>
|
||||
public Simulator Simulator { get; }
|
||||
|
||||
/// <summary>The Group ID</summary>
|
||||
public UUID GroupID { get; }
|
||||
|
||||
/// <summary>Set to true to accept invitation, false to decline</summary>
|
||||
public bool Accept { get; set; }
|
||||
|
||||
public GroupInvitationEventArgs(Simulator simulator, UUID agentID, string agentName, string message)
|
||||
public GroupInvitationEventArgs(Simulator simulator, UUID agentID, string agentName, string message, UUID groupID)
|
||||
{
|
||||
Simulator = simulator;
|
||||
AgentID = agentID;
|
||||
FromName = agentName;
|
||||
Message = message;
|
||||
GroupID = groupID;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,25 +78,73 @@ namespace SecondBotEvents.Commands
|
|||
return BasicReply("Error not in a sim");
|
||||
}
|
||||
List<NearMeDetails> BetterNearMe = [];
|
||||
List<Avatar> avcopy = [.. GetClient().Network.CurrentSim.ObjectsAvatars.Values];
|
||||
foreach (Avatar av in avcopy)
|
||||
|
||||
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);
|
||||
|
||||
foreach (var sim in GetClient().Network.Simulators)
|
||||
{
|
||||
if (av.ID != GetClient().Self.AgentID)
|
||||
Utils.LongToUInts(sim.Handle, out uint simX, out uint simY);
|
||||
HashSet<UUID> processedAvatars = [];
|
||||
|
||||
List<Avatar> avcopy = [.. sim.ObjectsAvatars.Values];
|
||||
foreach (Avatar av in avcopy)
|
||||
{
|
||||
Vector3 globalPos = av.Position;
|
||||
if (av.ParentID != 0 && GetClient().Network.CurrentSim.ObjectsPrimitives.TryGetValue(av.ParentID, out Primitive parentObj))
|
||||
if (av.ID == GetClient().Self.AgentID) continue;
|
||||
|
||||
processedAvatars.Add(av.ID);
|
||||
|
||||
Vector3 localPos = av.Position;
|
||||
if (av.ParentID != 0 && sim.ObjectsPrimitives.TryGetValue(av.ParentID, out Primitive parentObj))
|
||||
{
|
||||
globalPos = parentObj.Position + (av.Position * parentObj.Rotation);
|
||||
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);
|
||||
|
||||
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(globalPos.X),
|
||||
y = (int)Math.Round(globalPos.Y),
|
||||
x = (int)Math.Round(relX),
|
||||
y = (int)Math.Round(relY),
|
||||
z = (int)Math.Round(globalPos.Z),
|
||||
range = Math.Round(Vector3.Distance(globalPos, GetClient().Self.SimPosition), 2)
|
||||
range = dist
|
||||
};
|
||||
BetterNearMe.Add(details);
|
||||
}
|
||||
|
||||
foreach (var kvp in sim.AvatarPositions)
|
||||
{
|
||||
UUID avID = kvp.Key;
|
||||
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);
|
||||
|
||||
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";
|
||||
|
||||
NearMeDetails details = new()
|
||||
{
|
||||
id = avID.ToString(),
|
||||
name = avName,
|
||||
x = (int)Math.Round(relX),
|
||||
y = (int)Math.Round(relY),
|
||||
z = (int)Math.Round(globalPos.Z),
|
||||
range = dist
|
||||
};
|
||||
BetterNearMe.Add(details);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using OpenMetaverse;
|
||||
using SecondBotEvents.Services;
|
||||
|
||||
|
|
@ -31,7 +31,8 @@ namespace SecondBotEvents.Commands
|
|||
{
|
||||
return Failure("Invaild channel", [channel, message]);
|
||||
}
|
||||
master.DataStoreService.BotRecordLocalchatReply(message);
|
||||
string typeTag = (channelnum == 0) ? "" : $"[Channel {channelnum}] ";
|
||||
master.DataStoreService.BotRecordLocalchatReply(message, typeTag);
|
||||
GetClient().Self.Chat(message, channelnum, ChatType.Normal);
|
||||
return BasicReply("ok");
|
||||
}
|
||||
|
|
@ -52,7 +53,7 @@ namespace SecondBotEvents.Commands
|
|||
{
|
||||
return Failure("Invaild channel", [channel, message]);
|
||||
}
|
||||
master.DataStoreService.BotRecordLocalchatReply(message);
|
||||
master.DataStoreService.BotRecordLocalchatReply(message, "[Shout] ");
|
||||
GetClient().Self.Chat(message, channelnum, ChatType.Shout);
|
||||
return BasicReply("ok");
|
||||
}
|
||||
|
|
@ -73,7 +74,7 @@ namespace SecondBotEvents.Commands
|
|||
{
|
||||
return Failure("Invaild channel", [channel, message]);
|
||||
}
|
||||
master.DataStoreService.BotRecordLocalchatReply(message);
|
||||
master.DataStoreService.BotRecordLocalchatReply(message, "[Whisper] ");
|
||||
GetClient().Self.Chat(message, channelnum, ChatType.Whisper);
|
||||
return BasicReply("ok");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using OpenMetaverse;
|
||||
using SecondBotEvents.Services;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -444,5 +444,31 @@ namespace SecondBotEvents.Commands
|
|||
master.DataStoreService.BotRecordReplyIM(groupUUID, message);
|
||||
return BasicReply("Sending");
|
||||
}
|
||||
[About("fetchs a list of all pending group invitations")]
|
||||
[ReturnHints("array of GroupInvite objects")]
|
||||
[CmdTypeGet()]
|
||||
public object GetPendingGroupInvites()
|
||||
{
|
||||
return BasicReply(JsonSerializer.Serialize(master.DataStoreService.GetPendingGroupInvites(), JsonOptions.UnsafeRelaxed));
|
||||
}
|
||||
|
||||
[About("Responds to a group invitation")]
|
||||
[ArgHints("group", "the group UUID to respond to", "UUID")]
|
||||
[ArgHints("accept", "true to join, false to decline", "BOOL")]
|
||||
[CmdTypeDo()]
|
||||
public object GroupInviteRespond(string group, string accept)
|
||||
{
|
||||
if (UUID.TryParse(group, out UUID groupuuid) == false)
|
||||
{
|
||||
return Failure("Invaild group UUID", [group, accept]);
|
||||
}
|
||||
bool doAccept = accept.ToLower() == "true";
|
||||
if (doAccept == true)
|
||||
{
|
||||
GetClient().Groups.RequestJoinGroup(groupuuid);
|
||||
}
|
||||
master.DataStoreService.RemovePendingGroupInvite(groupuuid);
|
||||
return BasicReply("Done");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using OpenMetaverse;
|
||||
using SecondBotEvents.Services;
|
||||
using System;
|
||||
|
|
@ -160,6 +160,19 @@ namespace SecondBotEvents.Commands
|
|||
};
|
||||
return BasicReply(JsonSerializer.Serialize(pos, JsonOptions.UnsafeRelaxed));
|
||||
}
|
||||
[About("Fetchs the current RLV restrictions applied to the bot")]
|
||||
[ReturnHints("A JSON array of RLV rules")]
|
||||
[CmdTypeGet()]
|
||||
public object RLVStatus()
|
||||
{
|
||||
if (master.RLV == null)
|
||||
{
|
||||
return Failure("RLV service is not running");
|
||||
}
|
||||
var rules = master.RLV.Rules;
|
||||
if (rules == null) return BasicReply("[]");
|
||||
return BasicReply(JsonSerializer.Serialize(rules, JsonOptions.UnsafeRelaxed));
|
||||
}
|
||||
}
|
||||
|
||||
public class SculptysInfo
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using SecondBotEvents.Services;
|
||||
using SecondBotEvents.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
|
@ -16,6 +16,12 @@ namespace SecondBotEvents
|
|||
#endif
|
||||
LogFormater.Info("Welcome to Secondbot [Events build] version: " + AssemblyInfo.GetGitHash());
|
||||
EventsSecondBot worker = new(args);
|
||||
|
||||
AppDomain.CurrentDomain.ProcessExit += (s, e) => {
|
||||
LogFormater.Warn("SIGTERM received, initiating graceful shutdown...");
|
||||
worker.StopServices();
|
||||
Thread.Sleep(2000);
|
||||
};
|
||||
|
||||
while (worker.Exit() == false)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using OpenMetaverse;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Assets;
|
||||
using SecondBotEvents.Config;
|
||||
using System;
|
||||
|
|
@ -80,6 +80,13 @@ namespace SecondBotEvents.Services
|
|||
AutoRestartLoginTimer.Stop();
|
||||
AutoRestartLoginTimer = null;
|
||||
}
|
||||
if (client != null && client.Network != null && client.Network.Connected)
|
||||
{
|
||||
LogFormater.Warn("Service stopping - initiating logout");
|
||||
flagLogoutExpected();
|
||||
client.Network.Logout();
|
||||
Thread.Sleep(3000); // Wait for packets to send
|
||||
}
|
||||
ResetClient();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using OpenMetaverse;
|
||||
using SecondBotEvents.Config;
|
||||
using System;
|
||||
|
|
@ -15,6 +15,16 @@ using Swan;
|
|||
|
||||
namespace SecondBotEvents.Services
|
||||
{
|
||||
public class GroupInvite
|
||||
{
|
||||
public UUID GroupID { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
public UUID AgentID { get; set; }
|
||||
public string AgentName { get; set; }
|
||||
public UUID RoleID { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class DataStoreService : BotServices
|
||||
{
|
||||
protected new DataStoreConfig myConfig;
|
||||
|
|
@ -46,6 +56,7 @@ namespace SecondBotEvents.Services
|
|||
protected Dictionary<string, long> KeyValueStoreLastUsed = [];
|
||||
|
||||
protected List<CommandHistory> commandHistories = [];
|
||||
protected List<GroupInvite> PendingGroupInvites = [];
|
||||
|
||||
|
||||
|
||||
|
|
@ -469,11 +480,11 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
RecordChat(sessionid, "Bot", message);
|
||||
}
|
||||
public void BotRecordLocalchatReply(string message)
|
||||
public void BotRecordLocalchatReply(string message, string typeTag = "")
|
||||
{
|
||||
lock (localChatHistory)
|
||||
{
|
||||
localChatHistory.Add("{BOT} " + GetClient().Self.Name + ": " + message);
|
||||
localChatHistory.Add("{BOT} " + typeTag + GetClient().Self.Name + ": " + message);
|
||||
if (localChatHistory.Count <= myConfig.GetLocalChatHistoryLimit())
|
||||
{
|
||||
return;
|
||||
|
|
@ -599,6 +610,42 @@ namespace SecondBotEvents.Services
|
|||
return KnownAvatar(name, UUID.Zero);
|
||||
}
|
||||
|
||||
public void AddPendingGroupInvite(UUID groupID, UUID agentID, string agentName, string message)
|
||||
{
|
||||
lock (PendingGroupInvites)
|
||||
{
|
||||
// Avoid duplicates
|
||||
if (PendingGroupInvites.Any(i => i.GroupID == groupID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
PendingGroupInvites.Add(new GroupInvite
|
||||
{
|
||||
GroupID = groupID,
|
||||
GroupName = "Unknown Group", // Will be updated on lookup
|
||||
AgentID = agentID,
|
||||
AgentName = agentName,
|
||||
Message = message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public List<GroupInvite> GetPendingGroupInvites()
|
||||
{
|
||||
lock (PendingGroupInvites)
|
||||
{
|
||||
return [.. PendingGroupInvites];
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePendingGroupInvite(UUID groupID)
|
||||
{
|
||||
lock (PendingGroupInvites)
|
||||
{
|
||||
PendingGroupInvites.RemoveAll(i => i.GroupID == groupID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected int ObjectsAvatarsHash = 0;
|
||||
/*
|
||||
|
|
@ -1135,6 +1182,8 @@ namespace SecondBotEvents.Services
|
|||
case ChatType.Whisper:
|
||||
case ChatType.Normal:
|
||||
case ChatType.Shout:
|
||||
case ChatType.OwnerSay:
|
||||
case ChatType.RegionSayTo:
|
||||
{
|
||||
if (hard_blocked_agents.Contains(e.FromName.ToLowerInvariant()) == true)
|
||||
{
|
||||
|
|
@ -1144,7 +1193,11 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
break;
|
||||
}
|
||||
string source = "{Av}";
|
||||
if (string.IsNullOrWhiteSpace(e.Message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
string source = "{Av}";
|
||||
if (e.SourceType == ChatSourceType.Object)
|
||||
{
|
||||
source = "{Obj}";
|
||||
|
|
@ -1153,6 +1206,13 @@ namespace SecondBotEvents.Services
|
|||
{
|
||||
source = "{Sys}";
|
||||
}
|
||||
|
||||
string typeTag = "";
|
||||
if (e.Type == ChatType.Shout) typeTag = "[Shout] ";
|
||||
else if (e.Type == ChatType.Whisper) typeTag = "[Whisper] ";
|
||||
else if (e.Type == ChatType.OwnerSay) typeTag = "[OwnerSay] ";
|
||||
else if (e.Type == ChatType.RegionSayTo) typeTag = "[RegionSayTo] ";
|
||||
|
||||
lock (localChatHistory)
|
||||
{
|
||||
if (source == "{Av}")
|
||||
|
|
@ -1160,7 +1220,7 @@ namespace SecondBotEvents.Services
|
|||
AddAvatar(e.SourceID, e.FromName);
|
||||
}
|
||||
|
||||
localChatHistory.Add(source+ " "+e.FromName + ": " + e.Message);
|
||||
localChatHistory.Add(source + " " + typeTag + e.FromName + ": " + e.Message);
|
||||
if (localChatHistory.Count <= myConfig.GetLocalChatHistoryLimit())
|
||||
{
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using OpenMetaverse;
|
||||
using SecondBotEvents.Config;
|
||||
using System;
|
||||
|
|
@ -233,10 +233,13 @@ namespace SecondBotEvents.Services
|
|||
Dictionary<string, string> reply = new()
|
||||
{
|
||||
{ "message", e.Message },
|
||||
{ "fromuuid", e.AgentID.ToString() }
|
||||
{ "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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Discord;
|
||||
using Discord;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
|
|
@ -242,6 +242,7 @@ namespace SecondBotEvents.Services
|
|||
EventsSecondBot instance = null;
|
||||
readonly Regex rlv_regex = new(@"(?<behaviour>[^:=]+)(:(?<option>[^=]*))?=(?<param>\w+)", RegexOptions.Compiled);
|
||||
readonly List<RLVRule> rules = [];
|
||||
public List<RLVRule> Rules => rules;
|
||||
System.Timers.Timer CleanupTimer;
|
||||
|
||||
void StartTimer()
|
||||
|
|
|
|||
Loading…
Reference in a new issue