29 using System.Collections.Generic;
31 using System.Reflection;
36 using OpenMetaverse.StructuredData;
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40 using OpenSim.Services.Interfaces;
44 namespace OpenSim.Groups
46 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"GroupsMessagingModule")]
49 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51 private List<Scene> m_sceneList =
new List<Scene>();
59 private bool m_groupMessagingEnabled;
60 private bool m_debugEnabled;
65 private bool m_messageOnlineAgentsOnly;
79 private ExpiringCache<UUID, PresenceInfo[]> m_usersOnlineCache;
81 private int m_usersOnlineCacheExpirySeconds = 20;
83 private Dictionary<UUID, List<string>> m_groupsAgentsDroppedFromChatSession =
new Dictionary<UUID, List<string>>();
84 private Dictionary<UUID, List<string>> m_groupsAgentsInvitedToChatSession =
new Dictionary<UUID, List<string>>();
86 #region Region Module interfaceBase Members
90 IConfig groupsConfig = config.Configs[
"Groups"];
92 if (groupsConfig == null)
98 if ((groupsConfig.GetBoolean(
"Enabled",
false) ==
false)
99 || (groupsConfig.GetString(
"MessagingModule",
"") != Name))
101 m_groupMessagingEnabled =
false;
105 m_groupMessagingEnabled = groupsConfig.GetBoolean(
"MessagingEnabled",
true);
107 if (!m_groupMessagingEnabled)
110 m_messageOnlineAgentsOnly = groupsConfig.GetBoolean(
"MessageOnlineUsersOnly",
false);
112 if (m_messageOnlineAgentsOnly)
114 m_usersOnlineCache =
new ExpiringCache<UUID, PresenceInfo[]>();
118 m_log.Error(
"[Groups.Messaging]: GroupsMessagingModule V2 requires MessageOnlineUsersOnly = true");
119 m_groupMessagingEnabled =
false;
123 m_debugEnabled = groupsConfig.GetBoolean(
"MessagingDebugEnabled", m_debugEnabled);
126 "[Groups.Messaging]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
127 m_messageOnlineAgentsOnly, m_debugEnabled);
132 if (!m_groupMessagingEnabled)
136 m_sceneList.Add(scene);
138 scene.EventManager.OnNewClient += OnNewClient;
139 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
140 scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
141 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
142 scene.EventManager.OnClientLogin += OnClientLogin;
147 "debug groups messaging verbose",
148 "debug groups messaging verbose <true|false>",
149 "This setting turns on very verbose groups messaging debugging",
150 HandleDebugGroupsMessagingVerbose);
155 if (!m_groupMessagingEnabled)
158 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
163 if (m_groupData == null)
165 m_log.Error(
"[Groups.Messaging]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled.");
173 if (m_msgTransferModule == null)
175 m_log.Error(
"[Groups.Messaging]: Could not get MessageTransferModule");
183 if (m_UserManagement == null)
185 m_log.Error(
"[Groups.Messaging]: Could not get IUserManagement, GroupsMessagingModule is now disabled.");
190 if (m_presenceService == null)
191 m_presenceService = scene.PresenceService;
196 if (!m_groupMessagingEnabled)
199 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
201 m_sceneList.Remove(scene);
202 scene.EventManager.OnNewClient -= OnNewClient;
203 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
204 scene.EventManager.OnClientLogin -= OnClientLogin;
210 if (!m_groupMessagingEnabled)
213 if (m_debugEnabled) m_log.Debug(
"[Groups.Messaging]: Shutting down GroupsMessagingModule module.");
218 m_msgTransferModule = null;
221 public Type ReplaceableInterface
228 get {
return "Groups Messaging Module V2"; }
238 private void HandleDebugGroupsMessagingVerbose(
object modules,
string[] args)
242 MainConsole.Instance.Output(
"Usage: debug groups messaging verbose <true|false>");
246 bool verbose =
false;
247 if (!
bool.TryParse(args[4], out verbose))
249 MainConsole.Instance.Output(
"Usage: debug groups messaging verbose <true|false>");
253 m_debugEnabled = verbose;
255 MainConsole.Instance.OutputFormat(
"{0} verbose logging set to {1}", Name, m_debugEnabled);
264 m_log.DebugFormat(
"[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
266 GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null);
268 if (groupInfo != null)
280 SendMessageToGroup(im, groupID,
UUID.Zero, null);
284 GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func<GroupMembersData, bool> sendCondition)
286 int requestStartTick = Environment.TickCount;
292 List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(UUID.Zero.ToString(), groupID);
294 int groupMembersCount = groupMembers.Count;
299 string[] t1 = groupMembers.ConvertAll<
string>(gmd => gmd.AgentID.ToString()).ToArray();
304 if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
306 onlineAgents = m_presenceService.GetAgents(t1);
307 m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
310 HashSet<string> onlineAgentsUuidSet =
new HashSet<string>();
311 Array.ForEach<
PresenceInfo>(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID));
313 groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
320 im.imSessionID = groupID.Guid;
322 IClientAPI thisClient = GetActiveClient(fromAgentID);
323 if (thisClient != null)
325 im.RegionID = thisClient.Scene.RegionInfo.RegionID.Guid;
330 ExtendedGroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), groupID, null);
331 if (groupInfo != null)
332 im.binaryBucket = Util.StringToBytes256(groupInfo.GroupName);
336 im.toAgentID = im.fromAgentID;
338 ProcessMessageFromGroupSession(im);
340 List<UUID> regions =
new List<UUID>();
341 List<UUID> clientsAlreadySent =
new List<UUID>();
346 if (member.
AgentID.Guid == im.fromAgentID)
349 if (clientsAlreadySent.Contains(member.
AgentID))
352 clientsAlreadySent.Add(member.AgentID);
354 if (sendCondition != null)
356 if (!sendCondition(member))
360 "[Groups.Messaging]: Not sending to {0} as they do not fulfill send condition",
366 else if (hasAgentDroppedGroupChatSession(member.
AgentID.ToString(), groupID))
370 m_log.DebugFormat(
"[Groups.Messaging]: {0} has dropped session, not delivering to them", member.AgentID);
375 im.toAgentID = member.AgentID.Guid;
382 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: Delivering to {0} via Grid", member.AgentID);
384 bool reallySend =
true;
385 if (onlineAgents != null)
387 PresenceInfo presence = onlineAgents.First(p => p.UserID == member.AgentID.ToString());
388 if (regions.Contains(presence.
RegionID))
391 regions.Add(presence.RegionID);
399 m_msgTransferModule.SendInstantMessage(msg, delegate(
bool success) { });
405 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name);
407 ProcessMessageFromGroupSession(im);
414 "[Groups.Messaging]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms",
415 groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick);
418 #region SimGridEventHandlers
422 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name);
427 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name);
429 ResetAgentGroupChatSessions(client.
AgentId.ToString());
434 sp.ControllingClient.OnInstantMessage += OnInstantMessage;
439 sp.ControllingClient.OnInstantMessage -= OnInstantMessage;
454 m_log.DebugFormat(
"[Groups.Messaging]: {0} called, IM from region {1}",
455 System.Reflection.MethodBase.GetCurrentMethod().Name, regionID);
457 DebugGridInstantMessage(msg);
461 if ((msg.
fromGroup ==
true) && (msg.
dialog == (byte)InstantMessageDialog.SessionSend))
468 Scene aScene = m_sceneList[0];
469 GridRegion regionOfOrigin = aScene.GridService.GetRegionByUUID(aScene.RegionInfo.ScopeID, regionID);
471 List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(UUID.Zero.ToString(), GroupID);
477 foreach (
Scene s
in m_sceneList)
479 s.ForEachScenePresence(sp =>
488 return gmd.AgentID == sp.UUID;
493 m_log.DebugFormat(
"[Groups.Messaging]: skipping agent {0} because he is not a member of the group", sp.UUID);
500 if (regionOfOrigin != null)
502 AgentCircuitData aCircuit = s.AuthenticateHandler.GetAgentCircuitData(sp.UUID);
503 if (aCircuit != null)
508 m_log.DebugFormat(
"[Groups.Messaging]: skipping agent {0} because he has an agent in region of origin", sp.UUID);
514 m_log.DebugFormat(
"[Groups.Messaging]: not skipping agent {0}", sp.UUID);
519 UUID AgentID = sp.UUID;
520 msg.toAgentID = AgentID.Guid;
522 if (!hasAgentDroppedGroupChatSession(AgentID.ToString(), GroupID))
524 if (!hasAgentBeenInvitedToGroupChatSession(AgentID.ToString(), GroupID))
525 AddAgentToSession(AgentID, GroupID, msg);
528 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", sp.Name);
530 ProcessMessageFromGroupSession(msg);
541 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID);
549 case (byte)InstantMessageDialog.SessionAdd:
550 AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
553 case (byte)InstantMessageDialog.SessionDrop:
554 AgentDroppedFromGroupChatSession(AgentID.ToString(), GroupID);
557 case (byte)InstantMessageDialog.SessionSend:
564 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: Delivering to {0} locally", client.Name);
566 if (!hasAgentDroppedGroupChatSession(toAgentID.ToString(), GroupID))
568 if (!hasAgentBeenInvitedToGroupChatSession(toAgentID.ToString(), GroupID))
571 AddAgentToSession(toAgentID, GroupID, msg);
573 client.SendInstantMessage(msg);
578 m_log.WarnFormat(
"[Groups.Messaging]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID);
583 m_log.WarnFormat(
"[Groups.Messaging]: I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.
dialog).ToString());
592 AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
594 IClientAPI activeClient = GetActiveClient(AgentID);
595 if (activeClient != null)
597 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null);
598 if (groupInfo != null)
600 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: Sending chatterbox invite instant message");
605 eq.ChatterboxInvitation(
607 , groupInfo.GroupName
620 , OpenMetaverse.Utils.StringToBytes(groupInfo.GroupName)
623 eq.ChatterBoxSessionAgentListUpdates(
643 m_log.DebugFormat(
"[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
645 DebugGridInstantMessage(im);
649 if ((im.
dialog == (byte)InstantMessageDialog.SessionGroupStart))
651 if (m_debugEnabled) m_log.InfoFormat(
"[Groups.Messaging]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID);
656 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null);
658 if (groupInfo != null)
660 AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
662 ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID);
665 queue.ChatterBoxSessionAgentListUpdates(
677 if ((im.
dialog == (byte)InstantMessageDialog.SessionSend))
683 m_log.DebugFormat(
"[Groups.Messaging]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString());
686 AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
688 SendMessageToGroup(im, GroupID);
694 void ChatterBoxSessionStartReplyViaCaps(
IClientAPI remoteClient,
string groupName, UUID groupID)
696 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
699 moderatedMap.Add(
"voice", OSD.FromBoolean(
false));
702 sessionMap.Add(
"moderated_mode", moderatedMap);
703 sessionMap.Add(
"session_name", OSD.FromString(groupName));
704 sessionMap.Add(
"type", OSD.FromInteger(0));
705 sessionMap.Add(
"voice_enabled", OSD.FromBoolean(
false));
708 bodyMap.Add(
"session_id", OSD.FromUUID(groupID));
709 bodyMap.Add(
"temp_session_id", OSD.FromUUID(groupID));
710 bodyMap.Add(
"success", OSD.FromBoolean(
true));
711 bodyMap.Add(
"session_info", sessionMap);
717 queue.Enqueue(queue.BuildEvent(
"ChatterBoxSessionStartReply", bodyMap), remoteClient.
AgentId);
724 if (m_debugEnabled && im.
dialog != (byte)InstantMessageDialog.MessageFromAgent)
726 m_log.WarnFormat(
"[Groups.Messaging]: IM: fromGroup({0})", im.fromGroup ?
"True" :
"False");
727 m_log.WarnFormat(
"[Groups.Messaging]: IM: Dialog({0})", ((InstantMessageDialog)im.
dialog).ToString());
728 m_log.WarnFormat(
"[Groups.Messaging]: IM: fromAgentID({0})", im.fromAgentID.ToString());
729 m_log.WarnFormat(
"[Groups.Messaging]: IM: fromAgentName({0})", im.fromAgentName.ToString());
730 m_log.WarnFormat(
"[Groups.Messaging]: IM: imSessionID({0})", im.imSessionID.ToString());
731 m_log.WarnFormat(
"[Groups.Messaging]: IM: message({0})", im.message.ToString());
732 m_log.WarnFormat(
"[Groups.Messaging]: IM: offline({0})", im.offline.ToString());
733 m_log.WarnFormat(
"[Groups.Messaging]: IM: toAgentID({0})", im.toAgentID.ToString());
734 m_log.WarnFormat(
"[Groups.Messaging]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket,
"BinaryBucket"));
743 private IClientAPI GetActiveClient(UUID agentID)
745 if (m_debugEnabled) m_log.WarnFormat(
"[Groups.Messaging]: Looking for local client {0}", agentID);
750 foreach (
Scene scene
in m_sceneList)
757 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: Found root agent for client : {0}", sp.ControllingClient.Name);
758 return sp.ControllingClient;
762 if (m_debugEnabled) m_log.DebugFormat(
"[Groups.Messaging]: Found child agent for client : {0}", sp.ControllingClient.Name);
763 child = sp.ControllingClient;
771 if (m_debugEnabled) m_log.WarnFormat(
"[Groups.Messaging]: Could not find local client for agent : {0}", agentID);
775 if (m_debugEnabled) m_log.WarnFormat(
"[Groups.Messaging]: Returning child agent for client : {0}", child.Name);
782 #region GroupSessionTracking
786 foreach (List<string> agentList
in m_groupsAgentsDroppedFromChatSession.Values)
787 agentList.Remove(agentID);
789 foreach (List<string> agentList
in m_groupsAgentsInvitedToChatSession.Values)
790 agentList.Remove(agentID);
796 return m_groupsAgentsInvitedToChatSession.ContainsKey(groupID)
797 && m_groupsAgentsInvitedToChatSession[groupID].Contains(agentID);
804 return m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID)
805 && m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID);
810 if (m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID))
813 if (!m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID))
815 m_groupsAgentsDroppedFromChatSession[groupID].Add(agentID);
823 CreateGroupChatSessionTracking(groupID);
826 if (m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID))
828 m_groupsAgentsDroppedFromChatSession[groupID].Remove(agentID);
832 if (!m_groupsAgentsInvitedToChatSession[groupID].Contains(agentID))
833 m_groupsAgentsInvitedToChatSession[groupID].Add(agentID);
836 private void CreateGroupChatSessionTracking(UUID groupID)
838 if (!m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID))
840 m_groupsAgentsDroppedFromChatSession.Add(groupID,
new List<string>());
841 m_groupsAgentsInvitedToChatSession.Add(groupID,
new List<string>());
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
void AgentInvitedToGroupChatSession(string agentID, UUID groupID)
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
void SendMessageToGroup(GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func< GroupMembersData, bool > sendCondition)
Send a message to all the members of a group that fulfill a condition.
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
void ResetAgentGroupChatSessions(string agentID)
Provide mechanisms for messaging groups.
OpenMetaverse.StructuredData.OSDMap OSDMap
bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID)
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
Dictionary< ulong, string > ChildrenCapSeeds
Seed caps for neighbor regions that the user can see into
Circuit data for an agent. Connection information shared between regions that accept UDP connections ...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
bool StartGroupChatSession(UUID agentID, UUID groupID)
Not really needed, but does confirm that the group exists.
OpenSim.Services.Interfaces.PresenceInfo PresenceInfo
void SendMessageToGroup(GridInstantMessage im, UUID groupID)
Send a message to each member of a group whose chat session is active.
bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID)
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
OpenSim.Services.Interfaces.GridRegion GridRegion
void AgentDroppedFromGroupChatSession(string agentID, UUID groupID)
This maintains the relationship between a UUID and a user name.