Mercurial > libervia-backend
comparison frontends/src/quick_frontend/quick_chat.py @ 1367:f71a0fc26886
merged branch frontends_multi_profiles
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 18 Mar 2015 10:52:28 +0100 |
parents | ba87b940f07a |
children | 017270e6eea4 |
comparison
equal
deleted
inserted
replaced
1295:1e3b1f9ad6e2 | 1367:f71a0fc26886 |
---|---|
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from sat.core.i18n import _ | 20 from sat.core.i18n import _ |
21 from sat.core.log import getLogger | 21 from sat.core.log import getLogger |
22 log = getLogger(__name__) | 22 log = getLogger(__name__) |
23 from sat_frontends.tools.jid import JID | 23 from sat_frontends.tools import jid |
24 from sat_frontends.quick_frontend.quick_utils import unescapePrivate | 24 from sat_frontends.quick_frontend import quick_widgets |
25 from sat_frontends.quick_frontend.constants import Const as C | 25 from sat_frontends.quick_frontend.constants import Const as C |
26 | 26 |
27 | 27 |
28 class QuickChat(object): | 28 try: |
29 | 29 # FIXME: to be removed when an acceptable solution is here |
30 def __init__(self, target, host, type_='one2one'): | 30 unicode('') # XXX: unicode doesn't exist in pyjamas |
31 self.target = target | 31 except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options |
32 self.host = host | 32 unicode = lambda x: str(x) |
33 | |
34 | |
35 class QuickChat(quick_widgets.QuickWidget): | |
36 | |
37 def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None): | |
38 """ | |
39 @param type_: can be C.CHAT_ONE2ONE for single conversation or C.CHAT_GROUP for chat à la IRC | |
40 """ | |
41 quick_widgets.QuickWidget.__init__(self, host, target, profiles=profiles) | |
42 assert type_ in (C.CHAT_ONE2ONE, C.CHAT_GROUP) | |
43 if type_ == C.CHAT_GROUP and target.resource: | |
44 raise ValueError("A group chat entity can't have a resource") | |
45 self.current_target = target | |
33 self.type = type_ | 46 self.type = type_ |
34 self.id = "" | 47 self.id = "" # FIXME: to be removed |
35 self.nick = None | 48 self.nick = None |
36 self.occupants = set() | 49 self.occupants = set() |
37 | 50 self.games = {} |
38 def setType(self, type_): | 51 |
39 """Set the type of the chat | 52 def __str__(self): |
40 @param type: can be 'one2one' for single conversation or 'group' for chat à la IRC | 53 return u"Chat Widget [target: {}, type: {}, profile: {}]".format(self.target, self.type, self.profile) |
41 """ | 54 |
42 self.type = type_ | 55 @staticmethod |
56 def getWidgetHash(target, profile): | |
57 return (unicode(profile), target.bare) | |
58 | |
59 @staticmethod | |
60 def getPrivateHash(target, profile): | |
61 """Get unique hash for private conversations | |
62 | |
63 This method should be used with force_hash to get unique widget for private MUC conversations | |
64 """ | |
65 return (unicode(profile), target) | |
66 | |
67 | |
68 def addTarget(self, target): | |
69 super(QuickChat, self).addTarget(target) | |
70 if target.resource: | |
71 self.current_target = target # FIXME: tmp, must use resource priority throught contactList instead | |
72 | |
73 @property | |
74 def target(self): | |
75 if self.type == C.CHAT_GROUP: | |
76 return self.current_target.bare | |
77 return self.current_target | |
78 | |
79 def manageMessage(self, entity, mess_type): | |
80 """Tell if this chat widget manage an entity and message type couple | |
81 | |
82 @param entity (jid.JID): (full) jid of the sending entity | |
83 @param mess_type (str): message type as given by newMessage | |
84 @return (bool): True if this Chat Widget manage this couple | |
85 """ | |
86 if self.type == C.CHAT_GROUP: | |
87 if mess_type == C.MESS_TYPE_GROUPCHAT and self.target == entity.bare: | |
88 return True | |
89 else: | |
90 if mess_type != C.MESS_TYPE_GROUPCHAT and entity in self.targets: | |
91 return True | |
92 return False | |
43 | 93 |
44 def setPresents(self, nicks): | 94 def setPresents(self, nicks): |
45 """Set the users presents in the contact list for a group chat | 95 """Set the occupants of a group chat. |
46 @param nicks: list of nicknames | 96 |
47 """ | 97 @param nicks (list[unicode]): sorted list of nicknames |
48 log.debug (_("Adding users %s to room") % nicks) | 98 """ |
49 if self.type != "group": | 99 log.debug(_("Adding users %s to room") % nicks) |
50 log.error (_("[INTERNAL] trying to set presents nicks for a non group chat window")) | 100 if self.type != C.CHAT_GROUP: |
51 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here | 101 log.error(_("[INTERNAL] trying to set presents nicks for a non group chat window")) |
102 raise Exception("INTERNAL ERROR") # TODO: raise proper Exception here | |
52 self.occupants.update(nicks) | 103 self.occupants.update(nicks) |
53 | 104 |
54 def replaceUser(self, nick, show_info=True): | 105 def replaceUser(self, nick, show_info=True): |
55 """Add user if it is not in the group list""" | 106 """Add user if it is not in the group list""" |
56 log.debug (_("Replacing user %s") % nick) | 107 log.debug (_("Replacing user %s") % nick) |
57 if self.type != "group": | 108 if self.type != C.CHAT_GROUP: |
58 log.error (_("[INTERNAL] trying to replace user for a non group chat window")) | 109 log.error (_("[INTERNAL] trying to replace user for a non group chat window")) |
59 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here | 110 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here |
60 len_before = len(self.occupants) | 111 len_before = len(self.occupants) |
61 self.occupants.add(nick) | 112 self.occupants.add(nick) |
62 if len_before != len(self.occupants) and show_info: | 113 if len_before != len(self.occupants) and show_info: |
63 self.printInfo("=> %s has joined the room" % nick) | 114 self.printInfo("=> %s has joined the room" % nick) |
64 | 115 |
65 def removeUser(self, nick, show_info=True): | 116 def removeUser(self, nick, show_info=True): |
66 """Remove a user from the group list""" | 117 """Remove a user from the group list""" |
67 log.debug(_("Removing user %s") % nick) | 118 log.debug(_("Removing user %s") % nick) |
68 if self.type != "group": | 119 if self.type != C.CHAT_GROUP: |
69 log.error (_("[INTERNAL] trying to remove user for a non group chat window")) | 120 log.error (_("[INTERNAL] trying to remove user for a non group chat window")) |
70 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here | 121 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here |
71 self.occupants.remove(nick) | 122 self.occupants.remove(nick) |
72 if show_info: | 123 if show_info: |
73 self.printInfo("<= %s has left the room" % nick) | 124 self.printInfo("<= %s has left the room" % nick) |
80 return unicode(self.nick) | 131 return unicode(self.nick) |
81 | 132 |
82 def changeUserNick(self, old_nick, new_nick): | 133 def changeUserNick(self, old_nick, new_nick): |
83 """Change nick of a user in group list""" | 134 """Change nick of a user in group list""" |
84 log.debug(_("Changing nick of user %(old_nick)s to %(new_nick)s") % {"old_nick": old_nick, "new_nick": new_nick}) | 135 log.debug(_("Changing nick of user %(old_nick)s to %(new_nick)s") % {"old_nick": old_nick, "new_nick": new_nick}) |
85 if self.type != "group": | 136 if self.type != C.CHAT_GROUP: |
86 log.error (_("[INTERNAL] trying to change user nick for a non group chat window")) | 137 log.error (_("[INTERNAL] trying to change user nick for a non group chat window")) |
87 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here | 138 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here |
88 self.removeUser(old_nick, show_info=False) | 139 self.removeUser(old_nick, show_info=False) |
89 self.replaceUser(new_nick, show_info=False) | 140 self.replaceUser(new_nick, show_info=False) |
90 self.printInfo("%s is now known as %s" % (old_nick, new_nick)) | 141 self.printInfo("%s is now known as %s" % (old_nick, new_nick)) |
91 | 142 |
92 def setSubject(self, subject): | 143 def setSubject(self, subject): |
93 """Set title for a group chat""" | 144 """Set title for a group chat""" |
94 log.debug(_("Setting subject to %s") % subject) | 145 log.debug(_("Setting subject to %s") % subject) |
95 if self.type != "group": | 146 if self.type != C.CHAT_GROUP: |
96 log.error (_("[INTERNAL] trying to set subject for a non group chat window")) | 147 log.error (_("[INTERNAL] trying to set subject for a non group chat window")) |
97 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here | 148 raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here |
98 | 149 |
99 def afterHistoryPrint(self): | 150 def afterHistoryPrint(self): |
100 """Refresh or scroll down the focus after the history is printed""" | 151 """Refresh or scroll down the focus after the history is printed""" |
104 """Print the current history | 155 """Print the current history |
105 @param size (int): number of messages | 156 @param size (int): number of messages |
106 @param search (str): pattern to filter the history results | 157 @param search (str): pattern to filter the history results |
107 @param profile (str): %(doc_profile)s | 158 @param profile (str): %(doc_profile)s |
108 """ | 159 """ |
109 log.debug(_("now we print the history (%d messages)") % size) | 160 log_msg = _(u"now we print the history") |
161 if size != C.HISTORY_LIMIT_DEFAULT: | |
162 log_msg += _(u" (%d messages)" % size) | |
163 log.debug(log_msg) | |
110 | 164 |
111 def onHistory(history): | 165 def onHistory(history): |
112 for line in history: | 166 for line in history: |
113 timestamp, from_jid, to_jid, message, _type, extra = line | 167 timestamp, from_jid, to_jid, message, type_, extra = line # FIXME: extra is unused ! |
114 if ((self.type == 'group' and _type != 'groupchat') or | 168 if ((self.type == C.CHAT_GROUP and type_ != C.MESS_TYPE_GROUPCHAT) or |
115 (self.type == 'one2one' and _type == 'groupchat')): | 169 (self.type == C.CHAT_ONE2ONE and type_ == C.MESS_TYPE_GROUPCHAT)): |
116 continue | 170 continue |
117 self.printMessage(JID(from_jid), message, profile, timestamp) | 171 self.printMessage(jid.JID(from_jid), message, {'timestamp':timestamp}, profile) |
118 self.afterHistoryPrint() | 172 self.afterHistoryPrint() |
119 | 173 |
120 def onHistoryError(err): | 174 def onHistoryError(err): |
121 log.error(_("Can't get history")) | 175 log.error(_("Can't get history")) |
122 | 176 |
123 if self.target.startswith(C.PRIVATE_PREFIX): | 177 target = self.target.bare |
124 target = unescapePrivate(self.target) | 178 |
179 self.host.bridge.getHistory(unicode(self.host.profiles[profile].whoami.bare), unicode(target), size, True, search, profile, callback=onHistory, errback=onHistoryError) | |
180 | |
181 def _get_nick(self, entity): | |
182 """Return nick of this entity when possible""" | |
183 if self.type == C.CHAT_GROUP: | |
184 return entity.resource | |
185 contact_list = self.host.contact_lists[self.profile] | |
186 if entity.bare in contact_list: | |
187 return contact_list.getCache(entity,'nick') or contact_list.getCache(entity,'name') or entity.node or entity | |
188 return entity.node or entity | |
189 | |
190 def onPrivateCreated(self, widget): | |
191 """Method called when a new widget for private conversation (MUC) is created""" | |
192 raise NotImplementedError | |
193 | |
194 def getOrCreatePrivateWidget(self, entity): | |
195 """Create a widget for private conversation, or get it if it already exists | |
196 | |
197 @param entity: full jid of the target | |
198 """ | |
199 return self.host.widgets.getOrCreateWidget(QuickChat, entity, type_=C.CHAT_ONE2ONE, force_hash=self.getPrivateHash(self.profile, entity), on_new_widget=self.onPrivateCreated, profile=self.profile) # we force hash to have a new widget, not this one again | |
200 | |
201 def newMessage(self, from_jid, target, msg, type_, extra, profile): | |
202 if self.type == C.CHAT_GROUP and target.resource and type_ != C.MESS_TYPE_GROUPCHAT: | |
203 # we have a private message, we forward it to a private conversation widget | |
204 chat_widget = self.getOrCreatePrivateWidget(target) | |
205 chat_widget.newMessage(from_jid, target, msg, type_, extra, profile) | |
125 else: | 206 else: |
126 target = self.target.bare | 207 if type_ == C.MESS_TYPE_INFO: |
127 | 208 self.printInfo(msg, extra=extra) |
128 return self.host.bridge.getHistory(self.host.profiles[profile]['whoami'].bare, target, size, search=search, profile=profile, callback=onHistory, errback=onHistoryError) | 209 else: |
129 | 210 self.printMessage(from_jid, msg, extra, profile) |
130 def _get_nick(self, jid): | 211 |
131 """Return nick of this jid when possible""" | 212 def printMessage(self, from_jid, msg, extra=None, profile=C.PROF_KEY_NONE): |
132 if self.target.startswith(C.PRIVATE_PREFIX): | |
133 unescaped = unescapePrivate(self.target) | |
134 if jid.startswith(C.PRIVATE_PREFIX) or unescaped.bare == jid.bare: | |
135 return unescaped.resource | |
136 return jid.resource if self.type == "group" else (self.host.contact_list.getCache(jid,'nick') or self.host.contact_list.getCache(jid,'name') or jid.node) | |
137 | |
138 def printMessage(self, from_jid, msg, profile, timestamp=None): | |
139 """Print message in chat window. Must be implemented by child class""" | 213 """Print message in chat window. Must be implemented by child class""" |
140 jid = JID(from_jid) | 214 nick = self._get_nick(from_jid) |
141 nick = self._get_nick(jid) | 215 mymess = (from_jid.resource == self.nick) if self.type == C.CHAT_GROUP else (from_jid.bare == self.host.profiles[profile].whoami.bare) #mymess = True if message comes from local user |
142 mymess = (jid.resource == self.nick) if self.type == "group" else (jid.bare == self.host.profiles[profile]['whoami'].bare) #mymess = True if message comes from local user | |
143 if msg.startswith('/me '): | 216 if msg.startswith('/me '): |
144 self.printInfo('* %s %s' % (nick, msg[4:]), type_='me', timestamp=timestamp) | 217 self.printInfo('* %s %s' % (nick, msg[4:]), type_='me', extra=extra) |
145 return | 218 return |
146 return jid, nick, mymess | 219 return nick, mymess |
147 | 220 |
148 def printInfo(self, msg, type_='normal', timestamp=None): | 221 def printInfo(self, msg, type_='normal', extra=None): |
149 """Print general info | 222 """Print general info |
150 @param msg: message to print | 223 @param msg: message to print |
151 @type_: one of: | 224 @type_: one of: |
152 normal: general info like "toto has joined the room" | 225 normal: general info like "toto has joined the room" |
153 me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist" | 226 me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist" |
154 @param timestamp (float): number of seconds since epoch | 227 @param extra (dict): message data |
155 """ | 228 """ |
156 raise NotImplementedError | 229 raise NotImplementedError |
157 | 230 |
158 def startGame(self, game_type, referee, players): | 231 def updateChatState(self, from_jid, state): |
159 """Configure the chat window to start a game""" | 232 """Set the chat state (XEP-0085) of the contact. |
160 #No need to raise an error as game are not mandatory | 233 |
161 log.warning(_('startGame is not implemented in this frontend')) | |
162 | |
163 def getGame(self, game_type): | |
164 """Return class managing the game type""" | |
165 #No need to raise an error as game are not mandatory | |
166 log.warning(_('getGame is not implemented in this frontend')) | |
167 | |
168 def updateChatState(self, state, nick=None): | |
169 """Set the chat state (XEP-0085) of the contact. Leave nick to None | |
170 to set the state for a one2one conversation, or give a nickname or | |
171 C.ALL_OCCUPANTS to set the state of a participant within a MUC. | |
172 @param state: the new chat state | 234 @param state: the new chat state |
173 @param nick: None for one2one, the MUC user nick or ALL_OCCUPANTS | 235 """ |
174 """ | 236 raise NotImplementedError |
175 raise NotImplementedError | 237 |
176 | 238 def addGamePanel(self, widget): |
239 """Insert a game panel to this Chat dialog. | |
240 | |
241 @param widget (Widget): the game panel | |
242 """ | |
243 raise NotImplementedError | |
244 | |
245 def removeGamePanel(self, widget): | |
246 """Remove the game panel from this Chat dialog. | |
247 | |
248 @param widget (Widget): the game panel | |
249 """ | |
250 raise NotImplementedError | |
251 | |
252 | |
253 quick_widgets.register(QuickChat) |