comparison sat_frontends/primitivus/chat.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 4d5b9d4c7448
children ab7e8ade848a
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
33 from sat_frontends.primitivus.contact_list import ContactList 33 from sat_frontends.primitivus.contact_list import ContactList
34 from functools import total_ordering 34 from functools import total_ordering
35 import bisect 35 import bisect
36 36
37 37
38 OCCUPANTS_FOOTER = _(u"{} occupants") 38 OCCUPANTS_FOOTER = _("{} occupants")
39 39
40 40
41 class MessageWidget(urwid.WidgetWrap, quick_chat.MessageWidget): 41 class MessageWidget(urwid.WidgetWrap, quick_chat.MessageWidget):
42 def __init__(self, mess_data): 42 def __init__(self, mess_data):
43 """ 43 """
111 d = self.mess_data 111 d = self.mess_data
112 mention = d.mention 112 mention = d.mention
113 113
114 # message status 114 # message status
115 if d.status is None: 115 if d.status is None:
116 markup.append(u" ") 116 markup.append(" ")
117 elif d.status == "delivered": 117 elif d.status == "delivered":
118 markup.append(("msg_status_received", u"✔")) 118 markup.append(("msg_status_received", "✔"))
119 else: 119 else:
120 log.warning(u"Unknown status: {}".format(d.status)) 120 log.warning("Unknown status: {}".format(d.status))
121 121
122 # timestamp 122 # timestamp
123 if self.parent.show_timestamp: 123 if self.parent.show_timestamp:
124 attr = "msg_mention" if mention else "date" 124 attr = "msg_mention" if mention else "date"
125 markup.append((attr, u"[{}]".format(d.time_text))) 125 markup.append((attr, "[{}]".format(d.time_text)))
126 else: 126 else:
127 if mention: 127 if mention:
128 markup.append(("msg_mention", "[*]")) 128 markup.append(("msg_mention", "[*]"))
129 129
130 # nickname 130 # nickname
132 markup.append( 132 markup.append(
133 ("my_nick" if d.own_mess else "other_nick", "**" if d.own_mess else "*") 133 ("my_nick" if d.own_mess else "other_nick", "**" if d.own_mess else "*")
134 ) 134 )
135 else: 135 else:
136 markup.append( 136 markup.append(
137 ("my_nick" if d.own_mess else "other_nick", u"[{}] ".format(d.nick or "")) 137 ("my_nick" if d.own_mess else "other_nick", "[{}] ".format(d.nick or ""))
138 ) 138 )
139 139
140 msg = self.message # needed to generate self.selected_lang 140 msg = self.message # needed to generate self.selected_lang
141 141
142 if d.selected_lang: 142 if d.selected_lang:
143 markup.append(("msg_lang", u"[{}] ".format(d.selected_lang))) 143 markup.append(("msg_lang", "[{}] ".format(d.selected_lang)))
144 144
145 # message body 145 # message body
146 markup.append(msg) 146 markup.append(msg)
147 147
148 return markup 148 return markup
169 self.occupant_data.parent._occupantsClicked, 169 self.occupant_data.parent._occupantsClicked,
170 user_args=[self.occupant_data], 170 user_args=[self.occupant_data],
171 ) 171 )
172 super(OccupantWidget, self).__init__(text) 172 super(OccupantWidget, self).__init__(text)
173 173
174 def __hash__(self):
175 return id(self)
176
174 def __eq__(self, other): 177 def __eq__(self, other):
175 if other is None: 178 if other is None:
176 return False 179 return False
177 return self.occupant_data.nick == other.occupant_data.nick 180 return self.occupant_data.nick == other.occupant_data.nick
178 181
216 # TODO: role and affiliation are shown in a Q&D way 219 # TODO: role and affiliation are shown in a Q&D way
217 # should be more intuitive and themable 220 # should be more intuitive and themable
218 o = self.occupant_data 221 o = self.occupant_data
219 markup = [] 222 markup = []
220 markup.append( 223 markup.append(
221 ("info_msg", u"{}{} ".format(o.role[0].upper(), o.affiliation[0].upper())) 224 ("info_msg", "{}{} ".format(o.role[0].upper(), o.affiliation[0].upper()))
222 ) 225 )
223 markup.append(o.nick) 226 markup.append(o.nick)
224 if o.state is not None: 227 if o.state is not None:
225 markup.append(u" {}".format(C.CHAT_STATE_ICON[o.state])) 228 markup.append(" {}".format(C.CHAT_STATE_ICON[o.state]))
226 return markup 229 return markup
227 230
228 # events 231 # events
229 def update(self, update_dict=None): 232 def update(self, update_dict=None):
230 self.redraw() 233 self.redraw()
238 self.updateFooter() 241 self.updateFooter()
239 occupants_widget = urwid.Frame( 242 occupants_widget = urwid.Frame(
240 urwid.ListBox(self.occupants_walker), footer=self.occupants_footer 243 urwid.ListBox(self.occupants_walker), footer=self.occupants_footer
241 ) 244 )
242 super(OccupantsWidget, self).__init__(occupants_widget) 245 super(OccupantsWidget, self).__init__(occupants_widget)
243 occupants_list = sorted(self.parent.occupants.keys(), key=lambda o: o.lower()) 246 occupants_list = sorted(list(self.parent.occupants.keys()), key=lambda o: o.lower())
244 for occupant in occupants_list: 247 for occupant in occupants_list:
245 occupant_data = self.parent.occupants[occupant] 248 occupant_data = self.parent.occupants[occupant]
246 self.occupants_walker.append(OccupantWidget(occupant_data)) 249 self.occupants_walker.append(OccupantWidget(occupant_data))
247 250
248 def clear(self): 251 def clear(self):
251 def updateFooter(self): 254 def updateFooter(self):
252 """update footer widget""" 255 """update footer widget"""
253 txt = OCCUPANTS_FOOTER.format(len(self.parent.occupants)) 256 txt = OCCUPANTS_FOOTER.format(len(self.parent.occupants))
254 self.occupants_footer.set_text(txt) 257 self.occupants_footer.set_text(txt)
255 258
256 def getNicks(self, start=u""): 259 def getNicks(self, start=""):
257 """Return nicks of all occupants 260 """Return nicks of all occupants
258 261
259 @param start(unicode): only return nicknames which start with this text 262 @param start(unicode): only return nicknames which start with this text
260 """ 263 """
261 return [ 264 return [
279 282
280 283
281 class Chat(PrimitivusWidget, quick_chat.QuickChat): 284 class Chat(PrimitivusWidget, quick_chat.QuickChat):
282 def __init__(self, host, target, type_=C.CHAT_ONE2ONE, nick=None, occupants=None, 285 def __init__(self, host, target, type_=C.CHAT_ONE2ONE, nick=None, occupants=None,
283 subject=None, profiles=None): 286 subject=None, profiles=None):
284 quick_chat.QuickChat.__init__(
285 self, host, target, type_, nick, occupants, subject, profiles=profiles
286 )
287 self.filters = [] # list of filter callbacks to apply 287 self.filters = [] # list of filter callbacks to apply
288 self.mess_walker = urwid.SimpleListWalker([]) 288 self.mess_walker = urwid.SimpleListWalker([])
289 self.mess_widgets = urwid.ListBox(self.mess_walker) 289 self.mess_widgets = urwid.ListBox(self.mess_walker)
290 self.chat_widget = urwid.Frame(self.mess_widgets) 290 self.chat_widget = urwid.Frame(self.mess_widgets)
291 self.chat_colums = urwid.Columns([("weight", 8, self.chat_widget)]) 291 self.chat_colums = urwid.Columns([("weight", 8, self.chat_widget)])
292 self.pile = urwid.Pile([self.chat_colums]) 292 self.pile = urwid.Pile([self.chat_colums])
293 PrimitivusWidget.__init__(self, self.pile, self.target) 293 PrimitivusWidget.__init__(self, self.pile, target)
294 quick_chat.QuickChat.__init__(
295 self, host, target, type_, nick, occupants, subject, profiles=profiles
296 )
294 297
295 # we must adapt the behaviour with the type 298 # we must adapt the behaviour with the type
296 if type_ == C.CHAT_GROUP: 299 if type_ == C.CHAT_GROUP:
297 if len(self.chat_colums.contents) == 1: 300 if len(self.chat_colums.contents) == 1:
298 self.occupants_widget = OccupantsWidget(self) 301 self.occupants_widget = OccupantsWidget(self)
363 word_idx = 0 366 word_idx = 0
364 else: 367 else:
365 if word_idx == len(words): 368 if word_idx == len(words):
366 word_idx = 0 369 word_idx = 0
367 word = completion_data["last_word"] = words[word_idx] 370 word = completion_data["last_word"] = words[word_idx]
368 return u"{}{}{}".format(text[: space + 1], word, ": " if space < 0 else "") 371 return "{}{}{}".format(text[: space + 1], word, ": " if space < 0 else "")
369 372
370 def getMenu(self): 373 def getMenu(self):
371 """Return Menu bar""" 374 """Return Menu bar"""
372 menu = sat_widgets.Menu(self.host.loop) 375 menu = sat_widgets.Menu(self.host.loop)
373 if self.type == C.CHAT_GROUP: 376 if self.type == C.CHAT_GROUP:
477 return 480 return
478 481
479 if wid.mess_data.mention: 482 if wid.mess_data.mention:
480 from_jid = wid.mess_data.from_jid 483 from_jid = wid.mess_data.from_jid
481 msg = _( 484 msg = _(
482 u"You have been mentioned by {nick} in {room}".format( 485 "You have been mentioned by {nick} in {room}".format(
483 nick=wid.mess_data.nick, room=self.target 486 nick=wid.mess_data.nick, room=self.target
484 ) 487 )
485 ) 488 )
486 self.host.notify( 489 self.host.notify(
487 C.NOTIFY_MENTION, from_jid, msg, widget=self, profile=self.profile 490 C.NOTIFY_MENTION, from_jid, msg, widget=self, profile=self.profile
488 ) 491 )
489 elif not minor_notifs: 492 elif not minor_notifs:
490 return 493 return
491 elif self.type == C.CHAT_ONE2ONE: 494 elif self.type == C.CHAT_ONE2ONE:
492 from_jid = wid.mess_data.from_jid 495 from_jid = wid.mess_data.from_jid
493 msg = _(u"{entity} is talking to you".format(entity=from_jid)) 496 msg = _("{entity} is talking to you".format(entity=from_jid))
494 self.host.notify( 497 self.host.notify(
495 C.NOTIFY_MESSAGE, from_jid, msg, widget=self, profile=self.profile 498 C.NOTIFY_MESSAGE, from_jid, msg, widget=self, profile=self.profile
496 ) 499 )
497 else: 500 else:
498 self.host.notify( 501 self.host.notify(
555 558
556 def setSubject(self, subject, wrap="space"): 559 def setSubject(self, subject, wrap="space"):
557 """Set title for a group chat""" 560 """Set title for a group chat"""
558 quick_chat.QuickChat.setSubject(self, subject) 561 quick_chat.QuickChat.setSubject(self, subject)
559 self.subj_wid = urwid.Text( 562 self.subj_wid = urwid.Text(
560 unicode(subject.replace("\n", "|") if wrap == "clip" else subject), 563 str(subject.replace("\n", "|") if wrap == "clip" else subject),
561 align="left" if wrap == "clip" else "center", 564 align="left" if wrap == "clip" else "center",
562 wrap=wrap, 565 wrap=wrap,
563 ) 566 )
564 self.chat_widget.header = urwid.AttrMap(self.subj_wid, "title") 567 self.chat_widget.header = urwid.AttrMap(self.subj_wid, "title")
565 self.host.redraw() 568 self.host.redraw()
571 574
572 @param clear(bool): clear message before printing if true 575 @param clear(bool): clear message before printing if true
573 """ 576 """
574 if clear: 577 if clear:
575 del self.mess_walker[:] 578 del self.mess_walker[:]
576 for message in self.messages.itervalues(): 579 for message in self.messages.values():
577 self.appendMessage(message, minor_notifs=False) 580 self.appendMessage(message, minor_notifs=False)
578 581
579 def redraw(self): 582 def redraw(self):
580 """redraw all messages""" 583 """redraw all messages"""
581 for w in self.mess_walker: 584 for w in self.mess_walker:
587 def updateHistory(self, size=C.HISTORY_LIMIT_DEFAULT, filters=None, profile="@NONE@"): 590 def updateHistory(self, size=C.HISTORY_LIMIT_DEFAULT, filters=None, profile="@NONE@"):
588 del self.mess_walker[:] 591 del self.mess_walker[:]
589 if filters and "search" in filters: 592 if filters and "search" in filters:
590 self.mess_walker.append( 593 self.mess_walker.append(
591 urwid.Text( 594 urwid.Text(
592 _(u"Results for searching the globbing pattern: {}").format( 595 _("Results for searching the globbing pattern: {}").format(
593 filters["search"] 596 filters["search"]
594 ) 597 )
595 ) 598 )
596 ) 599 )
597 self.mess_walker.append( 600 self.mess_walker.append(
598 urwid.Text(_(u"Type ':history <lines>' to reset the chat history")) 601 urwid.Text(_("Type ':history <lines>' to reset the chat history"))
599 ) 602 )
600 super(Chat, self).updateHistory(size, filters, profile) 603 super(Chat, self).updateHistory(size, filters, profile)
601 604
602 def _onHistoryPrinted(self): 605 def _onHistoryPrinted(self):
603 """Refresh or scroll down the focus after the history is printed""" 606 """Refresh or scroll down the focus after the history is printed"""
673 self.changeSubject(dialog.text) 676 self.changeSubject(dialog.text)
674 self.host.removePopUp(dialog) 677 self.host.removePopUp(dialog)
675 678
676 def onSubjectDialog(self, new_subject=None): 679 def onSubjectDialog(self, new_subject=None):
677 dialog = sat_widgets.InputDialog( 680 dialog = sat_widgets.InputDialog(
678 _(u"Change title"), 681 _("Change title"),
679 _(u"Enter the new title"), 682 _("Enter the new title"),
680 default_txt=new_subject if new_subject is not None else self.subject, 683 default_txt=new_subject if new_subject is not None else self.subject,
681 ) 684 )
682 dialog.setCallback("ok", self._onSubjectDialogCb, dialog) 685 dialog.setCallback("ok", self._onSubjectDialogCb, dialog)
683 dialog.setCallback("cancel", lambda __: self.host.removePopUp(dialog)) 686 dialog.setCallback("cancel", lambda __: self.host.removePopUp(dialog))
684 self.host.showPopUp(dialog) 687 self.host.showPopUp(dialog)