comparison sat_frontends/quick_frontend/quick_chat.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents 78b5f356900c
children 4b842c1fb686
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
53 self.to_jid = to_jid 53 self.to_jid = to_jid
54 self.message = msg 54 self.message = msg
55 self.subject = subject 55 self.subject = subject
56 self.type = type_ 56 self.type = type_
57 self.extra = extra 57 self.extra = extra
58 self.nick = self.getNick(from_jid) 58 self.nick = self.get_nick(from_jid)
59 self._status = None 59 self._status = None
60 # own_mess is True if message was sent by profile's jid 60 # own_mess is True if message was sent by profile's jid
61 self.own_mess = ( 61 self.own_mess = (
62 (from_jid.resource == self.parent.nick) 62 (from_jid.resource == self.parent.nick)
63 if self.parent.type == C.CHAT_GROUP 63 if self.parent.type == C.CHAT_GROUP
67 if self.parent.type == C.CHAT_GROUP and not self.own_mess: 67 if self.parent.type == C.CHAT_GROUP and not self.own_mess:
68 for m in msg.values(): 68 for m in msg.values():
69 if self.parent.nick.lower() in m.lower(): 69 if self.parent.nick.lower() in m.lower():
70 self._mention = True 70 self._mention = True
71 break 71 break
72 self.handleMe() 72 self.handle_me()
73 self.widgets = set() # widgets linked to this message 73 self.widgets = set() # widgets linked to this message
74 74
75 def __str__(self): 75 def __str__(self):
76 return "Message<{mess_type}> [{time}]{nick}> {message}".format( 76 return "Message<{mess_type}> [{time}]{nick}> {message}".format(
77 mess_type=self.type, 77 mess_type=self.type,
145 contact_list = self.host.contact_lists[self.profile] 145 contact_list = self.host.contact_lists[self.profile]
146 try: 146 try:
147 return contact_list.getCache(entity, "avatar") 147 return contact_list.getCache(entity, "avatar")
148 except (exceptions.NotFound, KeyError): 148 except (exceptions.NotFound, KeyError):
149 # we don't check the result as the avatar listener will be called 149 # we don't check the result as the avatar listener will be called
150 self.host.bridge.avatarGet(entity, True, self.profile) 150 self.host.bridge.avatar_get(entity, True, self.profile)
151 return None 151 return None
152 152
153 @property 153 @property
154 def encrypted(self): 154 def encrypted(self):
155 return self.extra.get("encrypted", False) 155 return self.extra.get("encrypted", False)
156 156
157 def getNick(self, entity): 157 def get_nick(self, entity):
158 """Return nick of an entity when possible""" 158 """Return nick of an entity when possible"""
159 contact_list = self.host.contact_lists[self.profile] 159 contact_list = self.host.contact_lists[self.profile]
160 if self.type == C.MESS_TYPE_INFO and self.info_type in ROOM_USER_MOVED: 160 if self.type == C.MESS_TYPE_INFO and self.info_type in ROOM_USER_MOVED:
161 try: 161 try:
162 return self.extra["user_nick"] 162 return self.extra["user_nick"]
163 except KeyError: 163 except KeyError:
164 log.error("extra data is missing user nick for uid {}".format(self.uid)) 164 log.error("extra data is missing user nick for uid {}".format(self.uid))
165 return "" 165 return ""
166 # FIXME: converted getSpecials to list for pyjamas 166 # FIXME: converted get_specials to list for pyjamas
167 if self.parent.type == C.CHAT_GROUP or entity in list( 167 if self.parent.type == C.CHAT_GROUP or entity in list(
168 contact_list.getSpecials(C.CONTACT_SPECIAL_GROUP) 168 contact_list.get_specials(C.CONTACT_SPECIAL_GROUP)
169 ): 169 ):
170 return entity.resource or "" 170 return entity.resource or ""
171 if entity.bare in contact_list: 171 if entity.bare in contact_list:
172 172
173 try: 173 try:
174 nicknames = contact_list.getCache(entity, "nicknames") 174 nicknames = contact_list.getCache(entity, "nicknames")
175 except (exceptions.NotFound, KeyError): 175 except (exceptions.NotFound, KeyError):
176 # we check result as listener will be called 176 # we check result as listener will be called
177 self.host.bridge.identityGet( 177 self.host.bridge.identity_get(
178 entity.bare, ["nicknames"], True, self.profile) 178 entity.bare, ["nicknames"], True, self.profile)
179 return entity.node or entity 179 return entity.node or entity
180 180
181 if nicknames: 181 if nicknames:
182 return nicknames[0] 182 return nicknames[0]
198 if status != self._status: 198 if status != self._status:
199 self._status = status 199 self._status = status
200 for w in self.widgets: 200 for w in self.widgets:
201 w.update({"status": status}) 201 w.update({"status": status})
202 202
203 def handleMe(self): 203 def handle_me(self):
204 """Check if messages starts with "/me " and change them if it is the case 204 """Check if messages starts with "/me " and change them if it is the case
205 205
206 if several messages (different languages) are presents, they all need to start with "/me " 206 if several messages (different languages) are presents, they all need to start with "/me "
207 """ 207 """
208 # TODO: XHTML-IM /me are not handled 208 # TODO: XHTML-IM /me are not handled
304 self.encrypted = False # True if this session is currently encrypted 304 self.encrypted = False # True if this session is currently encrypted
305 self._locked = False 305 self._locked = False
306 # True when resync is in progress, avoid resynchronising twice when resync is called 306 # True when resync is in progress, avoid resynchronising twice when resync is called
307 # and history is still being updated. For internal use only 307 # and history is still being updated. For internal use only
308 self._resync_lock = False 308 self._resync_lock = False
309 self.setLocked() 309 self.set_locked()
310 if type_ == C.CHAT_GROUP: 310 if type_ == C.CHAT_GROUP:
311 if target.resource: 311 if target.resource:
312 raise exceptions.InternalError( 312 raise exceptions.InternalError(
313 "a group chat entity can't have a resource" 313 "a group chat entity can't have a resource"
314 ) 314 )
315 if nick is None: 315 if nick is None:
316 raise exceptions.InternalError("nick must not be None for group chat") 316 raise exceptions.InternalError("nick must not be None for group chat")
317 317
318 self.nick = nick 318 self.nick = nick
319 self.occupants = {} 319 self.occupants = {}
320 self.setOccupants(occupants) 320 self.set_occupants(occupants)
321 else: 321 else:
322 if occupants is not None or nick is not None: 322 if occupants is not None or nick is not None:
323 raise exceptions.InternalError( 323 raise exceptions.InternalError(
324 "only group chat can have occupants or nick" 324 "only group chat can have occupants or nick"
325 ) 325 )
338 lt.tm_wday, 338 lt.tm_wday,
339 lt.tm_yday, 339 lt.tm_yday,
340 lt.tm_isdst, 340 lt.tm_isdst,
341 ) # struct_time of day changing time 341 ) # struct_time of day changing time
342 if self.host.AVATARS_HANDLER: 342 if self.host.AVATARS_HANDLER:
343 self.host.addListener("avatar", self.onAvatar, profiles) 343 self.host.addListener("avatar", self.on_avatar, profiles)
344 344
345 def setLocked(self): 345 def set_locked(self):
346 """Set locked flag 346 """Set locked flag
347 347
348 To be set when we are waiting for history/search 348 To be set when we are waiting for history/search
349 """ 349 """
350 # FIXME: we don't use getter/setter here because of pyjamas 350 # FIXME: we don't use getter/setter here because of pyjamas
351 # TODO: use proper getter/setter once we get rid of pyjamas 351 # TODO: use proper getter/setter once we get rid of pyjamas
352 if self._locked: 352 if self._locked:
353 log.warning("{wid} is already locked!".format(wid=self)) 353 log.warning("{wid} is already locked!".format(wid=self))
354 return 354 return
355 self._locked = True 355 self._locked = True
356 # messageNew signals are cached when locked 356 # message_new signals are cached when locked
357 self._cache = OrderedDict() 357 self._cache = OrderedDict()
358 log.debug("{wid} is now locked".format(wid=self)) 358 log.debug("{wid} is now locked".format(wid=self))
359 359
360 def setUnlocked(self): 360 def set_unlocked(self):
361 if not self._locked: 361 if not self._locked:
362 log.debug("{wid} was already unlocked".format(wid=self)) 362 log.debug("{wid} was already unlocked".format(wid=self))
363 return 363 return
364 self._locked = False 364 self._locked = False
365 for uid, data in self._cache.items(): 365 for uid, data in self._cache.items():
366 if uid not in self.messages: 366 if uid not in self.messages:
367 self.messageNew(*data) 367 self.message_new(*data)
368 else: 368 else:
369 log.debug("discarding message already in history: {data}, ".format(data=data)) 369 log.debug("discarding message already in history: {data}, ".format(data=data))
370 del self._cache 370 del self._cache
371 log.debug("{wid} is now unlocked".format(wid=self)) 371 log.debug("{wid} is now unlocked".format(wid=self))
372 372
373 def postInit(self): 373 def post_init(self):
374 """Method to be called by frontend after widget is initialised 374 """Method to be called by frontend after widget is initialised
375 375
376 handle the display of history and subject 376 handle the display of history and subject
377 """ 377 """
378 self.historyPrint(profile=self.profile) 378 self.history_print(profile=self.profile)
379 if self.subject is not None: 379 if self.subject is not None:
380 self.setSubject(self.subject) 380 self.set_subject(self.subject)
381 if self.host.ENCRYPTION_HANDLERS: 381 if self.host.ENCRYPTION_HANDLERS:
382 self.getEncryptionState() 382 self.get_encryption_state()
383 383
384 def onDelete(self): 384 def on_delete(self):
385 if self.host.AVATARS_HANDLER: 385 if self.host.AVATARS_HANDLER:
386 self.host.removeListener("avatar", self.onAvatar) 386 self.host.removeListener("avatar", self.on_avatar)
387 387
388 @property 388 @property
389 def contact_list(self): 389 def contact_list(self):
390 return self.host.contact_lists[self.profile] 390 return self.host.contact_lists[self.profile]
391 391
401 401
402 @quick_widgets.QuickWidget.sync.setter 402 @quick_widgets.QuickWidget.sync.setter
403 def sync(self, state): 403 def sync(self, state):
404 quick_widgets.QuickWidget.sync.fset(self, state) 404 quick_widgets.QuickWidget.sync.fset(self, state)
405 if not state: 405 if not state:
406 self.setLocked() 406 self.set_locked()
407 407
408 def _resyncComplete(self): 408 def _resync_complete(self):
409 self.sync = True 409 self.sync = True
410 self._resync_lock = False 410 self._resync_lock = False
411 411
412 def occupantsClear(self): 412 def occupants_clear(self):
413 """Remove all occupants 413 """Remove all occupants
414 414
415 Must be overridden by frontends to clear their own representations of occupants 415 Must be overridden by frontends to clear their own representations of occupants
416 """ 416 """
417 self.occupants.clear() 417 self.occupants.clear()
426 continue 426 continue
427 last_message = mess 427 last_message = mess
428 break 428 break
429 else: 429 else:
430 # we have no message yet, we can get normal history 430 # we have no message yet, we can get normal history
431 self.historyPrint(callback=self._resyncComplete, profile=self.profile) 431 self.history_print(callback=self._resync_complete, profile=self.profile)
432 return 432 return
433 if self.type == C.CHAT_GROUP: 433 if self.type == C.CHAT_GROUP:
434 self.occupantsClear() 434 self.occupants_clear()
435 self.host.bridge.mucOccupantsGet( 435 self.host.bridge.muc_occupants_get(
436 str(self.target), self.profile, callback=self.updateOccupants, 436 str(self.target), self.profile, callback=self.update_occupants,
437 errback=log.error) 437 errback=log.error)
438 self.historyPrint( 438 self.history_print(
439 size=C.HISTORY_LIMIT_NONE, 439 size=C.HISTORY_LIMIT_NONE,
440 filters={'timestamp_start': last_message.timestamp}, 440 filters={'timestamp_start': last_message.timestamp},
441 callback=self._resyncComplete, 441 callback=self._resync_complete,
442 profile=self.profile) 442 profile=self.profile)
443 443
444 ## Widget management ## 444 ## Widget management ##
445 445
446 def __str__(self): 446 def __str__(self):
447 return "Chat Widget [target: {}, type: {}, profile: {}]".format( 447 return "Chat Widget [target: {}, type: {}, profile: {}]".format(
448 self.target, self.type, self.profile 448 self.target, self.type, self.profile
449 ) 449 )
450 450
451 @staticmethod 451 @staticmethod
452 def getWidgetHash(target, profiles): 452 def get_widget_hash(target, profiles):
453 profile = list(profiles)[0] 453 profile = list(profiles)[0]
454 return profile + "\n" + str(target.bare) 454 return profile + "\n" + str(target.bare)
455 455
456 @staticmethod 456 @staticmethod
457 def getPrivateHash(target, profile): 457 def get_private_hash(target, profile):
458 """Get unique hash for private conversations 458 """Get unique hash for private conversations
459 459
460 This method should be used with force_hash to get unique widget for private MUC conversations 460 This method should be used with force_hash to get unique widget for private MUC conversations
461 """ 461 """
462 return (str(profile), target) 462 return (str(profile), target)
463 463
464 def addTarget(self, target): 464 def add_target(self, target):
465 super(QuickChat, self).addTarget(target) 465 super(QuickChat, self).add_target(target)
466 if target.resource: 466 if target.resource:
467 self.current_target = ( 467 self.current_target = (
468 target 468 target
469 ) # FIXME: tmp, must use resource priority throught contactList instead 469 ) # FIXME: tmp, must use resource priority throught contactList instead
470 470
471 def recreateArgs(self, args, kwargs): 471 def recreate_args(self, args, kwargs):
472 """copy important attribute for a new widget""" 472 """copy important attribute for a new widget"""
473 kwargs["type_"] = self.type 473 kwargs["type_"] = self.type
474 if self.type == C.CHAT_GROUP: 474 if self.type == C.CHAT_GROUP:
475 kwargs["occupants"] = {o.nick: o.data for o in self.occupants.values()} 475 kwargs["occupants"] = {o.nick: o.data for o in self.occupants.values()}
476 kwargs["subject"] = self.subject 476 kwargs["subject"] = self.subject
477 try: 477 try:
478 kwargs["nick"] = self.nick 478 kwargs["nick"] = self.nick
479 except AttributeError: 479 except AttributeError:
480 pass 480 pass
481 481
482 def onPrivateCreated(self, widget): 482 def on_private_created(self, widget):
483 """Method called when a new widget for private conversation (MUC) is created""" 483 """Method called when a new widget for private conversation (MUC) is created"""
484 raise NotImplementedError 484 raise NotImplementedError
485 485
486 def getOrCreatePrivateWidget(self, entity): 486 def get_or_create_private_widget(self, entity):
487 """Create a widget for private conversation, or get it if it already exists 487 """Create a widget for private conversation, or get it if it already exists
488 488
489 @param entity: full jid of the target 489 @param entity: full jid of the target
490 """ 490 """
491 return self.host.widgets.getOrCreateWidget( 491 return self.host.widgets.get_or_create_widget(
492 QuickChat, 492 QuickChat,
493 entity, 493 entity,
494 type_=C.CHAT_ONE2ONE, 494 type_=C.CHAT_ONE2ONE,
495 force_hash=self.getPrivateHash(self.profile, entity), 495 force_hash=self.get_private_hash(self.profile, entity),
496 on_new_widget=self.onPrivateCreated, 496 on_new_widget=self.on_private_created,
497 profile=self.profile, 497 profile=self.profile,
498 ) # we force hash to have a new widget, not this one again 498 ) # we force hash to have a new widget, not this one again
499 499
500 @property 500 @property
501 def target(self): 501 def target(self):
503 return self.current_target.bare 503 return self.current_target.bare
504 return self.current_target 504 return self.current_target
505 505
506 ## occupants ## 506 ## occupants ##
507 507
508 def setOccupants(self, occupants): 508 def set_occupants(self, occupants):
509 """Set the whole list of occupants""" 509 """Set the whole list of occupants"""
510 assert len(self.occupants) == 0 510 assert len(self.occupants) == 0
511 for nick, data in occupants.items(): 511 for nick, data in occupants.items():
512 # XXX: this log is disabled because it's really too verbose 512 # XXX: this log is disabled because it's really too verbose
513 # but kept commented as it may be useful for debugging 513 # but kept commented as it may be useful for debugging
514 # log.debug(u"adding occupant {nick} to {room}".format( 514 # log.debug(u"adding occupant {nick} to {room}".format(
515 # nick=nick, room=self.target)) 515 # nick=nick, room=self.target))
516 self.occupants[nick] = Occupant(self, data, self.profile) 516 self.occupants[nick] = Occupant(self, data, self.profile)
517 517
518 def updateOccupants(self, occupants): 518 def update_occupants(self, occupants):
519 """Update occupants list 519 """Update occupants list
520 520
521 In opposition to setOccupants, this only add missing occupants and remove 521 In opposition to set_occupants, this only add missing occupants and remove
522 occupants who have left 522 occupants who have left
523 """ 523 """
524 # FIXME: occupants with modified status are not handled 524 # FIXME: occupants with modified status are not handled
525 local_occupants = set(self.occupants) 525 local_occupants = set(self.occupants)
526 updated_occupants = set(occupants) 526 updated_occupants = set(occupants)
551 except KeyError: 551 except KeyError:
552 log.warning("Trying to remove an unknown occupant: {}".format(nick)) 552 log.warning("Trying to remove an unknown occupant: {}".format(nick))
553 else: 553 else:
554 return occupant 554 return occupant
555 555
556 def setUserNick(self, nick): 556 def set_user_nick(self, nick):
557 """Set the nick of the user, usefull for e.g. change the color of the user""" 557 """Set the nick of the user, usefull for e.g. change the color of the user"""
558 self.nick = nick 558 self.nick = nick
559 559
560 def changeUserNick(self, old_nick, new_nick): 560 def change_user_nick(self, old_nick, new_nick):
561 """Change nick of a user in group list""" 561 """Change nick of a user in group list"""
562 log.info("{old} is now known as {new} in room {room_jid}".format( 562 log.info("{old} is now known as {new} in room {room_jid}".format(
563 old = old_nick, 563 old = old_nick,
564 new = new_nick, 564 new = new_nick,
565 room_jid = self.target)) 565 room_jid = self.target))
566 566
567 ## Messages ## 567 ## Messages ##
568 568
569 def manageMessage(self, entity, mess_type): 569 def manage_message(self, entity, mess_type):
570 """Tell if this chat widget manage an entity and message type couple 570 """Tell if this chat widget manage an entity and message type couple
571 571
572 @param entity (jid.JID): (full) jid of the sending entity 572 @param entity (jid.JID): (full) jid of the sending entity
573 @param mess_type (str): message type as given by messageNew 573 @param mess_type (str): message type as given by message_new
574 @return (bool): True if this Chat Widget manage this couple 574 @return (bool): True if this Chat Widget manage this couple
575 """ 575 """
576 if self.type == C.CHAT_GROUP: 576 if self.type == C.CHAT_GROUP:
577 if ( 577 if (
578 mess_type in (C.MESS_TYPE_GROUPCHAT, C.MESS_TYPE_INFO) 578 mess_type in (C.MESS_TYPE_GROUPCHAT, C.MESS_TYPE_INFO)
582 else: 582 else:
583 if mess_type != C.MESS_TYPE_GROUPCHAT and entity in self.targets: 583 if mess_type != C.MESS_TYPE_GROUPCHAT and entity in self.targets:
584 return True 584 return True
585 return False 585 return False
586 586
587 def updateHistory(self, size=C.HISTORY_LIMIT_DEFAULT, filters=None, profile="@NONE@"): 587 def update_history(self, size=C.HISTORY_LIMIT_DEFAULT, filters=None, profile="@NONE@"):
588 """Called when history need to be recreated 588 """Called when history need to be recreated
589 589
590 Remove all message from history then call historyPrint 590 Remove all message from history then call history_print
591 Must probably be overriden by frontend to clear widget 591 Must probably be overriden by frontend to clear widget
592 @param size (int): number of messages 592 @param size (int): number of messages
593 @param filters (str): patterns to filter the history results 593 @param filters (str): patterns to filter the history results
594 @param profile (str): %(doc_profile)s 594 @param profile (str): %(doc_profile)s
595 """ 595 """
596 self.setLocked() 596 self.set_locked()
597 self.messages.clear() 597 self.messages.clear()
598 self.historyPrint(size, filters, profile=profile) 598 self.history_print(size, filters, profile=profile)
599 599
600 def _onHistoryPrinted(self): 600 def _on_history_printed(self):
601 """Method called when history is printed (or failed) 601 """Method called when history is printed (or failed)
602 602
603 unlock the widget, and can be used to refresh or scroll down 603 unlock the widget, and can be used to refresh or scroll down
604 the focus after the history is printed 604 the focus after the history is printed
605 """ 605 """
606 self.setUnlocked() 606 self.set_unlocked()
607 607
608 def historyPrint(self, size=C.HISTORY_LIMIT_DEFAULT, filters=None, callback=None, 608 def history_print(self, size=C.HISTORY_LIMIT_DEFAULT, filters=None, callback=None,
609 profile="@NONE@"): 609 profile="@NONE@"):
610 """Print the current history 610 """Print the current history
611 611
612 Note: self.setUnlocked will be called once history is printed 612 Note: self.set_unlocked will be called once history is printed
613 @param size (int): number of messages 613 @param size (int): number of messages
614 @param search (str): pattern to filter the history results 614 @param search (str): pattern to filter the history results
615 @param callback(callable, None): method to call when history has been printed 615 @param callback(callable, None): method to call when history has been printed
616 @param profile (str): %(doc_profile)s 616 @param profile (str): %(doc_profile)s
617 """ 617 """
618 if filters is None: 618 if filters is None:
619 filters = {} 619 filters = {}
620 if size == 0: 620 if size == 0:
621 log.debug("Empty history requested, skipping") 621 log.debug("Empty history requested, skipping")
622 self._onHistoryPrinted() 622 self._on_history_printed()
623 return 623 return
624 log_msg = _("now we print the history") 624 log_msg = _("now we print the history")
625 if size != C.HISTORY_LIMIT_DEFAULT: 625 if size != C.HISTORY_LIMIT_DEFAULT:
626 log_msg += _(" ({} messages)".format(size)) 626 log_msg += _(" ({} messages)".format(size))
627 log.debug(log_msg) 627 log.debug(log_msg)
645 # FIXME: info not handled correctly 645 # FIXME: info not handled correctly
646 filters["types"] = C.MESS_TYPE_GROUPCHAT 646 filters["types"] = C.MESS_TYPE_GROUPCHAT
647 647
648 self.history_filters = filters 648 self.history_filters = filters
649 649
650 def _historyGetCb(history): 650 def _history_get_cb(history):
651 # day_format = "%A, %d %b %Y" # to display the day change 651 # day_format = "%A, %d %b %Y" # to display the day change
652 # previous_day = datetime.now().strftime(day_format) 652 # previous_day = datetime.now().strftime(day_format)
653 # message_day = datetime.fromtimestamp(timestamp).strftime(self.day_format) 653 # message_day = datetime.fromtimestamp(timestamp).strftime(self.day_format)
654 # if previous_day != message_day: 654 # if previous_day != message_day:
655 # self.printDayChange(message_day) 655 # self.print_day_change(message_day)
656 # previous_day = message_day 656 # previous_day = message_day
657 for data in history: 657 for data in history:
658 uid, timestamp, from_jid, to_jid, message, subject, type_, extra_s = data 658 uid, timestamp, from_jid, to_jid, message, subject, type_, extra_s = data
659 from_jid = jid.JID(from_jid) 659 from_jid = jid.JID(from_jid)
660 to_jid = jid.JID(to_jid) 660 to_jid = jid.JID(to_jid)
673 subject, 673 subject,
674 type_, 674 type_,
675 extra, 675 extra,
676 profile, 676 profile,
677 ) 677 )
678 self._onHistoryPrinted() 678 self._on_history_printed()
679 if callback is not None: 679 if callback is not None:
680 callback() 680 callback()
681 681
682 def _historyGetEb(err): 682 def _history_get_eb(err):
683 log.error(_("Can't get history: {}").format(err)) 683 log.error(_("Can't get history: {}").format(err))
684 self._onHistoryPrinted() 684 self._on_history_printed()
685 if callback is not None: 685 if callback is not None:
686 callback() 686 callback()
687 687
688 self.host.bridge.historyGet( 688 self.host.bridge.history_get(
689 str(self.host.profiles[profile].whoami.bare), 689 str(self.host.profiles[profile].whoami.bare),
690 str(target), 690 str(target),
691 size, 691 size,
692 True, 692 True,
693 {k: str(v) for k,v in filters.items()}, 693 {k: str(v) for k,v in filters.items()},
694 profile, 694 profile,
695 callback=_historyGetCb, 695 callback=_history_get_cb,
696 errback=_historyGetEb, 696 errback=_history_get_eb,
697 ) 697 )
698 698
699 def messageEncryptionGetCb(self, session_data): 699 def message_encryption_get_cb(self, session_data):
700 if session_data: 700 if session_data:
701 session_data = data_format.deserialise(session_data) 701 session_data = data_format.deserialise(session_data)
702 self.messageEncryptionStarted(session_data) 702 self.message_encryption_started(session_data)
703 703
704 def messageEncryptionGetEb(self, failure_): 704 def message_encryption_get_eb(self, failure_):
705 log.error(_("Can't get encryption state: {reason}").format(reason=failure_)) 705 log.error(_("Can't get encryption state: {reason}").format(reason=failure_))
706 706
707 def getEncryptionState(self): 707 def get_encryption_state(self):
708 """Retrieve encryption state with current target. 708 """Retrieve encryption state with current target.
709 709
710 Once state is retrieved, default messageEncryptionStarted will be called if 710 Once state is retrieved, default message_encryption_started will be called if
711 suitable 711 suitable
712 """ 712 """
713 if self.type == C.CHAT_GROUP: 713 if self.type == C.CHAT_GROUP:
714 return 714 return
715 self.host.bridge.messageEncryptionGet(str(self.target.bare), self.profile, 715 self.host.bridge.message_encryption_get(str(self.target.bare), self.profile,
716 callback=self.messageEncryptionGetCb, 716 callback=self.message_encryption_get_cb,
717 errback=self.messageEncryptionGetEb) 717 errback=self.message_encryption_get_eb)
718 718
719 719
720 def messageNew(self, uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, 720 def message_new(self, uid, timestamp, from_jid, to_jid, msg, subject, type_, extra,
721 profile): 721 profile):
722 if self._locked: 722 if self._locked:
723 self._cache[uid] = ( 723 self._cache[uid] = (
724 uid, 724 uid,
725 timestamp, 725 timestamp,
740 740
741 if self.type == C.CHAT_GROUP: 741 if self.type == C.CHAT_GROUP:
742 if to_jid.resource and type_ != C.MESS_TYPE_GROUPCHAT: 742 if to_jid.resource and type_ != C.MESS_TYPE_GROUPCHAT:
743 # we have a private message, we forward it to a private conversation 743 # we have a private message, we forward it to a private conversation
744 # widget 744 # widget
745 chat_widget = self.getOrCreatePrivateWidget(to_jid) 745 chat_widget = self.get_or_create_private_widget(to_jid)
746 chat_widget.messageNew( 746 chat_widget.message_new(
747 uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile 747 uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile
748 ) 748 )
749 return 749 return
750 if type_ == C.MESS_TYPE_INFO: 750 if type_ == C.MESS_TYPE_INFO:
751 try: 751 try:
766 ) 766 )
767 self.messages[uid] = message 767 self.messages[uid] = message
768 768
769 if "received_timestamp" in extra: 769 if "received_timestamp" in extra:
770 log.warning("Delayed message received after history, this should not happen") 770 log.warning("Delayed message received after history, this should not happen")
771 self.createMessage(message) 771 self.create_message(message)
772 772
773 def messageEncryptionStarted(self, session_data): 773 def message_encryption_started(self, session_data):
774 self.encrypted = True 774 self.encrypted = True
775 log.debug(_("message encryption started with {target} using {encryption}").format( 775 log.debug(_("message encryption started with {target} using {encryption}").format(
776 target=self.target, encryption=session_data['name'])) 776 target=self.target, encryption=session_data['name']))
777 777
778 def messageEncryptionStopped(self, session_data): 778 def message_encryption_stopped(self, session_data):
779 self.encrypted = False 779 self.encrypted = False
780 log.debug(_("message encryption stopped with {target} (was using {encryption})") 780 log.debug(_("message encryption stopped with {target} (was using {encryption})")
781 .format(target=self.target, encryption=session_data['name'])) 781 .format(target=self.target, encryption=session_data['name']))
782 782
783 def createMessage(self, message, append=False): 783 def create_message(self, message, append=False):
784 """Must be implemented by frontend to create and show a new message widget 784 """Must be implemented by frontend to create and show a new message widget
785 785
786 This is only called on messageNew, not on history. 786 This is only called on message_new, not on history.
787 You need to override historyPrint to handle the later 787 You need to override history_print to handle the later
788 @param message(Message): message data 788 @param message(Message): message data
789 """ 789 """
790 raise NotImplementedError 790 raise NotImplementedError
791 791
792 def isUserMoved(self, message): 792 def is_user_moved(self, message):
793 """Return True if message is a user left/joined message 793 """Return True if message is a user left/joined message
794 794
795 @param message(Message): message to check 795 @param message(Message): message to check
796 @return (bool): True is message is user moved info message 796 @return (bool): True is message is user moved info message
797 """ 797 """
802 except KeyError: 802 except KeyError:
803 return False 803 return False
804 else: 804 else:
805 return info_type in ROOM_USER_MOVED 805 return info_type in ROOM_USER_MOVED
806 806
807 def handleUserMoved(self, message): 807 def handle_user_moved(self, message):
808 """Check if this message is a UserMoved one, and merge it when possible 808 """Check if this message is a UserMoved one, and merge it when possible
809 809
810 "merge it" means that info message indicating a user joined/left will be 810 "merge it" means that info message indicating a user joined/left will be
811 grouped if no other non-info messages has been sent since 811 grouped if no other non-info messages has been sent since
812 @param message(Message): message to check 812 @param message(Message): message to check
813 @return (bool): True if this message has been merged 813 @return (bool): True if this message has been merged
814 if True, a new MessageWidget must not be created and appended to history 814 if True, a new MessageWidget must not be created and appended to history
815 """ 815 """
816 if self.isUserMoved(message): 816 if self.is_user_moved(message):
817 for wid in self.message_widgets_rev: 817 for wid in self.message_widgets_rev:
818 # we merge in/out messages if no message was sent meanwhile 818 # we merge in/out messages if no message was sent meanwhile
819 if not isinstance(wid, MessageWidget): 819 if not isinstance(wid, MessageWidget):
820 continue 820 continue
821 elif wid.mess_data.type != C.MESS_TYPE_INFO: 821 elif wid.mess_data.type != C.MESS_TYPE_INFO:
839 ).format(nick=nick, count=count) 839 ).format(nick=nick, count=count)
840 wid.reentered_count += 1 840 wid.reentered_count += 1
841 return True 841 return True
842 return False 842 return False
843 843
844 def printDayChange(self, day): 844 def print_day_change(self, day):
845 """Display the day on a new line. 845 """Display the day on a new line.
846 846
847 @param day(unicode): day to display (or not if this method is not overwritten) 847 @param day(unicode): day to display (or not if this method is not overwritten)
848 """ 848 """
849 # FIXME: not called anymore after refactoring 849 # FIXME: not called anymore after refactoring
850 pass 850 pass
851 851
852 ## Room ## 852 ## Room ##
853 853
854 def setSubject(self, subject): 854 def set_subject(self, subject):
855 """Set title for a group chat""" 855 """Set title for a group chat"""
856 if self.type != C.CHAT_GROUP: 856 if self.type != C.CHAT_GROUP:
857 raise exceptions.InternalError( 857 raise exceptions.InternalError(
858 "trying to set subject for a non group chat window" 858 "trying to set subject for a non group chat window"
859 ) 859 )
860 self.subject = subject 860 self.subject = subject
861 861
862 def changeSubject(self, new_subject): 862 def change_subject(self, new_subject):
863 """Change the subject of the room 863 """Change the subject of the room
864 864
865 This change the subject on the room itself (i.e. via XMPP), 865 This change the subject on the room itself (i.e. via XMPP),
866 while setSubject change the subject of this widget 866 while set_subject change the subject of this widget
867 """ 867 """
868 self.host.bridge.mucSubject(str(self.target), new_subject, self.profile) 868 self.host.bridge.muc_subject(str(self.target), new_subject, self.profile)
869 869
870 def addGamePanel(self, widget): 870 def add_game_panel(self, widget):
871 """Insert a game panel to this Chat dialog. 871 """Insert a game panel to this Chat dialog.
872 872
873 @param widget (Widget): the game panel 873 @param widget (Widget): the game panel
874 """ 874 """
875 raise NotImplementedError 875 raise NotImplementedError
876 876
877 def removeGamePanel(self, widget): 877 def remove_game_panel(self, widget):
878 """Remove the game panel from this Chat dialog. 878 """Remove the game panel from this Chat dialog.
879 879
880 @param widget (Widget): the game panel 880 @param widget (Widget): the game panel
881 """ 881 """
882 raise NotImplementedError 882 raise NotImplementedError
889 # FIXME: to remove ? 889 # FIXME: to remove ?
890 raise NotImplementedError 890 raise NotImplementedError
891 891
892 ## events ## 892 ## events ##
893 893
894 def onChatState(self, from_jid, state, profile): 894 def on_chat_state(self, from_jid, state, profile):
895 """A chat state has been received""" 895 """A chat state has been received"""
896 if self.type == C.CHAT_GROUP: 896 if self.type == C.CHAT_GROUP:
897 nick = from_jid.resource 897 nick = from_jid.resource
898 try: 898 try:
899 self.occupants[nick].state = state 899 self.occupants[nick].state = state
902 "{nick} not found in {room}, ignoring new chat state".format( 902 "{nick} not found in {room}, ignoring new chat state".format(
903 nick=nick, room=self.target.bare 903 nick=nick, room=self.target.bare
904 ) 904 )
905 ) 905 )
906 906
907 def onMessageState(self, uid, status, profile): 907 def on_message_state(self, uid, status, profile):
908 try: 908 try:
909 mess_data = self.messages[uid] 909 mess_data = self.messages[uid]
910 except KeyError: 910 except KeyError:
911 pass 911 pass
912 else: 912 else:
913 mess_data.status = status 913 mess_data.status = status
914 914
915 def onAvatar(self, entity, avatar_data, profile): 915 def on_avatar(self, entity, avatar_data, profile):
916 if self.type == C.CHAT_GROUP: 916 if self.type == C.CHAT_GROUP:
917 if entity.bare == self.target: 917 if entity.bare == self.target:
918 try: 918 try:
919 self.occupants[entity.resource].update({"avatar": avatar_data}) 919 self.occupants[entity.resource].update({"avatar": avatar_data})
920 except KeyError: 920 except KeyError: