public List <UUID> GetOnlineFriends(UUID foreignUserID, List <string> friends) { List <UUID> online = new List <UUID>(); if (m_FriendsService == null || m_PresenceService == null) { m_log.WarnFormat("[USER AGENT SERVICE]: Unable to get online friends because friends or presence services are missing"); return(online); } m_log.DebugFormat("[USER AGENT SERVICE]: Foreign user {0} wants to know status of {1} local friends", foreignUserID, friends.Count); // First, let's double check that the reported friends are, indeed, friends of that user // And let's check that the secret matches and the rights List <string> usersToBeNotified = new List <string>(); foreach (string uui in friends) { UUID localUserID; string secret = string.Empty, tmp = string.Empty; if (Util.ParseUniversalUserIdentifier(uui, out localUserID, out tmp, out tmp, out tmp, out secret)) { FriendInfo[] friendInfos = m_FriendsService.GetFriends(localUserID); foreach (FriendInfo finfo in friendInfos) { if (finfo.Friend.StartsWith(foreignUserID.ToString()) && finfo.Friend.EndsWith(secret) && (finfo.TheirFlags & (int)FriendRights.CanSeeOnline) != 0 && (finfo.TheirFlags != -1)) { // great! usersToBeNotified.Add(localUserID.ToString()); } } } } // Now, let's find out their status m_log.DebugFormat("[USER AGENT SERVICE]: GetOnlineFriends: user has {0} local friends with status rights", usersToBeNotified.Count); // First, let's send notifications to local users who are online in the home grid PresenceInfo[] friendSessions = m_PresenceService.GetAgents(usersToBeNotified.ToArray()); if (friendSessions != null && friendSessions.Length > 0) { foreach (PresenceInfo pi in friendSessions) { UUID presenceID; if (UUID.TryParse(pi.UserID, out presenceID)) { online.Add(presenceID); } } } return(online); }
public PresenceInfo[] GetAgents(string[] userIDs) { // Don't bother potentially making a useless network call if we not going to ask for any users anyway. if (userIDs.Length == 0) { return(new PresenceInfo[0]); } return(m_PresenceService.GetAgents(userIDs)); }
byte[] GetAgents(Dictionary <string, object> request) { string[] userIDs; if (!request.ContainsKey("uuids")) { m_log.DebugFormat("[PRESENCE HANDLER]: GetAgents called without required uuids argument"); return(FailureResult()); } if (!(request["uuids"] is List <string>)) { m_log.DebugFormat("[PRESENCE HANDLER]: GetAgents input argument was of unexpected type {0}", request["uuids"].GetType().ToString()); return(FailureResult()); } userIDs = ((List <string>)request["uuids"]).ToArray(); PresenceInfo[] pinfos = m_PresenceService.GetAgents(userIDs); Dictionary <string, object> result = new Dictionary <string, object>(); if ((pinfos == null) || ((pinfos != null) && (pinfos.Length == 0))) { result["result"] = "null"; } else { int i = 0; foreach (PresenceInfo pinfo in pinfos) { Dictionary <string, object> rinfoDict = pinfo.ToKeyValuePairs(); result["presence" + i] = rinfoDict; i++; } } string xmlString = ServerUtils.BuildXmlResponse(result); //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return(encoding.GetBytes(xmlString)); }
public void AddFriendship(IClientAPI client, UUID friendID) { StoreFriendships(client.AgentId, friendID); ICallingCardModule ccm = client.Scene.RequestModuleInterface <ICallingCardModule>(); if (ccm != null) { ccm.CreateCallingCard(client.AgentId, friendID, UUID.Zero); } // Update the local cache. RecacheFriends(client); // // Notify the friend // // Try Local if (LocalFriendshipApproved(client.AgentId, client.Name, friendID)) { client.SendAgentOnline(new UUID[] { friendID }); return; } // The friend is not here PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() }); if (friendSessions != null && friendSessions.Length > 0) { PresenceInfo friendSession = friendSessions[0]; if (friendSession != null) { GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); m_FriendsSimConnector.FriendshipApproved(region, client.AgentId, client.Name, friendID); client.SendAgentOnline(new UUID[] { friendID }); } } }
public void SendMessageToGroup( GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func <GroupMembersData, bool> sendCondition) { int requestStartTick = Environment.TickCount; UUID fromAgentID = new UUID(im.fromAgentID); // Unlike current XmlRpcGroups, Groups V2 can accept UUID.Zero when a perms check for the requesting agent // is not necessary. List <GroupMembersData> groupMembers = m_groupData.GetGroupMembers(UUID.Zero.ToString(), groupID); int groupMembersCount = groupMembers.Count; PresenceInfo[] onlineAgents = null; // In V2 we always only send to online members. // Sending to offline members is not an option. // We cache in order not to overwhelm the presence service on large grids with many groups. This does // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed. // (assuming this is the same across all grid simulators). if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents)) { string[] t1 = groupMembers.ConvertAll <string>(gmd => gmd.AgentID.ToString()).ToArray(); onlineAgents = m_presenceService.GetAgents(t1); m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds); } HashSet <string> onlineAgentsUuidSet = new HashSet <string>(); Array.ForEach <PresenceInfo>(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID)); groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList(); // if (m_debugEnabled) // m_log.DebugFormat( // "[Groups.Messaging]: SendMessageToGroup called for group {0} with {1} visible members, {2} online", // groupID, groupMembersCount, groupMembers.Count()); im.imSessionID = groupID.Guid; im.fromGroup = true; IClientAPI thisClient = GetActiveClient(fromAgentID); if (thisClient != null) { im.RegionID = thisClient.Scene.RegionInfo.RegionID.Guid; } if ((im.binaryBucket == null) || (im.binaryBucket.Length == 0) || ((im.binaryBucket.Length == 1 && im.binaryBucket[0] == 0))) { ExtendedGroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), groupID, null); if (groupInfo != null) { im.binaryBucket = Util.StringToBytes256(groupInfo.GroupName); } } // Send to self first of all im.toAgentID = im.fromAgentID; im.fromGroup = true; ProcessMessageFromGroupSession(im); List <UUID> regions = new List <UUID>(); List <UUID> clientsAlreadySent = new List <UUID>(); // Then send to everybody else foreach (GroupMembersData member in groupMembers) { if (member.AgentID.Guid == im.fromAgentID) { continue; } if (clientsAlreadySent.Contains(member.AgentID)) { continue; } clientsAlreadySent.Add(member.AgentID); if (sendCondition != null) { if (!sendCondition(member)) { if (m_debugEnabled) { m_log.DebugFormat( "[Groups.Messaging]: Not sending to {0} as they do not fulfill send condition", member.AgentID); } continue; } } else if (hasAgentDroppedGroupChatSession(member.AgentID.ToString(), groupID)) { // Don't deliver messages to people who have dropped this session if (m_debugEnabled) { m_log.DebugFormat("[Groups.Messaging]: {0} has dropped session, not delivering to them", member.AgentID); } continue; } im.toAgentID = member.AgentID.Guid; IClientAPI client = GetActiveClient(member.AgentID); if (client == null) { // If they're not local, forward across the grid // BUT do it only once per region, please! Sim would be even better! if (m_debugEnabled) { m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} via Grid", member.AgentID); } bool reallySend = true; if (onlineAgents != null) { PresenceInfo presence = onlineAgents.First(p => p.UserID == member.AgentID.ToString()); if (regions.Contains(presence.RegionID)) { reallySend = false; } else { regions.Add(presence.RegionID); } } if (reallySend) { // We have to create a new IM structure because the transfer module // uses async send GridInstantMessage msg = new GridInstantMessage(im, true); m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); } } else { // Deliver locally, directly if (m_debugEnabled) { m_log.DebugFormat("[Groups.Messaging]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); } ProcessMessageFromGroupSession(im); } } if (m_debugEnabled) { m_log.DebugFormat( "[Groups.Messaging]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms", groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick); } }
protected void HandleFriendsShowCommand(string module, string[] cmd) { Dictionary <string, object> options = new Dictionary <string, object>(); OptionSet optionSet = new OptionSet().Add("c|cache", delegate(string v) { options["cache"] = v != null; }); List <string> mainParams = optionSet.Parse(cmd); if (mainParams.Count != 4) { MainConsole.Instance.Output("Usage: friends show [--cache] <first-name> <last-name>"); return; } string firstName = mainParams[2]; string lastName = mainParams[3]; UUID userId = m_userManagementModule.GetUserIdByName(firstName, lastName); // UserAccount ua // = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, firstName, lastName); if (userId == UUID.Zero) { MainConsole.Instance.Output("No such user as {0} {1}", firstName, lastName); return; } FriendInfo[] friends; if (options.ContainsKey("cache")) { if (!m_friendsModule.AreFriendsCached(userId)) { MainConsole.Instance.Output("No friends cached on this simulator for {0} {1}", firstName, lastName); return; } else { friends = m_friendsModule.GetFriendsFromCache(userId); } } else { // FIXME: We're forced to do this right now because IFriendsService has no region connectors. We can't // just expose FriendsModule.GetFriendsFromService() because it forces an IClientAPI requirement that // can't currently be changed because of HGFriendsModule code that takes the scene from the client. friends = ((FriendsModule)m_friendsModule).FriendsService.GetFriends(userId); } MainConsole.Instance.Output("Friends for {0} {1} {2}:", firstName, lastName, userId); MainConsole.Instance.Output( "{0,-36} {1,-36} {2,-7} {3,7} {4,10}", "UUID", "Name", "Status", "MyFlags", "TheirFlags"); foreach (FriendInfo friend in friends) { // MainConsole.Instance.OutputFormat(friend.PrincipalID.ToString()); // string friendFirstName, friendLastName; // // UserAccount friendUa // = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, friend.PrincipalID); UUID friendId; string friendName; string onlineText; if (UUID.TryParse(friend.Friend, out friendId)) { friendName = m_userManagementModule.GetUserName(friendId); } else { friendName = friend.Friend; } OpenSim.Services.Interfaces.PresenceInfo[] pi = m_presenceService.GetAgents(new string[] { friend.Friend }); if (pi.Length > 0) { onlineText = "online"; } else { onlineText = "offline"; } MainConsole.Instance.Output( "{0,-36} {1,-36} {2,-7} {3,-7} {4,-10}", friend.Friend, friendName, onlineText, friend.MyFlags, friend.TheirFlags); } }
protected bool TrySendInstantMessage(GridInstantMessage im, object previousLocation, bool firstTime, bool foreigner) { UUID toAgentID = new UUID(im.toAgentID); PresenceInfo upd = null; string url = string.Empty; bool lookupAgent = false; lock (m_UserLocationMap) { if (m_UserLocationMap.ContainsKey(toAgentID)) { object o = m_UserLocationMap[toAgentID]; if (o is PresenceInfo) { upd = (PresenceInfo)o; } else if (o is string) { url = (string)o; } // We need to compare the current location with the previous // or the recursive loop will never end because it will never try to lookup the agent again if (!firstTime) { lookupAgent = true; upd = null; } } else { lookupAgent = true; } } //m_log.DebugFormat("[XXX] Neeed lookup ? {0}", (lookupAgent ? "yes" : "no")); // Are we needing to look-up an agent? if (lookupAgent) { // Non-cached user agent lookup. PresenceInfo[] presences = m_PresenceService.GetAgents(new string[] { toAgentID.ToString() }); if (presences != null && presences.Length > 0) { foreach (PresenceInfo p in presences) { if (p.RegionID != UUID.Zero) { //m_log.DebugFormat("[XXX]: Found presence in {0}", p.RegionID); upd = p; break; } } } if (upd == null && !foreigner) { // Let's check with the UAS if the user is elsewhere m_log.DebugFormat("[HG IM SERVICE]: User is not present. Checking location with User Agent service"); try { url = m_UserAgentService.LocateUser(toAgentID); } catch (Exception e) { m_log.Warn("[HG IM SERVICE]: LocateUser call failed ", e); url = string.Empty; } } // check if we've tried this before.. // This is one way to end the recursive loop // if (!firstTime && ((previousLocation is PresenceInfo && upd != null && upd.RegionID == ((PresenceInfo)previousLocation).RegionID) || (previousLocation is string && upd == null && previousLocation.Equals(url)))) { // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); m_log.DebugFormat("[HG IM SERVICE]: Fail 2 {0} {1}", previousLocation, url); return(false); } } if (upd != null) { // ok, the user is around somewhere. Let's send back the reply with "success" // even though the IM may still fail. Just don't keep the caller waiting for // the entire time we're trying to deliver the IM return(SendIMToRegion(upd, im, toAgentID, foreigner)); } else if (url != string.Empty) { // ok, the user is around somewhere. Let's send back the reply with "success" // even though the IM may still fail. Just don't keep the caller waiting for // the entire time we're trying to deliver the IM return(ForwardIMToGrid(url, im, toAgentID, foreigner)); } else if (firstTime && previousLocation is string && (string)previousLocation != string.Empty) { return(ForwardIMToGrid((string)previousLocation, im, toAgentID, foreigner)); } else { m_log.DebugFormat("[HG IM SERVICE]: Unable to locate user {0}", toAgentID); } return(false); }
public List <UUID> StatusNotification(List <string> friends, UUID foreignUserID, bool online) { if (m_FriendsService == null || m_PresenceService == null) { m_log.WarnFormat("[USER AGENT SERVICE]: Unable to perform status notifications because friends or presence services are missing"); return(new List <UUID>()); } List <UUID> localFriendsOnline = new List <UUID>(); m_log.DebugFormat("[USER AGENT SERVICE]: Status notification: foreign user {0} wants to notify {1} local friends", foreignUserID, friends.Count); // First, let's double check that the reported friends are, indeed, friends of that user // And let's check that the secret matches List <string> usersToBeNotified = new List <string>(); foreach (string uui in friends) { UUID localUserID; string secret = string.Empty, tmp = string.Empty; if (Util.ParseUniversalUserIdentifier(uui, out localUserID, out tmp, out tmp, out tmp, out secret)) { FriendInfo[] friendInfos = m_FriendsService.GetFriends(localUserID); foreach (FriendInfo finfo in friendInfos) { if (finfo.Friend.StartsWith(foreignUserID.ToString()) && finfo.Friend.EndsWith(secret)) { // great! usersToBeNotified.Add(localUserID.ToString()); } } } } // Now, let's send the notifications m_log.DebugFormat("[USER AGENT SERVICE]: Status notification: user has {0} local friends", usersToBeNotified.Count); // First, let's send notifications to local users who are online in the home grid PresenceInfo[] friendSessions = m_PresenceService.GetAgents(usersToBeNotified.ToArray()); if (friendSessions != null && friendSessions.Length > 0) { PresenceInfo friendSession = null; foreach (PresenceInfo pinfo in friendSessions) { if (pinfo.RegionID != UUID.Zero) // let's guard against traveling agents { friendSession = pinfo; break; } } if (friendSession != null) { ForwardStatusNotificationToSim(friendSession.RegionID, foreignUserID, friendSession.UserID, online); usersToBeNotified.Remove(friendSession.UserID.ToString()); UUID id; if (UUID.TryParse(friendSession.UserID, out id)) { localFriendsOnline.Add(id); } } } //// Lastly, let's notify the rest who may be online somewhere else //foreach (string user in usersToBeNotified) //{ // UUID id = new UUID(user); // if (m_Database.ContainsKey(id) && m_Database[id].GridExternalName != m_GridName) // { // string url = m_Database[id].GridExternalName; // // forward // m_log.WarnFormat("[USER AGENT SERVICE]: User {0} is visiting {1}. HG Status notifications still not implemented.", user, url); // } //} // and finally, let's send the online friends if (online) { return(localFriendsOnline); } else { return(new List <UUID>()); } }
public void SendMessageToGroup( GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func <GroupMembersData, bool> sendCondition) { int requestStartTick = Environment.TickCount; List <GroupMembersData> groupMembers = m_groupData.GetGroupMembers(sendingAgentForGroupCalls, groupID); int groupMembersCount = groupMembers.Count; HashSet <string> attemptDeliveryUuidSet = null; if (m_messageOnlineAgentsOnly) { string[] t1 = groupMembers.ConvertAll <string>(gmd => gmd.AgentID.ToString()).ToArray(); // We cache in order not to overwhlem the presence service on large grids with many groups. This does // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed. // (assuming this is the same across all grid simulators). PresenceInfo[] onlineAgents; if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents)) { onlineAgents = m_presenceService.GetAgents(t1); m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds); } attemptDeliveryUuidSet = new HashSet <string>(Array.ConvertAll <PresenceInfo, string>(onlineAgents, pi => pi.UserID)); } else { attemptDeliveryUuidSet = new HashSet <string>(groupMembers.ConvertAll <string>(gmd => gmd.AgentID.ToString())); if (m_debugEnabled) { m_log.DebugFormat( "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members", groupID, groupMembers.Count); } } foreach (GroupMembersData member in groupMembers) { if (sendCondition != null) { if (!sendCondition(member)) { if (m_debugEnabled) { m_log.DebugFormat( "[GROUPS-MESSAGING]: Not sending to {0} as they do not fulfill send condition", member.AgentID); } continue; } } else if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID)) { // Don't deliver messages to people who have dropped this session if (m_debugEnabled) { m_log.DebugFormat( "[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); } continue; } // Copy Message GridInstantMessage msg = new GridInstantMessage(); msg.imSessionID = im.imSessionID; msg.fromAgentName = im.fromAgentName; msg.message = im.message; msg.dialog = im.dialog; msg.offline = im.offline; msg.ParentEstateID = im.ParentEstateID; msg.Position = im.Position; msg.RegionID = im.RegionID; msg.binaryBucket = im.binaryBucket; msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); msg.fromAgentID = im.fromAgentID; msg.fromGroup = true; msg.toAgentID = member.AgentID.Guid; if (attemptDeliveryUuidSet.Contains(member.AgentID.ToString())) { IClientAPI client = GetActiveClient(member.AgentID); if (client == null) { int startTick = Environment.TickCount; // If they're not local, forward across the grid m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); if (m_debugEnabled) { m_log.DebugFormat( "[GROUPS-MESSAGING]: Delivering to {0} via grid took {1} ms", member.AgentID, Environment.TickCount - startTick); } } else { int startTick = Environment.TickCount; ProcessMessageFromGroupSession(msg, client); // Deliver locally, directly if (m_debugEnabled) { m_log.DebugFormat( "[GROUPS-MESSAGING]: Delivering to {0} locally took {1} ms", member.AgentID, Environment.TickCount - startTick); } } } else if (im.dialog != (byte)InstantMessageDialog.SessionAdd && im.dialog != (byte)InstantMessageDialog.SessionDrop) { int startTick = Environment.TickCount; m_msgTransferModule.HandleUndeliverableMessage(msg, delegate(bool success) { }); if (m_debugEnabled) { m_log.DebugFormat( "[GROUPS-MESSAGING]: Handling undeliverable message for {0} took {1} ms", member.AgentID, Environment.TickCount - startTick); } } } if (m_debugEnabled) { m_log.DebugFormat( "[GROUPS-MESSAGING]: Total SendMessageToGroup for group {0} with {1} members, {2} candidates for delivery took {3} ms", groupID, groupMembersCount, attemptDeliveryUuidSet.Count(), Environment.TickCount - requestStartTick); } }
public PresenceInfo[] GetAgents(string[] userIDs) { return(m_PresenceService.GetAgents(userIDs)); }
public PresenceInfo[] GetAgents(string[] userIDs) { return(m_RemoteConnector.GetAgents(userIDs)); }
public void SendMessageToGroup(GridInstantMessage im, UUID groupID) { List <GroupMembersData> groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID), groupID); int groupMembersCount = groupMembers.Count; if (m_messageOnlineAgentsOnly) { string[] t1 = groupMembers.ConvertAll <string>(gmd => gmd.AgentID.ToString()).ToArray(); // We cache in order not to overwhlem the presence service on large grids with many groups. This does // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed. // (assuming this is the same across all grid simulators). PresenceInfo[] onlineAgents; if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents)) { onlineAgents = m_presenceService.GetAgents(t1); m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds); } HashSet <string> onlineAgentsUuidSet = new HashSet <string>(); Array.ForEach <PresenceInfo>(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID)); groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList(); // if (m_debugEnabled) // m_log.DebugFormat( // "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members, {2} online", // groupID, groupMembersCount, groupMembers.Count()); } else { if (m_debugEnabled) { m_log.DebugFormat( "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members", groupID, groupMembers.Count); } } int requestStartTick = Environment.TickCount; foreach (GroupMembersData member in groupMembers) { if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID)) { // Don't deliver messages to people who have dropped this session if (m_debugEnabled) { m_log.DebugFormat("[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); } continue; } // Copy Message GridInstantMessage msg = new GridInstantMessage(); msg.imSessionID = groupID.Guid; msg.fromAgentName = im.fromAgentName; msg.message = im.message; msg.dialog = im.dialog; msg.offline = im.offline; msg.ParentEstateID = im.ParentEstateID; msg.Position = im.Position; msg.RegionID = im.RegionID; msg.binaryBucket = im.binaryBucket; msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); msg.fromAgentID = im.fromAgentID; msg.fromGroup = true; msg.toAgentID = member.AgentID.Guid; IClientAPI client = GetActiveClient(member.AgentID); if (client == null) { // If they're not local, forward across the grid if (m_debugEnabled) { m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} via Grid", member.AgentID); } m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); } else { // Deliver locally, directly if (m_debugEnabled) { m_log.DebugFormat("[GROUPS-MESSAGING]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); } ProcessMessageFromGroupSession(msg); } } // Temporary for assessing how long it still takes to send messages to large online groups. if (m_messageOnlineAgentsOnly) { m_log.DebugFormat( "[GROUPS-MESSAGING]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms", groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick); } }