Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_xep_0045.py @ 4270:0d7bb4df2343
Reformatted code base using black.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 19 Jun 2024 18:44:57 +0200 |
parents | a7d4007a8fa5 |
children |
comparison
equal
deleted
inserted
replaced
4269:64a85ce8be70 | 4270:0d7bb4df2343 |
---|---|
51 C.PI_PROTOCOLS: ["XEP-0045"], | 51 C.PI_PROTOCOLS: ["XEP-0045"], |
52 C.PI_DEPENDENCIES: ["XEP-0359"], | 52 C.PI_DEPENDENCIES: ["XEP-0359"], |
53 C.PI_RECOMMENDATIONS: [C.TEXT_CMDS, "XEP-0313"], | 53 C.PI_RECOMMENDATIONS: [C.TEXT_CMDS, "XEP-0313"], |
54 C.PI_MAIN: "XEP_0045", | 54 C.PI_MAIN: "XEP_0045", |
55 C.PI_HANDLER: "yes", | 55 C.PI_HANDLER: "yes", |
56 C.PI_DESCRIPTION: _("""Implementation of Multi-User Chat""") | 56 C.PI_DESCRIPTION: _("""Implementation of Multi-User Chat"""), |
57 } | 57 } |
58 | 58 |
59 NS_MUC = 'http://jabber.org/protocol/muc' | 59 NS_MUC = "http://jabber.org/protocol/muc" |
60 AFFILIATIONS = ('owner', 'admin', 'member', 'none', 'outcast') | 60 AFFILIATIONS = ("owner", "admin", "member", "none", "outcast") |
61 ROOM_USER_JOINED = 'ROOM_USER_JOINED' | 61 ROOM_USER_JOINED = "ROOM_USER_JOINED" |
62 ROOM_USER_LEFT = 'ROOM_USER_LEFT' | 62 ROOM_USER_LEFT = "ROOM_USER_LEFT" |
63 OCCUPANT_KEYS = ('nick', 'entity', 'affiliation', 'role') | 63 OCCUPANT_KEYS = ("nick", "entity", "affiliation", "role") |
64 ROOM_STATE_OCCUPANTS = "occupants" | 64 ROOM_STATE_OCCUPANTS = "occupants" |
65 ROOM_STATE_SELF_PRESENCE = "self-presence" | 65 ROOM_STATE_SELF_PRESENCE = "self-presence" |
66 ROOM_STATE_LIVE = "live" | 66 ROOM_STATE_LIVE = "live" |
67 ROOM_STATES = (ROOM_STATE_OCCUPANTS, ROOM_STATE_SELF_PRESENCE, ROOM_STATE_LIVE) | 67 ROOM_STATES = (ROOM_STATE_OCCUPANTS, ROOM_STATE_SELF_PRESENCE, ROOM_STATE_LIVE) |
68 HISTORY_LEGACY = "legacy" | 68 HISTORY_LEGACY = "legacy" |
69 HISTORY_MAM = "mam" | 69 HISTORY_MAM = "mam" |
70 | 70 |
71 | 71 |
72 CONFIG_SECTION = 'plugin muc' | 72 CONFIG_SECTION = "plugin muc" |
73 | 73 |
74 default_conf = {"default_muc": 'sat@chat.jabberfr.org'} | 74 default_conf = {"default_muc": "sat@chat.jabberfr.org"} |
75 | 75 |
76 | 76 |
77 class AlreadyJoined(exceptions.ConflictError): | 77 class AlreadyJoined(exceptions.ConflictError): |
78 | 78 |
79 def __init__(self, room): | 79 def __init__(self, room): |
90 self.host = host | 90 self.host = host |
91 self._sessions = memory.Sessions() | 91 self._sessions = memory.Sessions() |
92 # return same arguments as muc_room_joined + a boolean set to True is the room was | 92 # return same arguments as muc_room_joined + a boolean set to True is the room was |
93 # already joined (first argument) | 93 # already joined (first argument) |
94 host.bridge.add_method( | 94 host.bridge.add_method( |
95 "muc_join", ".plugin", in_sign='ssa{ss}s', out_sign='(bsa{sa{ss}}ssass)', | 95 "muc_join", |
96 method=self._join, async_=True) | 96 ".plugin", |
97 in_sign="ssa{ss}s", | |
98 out_sign="(bsa{sa{ss}}ssass)", | |
99 method=self._join, | |
100 async_=True, | |
101 ) | |
97 host.bridge.add_method( | 102 host.bridge.add_method( |
98 "muc_nick", ".plugin", in_sign='sss', out_sign='', method=self._nick) | 103 "muc_nick", ".plugin", in_sign="sss", out_sign="", method=self._nick |
104 ) | |
99 host.bridge.add_method( | 105 host.bridge.add_method( |
100 "muc_nick_get", ".plugin", in_sign='ss', out_sign='s', method=self._get_room_nick) | 106 "muc_nick_get", |
107 ".plugin", | |
108 in_sign="ss", | |
109 out_sign="s", | |
110 method=self._get_room_nick, | |
111 ) | |
101 host.bridge.add_method( | 112 host.bridge.add_method( |
102 "muc_leave", ".plugin", in_sign='ss', out_sign='', method=self._leave, | 113 "muc_leave", |
103 async_=True) | 114 ".plugin", |
115 in_sign="ss", | |
116 out_sign="", | |
117 method=self._leave, | |
118 async_=True, | |
119 ) | |
104 host.bridge.add_method( | 120 host.bridge.add_method( |
105 "muc_occupants_get", ".plugin", in_sign='ss', out_sign='a{sa{ss}}', | 121 "muc_occupants_get", |
106 method=self._get_room_occupants) | 122 ".plugin", |
123 in_sign="ss", | |
124 out_sign="a{sa{ss}}", | |
125 method=self._get_room_occupants, | |
126 ) | |
107 host.bridge.add_method( | 127 host.bridge.add_method( |
108 "muc_subject", ".plugin", in_sign='sss', out_sign='', method=self._subject) | 128 "muc_subject", ".plugin", in_sign="sss", out_sign="", method=self._subject |
129 ) | |
109 host.bridge.add_method( | 130 host.bridge.add_method( |
110 "muc_get_rooms_joined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ssas)', | 131 "muc_get_rooms_joined", |
111 method=self._get_rooms_joined) | 132 ".plugin", |
133 in_sign="s", | |
134 out_sign="a(sa{sa{ss}}ssas)", | |
135 method=self._get_rooms_joined, | |
136 ) | |
112 host.bridge.add_method( | 137 host.bridge.add_method( |
113 "muc_get_unique_room_name", ".plugin", in_sign='ss', out_sign='s', | 138 "muc_get_unique_room_name", |
114 method=self._get_unique_name) | 139 ".plugin", |
140 in_sign="ss", | |
141 out_sign="s", | |
142 method=self._get_unique_name, | |
143 ) | |
115 host.bridge.add_method( | 144 host.bridge.add_method( |
116 "muc_configure_room", ".plugin", in_sign='ss', out_sign='s', | 145 "muc_configure_room", |
117 method=self._configure_room, async_=True) | 146 ".plugin", |
147 in_sign="ss", | |
148 out_sign="s", | |
149 method=self._configure_room, | |
150 async_=True, | |
151 ) | |
118 host.bridge.add_method( | 152 host.bridge.add_method( |
119 "muc_get_default_service", ".plugin", in_sign='', out_sign='s', | 153 "muc_get_default_service", |
120 method=self.get_default_muc) | 154 ".plugin", |
155 in_sign="", | |
156 out_sign="s", | |
157 method=self.get_default_muc, | |
158 ) | |
121 host.bridge.add_method( | 159 host.bridge.add_method( |
122 "muc_get_service", ".plugin", in_sign='ss', out_sign='s', | 160 "muc_get_service", |
123 method=self._get_muc_service, async_=True) | 161 ".plugin", |
162 in_sign="ss", | |
163 out_sign="s", | |
164 method=self._get_muc_service, | |
165 async_=True, | |
166 ) | |
124 # called when a room will be joined but must be locked until join is received | 167 # called when a room will be joined but must be locked until join is received |
125 # (room is prepared, history is getting retrieved) | 168 # (room is prepared, history is getting retrieved) |
126 # args: room_jid, profile | 169 # args: room_jid, profile |
127 host.bridge.add_signal( | 170 host.bridge.add_signal("muc_room_prepare_join", ".plugin", signature="ss") |
128 "muc_room_prepare_join", ".plugin", signature='ss') | |
129 # args: room_jid, occupants, user_nick, subject, profile | 171 # args: room_jid, occupants, user_nick, subject, profile |
130 host.bridge.add_signal( | 172 host.bridge.add_signal("muc_room_joined", ".plugin", signature="sa{sa{ss}}ssass") |
131 "muc_room_joined", ".plugin", signature='sa{sa{ss}}ssass') | |
132 # args: room_jid, profile | 173 # args: room_jid, profile |
133 host.bridge.add_signal( | 174 host.bridge.add_signal("muc_room_left", ".plugin", signature="ss") |
134 "muc_room_left", ".plugin", signature='ss') | |
135 # args: room_jid, old_nick, new_nick, profile | 175 # args: room_jid, old_nick, new_nick, profile |
136 host.bridge.add_signal( | 176 host.bridge.add_signal("muc_room_user_changed_nick", ".plugin", signature="ssss") |
137 "muc_room_user_changed_nick", ".plugin", signature='ssss') | |
138 # args: room_jid, subject, profile | 177 # args: room_jid, subject, profile |
139 host.bridge.add_signal( | 178 host.bridge.add_signal("muc_room_new_subject", ".plugin", signature="sss") |
140 "muc_room_new_subject", ".plugin", signature='sss') | |
141 self.__submit_conf_id = host.register_callback( | 179 self.__submit_conf_id = host.register_callback( |
142 self._submit_configuration, with_data=True) | 180 self._submit_configuration, with_data=True |
181 ) | |
143 self._room_join_id = host.register_callback(self._ui_room_join_cb, with_data=True) | 182 self._room_join_id = host.register_callback(self._ui_room_join_cb, with_data=True) |
144 host.import_menu( | 183 host.import_menu( |
145 (D_("MUC"), D_("configure")), self._configure_room_menu, security_limit=0, | 184 (D_("MUC"), D_("configure")), |
146 help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM) | 185 self._configure_room_menu, |
186 security_limit=0, | |
187 help_string=D_("Configure Multi-User Chat room"), | |
188 type_=C.MENU_ROOM, | |
189 ) | |
147 try: | 190 try: |
148 self.text_cmds = self.host.plugins[C.TEXT_CMDS] | 191 self.text_cmds = self.host.plugins[C.TEXT_CMDS] |
149 except KeyError: | 192 except KeyError: |
150 log.info(_("Text commands not available")) | 193 log.info(_("Text commands not available")) |
151 else: | 194 else: |
155 self._mam = self.host.plugins.get("XEP-0313") | 198 self._mam = self.host.plugins.get("XEP-0313") |
156 self._si = self.host.plugins["XEP-0359"] | 199 self._si = self.host.plugins["XEP-0359"] |
157 | 200 |
158 host.trigger.add("presence_available", self.presence_trigger) | 201 host.trigger.add("presence_available", self.presence_trigger) |
159 host.trigger.add("presence_received", self.presence_received_trigger) | 202 host.trigger.add("presence_received", self.presence_received_trigger) |
160 host.trigger.add("message_received", self.message_received_trigger, priority=1000000) | 203 host.trigger.add( |
204 "message_received", self.message_received_trigger, priority=1000000 | |
205 ) | |
161 host.trigger.add("message_parse", self._message_parse_trigger) | 206 host.trigger.add("message_parse", self._message_parse_trigger) |
162 | 207 |
163 async def profile_connected(self, client): | 208 async def profile_connected(self, client): |
164 client.muc_service = await self.get_muc_service(client) | 209 client.muc_service = await self.get_muc_service(client) |
165 | 210 |
177 | 222 |
178 def message_received_trigger(self, client, message_elt, post_treat): | 223 def message_received_trigger(self, client, message_elt, post_treat): |
179 if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT: | 224 if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT: |
180 if message_elt.subject: | 225 if message_elt.subject: |
181 return False | 226 return False |
182 from_jid = jid.JID(message_elt['from']) | 227 from_jid = jid.JID(message_elt["from"]) |
183 room_jid = from_jid.userhostJID() | 228 room_jid = from_jid.userhostJID() |
184 if room_jid in client._muc_client.joined_rooms: | 229 if room_jid in client._muc_client.joined_rooms: |
185 room = client._muc_client.joined_rooms[room_jid] | 230 room = client._muc_client.joined_rooms[room_jid] |
186 if room.state != ROOM_STATE_LIVE: | 231 if room.state != ROOM_STATE_LIVE: |
187 if getattr(room, "_history_type", HISTORY_LEGACY) == HISTORY_LEGACY: | 232 if getattr(room, "_history_type", HISTORY_LEGACY) == HISTORY_LEGACY: |
188 # With MAM history, order is different, and we can get live | 233 # With MAM history, order is different, and we can get live |
189 # messages before history is complete, so this is not a warning | 234 # messages before history is complete, so this is not a warning |
190 # but an expected case. | 235 # but an expected case. |
191 # On the other hand, with legacy history, it's not normal. | 236 # On the other hand, with legacy history, it's not normal. |
192 log.warning(_( | 237 log.warning( |
193 "Received non delayed message in a room before its " | 238 _( |
194 "initialisation: state={state}, msg={msg}").format( | 239 "Received non delayed message in a room before its " |
195 state=room.state, | 240 "initialisation: state={state}, msg={msg}" |
196 msg=message_elt.toXml())) | 241 ).format(state=room.state, msg=message_elt.toXml()) |
242 ) | |
197 room._cache.append(message_elt) | 243 room._cache.append(message_elt) |
198 return False | 244 return False |
199 else: | 245 else: |
200 log.warning("Received groupchat message for a room which has not been " | 246 log.warning( |
201 "joined, ignoring it: {}".format(message_elt.toXml())) | 247 "Received groupchat message for a room which has not been " |
248 "joined, ignoring it: {}".format(message_elt.toXml()) | |
249 ) | |
202 return False | 250 return False |
203 return True | 251 return True |
204 | 252 |
205 def get_room(self, client: SatXMPPEntity, room_jid: jid.JID) -> muc.Room: | 253 def get_room(self, client: SatXMPPEntity, room_jid: jid.JID) -> muc.Room: |
206 """Retrieve Room instance from its jid | 254 """Retrieve Room instance from its jid |
282 # FIXME: getattr below is a Q&D fix as `subject` may not be set in early call | 330 # FIXME: getattr below is a Q&D fix as `subject` may not be set in early call |
283 # of _get_rooms_joined (during `join` call). The whole workflow of this | 331 # of _get_rooms_joined (during `join` call). The whole workflow of this |
284 # plugin should be refactored. | 332 # plugin should be refactored. |
285 getattr(room, "subject", ""), | 333 getattr(room, "subject", ""), |
286 [s.name for s in room.statuses], | 334 [s.name for s in room.statuses], |
287 profile | 335 profile, |
288 ] | 336 ] |
289 | 337 |
290 def _ui_room_join_cb(self, data, profile): | 338 def _ui_room_join_cb(self, data, profile): |
291 room_jid = jid.JID(data['index']) | 339 room_jid = jid.JID(data["index"]) |
292 client = self.host.get_client(profile) | 340 client = self.host.get_client(profile) |
293 defer.ensureDeferred( | 341 defer.ensureDeferred(self.join(client, room_jid)) |
294 self.join(client, room_jid) | |
295 ) | |
296 return {} | 342 return {} |
297 | 343 |
298 def _password_ui_cb(self, data, client, room_jid, nick): | 344 def _password_ui_cb(self, data, client, room_jid, nick): |
299 """Called when the user has given room password (or cancelled)""" | 345 """Called when the user has given room password (or cancelled)""" |
300 if C.bool(data.get(C.XMLUI_DATA_CANCELLED, "false")): | 346 if C.bool(data.get(C.XMLUI_DATA_CANCELLED, "false")): |
301 log.info("room join for {} is cancelled".format(room_jid.userhost())) | 347 log.info("room join for {} is cancelled".format(room_jid.userhost())) |
302 raise failure.Failure(exceptions.CancelError(D_("Room joining cancelled by user"))) | 348 raise failure.Failure( |
303 password = data[xml_tools.form_escape('password')] | 349 exceptions.CancelError(D_("Room joining cancelled by user")) |
304 return client._muc_client.join(room_jid, nick, password).addCallbacks(self._join_cb, self._join_eb, (client, room_jid, nick), errbackArgs=(client, room_jid, nick, password)) | 350 ) |
351 password = data[xml_tools.form_escape("password")] | |
352 return client._muc_client.join(room_jid, nick, password).addCallbacks( | |
353 self._join_cb, | |
354 self._join_eb, | |
355 (client, room_jid, nick), | |
356 errbackArgs=(client, room_jid, nick, password), | |
357 ) | |
305 | 358 |
306 def _show_list_ui(self, items, client, service): | 359 def _show_list_ui(self, items, client, service): |
307 xmlui = xml_tools.XMLUI(title=D_('Rooms in {}'.format(service.full()))) | 360 xmlui = xml_tools.XMLUI(title=D_("Rooms in {}".format(service.full()))) |
308 adv_list = xmlui.change_container('advanced_list', columns=1, selectable='single', callback_id=self._room_join_id) | 361 adv_list = xmlui.change_container( |
362 "advanced_list", | |
363 columns=1, | |
364 selectable="single", | |
365 callback_id=self._room_join_id, | |
366 ) | |
309 items = sorted(items, key=lambda i: i.name.lower()) | 367 items = sorted(items, key=lambda i: i.name.lower()) |
310 for item in items: | 368 for item in items: |
311 adv_list.set_row_index(item.entity.full()) | 369 adv_list.set_row_index(item.entity.full()) |
312 xmlui.addText(item.name) | 370 xmlui.addText(item.name) |
313 adv_list.end() | 371 adv_list.end() |
314 self.host.action_new({'xmlui': xmlui.toXml()}, profile=client.profile) | 372 self.host.action_new({"xmlui": xmlui.toXml()}, profile=client.profile) |
315 | 373 |
316 def _join_cb(self, room, client, room_jid, nick): | 374 def _join_cb(self, room, client, room_jid, nick): |
317 """Called when the user is in the requested room""" | 375 """Called when the user is in the requested room""" |
318 if room.locked: | 376 if room.locked: |
319 # FIXME: the current behaviour is to create an instant room | 377 # FIXME: the current behaviour is to create an instant room |
320 # and send the signal only when the room is unlocked | 378 # and send the signal only when the room is unlocked |
321 # a proper configuration management should be done | 379 # a proper configuration management should be done |
322 log.debug(_("room locked !")) | 380 log.debug(_("room locked !")) |
323 d = client._muc_client.configure(room.roomJID, {}) | 381 d = client._muc_client.configure(room.roomJID, {}) |
324 d.addErrback(self.host.log_errback, | 382 d.addErrback( |
325 msg=_('Error while configuring the room: {failure_}')) | 383 self.host.log_errback, |
384 msg=_("Error while configuring the room: {failure_}"), | |
385 ) | |
326 return room.fully_joined | 386 return room.fully_joined |
327 | 387 |
328 def _join_eb(self, failure_, client, room_jid, nick, password): | 388 def _join_eb(self, failure_, client, room_jid, nick, password): |
329 """Called when something is going wrong when joining the room""" | 389 """Called when something is going wrong when joining the room""" |
330 try: | 390 try: |
331 condition = failure_.value.condition | 391 condition = failure_.value.condition |
332 except AttributeError: | 392 except AttributeError: |
333 msg_suffix = f': {failure_}' | 393 msg_suffix = f": {failure_}" |
334 else: | 394 else: |
335 if condition == 'conflict': | 395 if condition == "conflict": |
336 # we have a nickname conflict, we try again with "_" suffixed to current nickname | 396 # we have a nickname conflict, we try again with "_" suffixed to current nickname |
337 nick += '_' | 397 nick += "_" |
338 return client._muc_client.join(room_jid, nick, password).addCallbacks(self._join_cb, self._join_eb, (client, room_jid, nick), errbackArgs=(client, room_jid, nick, password)) | 398 return client._muc_client.join(room_jid, nick, password).addCallbacks( |
339 elif condition == 'not-allowed': | 399 self._join_cb, |
400 self._join_eb, | |
401 (client, room_jid, nick), | |
402 errbackArgs=(client, room_jid, nick, password), | |
403 ) | |
404 elif condition == "not-allowed": | |
340 # room is restricted, we need a password | 405 # room is restricted, we need a password |
341 password_ui = xml_tools.XMLUI("form", title=D_('Room {} is restricted').format(room_jid.userhost()), submit_id='') | 406 password_ui = xml_tools.XMLUI( |
342 password_ui.addText(D_("This room is restricted, please enter the password")) | 407 "form", |
343 password_ui.addPassword('password') | 408 title=D_("Room {} is restricted").format(room_jid.userhost()), |
409 submit_id="", | |
410 ) | |
411 password_ui.addText( | |
412 D_("This room is restricted, please enter the password") | |
413 ) | |
414 password_ui.addPassword("password") | |
344 d = xml_tools.defer_xmlui(self.host, password_ui, profile=client.profile) | 415 d = xml_tools.defer_xmlui(self.host, password_ui, profile=client.profile) |
345 d.addCallback(self._password_ui_cb, client, room_jid, nick) | 416 d.addCallback(self._password_ui_cb, client, room_jid, nick) |
346 return d | 417 return d |
347 | 418 |
348 msg_suffix = ' with condition "{}"'.format(failure_.value.condition) | 419 msg_suffix = ' with condition "{}"'.format(failure_.value.condition) |
349 | 420 |
350 mess = D_("Error while joining the room {room}{suffix}".format( | 421 mess = D_( |
351 room = room_jid.userhost(), suffix = msg_suffix)) | 422 "Error while joining the room {room}{suffix}".format( |
423 room=room_jid.userhost(), suffix=msg_suffix | |
424 ) | |
425 ) | |
352 log.warning(mess) | 426 log.warning(mess) |
353 xmlui = xml_tools.note(mess, D_("Group chat error"), level=C.XMLUI_DATA_LVL_ERROR) | 427 xmlui = xml_tools.note(mess, D_("Group chat error"), level=C.XMLUI_DATA_LVL_ERROR) |
354 self.host.action_new({'xmlui': xmlui.toXml()}, profile=client.profile) | 428 self.host.action_new({"xmlui": xmlui.toXml()}, profile=client.profile) |
355 | 429 |
356 @staticmethod | 430 @staticmethod |
357 def _get_occupants(room): | 431 def _get_occupants(room): |
358 """Get occupants of a room in a form suitable for bridge""" | 432 """Get occupants of a room in a form suitable for bridge""" |
359 return {u.nick: {k:str(getattr(u,k) or '') for k in OCCUPANT_KEYS} for u in list(room.roster.values())} | 433 return { |
434 u.nick: {k: str(getattr(u, k) or "") for k in OCCUPANT_KEYS} | |
435 for u in list(room.roster.values()) | |
436 } | |
360 | 437 |
361 def _get_room_occupants(self, room_jid_s, profile_key): | 438 def _get_room_occupants(self, room_jid_s, profile_key): |
362 client = self.host.get_client(profile_key) | 439 client = self.host.get_client(profile_key) |
363 room_jid = jid.JID(room_jid_s) | 440 room_jid = jid.JID(room_jid_s) |
364 return self.get_room_occupants(client, room_jid) | 441 return self.get_room_occupants(client, room_jid) |
375 """Return rooms where user is""" | 452 """Return rooms where user is""" |
376 result = [] | 453 result = [] |
377 for room in list(client._muc_client.joined_rooms.values()): | 454 for room in list(client._muc_client.joined_rooms.values()): |
378 if room.state == ROOM_STATE_LIVE: | 455 if room.state == ROOM_STATE_LIVE: |
379 result.append( | 456 result.append( |
380 (room.roomJID.userhost(), | 457 ( |
381 self._get_occupants(room), | 458 room.roomJID.userhost(), |
382 room.nick, | 459 self._get_occupants(room), |
383 room.subject, | 460 room.nick, |
384 [s.name for s in room.statuses], | 461 room.subject, |
462 [s.name for s in room.statuses], | |
385 ) | 463 ) |
386 ) | 464 ) |
387 return result | 465 return result |
388 | 466 |
389 def _get_room_nick(self, room_jid_s, profile_key=C.PROF_KEY_NONE): | 467 def _get_room_nick(self, room_jid_s, profile_key=C.PROF_KEY_NONE): |
413 @param menu_data: %(menu_data)s | 491 @param menu_data: %(menu_data)s |
414 @param profile: %(doc_profile)s | 492 @param profile: %(doc_profile)s |
415 """ | 493 """ |
416 client = self.host.get_client(profile) | 494 client = self.host.get_client(profile) |
417 try: | 495 try: |
418 room_jid = jid.JID(menu_data['room_jid']) | 496 room_jid = jid.JID(menu_data["room_jid"]) |
419 except KeyError: | 497 except KeyError: |
420 log.error(_("room_jid key is not present !")) | 498 log.error(_("room_jid key is not present !")) |
421 return defer.fail(exceptions.DataError) | 499 return defer.fail(exceptions.DataError) |
422 | 500 |
423 def xmlui_received(xmlui): | 501 def xmlui_received(xmlui): |
424 if not xmlui: | 502 if not xmlui: |
425 msg = D_("No configuration available for this room") | 503 msg = D_("No configuration available for this room") |
426 return {"xmlui": xml_tools.note(msg).toXml()} | 504 return {"xmlui": xml_tools.note(msg).toXml()} |
427 return {"xmlui": xmlui.toXml()} | 505 return {"xmlui": xmlui.toXml()} |
506 | |
428 return self.configure_room(client, room_jid).addCallback(xmlui_received) | 507 return self.configure_room(client, room_jid).addCallback(xmlui_received) |
429 | 508 |
430 def configure_room(self, client, room_jid): | 509 def configure_room(self, client, room_jid): |
431 """return the room configuration form | 510 """return the room configuration form |
432 | 511 |
455 client = self.host.get_client(profile) | 534 client = self.host.get_client(profile) |
456 try: | 535 try: |
457 session_data = self._sessions.profile_get(raw_data["session_id"], profile) | 536 session_data = self._sessions.profile_get(raw_data["session_id"], profile) |
458 except KeyError: | 537 except KeyError: |
459 log.warning(D_("Session ID doesn't exist, session has probably expired.")) | 538 log.warning(D_("Session ID doesn't exist, session has probably expired.")) |
460 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration failed')) | 539 _dialog = xml_tools.XMLUI("popup", title=D_("Room configuration failed")) |
461 _dialog.addText(D_("Session ID doesn't exist, session has probably expired.")) | 540 _dialog.addText(D_("Session ID doesn't exist, session has probably expired.")) |
462 return defer.succeed({'xmlui': _dialog.toXml()}) | 541 return defer.succeed({"xmlui": _dialog.toXml()}) |
463 | 542 |
464 data = xml_tools.xmlui_result_2_data_form_result(raw_data) | 543 data = xml_tools.xmlui_result_2_data_form_result(raw_data) |
465 d = client._muc_client.configure(session_data['room_jid'], data) | 544 d = client._muc_client.configure(session_data["room_jid"], data) |
466 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration succeed')) | 545 _dialog = xml_tools.XMLUI("popup", title=D_("Room configuration succeed")) |
467 _dialog.addText(D_("The new settings have been saved.")) | 546 _dialog.addText(D_("The new settings have been saved.")) |
468 d.addCallback(lambda ignore: {'xmlui': _dialog.toXml()}) | 547 d.addCallback(lambda ignore: {"xmlui": _dialog.toXml()}) |
469 del self._sessions[raw_data["session_id"]] | 548 del self._sessions[raw_data["session_id"]] |
470 return d | 549 return d |
471 | 550 |
472 def is_nick_in_room(self, client, room_jid, nick): | 551 def is_nick_in_room(self, client, room_jid, nick): |
473 """Tell if a nick is currently present in a room""" | 552 """Tell if a nick is currently present in a room""" |
475 return client._muc_client.joined_rooms[room_jid].inRoster(muc.User(nick)) | 554 return client._muc_client.joined_rooms[room_jid].inRoster(muc.User(nick)) |
476 | 555 |
477 def _get_muc_service(self, jid_=None, profile=C.PROF_KEY_NONE): | 556 def _get_muc_service(self, jid_=None, profile=C.PROF_KEY_NONE): |
478 client = self.host.get_client(profile) | 557 client = self.host.get_client(profile) |
479 d = defer.ensureDeferred(self.get_muc_service(client, jid_ or None)) | 558 d = defer.ensureDeferred(self.get_muc_service(client, jid_ or None)) |
480 d.addCallback(lambda service_jid: service_jid.full() if service_jid is not None else '') | 559 d.addCallback( |
560 lambda service_jid: service_jid.full() if service_jid is not None else "" | |
561 ) | |
481 return d | 562 return d |
482 | 563 |
483 async def get_muc_service( | 564 async def get_muc_service( |
484 self, | 565 self, client: SatXMPPEntity, jid_: Optional[jid.JID] = None |
485 client: SatXMPPEntity, | 566 ) -> Optional[jid.JID]: |
486 jid_: Optional[jid.JID] = None) -> Optional[jid.JID]: | |
487 """Return first found MUC service of an entity | 567 """Return first found MUC service of an entity |
488 | 568 |
489 @param jid_: entity which may have a MUC service, or None for our own server | 569 @param jid_: entity which may have a MUC service, or None for our own server |
490 @return: found service jid or None | 570 @return: found service jid or None |
491 """ | 571 """ |
495 except AttributeError: | 575 except AttributeError: |
496 pass | 576 pass |
497 else: | 577 else: |
498 # we have a cached value, we return it | 578 # we have a cached value, we return it |
499 return muc_service | 579 return muc_service |
500 services = await self.host.find_service_entities(client, "conference", "text", jid_) | 580 services = await self.host.find_service_entities( |
581 client, "conference", "text", jid_ | |
582 ) | |
501 for service in services: | 583 for service in services: |
502 if ".irc." not in service.userhost(): | 584 if ".irc." not in service.userhost(): |
503 # FIXME: | 585 # FIXME: |
504 # This ugly hack is here to avoid an issue with openfire: the IRC gateway | 586 # This ugly hack is here to avoid an issue with openfire: the IRC gateway |
505 # use "conference/text" identity (instead of "conference/irc") | 587 # use "conference/text" identity (instead of "conference/irc") |
512 def _get_unique_name(self, muc_service="", profile_key=C.PROF_KEY_NONE): | 594 def _get_unique_name(self, muc_service="", profile_key=C.PROF_KEY_NONE): |
513 client = self.host.get_client(profile_key) | 595 client = self.host.get_client(profile_key) |
514 return self.get_unique_name(client, muc_service or None).full() | 596 return self.get_unique_name(client, muc_service or None).full() |
515 | 597 |
516 def get_unique_name( | 598 def get_unique_name( |
517 self, | 599 self, client: SatXMPPEntity, muc_service: jid.JID | None = None, prefix: str = "" |
518 client: SatXMPPEntity, | |
519 muc_service: jid.JID|None = None, | |
520 prefix: str = "" | |
521 ) -> jid.JID: | 600 ) -> jid.JID: |
522 """Return unique name for a room, avoiding collision | 601 """Return unique name for a room, avoiding collision |
523 | 602 |
524 @param client: Client instance. | 603 @param client: Client instance. |
525 @param muc_service: leave empty string to use the default service | 604 @param muc_service: leave empty string to use the default service |
544 def get_default_muc(self): | 623 def get_default_muc(self): |
545 """Return the default MUC. | 624 """Return the default MUC. |
546 | 625 |
547 @return: unicode | 626 @return: unicode |
548 """ | 627 """ |
549 return self.host.memory.config_get(CONFIG_SECTION, 'default_muc', default_conf['default_muc']) | 628 return self.host.memory.config_get( |
629 CONFIG_SECTION, "default_muc", default_conf["default_muc"] | |
630 ) | |
550 | 631 |
551 def _bridge_join_eb(self, failure_, client): | 632 def _bridge_join_eb(self, failure_, client): |
552 failure_.trap(AlreadyJoined) | 633 failure_.trap(AlreadyJoined) |
553 room = failure_.value.room | 634 room = failure_.value.room |
554 return [True] + self._get_room_joined_args(room, client.profile) | 635 return [True] + self._get_room_joined_args(room, client.profile) |
562 if room_jid_s: | 643 if room_jid_s: |
563 muc_service = client.muc_service | 644 muc_service = client.muc_service |
564 try: | 645 try: |
565 room_jid = jid.JID(room_jid_s) | 646 room_jid = jid.JID(room_jid_s) |
566 except (RuntimeError, jid.InvalidFormat, AttributeError): | 647 except (RuntimeError, jid.InvalidFormat, AttributeError): |
567 return defer.fail(jid.InvalidFormat(_("Invalid room identifier: {room_id}'. Please give a room short or full identifier like 'room' or 'room@{muc_service}'.").format( | 648 return defer.fail( |
568 room_id=room_jid_s, | 649 jid.InvalidFormat( |
569 muc_service=str(muc_service)))) | 650 _( |
651 "Invalid room identifier: {room_id}'. Please give a room short or full identifier like 'room' or 'room@{muc_service}'." | |
652 ).format(room_id=room_jid_s, muc_service=str(muc_service)) | |
653 ) | |
654 ) | |
570 if not room_jid.user: | 655 if not room_jid.user: |
571 room_jid.user, room_jid.host = room_jid.host, muc_service | 656 room_jid.user, room_jid.host = room_jid.host, muc_service |
572 else: | 657 else: |
573 room_jid = self.get_unique_name(client) | 658 room_jid = self.get_unique_name(client) |
574 # TODO: error management + signal in bridge | 659 # TODO: error management + signal in bridge |
575 d = defer.ensureDeferred( | 660 d = defer.ensureDeferred(self.join(client, room_jid, nick, options or None)) |
576 self.join(client, room_jid, nick, options or None) | 661 |
577 ) | 662 d.addCallback( |
578 | 663 lambda room: [False] + self._get_room_joined_args(room, client.profile) |
579 d.addCallback(lambda room: [False] + self._get_room_joined_args(room, client.profile)) | 664 ) |
580 d.addErrback(self._bridge_join_eb, client) | 665 d.addErrback(self._bridge_join_eb, client) |
581 return d | 666 return d |
582 | 667 |
583 async def join( | 668 async def join( |
584 self, | 669 self, |
585 client: SatXMPPEntity, | 670 client: SatXMPPEntity, |
586 room_jid: jid.JID, | 671 room_jid: jid.JID, |
587 nick: Optional[str] = None, | 672 nick: Optional[str] = None, |
588 options: Optional[dict] = None | 673 options: Optional[dict] = None, |
589 ) -> Optional[muc.Room]: | 674 ) -> Optional[muc.Room]: |
590 if not nick: | 675 if not nick: |
591 nick = client.jid.user | 676 nick = client.jid.user |
592 if options is None: | 677 if options is None: |
593 options = {} | 678 options = {} |
594 if room_jid in client._muc_client.joined_rooms: | 679 if room_jid in client._muc_client.joined_rooms: |
595 room = client._muc_client.joined_rooms[room_jid] | 680 room = client._muc_client.joined_rooms[room_jid] |
596 log.info(_('{profile} is already in room {room_jid}').format( | 681 log.info( |
597 profile=client.profile, room_jid = room_jid.userhost())) | 682 _("{profile} is already in room {room_jid}").format( |
683 profile=client.profile, room_jid=room_jid.userhost() | |
684 ) | |
685 ) | |
598 raise AlreadyJoined(room) | 686 raise AlreadyJoined(room) |
599 log.info(_("[{profile}] is joining room {room} with nick {nick}").format( | 687 log.info( |
600 profile=client.profile, room=room_jid.userhost(), nick=nick)) | 688 _("[{profile}] is joining room {room} with nick {nick}").format( |
689 profile=client.profile, room=room_jid.userhost(), nick=nick | |
690 ) | |
691 ) | |
601 self.host.bridge.muc_room_prepare_join(room_jid.userhost(), client.profile) | 692 self.host.bridge.muc_room_prepare_join(room_jid.userhost(), client.profile) |
602 | 693 |
603 password = options.get("password") | 694 password = options.get("password") |
604 | 695 |
605 try: | 696 try: |
609 self._join_eb(failure.Failure(e), client, room_jid, nick, password) | 700 self._join_eb(failure.Failure(e), client, room_jid, nick, password) |
610 ) | 701 ) |
611 else: | 702 else: |
612 room.on_joined_callbacks = [] | 703 room.on_joined_callbacks = [] |
613 room.on_left_callbacks = [] | 704 room.on_left_callbacks = [] |
614 await defer.ensureDeferred( | 705 await defer.ensureDeferred(self._join_cb(room, client, room_jid, nick)) |
615 self._join_cb(room, client, room_jid, nick) | |
616 ) | |
617 return room | 706 return room |
618 | 707 |
619 def pop_rooms(self, client): | 708 def pop_rooms(self, client): |
620 """Remove rooms and return data needed to re-join them | 709 """Remove rooms and return data needed to re-join them |
621 | 710 |
667 @param options (dict): attribute with extra info (reason, password) as in #XEP-0045 | 756 @param options (dict): attribute with extra info (reason, password) as in #XEP-0045 |
668 """ | 757 """ |
669 if options is None: | 758 if options is None: |
670 options = {} | 759 options = {} |
671 self.check_room_joined(client, room_jid) | 760 self.check_room_joined(client, room_jid) |
672 return client._muc_client.kick(room_jid, nick, reason=options.get('reason', None)) | 761 return client._muc_client.kick(room_jid, nick, reason=options.get("reason", None)) |
673 | 762 |
674 def ban(self, client, entity_jid, room_jid, options=None): | 763 def ban(self, client, entity_jid, room_jid, options=None): |
675 """Ban an entity from the room | 764 """Ban an entity from the room |
676 | 765 |
677 @param entity_jid (JID): bare jid of the entity to be banned | 766 @param entity_jid (JID): bare jid of the entity to be banned |
681 self.check_room_joined(client, room_jid) | 770 self.check_room_joined(client, room_jid) |
682 if options is None: | 771 if options is None: |
683 options = {} | 772 options = {} |
684 assert not entity_jid.resource | 773 assert not entity_jid.resource |
685 assert not room_jid.resource | 774 assert not room_jid.resource |
686 return client._muc_client.ban(room_jid, entity_jid, reason=options.get('reason', None)) | 775 return client._muc_client.ban( |
776 room_jid, entity_jid, reason=options.get("reason", None) | |
777 ) | |
687 | 778 |
688 def affiliate(self, client, entity_jid, room_jid, options): | 779 def affiliate(self, client, entity_jid, room_jid, options): |
689 """Change the affiliation of an entity | 780 """Change the affiliation of an entity |
690 | 781 |
691 @param entity_jid (JID): bare jid of the entity | 782 @param entity_jid (JID): bare jid of the entity |
693 @param options: attribute with extra info (reason, nick) as in #XEP-0045 | 784 @param options: attribute with extra info (reason, nick) as in #XEP-0045 |
694 """ | 785 """ |
695 self.check_room_joined(client, room_jid) | 786 self.check_room_joined(client, room_jid) |
696 assert not entity_jid.resource | 787 assert not entity_jid.resource |
697 assert not room_jid.resource | 788 assert not room_jid.resource |
698 assert 'affiliation' in options | 789 assert "affiliation" in options |
699 # TODO: handles reason and nick | 790 # TODO: handles reason and nick |
700 return client._muc_client.modifyAffiliationList(room_jid, [entity_jid], options['affiliation']) | 791 return client._muc_client.modifyAffiliationList( |
792 room_jid, [entity_jid], options["affiliation"] | |
793 ) | |
701 | 794 |
702 # Text commands # | 795 # Text commands # |
703 | 796 |
704 def cmd_nick(self, client, mess_data): | 797 def cmd_nick(self, client, mess_data): |
705 """change nickname | 798 """change nickname |
731 # the command has been entered in a one2one conversation, so we use | 824 # the command has been entered in a one2one conversation, so we use |
732 # our server MUC service as default service | 825 # our server MUC service as default service |
733 muc_service = client.muc_service or "" | 826 muc_service = client.muc_service or "" |
734 nick = client.jid.user | 827 nick = client.jid.user |
735 room_jid = self.text_cmds.get_room_jid(room_raw, muc_service) | 828 room_jid = self.text_cmds.get_room_jid(room_raw, muc_service) |
736 defer.ensureDeferred( | 829 defer.ensureDeferred(self.join(client, room_jid, nick, {})) |
737 self.join(client, room_jid, nick, {}) | |
738 ) | |
739 | 830 |
740 return False | 831 return False |
741 | 832 |
742 def cmd_leave(self, client, mess_data): | 833 def cmd_leave(self, client, mess_data): |
743 """quit a room | 834 """quit a room |
776 except (IndexError, AssertionError): | 867 except (IndexError, AssertionError): |
777 feedback = _("You must provide a member's nick to kick.") | 868 feedback = _("You must provide a member's nick to kick.") |
778 self.text_cmds.feed_back(client, feedback, mess_data) | 869 self.text_cmds.feed_back(client, feedback, mess_data) |
779 return False | 870 return False |
780 | 871 |
781 reason = ' '.join(options[1:]) if len(options) > 1 else None | 872 reason = " ".join(options[1:]) if len(options) > 1 else None |
782 | 873 |
783 d = self.kick(client, nick, mess_data["to"], {"reason": reason}) | 874 d = self.kick(client, nick, mess_data["to"], {"reason": reason}) |
784 | 875 |
785 def cb(__): | 876 def cb(__): |
786 feedback_msg = _('You have kicked {}').format(nick) | 877 feedback_msg = _("You have kicked {}").format(nick) |
787 if reason is not None: | 878 if reason is not None: |
788 feedback_msg += _(' for the following reason: {reason}').format( | 879 feedback_msg += _(" for the following reason: {reason}").format( |
789 reason=reason | 880 reason=reason |
790 ) | 881 ) |
791 self.text_cmds.feed_back(client, feedback_msg, mess_data) | 882 self.text_cmds.feed_back(client, feedback_msg, mess_data) |
792 return True | 883 return True |
884 | |
793 d.addCallback(cb) | 885 d.addCallback(cb) |
794 return d | 886 return d |
795 | 887 |
796 def cmd_ban(self, client, mess_data): | 888 def cmd_ban(self, client, mess_data): |
797 """ban an entity from the room | 889 """ban an entity from the room |
802 """ | 894 """ |
803 options = mess_data["unparsed"].strip().split() | 895 options = mess_data["unparsed"].strip().split() |
804 try: | 896 try: |
805 jid_s = options[0] | 897 jid_s = options[0] |
806 entity_jid = jid.JID(jid_s).userhostJID() | 898 entity_jid = jid.JID(jid_s).userhostJID() |
807 assert(entity_jid.user) | 899 assert entity_jid.user |
808 assert(entity_jid.host) | 900 assert entity_jid.host |
809 except (RuntimeError, jid.InvalidFormat, AttributeError, IndexError, | 901 except ( |
810 AssertionError): | 902 RuntimeError, |
903 jid.InvalidFormat, | |
904 AttributeError, | |
905 IndexError, | |
906 AssertionError, | |
907 ): | |
811 feedback = _( | 908 feedback = _( |
812 "You must provide a valid JID to ban, like in '/ban contact@example.net'" | 909 "You must provide a valid JID to ban, like in '/ban contact@example.net'" |
813 ) | 910 ) |
814 self.text_cmds.feed_back(client, feedback, mess_data) | 911 self.text_cmds.feed_back(client, feedback, mess_data) |
815 return False | 912 return False |
816 | 913 |
817 reason = ' '.join(options[1:]) if len(options) > 1 else None | 914 reason = " ".join(options[1:]) if len(options) > 1 else None |
818 | 915 |
819 d = self.ban(client, entity_jid, mess_data["to"], {"reason": reason}) | 916 d = self.ban(client, entity_jid, mess_data["to"], {"reason": reason}) |
820 | 917 |
821 def cb(__): | 918 def cb(__): |
822 feedback_msg = _('You have banned {}').format(entity_jid) | 919 feedback_msg = _("You have banned {}").format(entity_jid) |
823 if reason is not None: | 920 if reason is not None: |
824 feedback_msg += _(' for the following reason: {reason}').format( | 921 feedback_msg += _(" for the following reason: {reason}").format( |
825 reason=reason | 922 reason=reason |
826 ) | 923 ) |
827 self.text_cmds.feed_back(client, feedback_msg, mess_data) | 924 self.text_cmds.feed_back(client, feedback_msg, mess_data) |
828 return True | 925 return True |
926 | |
829 d.addCallback(cb) | 927 d.addCallback(cb) |
830 return d | 928 return d |
831 | 929 |
832 def cmd_affiliate(self, client, mess_data): | 930 def cmd_affiliate(self, client, mess_data): |
833 """affiliate an entity to the room | 931 """affiliate an entity to the room |
842 """ | 940 """ |
843 options = mess_data["unparsed"].strip().split() | 941 options = mess_data["unparsed"].strip().split() |
844 try: | 942 try: |
845 jid_s = options[0] | 943 jid_s = options[0] |
846 entity_jid = jid.JID(jid_s).userhostJID() | 944 entity_jid = jid.JID(jid_s).userhostJID() |
847 assert(entity_jid.user) | 945 assert entity_jid.user |
848 assert(entity_jid.host) | 946 assert entity_jid.host |
849 except (RuntimeError, jid.InvalidFormat, AttributeError, IndexError, AssertionError): | 947 except ( |
850 feedback = _("You must provide a valid JID to affiliate, like in '/affiliate contact@example.net member'") | 948 RuntimeError, |
949 jid.InvalidFormat, | |
950 AttributeError, | |
951 IndexError, | |
952 AssertionError, | |
953 ): | |
954 feedback = _( | |
955 "You must provide a valid JID to affiliate, like in '/affiliate contact@example.net member'" | |
956 ) | |
851 self.text_cmds.feed_back(client, feedback, mess_data) | 957 self.text_cmds.feed_back(client, feedback, mess_data) |
852 return False | 958 return False |
853 | 959 |
854 affiliation = options[1] if len(options) > 1 else 'none' | 960 affiliation = options[1] if len(options) > 1 else "none" |
855 if affiliation not in AFFILIATIONS: | 961 if affiliation not in AFFILIATIONS: |
856 feedback = _("You must provide a valid affiliation: %s") % ' '.join(AFFILIATIONS) | 962 feedback = _("You must provide a valid affiliation: %s") % " ".join( |
963 AFFILIATIONS | |
964 ) | |
857 self.text_cmds.feed_back(client, feedback, mess_data) | 965 self.text_cmds.feed_back(client, feedback, mess_data) |
858 return False | 966 return False |
859 | 967 |
860 d = self.affiliate(client, entity_jid, mess_data["to"], {'affiliation': affiliation}) | 968 d = self.affiliate( |
969 client, entity_jid, mess_data["to"], {"affiliation": affiliation} | |
970 ) | |
861 | 971 |
862 def cb(__): | 972 def cb(__): |
863 feedback_msg = _('New affiliation for {entity}: {affiliation}').format( | 973 feedback_msg = _("New affiliation for {entity}: {affiliation}").format( |
864 entity=entity_jid, affiliation=affiliation) | 974 entity=entity_jid, affiliation=affiliation |
975 ) | |
865 self.text_cmds.feed_back(client, feedback_msg, mess_data) | 976 self.text_cmds.feed_back(client, feedback_msg, mess_data) |
866 return True | 977 return True |
978 | |
867 d.addCallback(cb) | 979 d.addCallback(cb) |
868 return d | 980 return d |
869 | 981 |
870 def cmd_title(self, client, mess_data): | 982 def cmd_title(self, client, mess_data): |
871 """change room's subject | 983 """change room's subject |
899 """ | 1011 """ |
900 unparsed = mess_data["unparsed"].strip() | 1012 unparsed = mess_data["unparsed"].strip() |
901 try: | 1013 try: |
902 service = jid.JID(unparsed) | 1014 service = jid.JID(unparsed) |
903 except RuntimeError: | 1015 except RuntimeError: |
904 if mess_data['type'] == C.MESS_TYPE_GROUPCHAT: | 1016 if mess_data["type"] == C.MESS_TYPE_GROUPCHAT: |
905 room_jid = mess_data["to"] | 1017 room_jid = mess_data["to"] |
906 service = jid.JID(room_jid.host) | 1018 service = jid.JID(room_jid.host) |
907 elif client.muc_service is not None: | 1019 elif client.muc_service is not None: |
908 service = client.muc_service | 1020 service = client.muc_service |
909 else: | 1021 else: |
910 msg = D_("No known default MUC service {unparsed}").format( | 1022 msg = D_("No known default MUC service {unparsed}").format( |
911 unparsed=unparsed) | 1023 unparsed=unparsed |
1024 ) | |
912 self.text_cmds.feed_back(client, msg, mess_data) | 1025 self.text_cmds.feed_back(client, msg, mess_data) |
913 return False | 1026 return False |
914 except jid.InvalidFormat: | 1027 except jid.InvalidFormat: |
915 msg = D_("{} is not a valid JID!".format(unparsed)) | 1028 msg = D_("{} is not a valid JID!".format(unparsed)) |
916 self.text_cmds.feed_back(client, msg, mess_data) | 1029 self.text_cmds.feed_back(client, msg, mess_data) |
919 d.addCallback(self._show_list_ui, client, service) | 1032 d.addCallback(self._show_list_ui, client, service) |
920 | 1033 |
921 return False | 1034 return False |
922 | 1035 |
923 def _whois(self, client, whois_msg, mess_data, target_jid): | 1036 def _whois(self, client, whois_msg, mess_data, target_jid): |
924 """ Add MUC user information to whois """ | 1037 """Add MUC user information to whois""" |
925 if mess_data['type'] != "groupchat": | 1038 if mess_data["type"] != "groupchat": |
926 return | 1039 return |
927 if target_jid.userhostJID() not in client._muc_client.joined_rooms: | 1040 if target_jid.userhostJID() not in client._muc_client.joined_rooms: |
928 log.warning(_("This room has not been joined")) | 1041 log.warning(_("This room has not been joined")) |
929 return | 1042 return |
930 if not target_jid.resource: | 1043 if not target_jid.resource: |
931 return | 1044 return |
932 user = client._muc_client.joined_rooms[target_jid.userhostJID()].getUser(target_jid.resource) | 1045 user = client._muc_client.joined_rooms[target_jid.userhostJID()].getUser( |
1046 target_jid.resource | |
1047 ) | |
933 whois_msg.append(_("Nickname: %s") % user.nick) | 1048 whois_msg.append(_("Nickname: %s") % user.nick) |
934 if user.entity: | 1049 if user.entity: |
935 whois_msg.append(_("Entity: %s") % user.entity) | 1050 whois_msg.append(_("Entity: %s") % user.entity) |
936 if user.affiliation != 'none': | 1051 if user.affiliation != "none": |
937 whois_msg.append(_("Affiliation: %s") % user.affiliation) | 1052 whois_msg.append(_("Affiliation: %s") % user.affiliation) |
938 if user.role != 'none': | 1053 if user.role != "none": |
939 whois_msg.append(_("Role: %s") % user.role) | 1054 whois_msg.append(_("Role: %s") % user.role) |
940 if user.status: | 1055 if user.status: |
941 whois_msg.append(_("Status: %s") % user.status) | 1056 whois_msg.append(_("Status: %s") % user.status) |
942 if user.show: | 1057 if user.show: |
943 whois_msg.append(_("Show: %s") % user.show) | 1058 whois_msg.append(_("Show: %s") % user.show) |
946 # FIXME: should we add a privacy parameters in settings to activate before | 1061 # FIXME: should we add a privacy parameters in settings to activate before |
947 # broadcasting the presence to all MUC rooms ? | 1062 # broadcasting the presence to all MUC rooms ? |
948 muc_client = client._muc_client | 1063 muc_client = client._muc_client |
949 for room_jid, room in muc_client.joined_rooms.items(): | 1064 for room_jid, room in muc_client.joined_rooms.items(): |
950 elt = xml_tools.element_copy(presence_elt) | 1065 elt = xml_tools.element_copy(presence_elt) |
951 elt['to'] = room_jid.userhost() + '/' + room.nick | 1066 elt["to"] = room_jid.userhost() + "/" + room.nick |
952 client.presence.send(elt) | 1067 client.presence.send(elt) |
953 return True | 1068 return True |
954 | 1069 |
955 def presence_received_trigger(self, client, entity, show, priority, statuses): | 1070 def presence_received_trigger(self, client, entity, show, priority, statuses): |
956 entity_bare = entity.userhostJID() | 1071 entity_bare = entity.userhostJID() |
966 | 1081 |
967 def __init__(self, plugin_parent): | 1082 def __init__(self, plugin_parent): |
968 self.plugin_parent = plugin_parent | 1083 self.plugin_parent = plugin_parent |
969 muc.MUCClient.__init__(self) | 1084 muc.MUCClient.__init__(self) |
970 self._changing_nicks = set() # used to keep trace of who is changing nick, | 1085 self._changing_nicks = set() # used to keep trace of who is changing nick, |
971 # and to discard userJoinedRoom signal in this case | 1086 # and to discard userJoinedRoom signal in this case |
972 print("init SatMUCClient OK") | 1087 print("init SatMUCClient OK") |
973 | 1088 |
974 @property | 1089 @property |
975 def joined_rooms(self): | 1090 def joined_rooms(self): |
976 return self._rooms | 1091 return self._rooms |
998 """ | 1113 """ |
999 new_state_idx = ROOM_STATES.index(new_state) | 1114 new_state_idx = ROOM_STATES.index(new_state) |
1000 if new_state_idx == -1: | 1115 if new_state_idx == -1: |
1001 raise exceptions.InternalError("unknown room state") | 1116 raise exceptions.InternalError("unknown room state") |
1002 if new_state_idx < 1: | 1117 if new_state_idx < 1: |
1003 raise exceptions.InternalError("unexpected new room state ({room}): {state}".format( | 1118 raise exceptions.InternalError( |
1004 room=room.userhost(), | 1119 "unexpected new room state ({room}): {state}".format( |
1005 state=new_state)) | 1120 room=room.userhost(), state=new_state |
1006 expected_state = ROOM_STATES[new_state_idx-1] | 1121 ) |
1122 ) | |
1123 expected_state = ROOM_STATES[new_state_idx - 1] | |
1007 if room.state != expected_state: | 1124 if room.state != expected_state: |
1008 log.error(_( | 1125 log.error( |
1009 "room {room} is not in expected state: room is in state {current_state} " | 1126 _( |
1010 "while we were expecting {expected_state}").format( | 1127 "room {room} is not in expected state: room is in state {current_state} " |
1011 room=room.roomJID.userhost(), | 1128 "while we were expecting {expected_state}" |
1012 current_state=room.state, | 1129 ).format( |
1013 expected_state=expected_state)) | 1130 room=room.roomJID.userhost(), |
1131 current_state=room.state, | |
1132 expected_state=expected_state, | |
1133 ) | |
1134 ) | |
1014 room.state = new_state | 1135 room.state = new_state |
1015 | 1136 |
1016 def _addRoom(self, room): | 1137 def _addRoom(self, room): |
1017 super(LiberviaMUCClient, self)._addRoom(room) | 1138 super(LiberviaMUCClient, self)._addRoom(room) |
1018 room._roster_ok = False # True when occupants list has been fully received | 1139 room._roster_ok = False # True when occupants list has been fully received |
1024 room._cache = [] | 1145 room._cache = [] |
1025 # we only need to keep last presence status for each jid, so a dict is suitable | 1146 # we only need to keep last presence status for each jid, so a dict is suitable |
1026 room._cache_presence = {} | 1147 room._cache_presence = {} |
1027 | 1148 |
1028 async def _join_legacy( | 1149 async def _join_legacy( |
1029 self, | 1150 self, client: SatXMPPEntity, room_jid: jid.JID, nick: str, password: Optional[str] |
1030 client: SatXMPPEntity, | |
1031 room_jid: jid.JID, | |
1032 nick: str, | |
1033 password: Optional[str] | |
1034 ) -> muc.Room: | 1151 ) -> muc.Room: |
1035 """Join room an retrieve history with legacy method""" | 1152 """Join room an retrieve history with legacy method""" |
1036 mess_data_list = await self.host.memory.history_get( | 1153 mess_data_list = await self.host.memory.history_get( |
1037 room_jid, | 1154 room_jid, |
1038 client.jid.userhostJID(), | 1155 client.jid.userhostJID(), |
1039 limit=1, | 1156 limit=1, |
1040 between=True, | 1157 between=True, |
1041 profile=client.profile | 1158 profile=client.profile, |
1042 ) | 1159 ) |
1043 if mess_data_list: | 1160 if mess_data_list: |
1044 timestamp = mess_data_list[0][1] | 1161 timestamp = mess_data_list[0][1] |
1045 # we use seconds since last message to get backlog without duplicates | 1162 # we use seconds since last message to get backlog without duplicates |
1046 # and we remove 1 second to avoid getting the last message again | 1163 # and we remove 1 second to avoid getting the last message again |
1047 seconds = int(time.time() - timestamp) - 1 | 1164 seconds = int(time.time() - timestamp) - 1 |
1048 else: | 1165 else: |
1049 seconds = None | 1166 seconds = None |
1050 | 1167 |
1051 room = await super(LiberviaMUCClient, self).join( | 1168 room = await super(LiberviaMUCClient, self).join( |
1052 room_jid, nick, muc.HistoryOptions(seconds=seconds), password) | 1169 room_jid, nick, muc.HistoryOptions(seconds=seconds), password |
1170 ) | |
1053 # used to send bridge signal once backlog are written in history | 1171 # used to send bridge signal once backlog are written in history |
1054 room._history_type = HISTORY_LEGACY | 1172 room._history_type = HISTORY_LEGACY |
1055 room._history_d = defer.Deferred() | 1173 room._history_d = defer.Deferred() |
1056 room._history_d.callback(None) | 1174 room._history_d.callback(None) |
1057 return room | 1175 return room |
1058 | 1176 |
1059 async def _get_mam_history( | 1177 async def _get_mam_history( |
1060 self, | 1178 self, client: SatXMPPEntity, room: muc.Room, room_jid: jid.JID |
1061 client: SatXMPPEntity, | |
1062 room: muc.Room, | |
1063 room_jid: jid.JID | |
1064 ) -> None: | 1179 ) -> None: |
1065 """Retrieve history for rooms handling MAM""" | 1180 """Retrieve history for rooms handling MAM""" |
1066 history_d = room._history_d = defer.Deferred() | 1181 history_d = room._history_d = defer.Deferred() |
1067 # we trigger now the deferred so all callback are processed as soon as possible | 1182 # we trigger now the deferred so all callback are processed as soon as possible |
1068 # and in order | 1183 # and in order |
1071 last_mess = await self.host.memory.history_get( | 1186 last_mess = await self.host.memory.history_get( |
1072 room_jid, | 1187 room_jid, |
1073 None, | 1188 None, |
1074 limit=1, | 1189 limit=1, |
1075 between=False, | 1190 between=False, |
1076 filters={ | 1191 filters={"types": C.MESS_TYPE_GROUPCHAT, "last_stanza_id": True}, |
1077 'types': C.MESS_TYPE_GROUPCHAT, | 1192 profile=client.profile, |
1078 'last_stanza_id': True}, | 1193 ) |
1079 profile=client.profile) | |
1080 if last_mess: | 1194 if last_mess: |
1081 stanza_id = last_mess[0][-1]['stanza_id'] | 1195 stanza_id = last_mess[0][-1]["stanza_id"] |
1082 rsm_req = rsm.RSMRequest(max_=20, after=stanza_id) | 1196 rsm_req = rsm.RSMRequest(max_=20, after=stanza_id) |
1083 no_loop=False | 1197 no_loop = False |
1084 else: | 1198 else: |
1085 log.info("We have no MAM archive for room {room_jid}.".format( | 1199 log.info( |
1086 room_jid=room_jid)) | 1200 "We have no MAM archive for room {room_jid}.".format(room_jid=room_jid) |
1201 ) | |
1087 # we don't want the whole archive if we have no archive yet | 1202 # we don't want the whole archive if we have no archive yet |
1088 # as it can be huge | 1203 # as it can be huge |
1089 rsm_req = rsm.RSMRequest(max_=50, before='') | 1204 rsm_req = rsm.RSMRequest(max_=50, before="") |
1090 no_loop=True | 1205 no_loop = True |
1091 | 1206 |
1092 mam_req = mam.MAMRequest(rsm_=rsm_req) | 1207 mam_req = mam.MAMRequest(rsm_=rsm_req) |
1093 complete = False | 1208 complete = False |
1094 count = 0 | 1209 count = 0 |
1095 while not complete: | 1210 while not complete: |
1096 try: | 1211 try: |
1097 mam_data = await self._mam.get_archives(client, mam_req, | 1212 mam_data = await self._mam.get_archives(client, mam_req, service=room_jid) |
1098 service=room_jid) | |
1099 except xmpp_error.StanzaError as e: | 1213 except xmpp_error.StanzaError as e: |
1100 if last_mess and e.condition == 'item-not-found': | 1214 if last_mess and e.condition == "item-not-found": |
1101 log.warning( | 1215 log.warning( |
1102 f"requested item (with id {stanza_id!r}) can't be found in " | 1216 f"requested item (with id {stanza_id!r}) can't be found in " |
1103 f"history of {room_jid}, history has probably been purged on " | 1217 f"history of {room_jid}, history has probably been purged on " |
1104 f"server.") | 1218 f"server." |
1219 ) | |
1105 # we get last items like for a new room | 1220 # we get last items like for a new room |
1106 rsm_req = rsm.RSMRequest(max_=50, before='') | 1221 rsm_req = rsm.RSMRequest(max_=50, before="") |
1107 mam_req = mam.MAMRequest(rsm_=rsm_req) | 1222 mam_req = mam.MAMRequest(rsm_=rsm_req) |
1108 no_loop=True | 1223 no_loop = True |
1109 continue | 1224 continue |
1110 else: | 1225 else: |
1111 raise e | 1226 raise e |
1112 elt_list, rsm_response, mam_response = mam_data | 1227 elt_list, rsm_response, mam_response = mam_data |
1113 complete = True if no_loop else mam_response["complete"] | 1228 complete = True if no_loop else mam_response["complete"] |
1120 count += len(elt_list) | 1235 count += len(elt_list) |
1121 | 1236 |
1122 for mess_elt in elt_list: | 1237 for mess_elt in elt_list: |
1123 try: | 1238 try: |
1124 fwd_message_elt = self._mam.get_message_from_result( | 1239 fwd_message_elt = self._mam.get_message_from_result( |
1125 client, mess_elt, mam_req, service=room_jid) | 1240 client, mess_elt, mam_req, service=room_jid |
1241 ) | |
1126 except exceptions.DataError: | 1242 except exceptions.DataError: |
1127 continue | 1243 continue |
1128 if fwd_message_elt.getAttribute("to"): | 1244 if fwd_message_elt.getAttribute("to"): |
1129 log.warning( | 1245 log.warning( |
1130 'Forwarded message element has a "to" attribute while it is ' | 1246 'Forwarded message element has a "to" attribute while it is ' |
1131 'forbidden by specifications') | 1247 "forbidden by specifications" |
1248 ) | |
1132 fwd_message_elt["to"] = client.jid.full() | 1249 fwd_message_elt["to"] = client.jid.full() |
1133 client.messageProt.onMessage(fwd_message_elt) | 1250 client.messageProt.onMessage(fwd_message_elt) |
1134 client._muc_client._onGroupChat(fwd_message_elt) | 1251 client._muc_client._onGroupChat(fwd_message_elt) |
1135 | 1252 |
1136 if not count: | 1253 if not count: |
1137 log.info(_("No message received while offline in {room_jid}".format( | 1254 log.info( |
1138 room_jid=room_jid))) | 1255 _( |
1256 "No message received while offline in {room_jid}".format( | |
1257 room_jid=room_jid | |
1258 ) | |
1259 ) | |
1260 ) | |
1139 else: | 1261 else: |
1140 log.info( | 1262 log.info( |
1141 _("We have received {num_mess} message(s) in {room_jid} while " | 1263 _( |
1142 "offline.") | 1264 "We have received {num_mess} message(s) in {room_jid} while " |
1143 .format(num_mess=count, room_jid=room_jid)) | 1265 "offline." |
1266 ).format(num_mess=count, room_jid=room_jid) | |
1267 ) | |
1144 | 1268 |
1145 # for legacy history, the following steps are done in receivedSubject but for MAM | 1269 # for legacy history, the following steps are done in receivedSubject but for MAM |
1146 # the order is different (we have to join then get MAM archive, so subject | 1270 # the order is different (we have to join then get MAM archive, so subject |
1147 # is received before archive), so we change state and add the callbacks here. | 1271 # is received before archive), so we change state and add the callbacks here. |
1148 self.change_room_state(room, ROOM_STATE_LIVE) | 1272 self.change_room_state(room, ROOM_STATE_LIVE) |
1149 history_d.addCallbacks(self._history_cb, self._history_eb, [room], | 1273 history_d.addCallbacks( |
1150 errbackArgs=[room]) | 1274 self._history_cb, self._history_eb, [room], errbackArgs=[room] |
1275 ) | |
1151 | 1276 |
1152 # we wait for all callbacks to be processed | 1277 # we wait for all callbacks to be processed |
1153 await history_d | 1278 await history_d |
1154 | 1279 |
1155 async def _join_mam( | 1280 async def _join_mam( |
1156 self, | 1281 self, client: SatXMPPEntity, room_jid: jid.JID, nick: str, password: Optional[str] |
1157 client: SatXMPPEntity, | |
1158 room_jid: jid.JID, | |
1159 nick: str, | |
1160 password: Optional[str] | |
1161 ) -> muc.Room: | 1282 ) -> muc.Room: |
1162 """Join room and retrieve history using MAM""" | 1283 """Join room and retrieve history using MAM""" |
1163 room = await super(LiberviaMUCClient, self).join( | 1284 room = await super(LiberviaMUCClient, self).join( |
1164 # we don't want any history from room as we'll get it with MAM | 1285 # we don't want any history from room as we'll get it with MAM |
1165 room_jid, nick, muc.HistoryOptions(maxStanzas=0), password=password | 1286 room_jid, |
1287 nick, | |
1288 muc.HistoryOptions(maxStanzas=0), | |
1289 password=password, | |
1166 ) | 1290 ) |
1167 room._history_type = HISTORY_MAM | 1291 room._history_type = HISTORY_MAM |
1168 # MAM history retrieval can be very long, and doesn't need to be sync, so we don't | 1292 # MAM history retrieval can be very long, and doesn't need to be sync, so we don't |
1169 # wait for it | 1293 # wait for it |
1170 defer.ensureDeferred(self._get_mam_history(client, room, room_jid)) | 1294 defer.ensureDeferred(self._get_mam_history(client, room, room_jid)) |
1195 return | 1319 return |
1196 | 1320 |
1197 if user is None: | 1321 if user is None: |
1198 nick = presence.sender.resource | 1322 nick = presence.sender.resource |
1199 if not nick: | 1323 if not nick: |
1200 log.warning(_("missing nick in presence: {xml}").format( | 1324 log.warning( |
1201 xml = presence.toElement().toXml())) | 1325 _("missing nick in presence: {xml}").format( |
1326 xml=presence.toElement().toXml() | |
1327 ) | |
1328 ) | |
1202 return | 1329 return |
1203 user = muc.User(nick, presence.entity) | 1330 user = muc.User(nick, presence.entity) |
1204 | 1331 |
1205 # we want to keep statuses with room | 1332 # we want to keep statuses with room |
1206 # XXX: presence if broadcasted, and we won't have status code | 1333 # XXX: presence if broadcasted, and we won't have status code |
1207 # like 110 (REALJID_PUBLIC) after first <presence/> received | 1334 # like 110 (REALJID_PUBLIC) after first <presence/> received |
1208 # so we keep only the initial <presence> (with SELF_PRESENCE), | 1335 # so we keep only the initial <presence> (with SELF_PRESENCE), |
1209 # thus we check if attribute already exists | 1336 # thus we check if attribute already exists |
1210 if (not hasattr(room, 'statuses') | 1337 if ( |
1211 and muc.STATUS_CODE.SELF_PRESENCE in presence.mucStatuses): | 1338 not hasattr(room, "statuses") |
1339 and muc.STATUS_CODE.SELF_PRESENCE in presence.mucStatuses | |
1340 ): | |
1212 room.statuses = presence.mucStatuses | 1341 room.statuses = presence.mucStatuses |
1213 | 1342 |
1214 # Update user data | 1343 # Update user data |
1215 user.role = presence.role | 1344 user.role = presence.role |
1216 user.affiliation = presence.affiliation | 1345 user.affiliation = presence.affiliation |
1248 def userJoinedRoom(self, room, user): | 1377 def userJoinedRoom(self, room, user): |
1249 if user.nick == room.nick: | 1378 if user.nick == room.nick: |
1250 # we have received our own nick, | 1379 # we have received our own nick, |
1251 # this mean that the full room roster was received | 1380 # this mean that the full room roster was received |
1252 self.change_room_state(room, ROOM_STATE_SELF_PRESENCE) | 1381 self.change_room_state(room, ROOM_STATE_SELF_PRESENCE) |
1253 log.debug("room {room} joined with nick {nick}".format( | 1382 log.debug( |
1254 room=room.occupantJID.userhost(), nick=user.nick)) | 1383 "room {room} joined with nick {nick}".format( |
1384 room=room.occupantJID.userhost(), nick=user.nick | |
1385 ) | |
1386 ) | |
1255 # we set type so we don't have to use a deferred | 1387 # we set type so we don't have to use a deferred |
1256 # with disco to check entity type | 1388 # with disco to check entity type |
1257 self.host.memory.update_entity_data( | 1389 self.host.memory.update_entity_data( |
1258 self.client, room.roomJID, C.ENTITY_TYPE, C.ENTITY_TYPE_MUC | 1390 self.client, room.roomJID, C.ENTITY_TYPE, C.ENTITY_TYPE_MUC |
1259 ) | 1391 ) |
1260 elif room.state not in (ROOM_STATE_OCCUPANTS, ROOM_STATE_LIVE): | 1392 elif room.state not in (ROOM_STATE_OCCUPANTS, ROOM_STATE_LIVE): |
1261 log.warning( | 1393 log.warning( |
1262 "Received user presence data in a room before its initialisation " | 1394 "Received user presence data in a room before its initialisation " |
1263 "(current state: {state})," | 1395 "(current state: {state})," |
1264 "this is not standard! Ignoring it: {room} ({nick})".format( | 1396 "this is not standard! Ignoring it: {room} ({nick})".format( |
1265 state=room.state, | 1397 state=room.state, room=room.roomJID.userhost(), nick=user.nick |
1266 room=room.roomJID.userhost(), | 1398 ) |
1267 nick=user.nick)) | 1399 ) |
1268 return | 1400 return |
1269 else: | 1401 else: |
1270 if not room.fully_joined.called: | 1402 if not room.fully_joined.called: |
1271 return | 1403 return |
1272 for cb in room.on_joined_callbacks: | 1404 for cb in room.on_joined_callbacks: |
1273 defer.ensureDeferred(cb(room, user)) | 1405 defer.ensureDeferred(cb(room, user)) |
1274 try: | 1406 try: |
1275 self._changing_nicks.remove(user.nick) | 1407 self._changing_nicks.remove(user.nick) |
1276 except KeyError: | 1408 except KeyError: |
1277 # this is a new user | 1409 # this is a new user |
1278 log.debug(_("user {nick} has joined room {room_id}").format( | 1410 log.debug( |
1279 nick=user.nick, room_id=room.occupantJID.userhost())) | 1411 _("user {nick} has joined room {room_id}").format( |
1412 nick=user.nick, room_id=room.occupantJID.userhost() | |
1413 ) | |
1414 ) | |
1280 if not self.host.trigger.point( | 1415 if not self.host.trigger.point( |
1281 "MUC user joined", room, user, self.client.profile): | 1416 "MUC user joined", room, user, self.client.profile |
1417 ): | |
1282 return | 1418 return |
1283 | 1419 |
1284 extra = {'info_type': ROOM_USER_JOINED, | 1420 extra = { |
1285 'user_affiliation': user.affiliation, | 1421 "info_type": ROOM_USER_JOINED, |
1286 'user_role': user.role, | 1422 "user_affiliation": user.affiliation, |
1287 'user_nick': user.nick | 1423 "user_role": user.role, |
1288 } | 1424 "user_nick": user.nick, |
1425 } | |
1289 if user.entity is not None: | 1426 if user.entity is not None: |
1290 extra['user_entity'] = user.entity.full() | 1427 extra["user_entity"] = user.entity.full() |
1291 mess_data = { # dict is similar to the one used in client.onMessage | 1428 mess_data = { # dict is similar to the one used in client.onMessage |
1292 "from": room.roomJID, | 1429 "from": room.roomJID, |
1293 "to": self.client.jid, | 1430 "to": self.client.jid, |
1294 "uid": str(uuid.uuid4()), | 1431 "uid": str(uuid.uuid4()), |
1295 "message": {'': D_("=> {} has joined the room").format(user.nick)}, | 1432 "message": {"": D_("=> {} has joined the room").format(user.nick)}, |
1296 "subject": {}, | 1433 "subject": {}, |
1297 "type": C.MESS_TYPE_INFO, | 1434 "type": C.MESS_TYPE_INFO, |
1298 "extra": extra, | 1435 "extra": extra, |
1299 "timestamp": time.time(), | 1436 "timestamp": time.time(), |
1300 } | 1437 } |
1302 # while not indispensable. In the future an option may allow | 1439 # while not indispensable. In the future an option may allow |
1303 # to re-enable it | 1440 # to re-enable it |
1304 # self.client.message_add_to_history(mess_data) | 1441 # self.client.message_add_to_history(mess_data) |
1305 self.client.message_send_to_bridge(mess_data) | 1442 self.client.message_send_to_bridge(mess_data) |
1306 | 1443 |
1307 | |
1308 def userLeftRoom(self, room, user): | 1444 def userLeftRoom(self, room, user): |
1309 if not self.host.trigger.point("MUC user left", room, user, self.client.profile): | 1445 if not self.host.trigger.point("MUC user left", room, user, self.client.profile): |
1310 return | 1446 return |
1311 if user.nick == room.nick: | 1447 if user.nick == room.nick: |
1312 # we left the room | 1448 # we left the room |
1313 room_jid_s = room.roomJID.userhost() | 1449 room_jid_s = room.roomJID.userhost() |
1314 log.info(_("Room ({room}) left ({profile})").format( | 1450 log.info( |
1315 room = room_jid_s, profile = self.client.profile)) | 1451 _("Room ({room}) left ({profile})").format( |
1316 self.host.memory.del_entity_cache(room.roomJID, profile_key=self.client.profile) | 1452 room=room_jid_s, profile=self.client.profile |
1453 ) | |
1454 ) | |
1455 self.host.memory.del_entity_cache( | |
1456 room.roomJID, profile_key=self.client.profile | |
1457 ) | |
1317 self.host.bridge.muc_room_left(room.roomJID.userhost(), self.client.profile) | 1458 self.host.bridge.muc_room_left(room.roomJID.userhost(), self.client.profile) |
1318 elif room.state != ROOM_STATE_LIVE: | 1459 elif room.state != ROOM_STATE_LIVE: |
1319 log.warning("Received user presence data in a room before its initialisation (current state: {state})," | 1460 log.warning( |
1461 "Received user presence data in a room before its initialisation (current state: {state})," | |
1320 "this is not standard! Ignoring it: {room} ({nick})".format( | 1462 "this is not standard! Ignoring it: {room} ({nick})".format( |
1321 state=room.state, | 1463 state=room.state, room=room.roomJID.userhost(), nick=user.nick |
1322 room=room.roomJID.userhost(), | 1464 ) |
1323 nick=user.nick)) | 1465 ) |
1324 return | 1466 return |
1325 else: | 1467 else: |
1326 if not room.fully_joined.called: | 1468 if not room.fully_joined.called: |
1327 return | 1469 return |
1328 log.debug(_("user {nick} left room {room_id}").format(nick=user.nick, room_id=room.occupantJID.userhost())) | 1470 log.debug( |
1471 _("user {nick} left room {room_id}").format( | |
1472 nick=user.nick, room_id=room.occupantJID.userhost() | |
1473 ) | |
1474 ) | |
1329 for cb in room.on_left_callbacks: | 1475 for cb in room.on_left_callbacks: |
1330 defer.ensureDeferred(cb(room, user)) | 1476 defer.ensureDeferred(cb(room, user)) |
1331 extra = {'info_type': ROOM_USER_LEFT, | 1477 extra = { |
1332 'user_affiliation': user.affiliation, | 1478 "info_type": ROOM_USER_LEFT, |
1333 'user_role': user.role, | 1479 "user_affiliation": user.affiliation, |
1334 'user_nick': user.nick | 1480 "user_role": user.role, |
1335 } | 1481 "user_nick": user.nick, |
1482 } | |
1336 if user.entity is not None: | 1483 if user.entity is not None: |
1337 extra['user_entity'] = user.entity.full() | 1484 extra["user_entity"] = user.entity.full() |
1338 mess_data = { # dict is similar to the one used in client.onMessage | 1485 mess_data = { # dict is similar to the one used in client.onMessage |
1339 "from": room.roomJID, | 1486 "from": room.roomJID, |
1340 "to": self.client.jid, | 1487 "to": self.client.jid, |
1341 "uid": str(uuid.uuid4()), | 1488 "uid": str(uuid.uuid4()), |
1342 "message": {'': D_("<= {} has left the room").format(user.nick)}, | 1489 "message": {"": D_("<= {} has left the room").format(user.nick)}, |
1343 "subject": {}, | 1490 "subject": {}, |
1344 "type": C.MESS_TYPE_INFO, | 1491 "type": C.MESS_TYPE_INFO, |
1345 "extra": extra, | 1492 "extra": extra, |
1346 "timestamp": time.time(), | 1493 "timestamp": time.time(), |
1347 } | 1494 } |
1348 # FIXME: disable history, see userJoinRoom comment | 1495 # FIXME: disable history, see userJoinRoom comment |
1349 # self.client.message_add_to_history(mess_data) | 1496 # self.client.message_add_to_history(mess_data) |
1350 self.client.message_send_to_bridge(mess_data) | 1497 self.client.message_send_to_bridge(mess_data) |
1351 | 1498 |
1352 def user_changed_nick(self, room, user, new_nick): | 1499 def user_changed_nick(self, room, user, new_nick): |
1353 self.host.bridge.muc_room_user_changed_nick(room.roomJID.userhost(), user.nick, new_nick, self.client.profile) | 1500 self.host.bridge.muc_room_user_changed_nick( |
1501 room.roomJID.userhost(), user.nick, new_nick, self.client.profile | |
1502 ) | |
1354 | 1503 |
1355 def userUpdatedStatus(self, room, user, show, status): | 1504 def userUpdatedStatus(self, room, user, show, status): |
1356 entity = jid.JID(tuple=(room.roomJID.user, room.roomJID.host, user.nick)) | 1505 entity = jid.JID(tuple=(room.roomJID.user, room.roomJID.host, user.nick)) |
1357 if hasattr(room, "_cache_presence"): | 1506 if hasattr(room, "_cache_presence"): |
1358 # room has a cache for presence, meaning it has not been fully | 1507 # room has a cache for presence, meaning it has not been fully |
1363 cache[entity] = { | 1512 cache[entity] = { |
1364 "room": room, | 1513 "room": room, |
1365 "user": user, | 1514 "user": user, |
1366 "show": show, | 1515 "show": show, |
1367 "status": status, | 1516 "status": status, |
1368 } | 1517 } |
1369 return | 1518 return |
1370 statuses = {C.PRESENCE_STATUSES_DEFAULT: status or ''} | 1519 statuses = {C.PRESENCE_STATUSES_DEFAULT: status or ""} |
1371 self.host.bridge.presence_update( | 1520 self.host.bridge.presence_update( |
1372 entity.full(), show or '', 0, statuses, self.client.profile) | 1521 entity.full(), show or "", 0, statuses, self.client.profile |
1522 ) | |
1373 | 1523 |
1374 ## messages ## | 1524 ## messages ## |
1375 | 1525 |
1376 def receivedGroupChat(self, room, user, body): | 1526 def receivedGroupChat(self, room, user, body): |
1377 log.debug('receivedGroupChat: room=%s user=%s body=%s' % (room.roomJID.full(), user, body)) | 1527 log.debug( |
1528 "receivedGroupChat: room=%s user=%s body=%s" | |
1529 % (room.roomJID.full(), user, body) | |
1530 ) | |
1378 | 1531 |
1379 ## subject ## | 1532 ## subject ## |
1380 | 1533 |
1381 def groupChatReceived(self, message): | 1534 def groupChatReceived(self, message): |
1382 """ | 1535 """ |
1388 # We override this method to fix subject handling (empty strings were discarded) | 1541 # We override this method to fix subject handling (empty strings were discarded) |
1389 # FIXME: remove this once fixed upstream | 1542 # FIXME: remove this once fixed upstream |
1390 room, user = self._getRoomUser(message) | 1543 room, user = self._getRoomUser(message) |
1391 | 1544 |
1392 if room is None: | 1545 if room is None: |
1393 log.warning("No room found for message: {message}" | 1546 log.warning( |
1394 .format(message=message.toElement().toXml())) | 1547 "No room found for message: {message}".format( |
1548 message=message.toElement().toXml() | |
1549 ) | |
1550 ) | |
1395 return | 1551 return |
1396 | 1552 |
1397 if message.subject is not None: | 1553 if message.subject is not None: |
1398 self.receivedSubject(room, user, message.subject) | 1554 self.receivedSubject(room, user, message.subject) |
1399 elif message.delay is None: | 1555 elif message.delay is None: |
1422 cache_presence = room._cache_presence | 1578 cache_presence = room._cache_presence |
1423 del room._cache_presence | 1579 del room._cache_presence |
1424 for elem in cache: | 1580 for elem in cache: |
1425 self.client.xmlstream.dispatch(elem) | 1581 self.client.xmlstream.dispatch(elem) |
1426 for presence_data in cache_presence.values(): | 1582 for presence_data in cache_presence.values(): |
1427 if not presence_data['show'] and not presence_data['status']: | 1583 if not presence_data["show"] and not presence_data["status"]: |
1428 # occupants are already sent in muc_room_joined, so if we don't have | 1584 # occupants are already sent in muc_room_joined, so if we don't have |
1429 # extra information like show or statuses, we can discard the signal | 1585 # extra information like show or statuses, we can discard the signal |
1430 continue | 1586 continue |
1431 else: | 1587 else: |
1432 self.userUpdatedStatus(**presence_data) | 1588 self.userUpdatedStatus(**presence_data) |
1440 # cf. http://xmpp.org/extensions/xep-0045.html#enter-subject | 1596 # cf. http://xmpp.org/extensions/xep-0045.html#enter-subject |
1441 room.subject = subject # FIXME: subject doesn't handle xml:lang | 1597 room.subject = subject # FIXME: subject doesn't handle xml:lang |
1442 if room.state != ROOM_STATE_LIVE: | 1598 if room.state != ROOM_STATE_LIVE: |
1443 if room._history_type == HISTORY_LEGACY: | 1599 if room._history_type == HISTORY_LEGACY: |
1444 self.change_room_state(room, ROOM_STATE_LIVE) | 1600 self.change_room_state(room, ROOM_STATE_LIVE) |
1445 room._history_d.addCallbacks(self._history_cb, self._history_eb, [room], errbackArgs=[room]) | 1601 room._history_d.addCallbacks( |
1602 self._history_cb, self._history_eb, [room], errbackArgs=[room] | |
1603 ) | |
1446 else: | 1604 else: |
1447 # the subject has been changed | 1605 # the subject has been changed |
1448 log.debug(_("New subject for room ({room_id}): {subject}").format(room_id = room.roomJID.full(), subject = subject)) | 1606 log.debug( |
1449 self.host.bridge.muc_room_new_subject(room.roomJID.userhost(), subject, self.client.profile) | 1607 _("New subject for room ({room_id}): {subject}").format( |
1608 room_id=room.roomJID.full(), subject=subject | |
1609 ) | |
1610 ) | |
1611 self.host.bridge.muc_room_new_subject( | |
1612 room.roomJID.userhost(), subject, self.client.profile | |
1613 ) | |
1450 | 1614 |
1451 ## disco ## | 1615 ## disco ## |
1452 | 1616 |
1453 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 1617 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): |
1454 return [disco.DiscoFeature(NS_MUC)] | 1618 return [disco.DiscoFeature(NS_MUC)] |
1455 | 1619 |
1456 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | 1620 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |
1457 # TODO: manage room queries ? Bad for privacy, must be disabled by default | 1621 # TODO: manage room queries ? Bad for privacy, must be disabled by default |
1458 # see XEP-0045 § 6.7 | 1622 # see XEP-0045 § 6.7 |
1459 return [] | 1623 return [] |