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;
45 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"GroupsMessagingModule")]
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50 private List<Scene> m_sceneList =
new List<Scene>();
58 private bool m_groupMessagingEnabled;
59 private bool m_debugEnabled;
64 private bool m_messageOnlineAgentsOnly;
78 private ExpiringCache<UUID, PresenceInfo[]> m_usersOnlineCache;
80 private int m_usersOnlineCacheExpirySeconds = 20;
82 #region Region Module interfaceBase Members
86 IConfig groupsConfig = config.Configs[
"Groups"];
88 if (groupsConfig == null)
97 if ((groupsConfig.GetBoolean(
"Enabled",
false) ==
false)
98 || (groupsConfig.GetString(
"MessagingModule",
"") != Name))
100 m_groupMessagingEnabled =
false;
104 m_groupMessagingEnabled = groupsConfig.GetBoolean(
"MessagingEnabled",
true);
106 if (!m_groupMessagingEnabled)
111 m_messageOnlineAgentsOnly = groupsConfig.GetBoolean(
"MessageOnlineUsersOnly",
false);
113 if (m_messageOnlineAgentsOnly)
114 m_usersOnlineCache =
new ExpiringCache<UUID, PresenceInfo[]>();
116 m_debugEnabled = groupsConfig.GetBoolean(
"MessagingDebugEnabled", m_debugEnabled);
120 "[GROUPS-MESSAGING]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
121 m_messageOnlineAgentsOnly, m_debugEnabled);
126 if (!m_groupMessagingEnabled)
134 "debug groups messaging verbose",
135 "debug groups messaging verbose <true|false>",
136 "This setting turns on very verbose groups messaging debugging",
137 HandleDebugGroupsMessagingVerbose);
142 if (!m_groupMessagingEnabled)
145 if (m_debugEnabled) m_log.DebugFormat(
"[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
150 if (m_groupData == null)
152 m_log.Error(
"[GROUPS-MESSAGING]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled.");
154 m_groupMessagingEnabled =
false;
161 if (m_msgTransferModule == null)
163 m_log.Error(
"[GROUPS-MESSAGING]: Could not get MessageTransferModule");
165 m_groupMessagingEnabled =
false;
169 if (m_presenceService == null)
170 m_presenceService = scene.PresenceService;
172 m_sceneList.Add(scene);
174 scene.EventManager.OnNewClient += OnNewClient;
175 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
176 scene.EventManager.OnClientLogin += OnClientLogin;
181 if (!m_groupMessagingEnabled)
184 if (m_debugEnabled) m_log.DebugFormat(
"[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
186 m_sceneList.Remove(scene);
191 if (!m_groupMessagingEnabled)
194 if (m_debugEnabled) m_log.Debug(
"[GROUPS-MESSAGING]: Shutting down GroupsMessagingModule module.");
196 foreach (
Scene scene
in m_sceneList)
198 scene.EventManager.OnNewClient -= OnNewClient;
199 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
205 m_msgTransferModule = null;
208 public Type ReplaceableInterface
215 get {
return "GroupsMessagingModule"; }
220 #region ISharedRegionModule Members
229 private void HandleDebugGroupsMessagingVerbose(
object modules,
string[] args)
233 MainConsole.Instance.Output(
"Usage: debug groups messaging verbose <true|false>");
237 bool verbose =
false;
238 if (!
bool.TryParse(args[4], out verbose))
240 MainConsole.Instance.Output(
"Usage: debug groups messaging verbose <true|false>");
244 m_debugEnabled = verbose;
246 MainConsole.Instance.OutputFormat(
"{0} verbose logging set to {1}", Name, m_debugEnabled);
255 m_log.DebugFormat(
"[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
257 GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID, groupID, null);
259 if (groupInfo != null)
275 GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func<GroupMembersData, bool> sendCondition)
277 int requestStartTick = Environment.TickCount;
279 List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(sendingAgentForGroupCalls, groupID);
280 int groupMembersCount = groupMembers.Count;
281 HashSet<string> attemptDeliveryUuidSet = null;
283 if (m_messageOnlineAgentsOnly)
285 string[] t1 = groupMembers.ConvertAll<
string>(gmd => gmd.AgentID.ToString()).ToArray();
291 if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
293 onlineAgents = m_presenceService.GetAgents(t1);
294 m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
297 attemptDeliveryUuidSet
298 =
new HashSet<string>(Array.ConvertAll<
PresenceInfo,
string>(onlineAgents, pi => pi.UserID));
311 attemptDeliveryUuidSet
312 =
new HashSet<string>(groupMembers.ConvertAll<
string>(gmd => gmd.AgentID.ToString()));
316 "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members",
317 groupID, groupMembers.Count);
322 if (sendCondition != null)
324 if (!sendCondition(member))
328 "[GROUPS-MESSAGING]: Not sending to {0} as they do not fulfill send condition",
334 else if (m_groupData.hasAgentDroppedGroupChatSession(member.
AgentID, groupID))
339 "[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID);
346 msg.imSessionID = im.imSessionID;
347 msg.fromAgentName = im.fromAgentName;
348 msg.message = im.message;
349 msg.dialog = im.dialog;
350 msg.offline = im.offline;
351 msg.ParentEstateID = im.ParentEstateID;
352 msg.Position = im.Position;
353 msg.RegionID = im.RegionID;
354 msg.binaryBucket = im.binaryBucket;
355 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
357 msg.fromAgentID = im.fromAgentID;
358 msg.fromGroup =
true;
360 msg.toAgentID = member.AgentID.Guid;
362 if (attemptDeliveryUuidSet.Contains(member.
AgentID.ToString()))
367 int startTick = Environment.TickCount;
370 m_msgTransferModule.SendInstantMessage(msg, delegate(
bool success) { });
374 "[GROUPS-MESSAGING]: Delivering to {0} via grid took {1} ms",
375 member.AgentID, Environment.TickCount - startTick);
379 int startTick = Environment.TickCount;
381 ProcessMessageFromGroupSession(msg, client);
386 "[GROUPS-MESSAGING]: Delivering to {0} locally took {1} ms",
387 member.AgentID, Environment.TickCount - startTick);
392 int startTick = Environment.TickCount;
394 m_msgTransferModule.HandleUndeliverableMessage(msg, delegate(
bool success) { });
398 "[GROUPS-MESSAGING]: Handling undeliverable message for {0} took {1} ms",
399 member.AgentID, Environment.TickCount - startTick);
405 "[GROUPS-MESSAGING]: Total SendMessageToGroup for group {0} with {1} members, {2} candidates for delivery took {3} ms",
406 groupID, groupMembersCount, attemptDeliveryUuidSet.Count(), Environment.TickCount - requestStartTick);
409 #region SimGridEventHandlers
413 if (m_debugEnabled) m_log.DebugFormat(
"[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name);
418 if (m_debugEnabled) m_log.DebugFormat(
"[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name);
420 client.OnInstantMessage += OnInstantMessage;
433 m_log.DebugFormat(
"[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
435 DebugGridInstantMessage(msg);
440 ((msg.
dialog == (byte)InstantMessageDialog.SessionSend)
441 || (msg.
dialog == (byte)InstantMessageDialog.SessionAdd)
442 || (msg.
dialog == (byte)InstantMessageDialog.SessionDrop)))
446 if (msg.
dialog == (byte)InstantMessageDialog.SessionSend)
453 m_log.DebugFormat(
"[GROUPS-MESSAGING]: Delivering to {0} locally", client.Name);
457 m_log.WarnFormat(
"[GROUPS-MESSAGING]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID);
463 ProcessMessageFromGroupSession(msg, client);
471 "[GROUPS-MESSAGING]: Session message from {0} going to agent {1}, sessionID {2}, type {3}",
472 msg.fromAgentName, msg.toAgentID, msg.imSessionID, (InstantMessageDialog)msg.
dialog);
479 case (byte)InstantMessageDialog.SessionAdd:
480 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
483 case (byte)InstantMessageDialog.SessionDrop:
484 m_groupData.AgentDroppedFromGroupChatSession(AgentID, GroupID);
487 case (byte)InstantMessageDialog.SessionSend:
488 if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID)
489 && !m_groupData.hasAgentBeenInvitedToGroupChatSession(AgentID, GroupID)
494 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
496 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
497 if (groupInfo != null)
499 if (m_debugEnabled) m_log.DebugFormat(
"[GROUPS-MESSAGING]: Sending chatterbox invite instant message");
504 eq.ChatterboxInvitation(
506 , groupInfo.GroupName
519 , Utils.StringToBytes(groupInfo.GroupName)
522 eq.ChatterBoxSessionAgentListUpdates(
534 else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID))
538 client.SendInstantMessage(msg);
544 client.SendInstantMessage(msg);
557 m_log.DebugFormat(
"[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
559 DebugGridInstantMessage(im);
563 if ((im.
dialog == (byte)InstantMessageDialog.SessionGroupStart))
565 if (m_debugEnabled) m_log.InfoFormat(
"[GROUPS-MESSAGING]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID);
570 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
572 if (groupInfo != null)
574 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
576 ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.
GroupName, GroupID);
579 queue.ChatterBoxSessionAgentListUpdates(
591 if ((im.
dialog == (byte)InstantMessageDialog.SessionSend))
597 m_log.DebugFormat(
"[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString());
600 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
602 SendMessageToGroup(im, GroupID);
608 void ChatterBoxSessionStartReplyViaCaps(
IClientAPI remoteClient,
string groupName, UUID groupID)
610 if (m_debugEnabled) m_log.DebugFormat(
"[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
613 moderatedMap.Add(
"voice", OSD.FromBoolean(
false));
616 sessionMap.Add(
"moderated_mode", moderatedMap);
617 sessionMap.Add(
"session_name", OSD.FromString(groupName));
618 sessionMap.Add(
"type", OSD.FromInteger(0));
619 sessionMap.Add(
"voice_enabled", OSD.FromBoolean(
false));
622 bodyMap.Add(
"session_id", OSD.FromUUID(groupID));
623 bodyMap.Add(
"temp_session_id", OSD.FromUUID(groupID));
624 bodyMap.Add(
"success", OSD.FromBoolean(
true));
625 bodyMap.Add(
"session_info", sessionMap);
631 queue.Enqueue(queue.BuildEvent(
"ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId);
638 if (m_debugEnabled && im.
dialog != (byte)InstantMessageDialog.MessageFromAgent)
640 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: fromGroup({0})", im.fromGroup ?
"True" :
"False");
641 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: Dialog({0})", (InstantMessageDialog)im.
dialog);
642 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: fromAgentID({0})", im.fromAgentID);
643 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: fromAgentName({0})", im.fromAgentName);
644 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: imSessionID({0})", im.imSessionID);
645 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: message({0})", im.message);
646 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: offline({0})", im.offline);
647 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: toAgentID({0})", im.toAgentID);
648 m_log.DebugFormat(
"[GROUPS-MESSAGING]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket,
"BinaryBucket"));
657 private IClientAPI GetActiveClient(UUID agentID)
660 m_log.DebugFormat(
"[GROUPS-MESSAGING]: Looking for local client {0}", agentID);
665 foreach (
Scene scene
in m_sceneList)
673 m_log.DebugFormat(
"[GROUPS-MESSAGING]: Found root agent for client : {0}", sp.ControllingClient.Name);
675 return sp.ControllingClient;
680 m_log.DebugFormat(
"[GROUPS-MESSAGING]: Found child agent for client : {0}", sp.ControllingClient.Name);
682 child = sp.ControllingClient;
691 m_log.DebugFormat(
"[GROUPS-MESSAGING]: Could not find local client for agent : {0}", agentID);
696 m_log.DebugFormat(
"[GROUPS-MESSAGING]: Returning child agent for client : {0}", child.Name);
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void SendMessageToGroup(GridInstantMessage im, UUID groupID)
Send a message to each member of a group whose chat session is active.
Provide mechanisms for messaging groups.
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
OpenMetaverse.StructuredData.OSDMap OSDMap
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
Interactive OpenSim region server
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
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.
bool StartGroupChatSession(UUID agentID, UUID groupID)
Not really needed, but does confirm that the group exists.
OpenSim.Services.Interfaces.PresenceInfo PresenceInfo
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...