xmppchatsvc.py (80370B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\xmppchatclient\xmppchatsvc.py 2 import json 3 import logging 4 import socket 5 from player_messaging.client.ui_message import prompt_player_fsd_dialog 6 from xml.sax.saxutils import escape, quoteattr 7 import stackless 8 from stackless_tracing.attributes import SERVICE_NAME, SERVICE_NAME_PREFIX 9 import yaml 10 from collections import OrderedDict 11 import gametime 12 import localization 13 import locks 14 import uthread2 15 import carbonui.const as uiconst 16 import monolithhoneycomb_events as mhe 17 from bannedwords.client import bannedwords 18 from carbon.common.script.util.format import FmtDate 19 from monolithhoneycomb_events.events import CHAT_CLIENT_LOGIN, CHAT_CLIENT_HANDLE_LOGGED_IN, CHAT_CLIENT_HANDLE_CONNECTION_LOST, CHAT_CLIENT_CONNECTION_OBJECT_CREATED 20 from carbon.common.script.sys import service 21 from chatutil import IsInvite, GetErrorCode, GetUsersFromResponse, GetChannelCategory, GetInviteReason, GetInviteSender, GetChannelDifferentiator, UpdateChatFilterVariables, IsDecline, GetDeclineReason, GetDeclineSender, GetInvitePassword, TruncateVeryLongText, GetCharacterBannnedExpiry, GetErrorType, GetErrorTextElement, InvalidChatChannelNameError, InvalidSessionChatChannelNameError 22 from chatutil.channelnames import IsValidChannelName, GetSessionBasedChannelName 23 from eve.common.script.sys.idCheckers import IsNPC, IsCharacter, IsCorporation, IsAlliance 24 from eveexceptions import UserError 25 from eveexceptions.exceptionEater import ExceptionEater 26 from eveprefs import prefs 27 from uihighlighting import UiHighlightDirections 28 from xmpp import XmppConnection 29 import xmppchatclient.const as xmppchatConst 30 from xmppchatclient.chatinvitewindow import PresentChatInviteWindow 31 from xmppchatclient.xmppchatpassword import ChannelPasswordWindow 32 from xmppchatclient.xmppchatwindow import XmppChatWindow 33 from chat.common.util import is_local_chat_suppressed 34 from chat.common.const import MIGRATED_CHAT_CATEGORIES 35 from collections import defaultdict 36 from globalConfig import GetEjabberdAddress 37 logger = logging.getLogger('xmppchat') 38 SESSION_CHANGE_CATEGORIES = OrderedDict([('solarsystemid2', xmppchatConst.CATEGORY_LOCAL), 39 ('allianceid', xmppchatConst.CATEGORY_ALLIANCE), 40 ('corpid', xmppchatConst.CATEGORY_CORP), 41 ('fleetid', xmppchatConst.CATEGORY_FLEET), 42 ('warfactionid', xmppchatConst.CATEGORY_FACTION)]) 43 44 class XmppChatService(service.Service): 45 __guid__ = 'svc.XmppChat' 46 __displayname__ = 'Xmpp Chat Service' 47 __exportedcalls__ = {'AskYesNoQuestion': [service.ROLE_SERVICE]} 48 __notifyevents__ = ['OnSessionChanged', 49 'OnDisconnect', 50 'OnCharacterPokedByGM', 51 'ProcessSessionReset', 52 'OnSessionReset', 53 'OnCharacterPermanentlyBanned', 54 'OnCharacterTemporarilyBanned', 55 'OnCharacterUnbanned'] 56 57 def __init__(self): 58 super(XmppChatService, self).__init__() 59 self.verbose = prefs.GetValue('xmppChatVerbose', False) 60 self._has_local_chat_migrated = None 61 self.xmppConnection = None 62 self.hostname = None 63 self.ejabberd_address = None 64 self.messageWindows = {} 65 self.groupChatWindows = {} 66 self.myName = None 67 self.charsToPrime = set() 68 self.myPendingPrivateChats = {} 69 self.myPendingInvites = {} 70 self.pendingJoins = [] 71 self.outstandingJoins = {} 72 self.channelJoinsInProgress = set() 73 self.isReconnecting = False 74 self.chatFilters = None 75 self.userData = {} 76 self.translatedMsgText = localization.GetByLabel('UI/Common/Message') 77 self.hasLoggedInOnce = False 78 self.memberRolesByChannels = defaultdict(dict) 79 self.isDoingLogoff = False 80 self._threads = [] 81 self.banExpiry = xmppchatConst.BAN_EXPIRY_STATUS_NONE 82 83 def Run(self, memStream = None): 84 self.state = service.SERVICE_RUNNING 85 86 def UpdateLocalChatMigrated(self, local_chat_migrated): 87 if local_chat_migrated == self._has_local_chat_migrated: 88 return 89 self._has_local_chat_migrated = local_chat_migrated 90 if not session.charid: 91 return 92 channelName = GetSessionBasedChannelName(xmppchatConst.CATEGORY_LOCAL, session.solarsystemid2) 93 if self._has_local_chat_migrated: 94 self.CloseChannelWindow(channelName) 95 else: 96 self.GetOrCreateGroupChatWindow(channelName) 97 98 def _IsCategoryMigrated(self, category): 99 return self._has_local_chat_migrated and category in MIGRATED_CHAT_CATEGORIES 100 101 def HandleLoggingIn(self): 102 uthread2.start_tasklet(self.Login) 103 uthread2.start_tasklet(self.InitChatFilters) 104 105 def IsCharacterBanned(self): 106 return self.banExpiry == xmppchatConst.BAN_EXPIRY_STATUS_NEVER or gametime.GetWallclockTime() <= self.banExpiry 107 108 def OnCharacterPermanentlyBanned(self): 109 self._CharacterBanned(xmppchatConst.BAN_EXPIRY_STATUS_NEVER) 110 111 def OnCharacterTemporarilyBanned(self, banExpiry): 112 self._CharacterBanned(banExpiry) 113 114 def _CharacterBanned(self, banExpiry): 115 self._SetBanExpiry(banExpiry) 116 for window in self.groupChatWindows.values(): 117 window.OnCharacterBannedNotify() 118 119 prompt_player_fsd_dialog('CharacterBannedFromChat') 120 121 def _SetBanExpiry(self, banExpiry): 122 self.banExpiry = banExpiry 123 124 def OnCharacterUnbanned(self): 125 self._SetBanExpiry(xmppchatConst.BAN_EXPIRY_STATUS_NONE) 126 127 def GetCharacterBannedMessage(self): 128 if self.banExpiry == xmppchatConst.BAN_EXPIRY_STATUS_NEVER: 129 return localization.GetByLabel('UI/Chat/ChatSvc/CharacterPermanentlyBanned') 130 if self.banExpiry > 0: 131 return localization.GetByLabel('UI/Chat/ChatSvc/CharacterTemporarilyBanned', datetime=FmtDate(self.banExpiry, 'll')) 132 raise RuntimeError('Character is not banned') 133 134 def OnSessionChanged(self, isremote, sess, changes): 135 if 'charid' in changes and changes['charid'][1] is not None: 136 self.HandleLoggingIn() 137 channelsToLeave = [] 138 for category, prefix in SESSION_CHANGE_CATEGORIES.iteritems(): 139 if self._IsCategoryMigrated(category): 140 continue 141 if category in changes: 142 old, _new = changes[category] 143 if old: 144 channelName = GetSessionBasedChannelName(prefix, old) 145 channelsToLeave.append(channelName) 146 147 uthread2.start_tasklet(self._LeaveChannels, channelsToLeave) 148 uthread2.start_tasklet(self._HandleLocalSuppressedSessionChange, changes) 149 150 def ProcessSessionReset(self): 151 self.isDoingLogoff = True 152 153 def OnSessionReset(self): 154 self.xmppConnection.Disconnect() 155 self.xmppConnection = None 156 self.messageWindows = {} 157 self.groupChatWindows = {} 158 self.myName = None 159 self.charsToPrime = set() 160 self.myPendingPrivateChats = {} 161 self.myPendingInvites = {} 162 self.pendingJoins = [] 163 self.outstandingJoins = {} 164 self.channelJoinsInProgress = set() 165 self.chatFilters = None 166 self.userData = {} 167 self.memberRolesByChannels = defaultdict(dict) 168 self.pendingJoins = [] 169 threads = self._threads[:] 170 self._threads = [] 171 for t in threads: 172 t.kill() 173 174 self.isDoingLogoff = False 175 self.banExpiry = xmppchatConst.BAN_EXPIRY_STATUS_NONE 176 177 def _HandleLocalSuppressedSessionChange(self, changes): 178 if 'solarsystemid2' in changes: 179 newSolarSystemID = changes['solarsystemid2'][1] 180 if is_local_chat_suppressed(newSolarSystemID): 181 self.GetOrCreateGroupChatWindow(xmppchatConst.CATEGORY_LOCAL, setWindowsChannel=False) 182 channelName = GetSessionBasedChannelName(xmppchatConst.CATEGORY_LOCAL_SUPPRESSED, newSolarSystemID) 183 self.JoinChannel(channelName) 184 185 def OnDisconnect(self, reason = 0, msg = ''): 186 if self.xmppConnection: 187 self.xmppConnection.Disconnect() 188 189 def TryJoinPlayerChannelsFromOldSettings_Temp(self): 190 if settings.char.ui.Get('chat_OldChannelsMigrated', False): 191 return 192 oldChannels = settings.char.ui.Get('lscengine_mychannels', []) 193 channelsToAttemptToJoin = [] 194 for channelID in oldChannels: 195 if not isinstance(channelID, int): 196 continue 197 if 0 <= channelID <= xmppchatConst.SYSTEM_CHANNEL_MAX_CHANNELID: 198 continue 199 newChannelID = '%s_%s' % (xmppchatConst.CATEGORY_PLAYER, channelID) 200 channelsToAttemptToJoin.append(newChannelID) 201 202 if channelsToAttemptToJoin: 203 uthread2.start_tasklet(self._JoinChannels, channelsToAttemptToJoin) 204 settings.char.ui.Set('chat_OldChannelsMigrated', True) 205 206 def _LeaveChannels(self, channelsToLeave): 207 for each in channelsToLeave: 208 self.LeaveChannel(each) 209 210 def _JoinChannels(self, channelsToJoin): 211 for each in channelsToJoin: 212 if not self.JoinChannel(each): 213 self._RemoveWindowForChannel(each) 214 215 def _RemoveWindowForChannel(self, each): 216 window = self.groupChatWindows.pop(each, None) 217 if window: 218 window.access_lost = True 219 window.Close() 220 221 def Login(self): 222 self._CreateConnection() 223 self.myName = self.GetUserName() 224 logger.info('Logging in as %s on %s', self.myName, self.xmppConnection.ejabberd_address) 225 sm.GetService('settings').LoadCharSettingsIfNeeded() 226 channelListInSettings = settings.char.ui.Get('chatchannels', []) 227 self.CreateSessionChannelWindows() 228 self._RestoreChannelList(channelListInSettings) 229 while True: 230 try: 231 self.xmppConnection.Connect(self.myName, 'eveclient') 232 self.hasLoggedInOnce = True 233 self.send_honey_event(CHAT_CLIENT_LOGIN) 234 break 235 except socket.error as e: 236 msg = 'Error connecting to {0}'.format(self.xmppConnection.ejabberd_address) 237 logger.exception(msg) 238 ret = prompt_player_fsd_dialog('RetryChatServerConnection', {'server': self.xmppConnection.ejabberd_address, 239 'port': self.xmppConnection.port}, uiconst.YESNO) 240 if ret != uiconst.ID_YES: 241 self._DisableChat() 242 self.send_honey_event(CHAT_CLIENT_LOGIN, {'error': True, 243 'eve.xmpp.connection.retry': False}) 244 return 245 self.send_honey_event(CHAT_CLIENT_LOGIN, {'error': True, 246 'eve.xmpp.connection.retry': True}) 247 248 def _DisableChat(self): 249 for window in self.groupChatWindows.values(): 250 try: 251 msg = localization.GetByLabel('UI/Chat/ChatSvc/NoChatServer', server=self.xmppConnection.ejabberd_address, port=self.xmppConnection.port) 252 window.Receive(const.ownerSystem, msg) 253 except Exception: 254 pass 255 256 def _CreateConnection(self): 257 self.hostname = self.XmppChatMgr.Hostname() 258 self.ejabberd_address = self.get_ejabberd_address() 259 if self.xmppConnection is not None: 260 self.xmppConnection.hostname = self.hostname 261 self.xmppConnection.ejabberd_address = self.ejabberd_address 262 return 263 if self.ejabberd_address is None: 264 self.send_honey_event(CHAT_CLIENT_CONNECTION_OBJECT_CREATED, {'error': True}) 265 logger.exception('No ejabberd address configured for client.') 266 return 267 logger.info('xmppchatsvc._CreateConnection::ejabberd_address={}, hostname={}'.format(self.ejabberd_address, self.hostname)) 268 self.xmppConnection = XmppConnection(hostname=self.hostname, ejabberd_address=self.ejabberd_address) 269 self.xmppConnection.handle_message = self.HandleXmppMessage 270 self.xmppConnection.handle_logged_in = self.HandleXmppLoggedIn 271 self.xmppConnection.handle_connection_lost = self.HandleXmppConnectionLost 272 self.xmppConnection.handle_presence = self.HandleXmppPresence 273 self.xmppConnection.handle_stream_error = self.HandleStreamError 274 self.xmppConnection.get_password = self.GetAuthenticationToken 275 self.xmppConnection.verbose = self.verbose 276 self.send_honey_event(CHAT_CLIENT_CONNECTION_OBJECT_CREATED, {'eve.xmpp.connection.created.id': id(self.xmppConnection)}) 277 278 def get_ejabberd_address(self): 279 address = GetEjabberdAddress(sm.GetService('machoNet')) 280 if address is not None and len(address) > 0: 281 return address 282 return self.XmppChatMgr.GetDeprecatedPrefsFallback() 283 284 def GetUserName(self): 285 return self.GetUserNameFromCharId(session.charid) 286 287 def GetAuthenticationToken(self): 288 token = sm.RemoteSvc('chatAuthenticationService').GetAuthenticationToken() 289 if token: 290 return token 291 else: 292 logger.debug("Can't get token from authentication service - are you on a local server?") 293 return 'ejabberd' 294 295 def SendGroupChat(self, group, messageText): 296 sm.ScatterEvent('OnClientEvent_ChatMessageSent') 297 if not self.xmppConnection or not self.xmppConnection.IsConnected(): 298 self.Login() 299 if not self.xmppConnection or not self.xmppConnection.IsConnected(): 300 logger.warning("Can't send message when not connected") 301 return 302 if group in self.myPendingPrivateChats: 303 other, isInvited = self.myPendingPrivateChats[group] 304 if not isInvited: 305 self.myPendingPrivateChats[group] = (other, True) 306 roomName = self.GetFullRoomName(group) 307 self.xmppConnection.Invite(self.GetUserNameFromCharId(other), roomName, 'Private chat') 308 receiver = self.GetFullRoomName(group) 309 messageText = TruncateVeryLongText(messageText) 310 return self.xmppConnection.GroupChat(receiver, messageText) 311 312 def _JoinSystemChannels(self): 313 channelsToJoin = None 314 while channelsToJoin is None: 315 if self.verbose: 316 logger.debug('Calling ResyncSystemChannelAccess on proxy') 317 channelsToJoin = self.XmppChatMgr.ResyncSystemChannelAccess() 318 if not channelsToJoin: 319 uthread2.sleep(5) 320 321 categoriesToJoin = set() 322 for each in channelsToJoin: 323 self.JoinChannel(each) 324 with ExceptionEater('_JoinSystemChannels'): 325 category = GetChannelCategory(each) 326 if category in (xmppchatConst.CATEGORY_LOCAL_SUPPRESSED, 327 xmppchatConst.CATEGORY_WORMHOLE, 328 xmppchatConst.CATEGORY_NULLSEC, 329 xmppchatConst.CATEGORY_TRIGLAVIAN): 330 category = xmppchatConst.CATEGORY_LOCAL 331 categoriesToJoin.add(category) 332 333 uthread2.start_tasklet(self._JoinChannels, channelsToJoin) 334 windowsToClose = set(SESSION_CHANGE_CATEGORIES.values()) - categoriesToJoin 335 for eachWindowName in windowsToClose: 336 self._RemoveWindowForChannel(eachWindowName) 337 338 def HandleXmppLoggedIn(self): 339 self.send_honey_event(CHAT_CLIENT_HANDLE_LOGGED_IN) 340 logger.info('Logged in to %s at %s as %s', self.xmppConnection.hostname, self.xmppConnection.ejabberd_address, self.xmppConnection.username) 341 channelsToJoin = self.pendingJoins 342 self.pendingJoins = [] 343 for window in self.groupChatWindows.values(): 344 uthread2.start_tasklet(window.SetCaptionAsDisplayName) 345 channel = window.receiver 346 category = GetChannelCategory(channel) 347 if category in [xmppchatConst.CATEGORY_PRIVATE, 348 xmppchatConst.CATEGORY_PLAYER, 349 xmppchatConst.CATEGORY_BOT, 350 xmppchatConst.CATEGORY_SYSTEM]: 351 channelsToJoin.append(channel) 352 353 self._threads.append(uthread2.start_tasklet(self._JoinChannels, channelsToJoin)) 354 self._threads.append(uthread2.start_tasklet(self._JoinSystemChannels)) 355 self._threads.append(uthread2.start_tasklet(self.TryJoinPlayerChannelsFromOldSettings_Temp)) 356 if session.role & service.ROLE_NEWBIE: 357 self._threads.append(uthread2.start_tasklet(self.JoinRookieHelpChannel)) 358 if self.isReconnecting: 359 for window in self.groupChatWindows.values(): 360 window.Receive(const.ownerSystem, localization.GetByLabel('UI/Chat/ChatSvc/ReconnectedToChatServer')) 361 window.ResetAfterReconnect() 362 363 self.isReconnecting = False 364 sm.ScatterEvent('OnRefreshChannels') 365 366 def HandleXmppConnectionLost(self): 367 logger.warning('Connection to chat server lost\n%s\n%s\n%s', self.xmppConnection.hostname, self.xmppConnection.ejabberd_address, self.xmppConnection.username) 368 self.send_honey_event(CHAT_CLIENT_HANDLE_CONNECTION_LOST) 369 if self.isReconnecting or session.charid is None: 370 return 371 for window in self.groupChatWindows.values(): 372 try: 373 window.Receive(const.ownerSystem, localization.GetByLabel('UI/Chat/ChatSvc/ConnectionChatServerLost')) 374 except Exception: 375 pass 376 377 self.pendingJoins = [] 378 self.channelJoinsInProgress.clear() 379 for each in self.outstandingJoins.itervalues(): 380 each.send(None) 381 382 self.outstandingJoins.clear() 383 self.isReconnecting = True 384 385 def HandleXmppMessage(self, response): 386 sender = response.attributes.getValue('from') 387 what = response.attributes.get('type', '') 388 if what == 'groupchat': 389 self._HandleGroupchat(sender, response) 390 elif what == 'error': 391 self._HandleMessageError(sender, response) 392 else: 393 uthread2.start_tasklet(self._HandleMessage, sender, response) 394 395 def _HandleMessageError(self, sender, response): 396 errorType = GetErrorType(response) 397 if errorType == xmppchatConst.ERROR_CHAT_BANNED: 398 textElement = GetErrorTextElement(response) 399 if textElement: 400 values = textElement.text.split(' ', 2) 401 if len(values) >= 2 and values[0] == 'mod_ban': 402 self._SetBanExpiry(GetCharacterBannnedExpiry(values[1])) 403 channelname, _ = sender.split('@', 1) 404 window = self.GetGroupChatWindow(channelname) 405 if window: 406 window.OnCharacterBannedNotify() 407 return 408 errorCode = GetErrorCode(response) 409 if errorCode == xmppchatConst.ERROR_CHANNEL_UNAVAILABLE: 410 raise UserError('LSCCannotSendMessage', {'msg': localization.GetByLabel('UI/Chat/ChatEngine/YouAreNotAbleToSpeak')}) 411 elif errorCode == xmppchatConst.ERROR_LACKING_CONTROL_RIGHTS: 412 if IsInvite(response): 413 raise UserError('LSCCannotAccessControl', {'msg': (101, 'UI/Chat/ChatSvc/YouDontHavePriviledgeToAccessControl')}) 414 elif errorCode == xmppchatConst.ERROR_CONSTRAINED: 415 text = 'Rate limited' 416 errorElem = response.find_child_with_tag('error') 417 if errorElem: 418 textElem = errorElem.find_child_with_tag('text') 419 text = textElem.text 420 logger.warning(text) 421 channelname, rest = sender.split('@', 1) 422 window = self.GetGroupChatWindow(channelname) 423 if window: 424 window.HandleErrorResponse(response) 425 return 426 logger.warning('Error response: %s', response.toXml()) 427 428 def _HandleGroupchat(self, sender, response): 429 group, rest = sender.split('@', 1) 430 senderparts = rest.split('/') 431 if len(senderparts) > 1: 432 sender = senderparts[1] 433 else: 434 sender = 'admin' 435 if GetChannelCategory(group) == xmppchatConst.CATEGORY_LOCAL: 436 if GetChannelDifferentiator(group) != str(session.solarsystemid2): 437 return 438 window = self.GetOrCreateGroupChatWindow(group) 439 if window is None or window.receiver != group: 440 return 441 body = response.find_child_with_tag('body') 442 if body and body.text is not None: 443 text = body.text 444 if sender == 'admin': 445 self._HandleMessageFromAdmin(text, window) 446 else: 447 senderID = int(sender) 448 userdata = self.GetUserData(senderID) 449 addressBookSvc = sm.GetService('addressbook') 450 isBlocked = addressBookSvc.IsBlocked(senderID) or addressBookSvc.IsBlocked(userdata.get('corpid')) or addressBookSvc.IsBlocked(userdata.get('allianceid')) 451 if not isBlocked: 452 isBlocked = sm.GetService('chat').is_reported_spammer(senderID) 453 if not isBlocked: 454 msg_id = response.attributes.get('id') 455 text = TruncateVeryLongText(text) 456 window.Receive(senderID, text, msg_id=msg_id) 457 subject = response.find_child_with_tag('subject') 458 if subject: 459 if self.verbose: 460 logger.debug("PAT: _HandleGroupchat 'subject' (motd) change with sender=%s, response=%s", sender, response) 461 if sender == 'admin': 462 sender = None 463 text = subject.text 464 window.SetMotd(sender, text) 465 x = response.find_child_with_tag('x') 466 if x: 467 status = x.find_child_with_tag('status') 468 if status: 469 if status.attributes.get('code', 0) == '104': 470 if self.verbose: 471 logger.debug('Config changed for %s', group) 472 window.SetCaptionAsDisplayName() 473 msg = localization.GetByLabel('UI/Chat/ChannelConfigChanged') 474 window.Receive(const.ownerSystem, msg) 475 476 def _HandleMessageFromAdmin(self, text, window): 477 try: 478 data = json.loads(text) 479 if self.verbose: 480 logger.debug('Message from admin:\n{0}'.format(data)) 481 cmd = data['cmd'] 482 who = data['charid'] 483 if cmd == 'join': 484 userdata = data['userdata'] 485 infoLine = userdata.get('info') 486 if infoLine: 487 self._PopulateEveownersFromInfo(who, infoLine) 488 del userdata['info'] 489 self.userData[who] = userdata 490 window.CharacterJoinedChannel(who) 491 elif cmd == 'leave': 492 window.CharacterLeftChannel(who) 493 elif cmd == 'speak': 494 messageText = data['messageText'] 495 window.Receive(who, messageText) 496 elif cmd == 'refresh_char': 497 charID = data['charid'] 498 logger.debug('info on char %s is stale', charID) 499 if charID == session.charid: 500 sm.GetService('stateSvc').NotifyOnStateSetupChange('flagState') 501 else: 502 sm.ScatterEvent('OnCharStateChanged', charID, data) 503 except ValueError: 504 logger.warning('Invalid message from admin: %s', text) 505 506 def _HandleMessage(self, sender, response): 507 channel_name = sender.split('@')[0] 508 if IsInvite(response): 509 self._HandleInvite(channel_name, response) 510 elif IsDecline(response): 511 self._HandleDecline(channel_name, response) 512 513 def _HandleInvite(self, channel_name, response): 514 inviteDetails = GetInviteReason(response) 515 sender = GetInviteSender(response) 516 if self.verbose: 517 logger.debug('Channel invite from %s to %s: %s', sender, channel_name, inviteDetails) 518 if sender == 'admin': 519 uthread2.start_tasklet(self.JoinChannel, channel_name) 520 else: 521 uthread2.start_tasklet(self._HandleOptionalInvite, sender, channel_name, response) 522 523 def _HandleOptionalInvite(self, sender, channel_name, response): 524 if self._AcceptInvitation(sender, channel_name): 525 password = GetInvitePassword(response) 526 if password: 527 settings.user.ui.Set('%sPassword' % channel_name, password) 528 self.JoinChannel(channel_name) 529 530 def _AcceptInvitation(self, sender, channel_name): 531 try: 532 senderId = int(sender) 533 except ValueError: 534 logger.warning('Not a valid user id: %s', sender) 535 return False 536 537 if sm.GetService('addressbook').IsBlocked(senderId): 538 if self.verbose: 539 logger.debug('%s is blocked', senderId) 540 self.RejectInvite(sender, channel_name, 'blocked') 541 return False 542 if settings.user.ui.Get('autoRejectInvitations', 0): 543 if self.verbose: 544 logger.debug('Auto-reject invitations enabled') 545 self.RejectInvite(sender, channel_name, 'rejected') 546 return False 547 if sm.GetService('addressbook').IsInAddressBook(senderId, 'contact'): 548 if self.verbose: 549 logger.debug('%s is in address book - accepting', senderId) 550 return True 551 if self.IsJoined(channel_name): 552 if self.verbose: 553 logger.debug('Already in channel %s', channel_name) 554 return False 555 sm.GetService('logger').AddText(localization.GetByLabel('UI/Chat/InvitedToConversation', inviter=senderId)) 556 accepted, extra = PresentChatInviteWindow(senderId) 557 if not accepted: 558 if extra == 'block': 559 self.RejectInvite(sender, channel_name, 'rejected_and_blocked') 560 else: 561 self.RejectInvite(sender, channel_name, 'rejected') 562 return accepted 563 564 def _HandleDecline(self, channel_name, response): 565 who = GetDeclineSender(response) 566 try: 567 who = int(who) 568 except ValueError: 569 pass 570 571 if who not in self.myPendingInvites: 572 return 573 del self.myPendingInvites[who] 574 if channel_name in self.myPendingPrivateChats: 575 self.CloseChannelWindow(channel_name) 576 reason = GetDeclineReason(response) 577 if self.verbose: 578 logger.debug('Invite from %s was declined: %s', who, reason) 579 if reason == 'rejected': 580 raise UserError('ChtRejected', {'char': who}) 581 elif reason == 'blocked': 582 raise UserError('ChtCharNotReachable', {'char': who}) 583 elif reason == 'rejected_and_blocked': 584 raise UserError('ChtBlockedNow', {'char': who}) 585 586 def HandleXmppPresence(self, response): 587 if self.isDoingLogoff: 588 return 589 self._HandleXmppPresence(response) 590 591 def _HandleXmppPresence(self, response): 592 if 'id' in response.attributes: 593 response_id = response.attributes['id'] 594 if response_id in self.outstandingJoins: 595 channel = self.outstandingJoins[response_id] 596 del self.outstandingJoins[response_id] 597 channel.send(response) 598 who = response.attributes['from'] 599 user, domain = who.split('@', 1) 600 if domain.startswith('conference.'): 601 channel = user 602 category = GetChannelCategory(channel) 603 character = domain.split('/')[1] 604 try: 605 charid = int(character) 606 except ValueError: 607 charid = character 608 609 if charid == 'admin': 610 return 611 if category in [xmppchatConst.CATEGORY_WORMHOLE, xmppchatConst.CATEGORY_NULLSEC, xmppchatConst.CATEGORY_TRIGLAVIAN]: 612 if charid == session.charid: 613 return 614 if charid == 'viking': 615 self._UnpackVikingPresence(channel, response) 616 return 617 other, isInvited = self.myPendingPrivateChats.get(channel, (None, False)) 618 if other == charid: 619 del self.myPendingPrivateChats[channel] 620 channelForPendingInvite = self.myPendingInvites.get(charid) 621 if channelForPendingInvite and channelForPendingInvite == channel: 622 del self.myPendingInvites[charid] 623 window = self.GetGroupChatWindow(channel) 624 presenceType = response.attributes.get('type', None) 625 if self.verbose: 626 logger.debug('Presence type: %s', presenceType) 627 if presenceType in ('unavailable', 'leave'): 628 if charid != session.charid: 629 if category in xmppchatConst.CHANNELS_WITH_SPECIAL_PRESENCE_HANDLING: 630 return 631 window = self.GetGroupChatWindow(channel) 632 if window and window.receiver == channel: 633 window.CharacterLeftChannel(charid) 634 else: 635 category = GetChannelCategory(channel) 636 if category not in xmppchatConst.UNCLOSABLE_CHAT_WNDS: 637 self.CloseChannelWindow(channel) 638 self.memberRolesByChannels[channel].pop(charid, None) 639 elif presenceType is None: 640 userData = self._GetPresenceUserData(charid, response) 641 if userData: 642 self.userData[charid] = userData 643 if self.verbose: 644 logger.debug('User data for %s: %s', charid, userData) 645 self._HandleRoleChange(channel, charid, response) 646 window = self.GetOrCreateGroupChatWindow(channel) 647 if window and window.receiver == channel: 648 self._AddCharacterToChannelWindow(window, charid) 649 memberRole = self.GetRoleFromResponse(response) 650 if memberRole: 651 self.memberRolesByChannels[channel][charid] = memberRole 652 else: 653 self.memberRolesByChannels[channel].pop(charid, None) 654 elif presenceType == 'error': 655 errorCode = GetErrorCode(response) 656 if errorCode == xmppchatConst.ERROR_CHANNEL_UNAVAILABLE: 657 prompt_player_fsd_dialog('CustomNotify', {'notify': localization.GetByLabel('UI/Chat/ChatSvc/ChannelUnavailable')}) 658 else: 659 raise RuntimeError('Unhandled presence type') 660 661 def _UnpackVikingPresence(self, channel, packet): 662 window = self.GetOrCreateGroupChatWindow(channel) 663 if window is None or window.receiver != channel: 664 return 665 for vp in packet.children: 666 userData = {} 667 for key in ['corpid', 668 'allianceid', 669 'warfactionid', 670 'typeid']: 671 try: 672 value = int(vp.attributes.get(key, 0)) 673 except ValueError: 674 value = 0 675 676 userData[key] = value 677 678 try: 679 value = long(vp.attributes.get('role', 0)) 680 except ValueError: 681 value = 0 682 683 userData['role'] = value 684 try: 685 infoLine = vp.attributes.get('info') 686 charid = json.loads(infoLine)[0] 687 self._PopulateEveownersFromInfo(charid, infoLine) 688 except (TypeError, IndexError): 689 logger.warning('Skipping a bad chat viking presence record. Channel: %s', channel) 690 continue 691 692 if userData: 693 self.userData[charid] = userData 694 self._AddCharacterToChannelWindow(window, charid) 695 self.memberRolesByChannels[channel][charid] = 'participant' 696 697 def _AddCharacterToChannelWindow(self, window, charID): 698 charID = int(charID) 699 if cfg.eveowners.GetIfExists(charID): 700 self._DoAddCharToWindow(charID, window) 701 return 702 self.charsToPrime.add(charID) 703 with locks.TempLock('xmppChatPrimingCharForChannel'): 704 isPrimed = cfg.eveowners.GetIfExists(charID) 705 if not isPrimed: 706 charsToPrime = self.charsToPrime.copy() 707 self.charsToPrime.clear() 708 cfg.eveowners.Prime(charsToPrime) 709 uthread2.sleep(0.1) 710 self._DoAddCharToWindow(charID, window) 711 712 def _DoAddCharToWindow(self, charID, window): 713 if window and not window.destroyed: 714 window.CharacterJoinedChannel(charID) 715 716 def _HandleRoleChange(self, channel, charid, response): 717 presenceItem = self._GetPresenceItem(response) 718 if presenceItem: 719 window = self.GetOrCreateGroupChatWindow(channel) 720 if window is None: 721 return 722 reason = self._GetPresenceReason(presenceItem) 723 user = self._GetUserFromJidAttribute(presenceItem) 724 if not user: 725 user = charid 726 role = presenceItem.attributes.get('role', None) 727 if role == 'visitor': 728 if not window.IsCharacterMuted(user): 729 if self.verbose: 730 logger.debug('User is being muted\n%s', response.toXml()) 731 window.SetCharacterMuted(user, reason) 732 elif role == 'participant': 733 if window.IsCharacterMuted(user): 734 if self.verbose: 735 logger.debug('User is no longer muted\n%s', response.toXml()) 736 window.SetCharacterUnmuted(user) 737 738 def _GetPresenceUserData(self, charid, presenceItem): 739 userDataElem = presenceItem.find_child_with_tag('eve_user_data') 740 userData = {} 741 if userDataElem: 742 for key in ['corpid', 743 'allianceid', 744 'warfactionid', 745 'typeid']: 746 try: 747 value = int(userDataElem.attributes.get(key, 0)) 748 except ValueError: 749 value = 0 750 751 userData[key] = value 752 753 try: 754 value = long(userDataElem.attributes.get('role', 0)) 755 except ValueError: 756 value = 0 757 758 userData['role'] = value 759 infoLine = userDataElem.attributes.get('info') 760 self._PopulateEveownersFromInfo(charid, infoLine) 761 return userData 762 763 def _PopulateEveownersFromInfo(self, charid, infoLine): 764 if charid not in cfg.eveowners: 765 if infoLine: 766 info = json.loads(infoLine) 767 cfg.eveowners.Hint(charid, info) 768 769 def _GetPresenceReason(self, presenceItem): 770 reasonElem = presenceItem.find_child_with_tag('reason') 771 if reasonElem: 772 reason = reasonElem.text 773 else: 774 reason = None 775 return reason 776 777 def GetRoleFromResponse(self, response): 778 presenceItem = self._GetPresenceItem(response) 779 return presenceItem.attributes.get('role', None) 780 781 def _GetPresenceItem(self, response): 782 presenceItem = None 783 x = response.find_child_with_tag('x') 784 if x: 785 xmlns = x.attributes['xmlns'] 786 if xmlns == 'http://jabber.org/protocol/muc#user': 787 item = x.find_child_with_tag('item') 788 if item: 789 presenceItem = item 790 return presenceItem 791 792 def _GetUserFromJidAttribute(self, item): 793 user = None 794 jid = item.attributes.get('jid', None) 795 if jid: 796 user = jid.split('@')[0] 797 try: 798 user = int(user) 799 except ValueError: 800 pass 801 802 return user 803 804 def HandleStreamError(self, state, response): 805 child = response.children[0] 806 if child.tag == 'host-unknown': 807 logger.error('Virtual host has not been set up for %s', self.hostname) 808 self.xmppConnection.Disconnect() 809 810 def GetOrCreateGroupChatWindow(self, channel, setWindowsChannel = True): 811 window = self.GetGroupChatWindow(channel) 812 if not window: 813 windowName = self.GetWindowNameForGroup(channel) 814 if self._IsCategoryMigrated(windowName): 815 return 816 stackless_channel = stackless.channel() 817 self.groupChatWindows[windowName] = stackless_channel 818 try: 819 if self.verbose: 820 logger.debug('Creating window for %s', channel) 821 displayName = self.GetDisplayName(channel) 822 windowId = 'chatchannel_%s' % windowName 823 window = XmppChatWindow(receiver=channel, isGroupChat=True, channel=channel, name=windowName, caption=displayName, windowID=windowId, check_open_minimized=True) 824 window.verbose = self.verbose 825 self.groupChatWindows[windowName] = window 826 finally: 827 if self.verbose: 828 logger.debug('Window for %s has been created', channel) 829 while stackless_channel.balance < 0: 830 stackless_channel.send(window) 831 832 if self.verbose: 833 logger.debug('Channels waiting for %s have been notified', channel) 834 835 uthread2.start_tasklet(self.StoreChannelList) 836 if setWindowsChannel: 837 window.SetChannel(channel) 838 return window 839 840 def GetGroupChatWindow(self, group): 841 windowName = self.GetWindowNameForGroup(group) 842 window = self.groupChatWindows.get(windowName, None) 843 if type(window) == stackless.channel: 844 if self.verbose: 845 logger.debug('Waiting for window for %s', group) 846 window = window.receive() 847 if window and window.destroyed: 848 del self.groupChatWindows[windowName] 849 window = None 850 return window 851 852 def GetWindowNameForGroup(self, group): 853 parts = group.split('_') 854 try: 855 windowName = parts[0] 856 except IndexError: 857 windowName = group 858 859 if windowName in [xmppchatConst.CATEGORY_PRIVATE, 860 xmppchatConst.CATEGORY_PLAYER, 861 xmppchatConst.CATEGORY_BOT, 862 xmppchatConst.CATEGORY_SYSTEM]: 863 windowName = group 864 if windowName in [xmppchatConst.CATEGORY_LOCAL_SUPPRESSED, 865 xmppchatConst.CATEGORY_WORMHOLE, 866 xmppchatConst.CATEGORY_NULLSEC, 867 xmppchatConst.CATEGORY_TRIGLAVIAN]: 868 windowName = xmppchatConst.CATEGORY_LOCAL 869 return windowName 870 871 def GetChannels(self): 872 if not self.xmppConnection: 873 return [] 874 channels = [] 875 playerChannels = set() 876 to = 'conference.{0}'.format(self.hostname) 877 queryTemplate = "<query xmlns='http://jabber.org/protocol/disco#items' node='forme'></query>" 878 initialQuery = queryTemplate.format('') 879 response = self.xmppConnection.IssueRequest(initialQuery, 'get', to=to, method_tag='get_channels') 880 response_channels = [] 881 try: 882 queryNode = response.children[0] 883 response_channels = queryNode.children 884 except AttributeError: 885 logger.error('GetChannel request returned nothing') 886 887 for item in response_channels: 888 jid = item.attributes.get('jid', None) 889 if jid is None: 890 logger.error('Error with getting jid') 891 continue 892 if item.tag == 'item': 893 channel = jid.split('@')[0] 894 name = item.attributes.get('name', '') 895 affiliation = item.attributes.get('node') 896 channels.append((channel, name, affiliation)) 897 category = GetChannelCategory(channel) 898 if category == xmppchatConst.CATEGORY_PLAYER: 899 playerChannels.add(channel) 900 901 playerChannelsJoined = settings.user.ui.Get('chatPlayerChannelsJoined', {}) 902 for pc, displayName in playerChannelsJoined.iteritems(): 903 if pc not in playerChannels and displayName: 904 channels.append((pc, displayName)) 905 906 return channels 907 908 def GetNumChannelOccupants(self, channelName): 909 if not self.xmppConnection: 910 return 0 911 query = "<query xmlns='http://jabber.org/protocol/disco#info'/>" 912 roomName = self.GetFullRoomName(channelName) 913 response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_num_channel_occupants') 914 if response and response.attributes.get('type') == 'result': 915 num_occupants = self._GetNumOccupantsFromInfoQuery(response) 916 else: 917 num_occupants = 0 918 return num_occupants 919 920 def GetChannelByDisplayName(self, displayName): 921 channels = [] 922 if not self.xmppConnection: 923 return channels 924 to = 'conference.{0}'.format(self.hostname) 925 queryTemplate = "<query xmlns='http://jabber.org/protocol/disco#items' node={0}></query>" 926 node = 'byname/' + escape(displayName).encode('utf-8') 927 quoted_node = quoteattr(node) 928 initialQuery = queryTemplate.format(quoted_node) 929 response = self.xmppConnection.IssueRequest(initialQuery, 'get', to=to, method_tag='get_channel_by_display_name') 930 queryNode = response.children[0] 931 for item in queryNode.children: 932 if item.tag == 'item': 933 channel = item.attributes['jid'].split('@')[0] 934 name = item.attributes['name'] 935 channels.append((channel, name)) 936 937 return channels 938 939 def GetMembersWithNoRole(self, channel): 940 query = "<query xmlns='http://jabber.org/protocol/disco#items' node='norole'></query>" 941 roomName = self.GetFullRoomName(channel) 942 response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_members_with_no_role') 943 queryNode = response.children[0] 944 chars = [] 945 for item in queryNode.children: 946 if item.tag == 'item': 947 name = item.attributes['name'] 948 try: 949 name = int(name) 950 except ValueError: 951 pass 952 953 chars.append(name) 954 955 return chars 956 957 def JoinRookieHelpChannel(self): 958 self.JoinChannel(self.XmppChatMgr.GetRookieChannel()) 959 960 def JoinHelpChannelForLanguage(self, languageID): 961 self.JoinChannel(self.XmppChatMgr.GetHelpChannel(languageID)) 962 963 def JoinHelpChannels(self): 964 normal_help_channel = self.XmppChatMgr.GetHelpChannel() 965 self.JoinChannel(normal_help_channel) 966 if session.role & service.ROLE_NEWBIE: 967 rookie_help_channel = self.XmppChatMgr.GetRookieChannel() 968 if rookie_help_channel != normal_help_channel: 969 self.JoinChannel(rookie_help_channel) 970 971 def JoinChannel(self, channelName, password = '', suppressMessage = False, isConnectionCheck = False, historySeconds = 0): 972 if not IsValidChannelName(channelName): 973 if channelName in SESSION_CHANGE_CATEGORIES.values(): 974 raise InvalidSessionChatChannelNameError('Invalid channel name') 975 raise InvalidChatChannelNameError('Invalid channel name') 976 if channelName in self.channelJoinsInProgress: 977 if self.verbose: 978 logger.debug('Joining channel %s already in progress', channelName) 979 return 980 channelCategory = GetChannelCategory(channelName) 981 if self._IsCategoryMigrated(channelCategory): 982 return 983 self.channelJoinsInProgress.add(channelName) 984 if channelCategory == xmppchatConst.CATEGORY_PLAYER and not suppressMessage: 985 prompt_player_fsd_dialog('ChannelTryingToJoin') 986 try: 987 result = self._JoinChannelInternal(channelName, password=password, isConnectionCheck=isConnectionCheck, historySeconds=historySeconds) 988 finally: 989 self.channelJoinsInProgress.discard(channelName) 990 991 return result 992 993 def _JoinChannelInternal(self, channelName, password = '', isConnectionCheck = False, historySeconds = 0): 994 if self.xmppConnection and self.xmppConnection.disconnectRequested: 995 if self.verbose: 996 logger.debug("Can't join channel %s because we're disconnecting...") 997 self.pendingJoins = [] 998 return 999 if not self.xmppConnection or not self.xmppConnection.IsConnected(): 1000 if self.verbose: 1001 logger.debug("Can't join channel %s until logged in to chat server - adding to pending list", channelName) 1002 self.pendingJoins.append(channelName) 1003 return 1004 if self.verbose: 1005 logger.debug('Joining channel %s', channelName) 1006 category = GetChannelCategory(channelName) 1007 if category == xmppchatConst.CATEGORY_LOCAL_SUPPRESSED: 1008 if not isConnectionCheck: 1009 sm.ScatterEvent('OnChannelJoined', channelName) 1010 self.GetOrCreateGroupChatWindow(channelName) 1011 return True 1012 if not password: 1013 password = settings.user.ui.Get('%sPassword' % channelName) 1014 retriesRemaining = 4 1015 isRetry = False 1016 while retriesRemaining > 0: 1017 retriesRemaining -= 1 1018 response = self._JoinChannelHelper(channelName, password, historySeconds) 1019 if not response: 1020 return False 1021 responseType = response.attributes.get('type', None) 1022 if responseType == 'error': 1023 errorCode = GetErrorCode(response) 1024 if self.verbose: 1025 logger.debug('Error response while attempting to join channel %s: %s', channelName, str(response)) 1026 if errorCode == xmppchatConst.ERROR_WRONG_PASSWORD: 1027 displayName = self.GetDisplayName(channelName) 1028 if retriesRemaining == 0: 1029 prompt_player_fsd_dialog('LSCWrongPassword', {'channelName': displayName}) 1030 return False 1031 password = self._GetPasswordHelper(channelName, displayName, isRetry) 1032 if not password: 1033 break 1034 isRetry = True 1035 else: 1036 if errorCode == xmppchatConst.ERROR_CHANNEL_UNAVAILABLE: 1037 return False 1038 if errorCode == xmppchatConst.ERROR_CANT_JOIN_CHANNEL: 1039 if category in [xmppchatConst.CATEGORY_PLAYER] and session.role & (service.ROLE_CHTADMINISTRATOR | service.ROLE_GML): 1040 self.XmppChatMgr.GrantChannelOwnership(channelName) 1041 elif category in xmppchatConst.PROXY_CONTROLLED_CATEGORIES: 1042 if retriesRemaining == 0: 1043 gotSomething = self._ResyncWithRetries() 1044 if gotSomething: 1045 logger.warning("Can't join channel %s, waiting for proxy to resync" % channelName) 1046 else: 1047 logger.exception("Can't join channel that proxy should have given us access to") 1048 return False 1049 logger.warning("Can't join channel %s, retrying" % channelName) 1050 uthread2.sleep(3) 1051 else: 1052 displayName = self.GetDisplayName(channelName) 1053 prompt_player_fsd_dialog('LSCCannotJoin', {'msg': (const.UE_LOC, 'UI/Chat/ChatSvc/YouHaveBeenBanned', {'channelName': displayName})}) 1054 return False 1055 elif errorCode == xmppchatConst.ERROR_CONSTRAINED: 1056 prompt_player_fsd_dialog('LSCCannotCreate', {'msg': (const.UE_LOC, 'UI/Chat/ChatSvc/YouHaveTooManyChannels', {'maxChannels': 10})}) 1057 return False 1058 else: 1059 logger.debug("Can't join channel %s", channelName) 1060 return False 1061 1062 else: 1063 if category == xmppchatConst.CATEGORY_PLAYER: 1064 playerChannelsJoined = settings.user.ui.Get('chatPlayerChannelsJoined', {}) 1065 if channelName not in playerChannelsJoined: 1066 displayName = self.GetDisplayName(channelName) 1067 playerChannelsJoined[channelName] = displayName 1068 settings.user.ui.Set('chatPlayerChannelsJoined', playerChannelsJoined) 1069 if not isConnectionCheck: 1070 sm.ScatterEvent('OnChannelJoined', channelName) 1071 return True 1072 1073 return False 1074 1075 def _ResyncWithRetries(self): 1076 resyncRetriesRemaining = 5 1077 gotSomething = False 1078 while resyncRetriesRemaining > 0 and not gotSomething: 1079 resyncRetriesRemaining -= 1 1080 channelsToJoin = self.XmppChatMgr.ResyncSystemChannelAccess() 1081 if not channelsToJoin: 1082 logger.warning('ResyncSystemChannelAccess returned None, retrying') 1083 uthread2.sleep(3) 1084 else: 1085 gotSomething = True 1086 1087 return gotSomething 1088 1089 def _GetPasswordHelper(self, channelName, displayName, isRetry): 1090 if session.role & (service.ROLE_CHTADMINISTRATOR | service.ROLE_GML): 1091 self.XmppChatMgr.GrantChannelOwnership(channelName) 1092 return 'bypass_with_roles' 1093 else: 1094 title = localization.GetByLabel('UI/Chat/PasswordRequired', channelName=displayName) 1095 dlg = ChannelPasswordWindow.Open(channel=channelName, title=title, retry=isRetry) 1096 dlg.ShowDialog() 1097 password = dlg.password 1098 return password 1099 1100 def _JoinChannelHelper(self, channelName, password, historySeconds): 1101 roomName = self.GetFullRoomName(channelName) 1102 response_id = self.xmppConnection.JoinRoom(roomName, password=password, historySeconds=historySeconds) 1103 stackless_channel = stackless.channel() 1104 self.outstandingJoins[response_id] = stackless_channel 1105 response = stackless_channel.receive() 1106 return response 1107 1108 def LeaveChannel(self, channelName): 1109 if self.verbose: 1110 logger.debug('LeaveChannel %s', channelName) 1111 if channelName in self.pendingJoins: 1112 self.pendingJoins.remove(channelName) 1113 return 1114 roomName = self.GetFullRoomName(channelName) 1115 self.xmppConnection.LeaveRoom(roomName) 1116 category = GetChannelCategory(channelName) 1117 if category not in xmppchatConst.UNCLOSABLE_CHAT_WNDS: 1118 self.CloseChannelWindow(channelName) 1119 self.memberRolesByChannels.pop(channelName, None) 1120 1121 def CloseChannelWindow(self, channelName): 1122 if self.verbose: 1123 logger.debug('Scattering OnChannelLeft for %s', channelName) 1124 sm.ScatterEvent('OnChannelLeft', channelName) 1125 if channelName in self.myPendingPrivateChats: 1126 other, isInvited = self.myPendingPrivateChats[channelName] 1127 del self.myPendingPrivateChats[channelName] 1128 if other in self.myPendingInvites: 1129 del self.myPendingInvites[other] 1130 window = self.GetGroupChatWindow(channelName) 1131 if window: 1132 window.access_lost = True 1133 window.Close() 1134 windowName = self.GetWindowNameForGroup(channelName) 1135 del self.groupChatWindows[windowName] 1136 self.StoreChannelList() 1137 1138 def IsJoined(self, channelName): 1139 return channelName in self.groupChatWindows 1140 1141 def GetFullRoomName(self, channelName): 1142 roomName = '{0}@conference.{1}'.format(channelName, self.hostname) 1143 return roomName 1144 1145 def GetUserNameFromCharId(self, charid): 1146 return self.xmppConnection.GetFullUsername(charid) 1147 1148 def CreatePrivateChannel(self, charid): 1149 channelName = self.XmppChatMgr.CreatePrivateChannel(charid) 1150 self.myPendingPrivateChats[channelName] = (charid, False) 1151 self.JoinChannel(channelName) 1152 return channelName 1153 1154 def CreateChannel(self, name): 1155 return self.XmppChatMgr.CreatePlayerOwnedChannel(name) 1156 1157 def DeleteChannel(self, channelName): 1158 query = "<query xmlns='http://jabber.org/protocol/muc#owner'><destroy/></query>" 1159 roomName = self.GetFullRoomName(channelName) 1160 displayName = self.GetDisplayName(channelName) 1161 isElevatedPlayer = session.role & service.ROLEMASK_ELEVATEDPLAYER 1162 if isElevatedPlayer: 1163 msgName = 'LSCConfirmDestroyChannelElevatedPlayer' 1164 else: 1165 msgName = 'LSCConfirmDestroyChannel' 1166 if prompt_player_fsd_dialog(msgName, {'displayName': displayName}, uiconst.YESNO) != uiconst.ID_YES: 1167 return 1168 response = self.xmppConnection.IssueRequest(query, 'set', to=roomName, method_tag='delete_channel') 1169 if response.attributes.getValue('type') == 'result': 1170 playerChannelsJoined = settings.user.ui.Get('chatPlayerChannelsJoined', {}) 1171 try: 1172 del playerChannelsJoined[channelName] 1173 except KeyError: 1174 pass 1175 1176 settings.user.ui.Set('chatPlayerChannelsJoined', playerChannelsJoined) 1177 sm.ScatterEvent('OnChannelDeleted', channelName) 1178 else: 1179 logger.warn('DeleteChannel: Incorrect response') 1180 1181 def ForgetChannel(self, channelName): 1182 playerChannelsJoined = settings.user.ui.Get('chatPlayerChannelsJoined', {}) 1183 if channelName in playerChannelsJoined: 1184 del playerChannelsJoined[channelName] 1185 settings.user.ui.Set('chatPlayerChannelsJoined', playerChannelsJoined) 1186 sm.ScatterEvent('OnRefreshChannels') 1187 1188 def Invite(self, charid, channelName = None, isRecruiting = None): 1189 if charid == session.charid: 1190 raise UserError('ChtCannotInviteSelf', {'char': charid, 1191 'channel': channelName}) 1192 if IsNPC(charid) or not IsCharacter(charid): 1193 prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/Chat/ErrorNotAPlayerCharacter')}) 1194 return 1195 reason = '' 1196 isPrivate = False 1197 if channelName is None: 1198 if charid in self.myPendingInvites: 1199 raise UserError('ChtAlreadyInvited', {'char': charid}) 1200 channelName = self.CreatePrivateChannel(charid) 1201 if not channelName: 1202 raise RuntimeError('Could not create channel for private chat') 1203 reason = 'Private chat' 1204 isPrivate = True 1205 self.myPendingInvites[charid] = channelName 1206 if session.role & (service.ROLE_CHTADMINISTRATOR | service.ROLE_GML | service.ROLE_LEGIONEER): 1207 self.XmppChatMgr.AddCharacterToChannel(channelName, charid, 'Added by %s' % session.charid) 1208 elif not isPrivate: 1209 roomName = self.GetFullRoomName(channelName) 1210 self.xmppConnection.Invite(self.GetUserNameFromCharId(charid), roomName, reason) 1211 1212 def RejectInvite(self, charid, channelName, reason): 1213 roomName = self.GetFullRoomName(channelName) 1214 self.xmppConnection.RejectInvite(self.GetUserNameFromCharId(charid), roomName, reason) 1215 1216 def GetChannelsAvailableForInvite(self, charid): 1217 channels = [] 1218 for each in self.groupChatWindows.itervalues(): 1219 if each.CanInvite(charid): 1220 channels.append(each.GetChannelId()) 1221 1222 return channels 1223 1224 def GetDisplayName(self, channelName): 1225 if self.verbose: 1226 logger.debug('GetDisplayName %s', channelName) 1227 k = GetChannelCategory(channelName) 1228 if k in ('incursion', 'spreadingIncursion'): 1229 try: 1230 v = int(GetChannelDifferentiator(channelName)) 1231 except ValueError: 1232 return channelName 1233 1234 if k == 'global': 1235 return localization.GetByLabel('UI/Common/Global') 1236 if k == xmppchatConst.CATEGORY_FLEET: 1237 return localization.GetByLabel('UI/Fleet/Fleet') 1238 if k == 'squad': 1239 return localization.GetByLabel('UI/Fleet/Squad') 1240 if k == 'wing': 1241 return localization.GetByLabel('UI/Fleet/Wing') 1242 if k == xmppchatConst.CATEGORY_CORP: 1243 return localization.GetByLabel('UI/Common/Corp') 1244 if k == xmppchatConst.CATEGORY_FACTION: 1245 return localization.GetByLabel('UI/Common/Militia') 1246 if k == xmppchatConst.CATEGORY_ALLIANCE: 1247 return localization.GetByLabel('UI/Common/Alliance') 1248 if k in (xmppchatConst.CATEGORY_LOCAL, 1249 xmppchatConst.CATEGORY_LOCAL_SUPPRESSED, 1250 xmppchatConst.CATEGORY_WORMHOLE, 1251 xmppchatConst.CATEGORY_NULLSEC, 1252 xmppchatConst.CATEGORY_TRIGLAVIAN): 1253 return localization.GetByLabel('UI/Chat/Local') 1254 if k == 'solarsystem': 1255 return localization.GetByLabel('UI/Common/LocationTypes/System') 1256 if k == 'constellation': 1257 return localization.GetByLabel('UI/Common/LocationTypes/Constellation') 1258 if k == 'region': 1259 return localization.GetByLabel('UI/Common/LocationTypes/Region') 1260 if k == xmppchatConst.CATEGORY_RW: 1261 return sm.GetService('rwService').get_site_name() 1262 if k.startswith('incursion'): 1263 incursionSvc = sm.GetService('incursion') 1264 distributionName = incursionSvc.GetDistributionName(v) 1265 constellationName = incursionSvc.GetConstellationNameFromTaleIDForIncursionChat(v) 1266 return localization.GetByLabel('UI/Chat/IncursionConstellationChannel', constellationName=constellationName, distributionName=distributionName) 1267 if k.startswith('spreadingIncursion'): 1268 incursionSvc = sm.GetService('incursion') 1269 distributionName = incursionSvc.GetDistributionName(v) 1270 return distributionName 1271 if k.startswith('team'): 1272 return localization.GetByLabel('UI/Chat/District') 1273 if k == xmppchatConst.CATEGORY_SYSTEM: 1274 _, groupMessageId, channelMessageId = channelName.split('_', 3) 1275 return localization.GetByMessageID(int(channelMessageId)) 1276 roomName = self.GetFullRoomName(channelName) 1277 if k == xmppchatConst.CATEGORY_PRIVATE: 1278 query = "<query xmlns='http://jabber.org/protocol/disco#info'/>" 1279 response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_private_display_name') 1280 if not response.attributes.getValue('type') == 'result': 1281 return None 1282 num_occupants = self._GetNumOccupantsFromInfoQuery(response) 1283 if num_occupants < 2: 1284 return localization.GetByLabel('UI/Chat/PrivateChatAlone') 1285 elif channelName in self.groupChatWindows and hasattr(self.groupChatWindows[channelName], 'GetMembers'): 1286 return self.GetPrivateChannelDisplayName(channelName) 1287 else: 1288 return localization.GetByLabel('UI/Chat/PrivateChat', title=num_occupants) 1289 if k == xmppchatConst.CATEGORY_PLAYER: 1290 query = "<query xmlns='http://jabber.org/protocol/disco#info' node='{0}'/>".format(channelName) 1291 response = self.xmppConnection.IssueRequest(query, 'get', to='conference.' + self.hostname, method_tag='get_player_display_name') 1292 return self._GetNameFromInfoQuery(response) 1293 return channelName 1294 1295 def GetPrivateChannelDisplayName(self, channelName): 1296 window = self.groupChatWindows[channelName] 1297 if not window: 1298 return localization.GetByLabel('UI/Chat/PrivateChatAlone') 1299 members = window.GetMembers() 1300 if not members: 1301 return localization.GetByLabel('UI/Chat/PrivateChatAlone') 1302 member_names = [] 1303 for each in members: 1304 if each == session.charid: 1305 continue 1306 name = cfg.eveowners.Get(each).name 1307 member_names.append(name) 1308 1309 if len(member_names) == 0: 1310 if channelName in self.myPendingPrivateChats: 1311 other, isInvited = self.myPendingPrivateChats[channelName] 1312 name = cfg.eveowners.Get(other).name 1313 member_names.append(name) 1314 if len(member_names) == 0: 1315 return localization.GetByLabel('UI/Chat/PrivateChatAlone') 1316 if len(member_names) == 1: 1317 return localization.GetByLabel('UI/Chat/PrivateChat', title=member_names[0]) 1318 return localization.GetByLabel('UI/Chat/GroupChat', title=', '.join(member_names)) 1319 1320 def GetMemberIDsForNamedWindow(self, windowName): 1321 window = self.groupChatWindows[windowName] 1322 memberIDs = window.GetMembers() 1323 return memberIDs 1324 1325 def _GetNameFromInfoQuery(self, response): 1326 displayName = None 1327 if response: 1328 queryElement = response.find_child_with_tag('query') 1329 if queryElement: 1330 identityElement = queryElement.find_child_with_tag('identity') 1331 if identityElement: 1332 displayName = identityElement.attributes.get('name', None) 1333 return displayName 1334 1335 def _GetNumOccupantsFromInfoQuery(self, response): 1336 num_occupants = 0 1337 queryElement = response.find_child_with_tag('query') 1338 if queryElement: 1339 xElement = queryElement.find_child_with_tag('x') 1340 if xElement: 1341 for child in xElement.children: 1342 var = child.attributes.get('var', None) 1343 if var == 'muc#roominfo_occupants': 1344 valueElement = child.find_child_with_tag('value') 1345 if valueElement: 1346 occupants = valueElement.text 1347 try: 1348 num_occupants = int(occupants) 1349 except ValueError: 1350 pass 1351 1352 return num_occupants 1353 1354 def GetDetailedDisplayName(self, channelName): 1355 k = GetChannelCategory(channelName) 1356 try: 1357 v = int(GetChannelDifferentiator(channelName)) 1358 except ValueError: 1359 return self.GetDisplayName(channelName) 1360 1361 if k == xmppchatConst.CATEGORY_CORP: 1362 return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/Corp'), featureName=cfg.eveowners.Get(v).name) 1363 if k == xmppchatConst.CATEGORY_WARFACTION: 1364 return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/Militia'), featureName=cfg.eveowners.Get(v).name) 1365 if k == xmppchatConst.CATEGORY_ALLIANCE: 1366 return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/Alliance'), featureName=cfg.eveowners.Get(v).name) 1367 if k == xmppchatConst.CATEGORY_LOCAL_SUPPRESSED: 1368 return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Chat/Local'), featureName=localization.GetByLabel('UI/Chat/Unknown')) 1369 if k in (xmppchatConst.CATEGORY_LOCAL, 1370 xmppchatConst.CATEGORY_WORMHOLE, 1371 xmppchatConst.CATEGORY_NULLSEC, 1372 xmppchatConst.CATEGORY_TRIGLAVIAN): 1373 return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Chat/Local'), featureName=cfg.evelocations.Get(v).name) 1374 if k == 'solarsystem': 1375 return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/LocationTypes/System'), featureName=cfg.evelocations.Get(v).name) 1376 if k == 'constellation': 1377 return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/LocationTypes/Constellation'), featureName=cfg.evelocations.Get(v).name) 1378 if k == 'region': 1379 return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/LocationTypes/Region'), featureName=cfg.evelocations.Get(v).name) 1380 if k == xmppchatConst.CATEGORY_SYSTEM: 1381 _, groupMessageId, channelMessageId = channelName.split('_', 3) 1382 return localization.GetByMessageID(int(channelMessageId)) 1383 return self.GetDisplayName(channelName) 1384 1385 def IsOwnerOrOperatorOfChannel(self, channelName, ignoreRole = False): 1386 if not ignoreRole: 1387 if session.role & (service.ROLE_CHTADMINISTRATOR | service.ROLE_GML): 1388 category = GetChannelCategory(channelName) 1389 if category in [xmppchatConst.CATEGORY_PLAYER, xmppchatConst.CATEGORY_SYSTEM]: 1390 return True 1391 for affiliationType in ('owner', 'admin'): 1392 try: 1393 users = self.GetUsersWithAffiliation(channelName, affiliationType) 1394 except AttributeError: 1395 logger.exception('Error when getting users with %s affiliation' % affiliationType) 1396 users = [] 1397 1398 if session.charid in users: 1399 return True 1400 if session.corpid in users: 1401 return True 1402 if session.allianceid in users: 1403 return True 1404 1405 return False 1406 1407 def IsOwnerOfChannel(self, channelName): 1408 try: 1409 users = self.GetUsersWithAffiliation(channelName, 'owner') 1410 except AttributeError: 1411 logger.exception('Error when getting users with owner affiliation') 1412 users = [] 1413 1414 if session.charid in users: 1415 return True 1416 return False 1417 1418 def IsOperatorOfChannel(self, channelName): 1419 try: 1420 users = self.GetUsersWithAffiliation(channelName, 'admin') 1421 except AttributeError: 1422 logger.exception('Error when getting users with operator affiliation') 1423 users = [] 1424 1425 if session.charid in users: 1426 return True 1427 return False 1428 1429 def SetMotd(self, channelName, motd): 1430 bannedwords.check_words_allowed(motd) 1431 if self.verbose: 1432 logger.debug('SetMotd for %s: %s', channelName, motd) 1433 roomName = self.GetFullRoomName(channelName) 1434 channelCategory = GetChannelCategory(channelName) 1435 if channelCategory == xmppchatConst.CATEGORY_FLEET: 1436 sm.GetService('fleet').SetRemoteMotd(motd) 1437 return 1438 if channelCategory in [xmppchatConst.CATEGORY_CORP, xmppchatConst.CATEGORY_ALLIANCE]: 1439 return self.XmppChatMgr.SetMotd(roomName, motd) 1440 self.xmppConnection.SendXmppSubject(roomName, motd) 1441 1442 def GetMotd(self, channelName): 1443 window = self.GetGroupChatWindow(channelName) 1444 return window.GetMotd() 1445 1446 def ConfigureChannel(self, channelName, roomconfig): 1447 form = "<field var='FORM_TYPE'><value>http://jabber.org/protocol/muc#roomconfig</value></field>\n" 1448 for key, value in roomconfig.items(): 1449 field = "<field var='{0}'><value>{1}</value></field>\n".format(key, value) 1450 form += field 1451 1452 query = "\n<query xmlns='http://jabber.org/protocol/muc#owner'>\n<x xmlns='jabber:x:data' type='submit'>\n" 1453 query += form 1454 query += '</x>\n</query>' 1455 roomName = self.GetFullRoomName(channelName) 1456 response = self.xmppConnection.IssueRequest(query, 'set', to=roomName, method_tag='configure_channel') 1457 if not response.attributes.getValue('type') == 'result': 1458 logger.warn('ConfigureChannel: Incorrect response') 1459 return False 1460 return True 1461 1462 def BanUserFromChannel(self, channelName, charid, reason, durationInSecs): 1463 self.SetAffiliation(channelName, charid, 'outcast') 1464 self._RegisterTemporaryRestriction('ban', channelName, charid, durationInSecs) 1465 if durationInSecs: 1466 self.SetAffiliation(channelName, charid, 'none') 1467 1468 def UnbanUserFromChannel(self, channelName, charid): 1469 if self.verbose: 1470 logger.debug('UnbanUserFromChannel(%s, %s)', channelName, charid) 1471 self.SetAffiliation(channelName, charid, 'none') 1472 if str(charid).startswith('set_'): 1473 prefix, charid = charid.split('_', 1) 1474 self.SetAffiliation(channelName, charid, 'none') 1475 self._UnregisterTemporaryRestriction('ban', channelName, charid) 1476 1477 def MuteUser(self, channelName, charid, reason, durationInSecs = 0): 1478 self.SetRole(channelName, charid, 'visitor', reason) 1479 if durationInSecs <= 0: 1480 durationInSecs = 315360000 1481 self._RegisterTemporaryRestriction('mute', channelName, charid, durationInSecs) 1482 1483 def UnmuteUser(self, channelName, charid): 1484 self.SetRole(channelName, charid, 'participant') 1485 self._UnregisterTemporaryRestriction('mute', channelName, charid) 1486 1487 def _RegisterTemporaryRestriction(self, category, channelName, charid, durationInSecs): 1488 roomName = self.GetFullRoomName(channelName) 1489 jid = self.GetUserNameFromCharId(charid) 1490 self.xmppConnection.RegisterTemporaryRestriction(category, roomName, jid, durationInSecs) 1491 1492 def _UnregisterTemporaryRestriction(self, category, channelName, charID): 1493 roomName = self.GetFullRoomName(channelName) 1494 jid = self.GetUserNameFromCharId(charID) 1495 self.xmppConnection.UnregisterTemporaryRestriction(category, roomName, jid) 1496 1497 def IsMuted(self, channelID, charID): 1498 muted = self.GetUsersWithRole(channelID, 'visitor') 1499 if self.verbose: 1500 logger.debug('IsGagged(%s, %s): %s', channelID, charID, muted) 1501 return charID in muted 1502 1503 def IsOperator(self, channelID, charID): 1504 memberRole = self.memberRolesByChannels[channelID].get(charID, None) 1505 if memberRole: 1506 return memberRole == 'moderator' 1507 else: 1508 moderators = self.GetUsersWithRole(channelID, 'moderator') 1509 return charID in moderators 1510 1511 def SetAffiliation(self, channelName, charid, affiliation): 1512 if self.verbose: 1513 logger.debug('SetAffiliation(%s, %s, %s)', channelName, charid, affiliation) 1514 roomName = self.GetFullRoomName(channelName) 1515 idtype = 'character' 1516 if IsCorporation(charid): 1517 idtype = 'corporation' 1518 elif IsAlliance(charid): 1519 idtype = 'alliance' 1520 template = "<query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='{1}' jid='{0}'><reason>{2}</reason></item></query>" 1521 query = template.format(self.GetUserNameFromCharId(charid), affiliation, idtype) 1522 response = self.xmppConnection.IssueRequest(query, 'set', to=roomName, method_tag='set_affiliation') 1523 if not response.attributes.getValue('type') == 'result': 1524 logger.warn('SetAffiliation: Incorrect response') 1525 1526 def GetUsersWithAffiliation(self, channelName, affiliation): 1527 if GetChannelCategory(channelName) == xmppchatConst.CATEGORY_LOCAL_SUPPRESSED: 1528 return [] 1529 roomName = self.GetFullRoomName(channelName) 1530 template = "<query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='{0}'/></query>" 1531 query = template.format(affiliation) 1532 response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_users_with_affiliation') 1533 if response.attributes.getValue('type') == 'result': 1534 return GetUsersFromResponse(response) 1535 else: 1536 return [] 1537 1538 def SetRole(self, channelName, charId, role, reason = None): 1539 roomName = self.GetFullRoomName(channelName) 1540 self.xmppConnection.SetRole(roomName, charId, role, reason) 1541 1542 def GetUsersWithRole(self, channelName, role): 1543 if GetChannelCategory(channelName) == xmppchatConst.CATEGORY_LOCAL_SUPPRESSED: 1544 return [] 1545 roomName = self.GetFullRoomName(channelName) 1546 template = "<query xmlns='http://jabber.org/protocol/muc#admin'><item role='{0}'/></query>" 1547 query = template.format(role) 1548 response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_users_with_role') 1549 if response.attributes.getValue('type') == 'result': 1550 return GetUsersFromResponse(response) 1551 else: 1552 return [] 1553 1554 def GetUsersWithTemporaryRestriction(self, channelName, category): 1555 roomName = self.GetFullRoomName(channelName) 1556 template = "<query xmlns='urn:xmpp:expiring_record#search' room='{0}' category='{1}'/>" 1557 query = template.format(roomName, category) 1558 response = self.xmppConnection.IssueRequest(query, 'get', to=self.hostname, method_tag='get_users_with_temporary_restriction') 1559 if response.attributes.getValue('type') == 'result': 1560 return GetUsersFromResponse(response) 1561 else: 1562 return [] 1563 1564 def GetChannelConfig(self, channel): 1565 roomName = self.GetFullRoomName(channel) 1566 query = "<query xmlns='http://jabber.org/protocol/muc#owner'/>" 1567 response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_channel_config') 1568 if response.attributes.getValue('type') == 'result': 1569 return response 1570 else: 1571 return None 1572 1573 def LocalEchoAll(self, msg, charid): 1574 window = self.GetGroupChatWindow(xmppchatConst.CATEGORY_LOCAL) 1575 if window: 1576 if charid is None: 1577 charid = localization.GetByLabel('UI/Common/Message') 1578 window.Receive(charid, msg) 1579 1580 def StoreChannelList(self): 1581 channelList = [] 1582 for windowName, window in self.groupChatWindows.items(): 1583 if type(window) == stackless.channel: 1584 continue 1585 displayName = window.displayName 1586 channel = window.GetChannelId() 1587 channelList.append((windowName, channel, displayName)) 1588 1589 settings.char.ui.Set('chatchannels', channelList) 1590 1591 def RemoveChannelFromCurrentChannelsSettings(self, windowName): 1592 allChannels = settings.char.ui.Get('chatchannels', []) 1593 allChannelsNew = [] 1594 for eachChannel in allChannels: 1595 if eachChannel[0] != windowName: 1596 allChannelsNew.append(eachChannel) 1597 1598 settings.char.ui.Set('chatchannels', allChannelsNew) 1599 1600 def CreateSessionChannelWindows(self): 1601 for sessionVariable, groupName in SESSION_CHANGE_CATEGORIES.iteritems(): 1602 if getattr(session, sessionVariable) and not self._IsCategoryMigrated(groupName): 1603 self.GetOrCreateGroupChatWindow(groupName, False) 1604 1605 def _RestoreChannelList(self, channelListInSettings): 1606 if self.verbose: 1607 logger.debug('Restoring channels from settings') 1608 for windowName, channel, caption in channelListInSettings: 1609 if not self.ShouldChannelBeRestoredFromSettings(windowName, channel): 1610 continue 1611 w = self.groupChatWindows.get(windowName, None) 1612 if w is None or w.destroyed: 1613 if self.verbose: 1614 logger.debug('Creating window %s for channel %s (%s)', windowName, channel, caption) 1615 windowId = 'chatchannel_%s' % windowName 1616 window = XmppChatWindow(receiver=channel, isGroupChat=True, channel=channel, name=windowName, caption=caption, windowID=windowId, check_open_minimized=True) 1617 window.verbose = self.verbose 1618 self.groupChatWindows[windowName] = window 1619 1620 def ShouldChannelBeRestoredFromSettings(self, windowName, channel): 1621 if windowName in SESSION_CHANGE_CATEGORIES: 1622 return False 1623 try: 1624 if GetChannelCategory(channel) in (xmppchatConst.CATEGORY_PLAYER, xmppchatConst.CATEGORY_SYSTEM): 1625 return True 1626 except IndexError: 1627 logger.warn('ShouldChannelBeRestoredFromSettings: Could not find channel category: %s' % channel) 1628 1629 return False 1630 1631 def InitChatFilters(self): 1632 self.characterSettings = sm.GetService('characterSettings') 1633 chatFilters = self.GetChatFilters() 1634 UpdateChatFilterVariables(chatFilters.get('bannedWords', []), chatFilters.get('highlightWords', [])) 1635 1636 def GetChatFilters(self): 1637 if session.charid is None: 1638 return 1639 if self.chatFilters is None: 1640 self.chatFilters = self.FetchFiltersFromServer() 1641 return self.chatFilters 1642 1643 def FetchFiltersFromServer(self): 1644 chatFilters = {} 1645 yamlStr = self.characterSettings.Get('chatFilters') 1646 if yamlStr is not None: 1647 chatFilters = yaml.load(yamlStr, Loader=yaml.CLoader) 1648 return chatFilters 1649 1650 def SaveChatFiltersOnServer(self, bannedWords, highlightWords, blinkOnHighlightWords): 1651 currentFilters = self.GetChatFilters() 1652 if currentFilters: 1653 currentBanned = currentFilters.get('bannedWords', []) 1654 currentHighlight = currentFilters.get('highlightWords') 1655 currentBlink = currentFilters.get('blinkOnHighlightWords') 1656 if currentBanned == bannedWords and currentHighlight == highlightWords and currentBlink == blinkOnHighlightWords: 1657 return 1658 self.chatFilters = {'bannedWords': bannedWords, 1659 'highlightWords': highlightWords, 1660 'blinkOnHighlightWords': blinkOnHighlightWords} 1661 yamlFilters = yaml.safe_dump(self.chatFilters) 1662 self.characterSettings.Save('chatFilters', yamlFilters) 1663 UpdateChatFilterVariables(bannedWords, highlightWords) 1664 1665 def GetChannelMessages(self): 1666 messageText = '' 1667 for window in self.groupChatWindows.itervalues(): 1668 messageText += window.GetMessages() 1669 1670 return messageText 1671 1672 def AskYesNoQuestion(self, question, props, defaultChoice = 1): 1673 if defaultChoice: 1674 defaultChoice = uiconst.ID_YES 1675 else: 1676 defaultChoice = uiconst.ID_NO 1677 return prompt_player_fsd_dialog(question, props, uiconst.YESNO, defaultChoice) == uiconst.ID_YES 1678 1679 def ReloadChannel(self, channelID): 1680 window = self.GetGroupChatWindow(channelID) 1681 if window: 1682 window.LoadMessages() 1683 1684 def GetUserData(self, charid): 1685 userData = self.userData.get(charid) 1686 if userData and userData.get('corpid', 0) != 0: 1687 return userData 1688 user = self.GetUserNameFromCharId(charid) 1689 retries = 3 1690 while retries > 0: 1691 response = self.xmppConnection.GetEveUserData(user) 1692 if response: 1693 userData = self._GetPresenceUserData(charid, response) 1694 if userData and userData.get('corpid', 0) != 0: 1695 self.userData[charid] = userData 1696 return userData 1697 if userData: 1698 logger.warning('User data has no corp for %s', charid) 1699 else: 1700 logger.warning('Empty user data for %s', charid) 1701 else: 1702 logger.warning('Fetching user data failed for %s', charid) 1703 retries -= 1 1704 uthread2.sleep(0.1) 1705 1706 logger.warning('Returning empty user data for %s', charid) 1707 return {} 1708 1709 def IsPrivateChatPending(self, channelName): 1710 return channelName in self.myPendingPrivateChats 1711 1712 def IsSenderMessage(self, sender): 1713 return sender == self.translatedMsgText 1714 1715 def GetNumPilotsInSystem(self): 1716 window = self.GetGroupChatWindow('local') 1717 if window: 1718 return len(window.GetMembers()) 1719 1720 def PokePlayerAboutChatMsgGm(self, charID, channelName): 1721 sm.RemoteSvc('mailMgr').PokePlayerAboutChatMsgGm(charID, channelName) 1722 1723 def OnCharacterPokedByGM(self, gmCharID, channelName): 1724 text = localization.GetByLabel('UI/Chat/ChatSvc/GmIsTryingToReachYou', charID=session.charid, gmCharID=gmCharID) 1725 elelmentName = 'chatchannel_%s' % channelName 1726 sm.GetService('uiHighlightingService').highlight_ui_element_by_name(elelmentName, text, 7, default_direction=UiHighlightDirections.UP) 1727 1728 def send_honey_event(self, event_name, extra_fields = None): 1729 event_fields = {'eve.xmpp.connection.has_logged_in': self.hasLoggedInOnce, 1730 'eve.xmpp.connection.address': self.xmppConnection.hostname, 1731 'enduser.id': self.xmppConnection.username, 1732 'eve.xmpp.connection.reconnecting': self.isReconnecting, 1733 'eve.xmpp.connection.id': id(self.xmppConnection), 1734 SERVICE_NAME: SERVICE_NAME_PREFIX + 'xmppchatsvc'} 1735 if extra_fields is not None: 1736 event_fields.update(extra_fields) 1737 mhe.send(event_name, event_fields) 1738 1739 @property 1740 def XmppChatMgr(self): 1741 return sm.ProxySvc('XmppChatMgr')