notificationSvc.py (19369B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\mail\notificationSvc.py 2 import copy 3 import sys 4 import yaml 5 import blue 6 import carbon.common.script.util.format as fmtUtils 7 import carbonui.const as uiconst 8 import eve.common.lib.appConst as const 9 import eve.common.script.util.notificationUtil as notificationUtil 10 import localization 11 import log 12 from carbon.common.script.sys.service import Service 13 from carbon.common.script.sys.serviceConst import SERVICE_RUNNING 14 from eve.client.script.ui.shared.neocom.evemail import MailReadingWnd 15 from eve.common.script.util.notificationconst import GetTypeGroup, IsTypeInCommunications, depricatedNotifications 16 from eveexceptions import UserError 17 from notifications.common.formatting.notificationFormatMapping import GetFormatterForType 18 from notifications.common.notification import Notification 19 from player_messaging.client.ui_message import prompt_player_fsd_dialog 20 21 class notificationSvc(Service): 22 __guid__ = 'svc.notificationSvc' 23 __displayname__ = 'Notification service' 24 __exportedcalls__ = {} 25 __notifyevents__ = ['OnNotificationReceived', 26 'OnNotificationDeleted', 27 'OnNotificationUndeleted', 28 'OnSessionReset'] 29 __startupdependencies__ = ['settings'] 30 31 def Run(self, ms = None): 32 self.state = SERVICE_RUNNING 33 self.notificationMgr = sm.RemoteSvc('notificationMgr') 34 self.notifications = {} 35 self.unreadCount = {} 36 self.unreadNotifications = None 37 self.allNotifications = None 38 self.blinkTab = False 39 self.blinkNeocom = False 40 self.delayedNotificationsByID = {const.notificationTypeCloneActivationMsg: set(), 41 const.notificationTypeCloneActivationMsg2: set()} 42 self.isEnabled = True 43 44 def EnableNotifications(self): 45 self.isEnabled = True 46 47 def DisableNotifications(self): 48 self.isEnabled = False 49 50 def ClearCache(self): 51 self.notifications = {} 52 self.unreadCount = {} 53 self.unreadNotifications = None 54 55 def OnSessionReset(self): 56 self.ClearCache() 57 58 def GetNotificationsByGroupID(self, groupID): 59 if groupID not in self.notifications: 60 self.notifications[groupID] = [] 61 cache = self.notifications[groupID] 62 newNotifications = self.notificationMgr.GetByGroupID(groupID) 63 newSenders = [] 64 for newNotification in newNotifications: 65 notificationObj = Notification.FromDTO(newNotification) 66 if IsTypeInCommunications(notificationObj.typeID): 67 cache.append(notificationObj) 68 newSenders.append(newNotification.senderID) 69 70 sm.GetService('mailSvc').PrimeOwners(newSenders) 71 return self.notifications[groupID] 72 73 def MakeAndScatterNotification(self, type, data): 74 sm.ScatterEvent('OnNotificationReceived', -1, type, session.charid, blue.os.GetWallclockTime(), data=data) 75 76 def GetAllNotifications(self, fromID = 0): 77 if self.allNotifications is None: 78 allServerNotifications = self._GetAllNotifications(fromID) 79 newSenders = set() 80 allClientNotifications = [] 81 for notification in allServerNotifications: 82 allClientNotifications.append(Notification.FromDTO(notification)) 83 newSenders.add(notification.senderID) 84 85 sm.GetService('mailSvc').PrimeOwners(newSenders) 86 self.allNotifications = allClientNotifications 87 return self.allNotifications 88 89 def _GetAllNotifications(self, fromID): 90 notifications = self.notificationMgr.GetAllNotifications(fromID=fromID) 91 return [ notification for notification in notifications if notification.typeID not in depricatedNotifications ] 92 93 def ClearAllNotificationsCache(self): 94 self.allNotifications = None 95 96 def GetAllFormattedNotifications(self, fromID = 0): 97 return self.FormatNotifications(self.GetAllNotifications(fromID=fromID)) 98 99 def GetUnreadNotifications(self): 100 self.LogInfo('Getting unread notifications') 101 if self.unreadNotifications is None: 102 unreadNotifications = [] 103 newNotifications = self.notificationMgr.GetUnprocessed() 104 newSenders = [] 105 for newNotification in newNotifications: 106 notificationObj = Notification.FromDTO(newNotification) 107 if IsTypeInCommunications(notificationObj.typeID): 108 unreadNotifications.append(Notification.FromDTO(newNotification)) 109 newSenders.append(newNotification.senderID) 110 groupID = GetTypeGroup(newNotification.typeID) 111 if groupID in self.unreadCount: 112 self.unreadCount[groupID] += 1 113 else: 114 self.unreadCount[groupID] = 1 115 116 self.unreadNotifications = unreadNotifications 117 sm.GetService('mailSvc').PrimeOwners(newSenders) 118 return self.unreadNotifications 119 120 def GetUnreadCountDictionary(self): 121 if self.unreadNotifications is None: 122 self.GetUnreadNotifications() 123 return self.unreadCount 124 125 def GetAllUnreadCount(self): 126 unreadCounts = self.GetUnreadCountDictionary().copy() 127 unreadGroupCounter = 0 128 for counter in unreadCounts.itervalues(): 129 unreadGroupCounter += counter 130 131 unreadCounts[const.notificationGroupUnread] = unreadGroupCounter 132 return unreadCounts 133 134 def GetFormattedNotifications(self, groupID): 135 notifications = self.GetNotificationsByGroupID(groupID) 136 ret = self.FormatNotifications(notifications) 137 return ret 138 139 def GetFormattedUnreadNotifications(self): 140 notifications = self.GetUnreadNotifications() 141 ret = self.FormatNotifications(notifications) 142 return ret 143 144 def TryUnYamlNotificationData(self, notification): 145 try: 146 if isinstance(notification.data, basestring): 147 notification.data = yaml.load(notification.data, Loader=yaml.CSafeLoader) 148 except Exception as e: 149 self.LogWarn('Exception while un-yamling notification data', repr(notification.data), e) 150 sys.exc_clear() 151 152 def UnYamlNotificationsData(self, notifications): 153 for notification in notifications: 154 self.TryUnYamlNotificationData(notification) 155 156 def FormatNotifications(self, notifications): 157 self.UnYamlNotificationsData(notifications) 158 self.PrimeNotificationLinkInfo(notifications) 159 ret = [] 160 for notification in notifications: 161 notificationCopy = notification.copy() 162 newFormatter = GetFormatterForType(notification.typeID) 163 if newFormatter: 164 try: 165 newFormatter.Format(notificationCopy) 166 except Exception as e: 167 log.LogException('Error processing notification of type %s, error=%s' % (notification.typeID, e)) 168 notificationCopy.subject = localization.GetByLabel('Notifications/subjBadNotificationMessage') 169 notificationCopy.body = localization.GetByLabel('Notifications/bodyBadNotificationMessage', id=notification.notificationID) 170 171 else: 172 subject, body = notificationUtil.Format(notification) 173 notificationCopy.subject = subject 174 notificationCopy.body = body 175 if not notificationCopy.subject and not notificationCopy.body: 176 log.LogWarn('Processed notification has empty subject and body (notification type %s)' % notification.typeID) 177 ret.append(notificationCopy) 178 179 return ret 180 181 def PrimeNotificationLinkInfo(self, notifications): 182 locationKeys = ('solarsystemid', 'stationid', 'clonestationid', 'corpstationid', 'moonid', 'locationid', 'structureid') 183 ownerKeys = ('characterid', 'charid', 'corporationid', 'corpid', 'allianceid', 'deptorid', 'creditorid', 'aggressorid', 'aggressorcorpid', 'aggressorallianceid', 'factionid', 'podkillerid', 'againstid', 'declaredbyid', 'ownerid', 'oldownerid', 'newownerid', 'locationownerid', 'victimid') 184 locationIDs = set() 185 ownerIDs = set() 186 for notification in notifications: 187 data = notification.data 188 if data is not None and isinstance(data, dict): 189 for key, value in data.iteritems(): 190 if isinstance(value, (int, long)): 191 if key.lower() in locationKeys: 192 locationIDs.add(value) 193 elif key.lower() in ownerKeys: 194 ownerIDs.add(value) 195 196 cfg.evelocations.Prime(locationIDs) 197 cfg.eveowners.Prime(ownerIDs) 198 199 def MarkAllReadInGroup(self, groupID): 200 notifications = self.GetNotificationsByGroupID(groupID) 201 toMarkRead = [] 202 for read in notifications: 203 for unread in self.unreadNotifications: 204 if unread.notificationID == read.notificationID: 205 toMarkRead.append(read.notificationID) 206 207 if len(toMarkRead) < 1: 208 return 209 if prompt_player_fsd_dialog('EvemailNotificationsMarkGroupRead', {}, uiconst.YESNO, suppress=uiconst.ID_YES) == uiconst.ID_YES: 210 self.notificationMgr.MarkGroupAsProcessed(groupID) 211 self.UpdateCacheAfterMarkingRead(toMarkRead) 212 sm.ScatterEvent('OnNotificationsRefresh') 213 214 def MarkAllRead(self): 215 if not self.unreadNotifications: 216 return 217 self.notificationMgr.MarkAllAsProcessed() 218 toMarkRead = [] 219 for notification in self.unreadNotifications: 220 toMarkRead.append(notification.notificationID) 221 222 self.UpdateCacheAfterMarkingRead(toMarkRead) 223 sm.ScatterEvent('OnNotificationsRefresh') 224 225 def MarkAsRead(self, notificationIDs): 226 if self.unreadNotifications is None: 227 return 228 notificationIDsToMarkAsRead = set() 229 for notificationID in notificationIDs: 230 for notification in self.unreadNotifications: 231 if notificationID == notification.notificationID: 232 notificationIDsToMarkAsRead.add(notificationID) 233 234 numToMark = len(notificationIDsToMarkAsRead) 235 if numToMark < 1: 236 return 237 if numToMark > const.notificationsMaxUpdated: 238 txt = localization.GetByLabel('UI/Mail/Notifications/TooManySelected', num=numToMark, max=const.notificationsMaxUpdated) 239 raise UserError('CustomInfo', {'info': txt}) 240 notificationsList = list(notificationIDsToMarkAsRead) 241 self.notificationMgr.MarkAsProcessed(notificationsList) 242 self.UpdateCacheAfterMarkingRead(notificationsList) 243 244 def UpdateCacheAfterMarkingRead(self, notificationIDs): 245 for readID in notificationIDs: 246 wasUnread = False 247 typeID = None 248 for unread in self.unreadNotifications: 249 if unread.notificationID == readID: 250 typeID = unread.typeID 251 self.unreadNotifications.remove(unread) 252 wasUnread = True 253 break 254 255 if not wasUnread: 256 continue 257 groupID = GetTypeGroup(typeID) 258 if groupID in self.notifications: 259 for grouped in self.notifications[groupID]: 260 if grouped.notificationID == readID: 261 grouped.processed = True 262 break 263 264 if groupID in self.unreadCount: 265 self.unreadCount[groupID] = self.unreadCount[groupID] - 1 266 267 def DeleteAllFromGroup(self, groupID): 268 notifications = self.GetNotificationsByGroupID(groupID) 269 if len(notifications) < 1: 270 return 271 self.notificationMgr.DeleteGroupNotifications(groupID) 272 deleted = [] 273 for notification in notifications: 274 deleted.append(notification.notificationID) 275 276 self.UpdateCacheAfterDeleting(deleted) 277 sm.ScatterEvent('OnNotificationsRefresh') 278 279 def DeleteAll(self): 280 self.notificationMgr.DeleteAllNotifications() 281 self.unreadNotifications = [] 282 self.allNotifications = [] 283 self.unreadCount = {} 284 if self.notifications is not None: 285 for groupID in self.notifications.iterkeys(): 286 self.notifications[groupID] = [] 287 288 sm.ScatterEvent('OnNotificationsRefresh') 289 290 def DeleteNotifications(self, notificationIDs): 291 numToDelete = len(notificationIDs) 292 if numToDelete < 1: 293 return 294 if numToDelete > const.notificationsMaxUpdated: 295 txt = localization.GetByLabel('UI/Mail/Notifications/TooManySelected', num=numToDelete, max=const.notificationsMaxUpdated) 296 raise UserError('CustomInfo', {'info': txt}) 297 self.notificationMgr.DeleteNotifications(notificationIDs) 298 self.UpdateCacheAfterDeleting(notificationIDs) 299 300 def UpdateCacheAfterDeleting(self, notificationIDs): 301 toclear = copy.copy(notificationIDs) 302 if self.unreadNotifications is not None: 303 for deletedID in notificationIDs: 304 for unread in self.unreadNotifications: 305 if unread.notificationID == deletedID: 306 groupID = GetTypeGroup(unread.typeID) 307 self.unreadNotifications.remove(unread) 308 if groupID in self.unreadCount: 309 self.unreadCount[groupID] = self.unreadCount[groupID] - 1 310 if groupID in self.notifications: 311 for grouped in self.notifications[groupID]: 312 if grouped.notificationID == deletedID: 313 self.notifications[groupID].remove(grouped) 314 break 315 316 toclear.remove(deletedID) 317 break 318 319 if len(toclear) > 0: 320 for groupID in self.notifications: 321 for deletedID in copy.copy(toclear): 322 for grouped in self.notifications[groupID]: 323 if grouped.notificationID == deletedID: 324 self.notifications[groupID].remove(grouped) 325 toclear.remove(deletedID) 326 break 327 328 def OnNotificationDeleted(self, notificationIDs): 329 toclear = copy.copy(notificationIDs) 330 if self.unreadNotifications is not None: 331 for deletedID in notificationIDs: 332 for unread in self.unreadNotifications: 333 if unread.notificationID == deletedID: 334 groupID = GetTypeGroup(self.unreadNotifications[deletedID].typeID) 335 self.unreadNotifications.remove(unread) 336 if groupID in self.unreadCount: 337 self.unreadCount[groupID] = self.unreadCount[groupID] - 1 338 if groupID in self.notifications: 339 for grouped in self.notifications[groupID]: 340 if grouped.notificationID == deletedID: 341 self.notifications[groupID].remove(grouped) 342 break 343 344 toclear.remove(deletedID) 345 break 346 347 if len(toclear) > 0: 348 for groupID in self.notifications: 349 for deletedID in copy.copy(toclear): 350 for grouped in self.notifications[groupID]: 351 if grouped.notificationID == deletedID: 352 self.notifications[groupID].remove(grouped) 353 toclear.remove(deletedID) 354 break 355 356 sm.ScatterEvent('OnNotificationsRefresh') 357 358 def OnNotificationUndeleted(self, notificationIDs): 359 self.ClearCache() 360 sm.ScatterEvent('OnNotificationsRefresh') 361 362 def InsertNotification(self, groupID, notification): 363 if groupID in self.notifications: 364 self.notifications[groupID].insert(0, notification) 365 366 def AddToUnread(self, groupID, notification): 367 if self.unreadNotifications is not None: 368 if groupID in self.unreadCount: 369 self.unreadCount[groupID] += 1 370 else: 371 self.unreadCount[groupID] = 1 372 self.unreadNotifications.insert(0, notification) 373 374 def OnNotificationReceived(self, notificationID, typeID, senderID, created, data = {}): 375 if not self.isEnabled: 376 return 377 notification = Notification(notificationID=notificationID, typeID=typeID, senderID=senderID, receiverID=session.charid, processed=False, created=created, data=data) 378 groupID = GetTypeGroup(typeID) 379 if groupID is not None and IsTypeInCommunications(typeID): 380 self.InsertNotification(groupID, notification) 381 self.AddToUnread(groupID, notification) 382 if typeID in self.delayedNotificationsByID: 383 self.delayedNotificationsByID[typeID].add(notification) 384 else: 385 self.ProcessNotification(notification) 386 387 def ProcessNotification(self, notification): 388 n = self.FormatNotifications([notification]) 389 first = n[0] 390 self.ScatterNotificationEvent(first) 391 392 def ScatterNotificationEvent(self, notification): 393 sm.ScatterEvent('OnNewNotificationReceived', notification) 394 395 def ProcessDelayedNotifications(self, notificationTypeID): 396 delayedNotifications = self.delayedNotificationsByID.get(notificationTypeID, None) 397 if delayedNotifications is None: 398 return 399 for eachNotification in delayedNotifications: 400 self.ProcessNotification(eachNotification) 401 402 self.delayedNotificationsByID[notificationTypeID] = set() 403 404 def GetReadingText(self, senderID, subject, created, message): 405 if senderID == -1: 406 senderID = const.ownerSystem 407 sender = cfg.eveowners.Get(senderID) 408 senderTypeID = sender.typeID 409 if senderTypeID is not None and senderTypeID > 0: 410 senderText = '<a href="showinfo:%(senderType)s//%(senderID)s">%(senderName)s</a>' % {'senderType': senderTypeID, 411 'senderID': senderID, 412 'senderName': sender.name} 413 else: 414 senderText = sender.name 415 date = fmtUtils.FmtDate(created, 'ls') 416 newmsgText = localization.GetByLabel('UI/Mail/NotificationText', subject=subject, sender=senderText, date=date, body=message) 417 return newmsgText 418 419 def OpenNotificationReadingWnd(self, info, *args): 420 wndName = 'mail_readingWnd_%s' % info.notificationID 421 wnd = MailReadingWnd.Open(windowID=wndName, mail=None, msgID=info.notificationID, txt='', toolbar=False, trashed=False, type=const.mailTypeNotifications) 422 if not info.processed: 423 self.MarkAsRead([info.notificationID]) 424 sm.ScatterEvent('OnNotificationReadOutside', info.notificationID) 425 if wnd is not None: 426 wnd.Maximize() 427 blue.pyos.synchro.SleepWallclock(1) 428 txt = sm.GetService('notificationSvc').GetReadingText(info.senderID, info.subject, info.created, info.body) 429 wnd.SetText(txt)