/Aurora/Modules/Avatar/AuroraChat/AuroraOfflineMessagesModule.cs
C# | 273 lines | 212 code | 29 blank | 32 comment | 73 complexity | 9b5f4c550c86584d3eca86da7f9ae451 MD5 | raw file
1/* 2 * Copyright (c) Contributors, http://aurora-sim.org/ 3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * * Neither the name of the Aurora-Sim Project nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28using Aurora.Framework; 29using Aurora.Framework.ClientInterfaces; 30using Aurora.Framework.ConsoleFramework; 31using Aurora.Framework.DatabaseInterfaces; 32using Aurora.Framework.Modules; 33using Aurora.Framework.PresenceInfo; 34using Aurora.Framework.SceneInfo; 35using Aurora.Framework.Services; 36using Aurora.Framework.Services.ClassHelpers.Profile; 37using Aurora.Framework.Utilities; 38using Nini.Config; 39using OpenMetaverse; 40using System; 41using System.Collections.Generic; 42 43namespace Aurora.Modules.Chat 44{ 45 public class AuroraOfflineMessageModule : INonSharedRegionModule 46 { 47 private bool enabled = true; 48 private IScene m_Scene; 49 private IMessageTransferModule m_TransferModule = null; 50 private IOfflineMessagesConnector OfflineMessagesConnector; 51 private bool m_SendOfflineMessagesToEmail = false; 52 53 private Dictionary<UUID, List<GridInstantMessage>> m_offlineMessagesCache = 54 new Dictionary<UUID, List<GridInstantMessage>>(); 55 56 public void Initialise(IConfigSource config) 57 { 58 IConfig cnf = config.Configs["Messaging"]; 59 if (cnf == null) 60 { 61 enabled = false; 62 return; 63 } 64 if (cnf.GetString("OfflineMessageModule", "AuroraOfflineMessageModule") != 65 "AuroraOfflineMessageModule") 66 { 67 enabled = false; 68 return; 69 } 70 71 m_SendOfflineMessagesToEmail = cnf.GetBoolean("SendOfflineMessagesToEmail", m_SendOfflineMessagesToEmail); 72 } 73 74 public void AddRegion(IScene scene) 75 { 76 if (!enabled) 77 return; 78 79 m_Scene = scene; 80 81 scene.EventManager.OnNewClient += OnNewClient; 82 scene.EventManager.OnClosingClient += OnClosingClient; 83 scene.EventManager.OnCachedUserInfo += UpdateCachedInfo; 84 } 85 86 public void RegionLoaded(IScene scene) 87 { 88 if (!enabled) 89 return; 90 91 if (m_TransferModule == null) 92 { 93 OfflineMessagesConnector = Framework.Utilities.DataManager.RequestPlugin<IOfflineMessagesConnector>(); 94 m_TransferModule = scene.RequestModuleInterface<IMessageTransferModule>(); 95 if (m_TransferModule == null || OfflineMessagesConnector == null) 96 { 97 scene.EventManager.OnNewClient -= OnNewClient; 98 scene.EventManager.OnClosingClient -= OnClosingClient; 99 100 enabled = false; 101 m_Scene = null; 102 103 MainConsole.Instance.Error( 104 "[OFFLINE MESSAGING] No message transfer module or OfflineMessagesConnector is enabled. Diabling offline messages"); 105 return; 106 } 107 m_TransferModule.OnUndeliveredMessage += UndeliveredMessage; 108 } 109 } 110 111 public void RemoveRegion(IScene scene) 112 { 113 if (!enabled) 114 return; 115 116 m_Scene = null; 117 118 if (m_TransferModule != null) 119 { 120 scene.EventManager.OnNewClient -= OnNewClient; 121 scene.EventManager.OnClosingClient -= OnClosingClient; 122 scene.EventManager.OnCachedUserInfo -= UpdateCachedInfo; 123 m_TransferModule.OnUndeliveredMessage -= UndeliveredMessage; 124 } 125 } 126 127 public string Name 128 { 129 get { return "AuroraOfflineMessageModule"; } 130 } 131 132 public Type ReplaceableInterface 133 { 134 get { return null; } 135 } 136 137 public void Close() 138 { 139 } 140 141 private IClientAPI FindClient(UUID agentID) 142 { 143 IScenePresence presence = m_Scene.GetScenePresence(agentID); 144 return (presence != null && !presence.IsChildAgent) ? presence.ControllingClient : null; 145 } 146 147 private void UpdateCachedInfo(UUID agentID, CachedUserInfo info) 148 { 149 lock (m_offlineMessagesCache) 150 m_offlineMessagesCache[agentID] = info.OfflineMessages; 151 } 152 153 private void OnNewClient(IClientAPI client) 154 { 155 client.OnRetrieveInstantMessages += RetrieveInstantMessages; 156 } 157 158 private void OnClosingClient(IClientAPI client) 159 { 160 client.OnRetrieveInstantMessages -= RetrieveInstantMessages; 161 } 162 163 private void RetrieveInstantMessages(IClientAPI client) 164 { 165 if (OfflineMessagesConnector == null) 166 return; 167 168 List<GridInstantMessage> msglist; 169 lock (m_offlineMessagesCache) 170 { 171 if (m_offlineMessagesCache.TryGetValue(client.AgentId, out msglist)) 172 m_offlineMessagesCache.Remove(client.AgentId); 173 } 174 175 if (msglist == null) 176 msglist = OfflineMessagesConnector.GetOfflineMessages(client.AgentId); 177 msglist.Sort( 178 delegate(GridInstantMessage a, GridInstantMessage b) { return a.timestamp.CompareTo(b.timestamp); }); 179 foreach (GridInstantMessage IM in msglist) 180 { 181 // Send through scene event manager so all modules get a chance 182 // to look at this message before it gets delivered. 183 // 184 // Needed for proper state management for stored group 185 // invitations 186 // 187 IM.offline = 1; 188 m_Scene.EventManager.TriggerIncomingInstantMessage(IM); 189 } 190 } 191 192 private void UndeliveredMessage(GridInstantMessage im, string reason) 193 { 194 if (OfflineMessagesConnector == null || im == null) 195 return; 196 IClientAPI client = FindClient(im.fromAgentID); 197 if ((client == null) && (im.dialog != 32)) 198 return; 199 if (!OfflineMessagesConnector.AddOfflineMessage(im)) 200 { 201 if ((!im.fromGroup) && (reason != "User does not exist.") && (client != null)) 202 client.SendInstantMessage(new GridInstantMessage( 203 null, im.toAgentID, 204 "System", im.fromAgentID, 205 (byte) InstantMessageDialog.MessageFromAgent, 206 "User has too many IMs already, please try again later.", 207 false, Vector3.Zero)); 208 else if (client == null) 209 return; 210 } 211 else if ((im.offline != 0) 212 && (!im.fromGroup || im.fromGroup)) 213 { 214 if (im.dialog == 32) //Group notice 215 { 216 IGroupsModule module = m_Scene.RequestModuleInterface<IGroupsModule>(); 217 if (module != null) 218 im = module.BuildOfflineGroupNotice(im); 219 return; 220 } 221 if (client == null) return; 222 IEmailModule emailModule = m_Scene.RequestModuleInterface<IEmailModule>(); 223 if (emailModule != null && m_SendOfflineMessagesToEmail) 224 { 225 IUserProfileInfo profile = 226 Framework.Utilities.DataManager.RequestPlugin<IProfileConnector>().GetUserProfile(im.toAgentID); 227 if (profile != null && profile.IMViaEmail) 228 { 229 UserAccount account = m_Scene.UserAccountService.GetUserAccount(null, im.toAgentID.ToString()); 230 if (account != null && !string.IsNullOrEmpty(account.Email)) 231 { 232 emailModule.SendEmail(UUID.Zero, account.Email, 233 string.Format("Offline Message from {0}", im.fromAgentName), 234 string.Format("Time: {0}\n", 235 Util.ToDateTime(im.timestamp).ToShortDateString()) + 236 string.Format("From: {0}\n", im.fromAgentName) + 237 string.Format("Message: {0}\n", im.message), m_Scene); 238 } 239 } 240 } 241 242 if (im.dialog == (byte) InstantMessageDialog.MessageFromAgent && !im.fromGroup) 243 { 244 client.SendInstantMessage(new GridInstantMessage( 245 null, im.toAgentID, 246 "System", im.fromAgentID, 247 (byte) InstantMessageDialog.MessageFromAgent, 248 "Message saved, reason: " + reason, 249 false, new Vector3())); 250 } 251 252 if (im.dialog == (byte) InstantMessageDialog.InventoryOffered) 253 client.SendAlertMessage("User is not online. Inventory has been saved"); 254 } 255 else if (im.offline == 0) 256 { 257 if (client == null) return; 258 if (im.dialog == (byte) InstantMessageDialog.MessageFromAgent && !im.fromGroup) 259 { 260 client.SendInstantMessage(new GridInstantMessage( 261 null, im.toAgentID, 262 "System", im.fromAgentID, 263 (byte) InstantMessageDialog.MessageFromAgent, 264 "Message saved, reason: " + reason, 265 false, new Vector3())); 266 } 267 268 if (im.dialog == (byte) InstantMessageDialog.InventoryOffered) 269 client.SendAlertMessage("User not able to be found. Inventory has been saved"); 270 } 271 } 272 } 273}