Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0045.py @ 1970:200cd707a46d
plugin XEP-0045, quick_frontend + primitivus (chat): cleaning of XEP-0045 (first pass):
- bridge methods/signals now all start with "muc" to follow new convention
- internal method use client instead of profile to follow new convention
- removed excetpions from plugin XEP-0045 in favor of core.exceptions, NotReady added
- cleaned/simplified several part of the code. checkClient removed as it is not needed anymore
- self.clients map removed, muc data are now stored directly in client
- getRoomEntityNick and getRoomNicksOfUsers are removed as they don't look sane.
/!\ This break all room game plugins for the moment
- use of uuid4 instead of uuid1 for getUniqueName, as host ID and current time are used for uuid1
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 27 Jun 2016 21:45:11 +0200 |
parents | a2bc5089c2eb |
children | bdc6a5b07922 |
comparison
equal
deleted
inserted
replaced
1969:5fbe09b9b568 | 1970:200cd707a46d |
---|---|
61 CONFIG_SECTION = u'plugin muc' | 61 CONFIG_SECTION = u'plugin muc' |
62 | 62 |
63 default_conf = {"default_muc": u'sat@chat.jabberfr.org'} | 63 default_conf = {"default_muc": u'sat@chat.jabberfr.org'} |
64 | 64 |
65 | 65 |
66 class UnknownRoom(Exception): | |
67 pass | |
68 | |
69 | |
70 class AlreadyJoinedRoom(Exception): | |
71 pass | |
72 | |
73 class NotReadyYet(Exception): | |
74 pass | |
75 | |
76 | |
77 class XEP_0045(object): | 66 class XEP_0045(object): |
78 # TODO: this plugin is messy, need a big cleanup/refactoring | |
79 | 67 |
80 def __init__(self, host): | 68 def __init__(self, host): |
81 log.info(_("Plugin XEP_0045 initialization")) | 69 log.info(_("Plugin XEP_0045 initialization")) |
82 self.host = host | 70 self.host = host |
83 self.clients = {} # FIXME: should be moved to profile's client | |
84 self._sessions = memory.Sessions() | 71 self._sessions = memory.Sessions() |
85 host.bridge.addMethod("mucJoin", ".plugin", in_sign='ssa{ss}s', out_sign='s', method=self._join, async=True) | 72 host.bridge.addMethod("mucJoin", ".plugin", in_sign='ssa{ss}s', out_sign='s', method=self._join, async=True) |
86 host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick) | 73 host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self._nick) |
87 host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.mucLeave, async=True) | 74 host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self._leave, async=True) |
88 host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ss)', method=self.getRoomsJoined) | 75 host.bridge.addMethod("mucGetRoomsJoined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ss)', method=self._getRoomsJoined) |
89 host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects) | 76 host.bridge.addMethod("mucGetUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self._getUniqueName) |
90 host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self._getUniqueName) | 77 host.bridge.addMethod("mucConfigureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async=True) |
91 host.bridge.addMethod("configureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async=True) | 78 host.bridge.addMethod("mucGetDefaultService", ".plugin", in_sign='', out_sign='s', method=self.getDefaultMUC) |
92 host.bridge.addMethod("getDefaultMUC", ".plugin", in_sign='', out_sign='s', method=self.getDefaultMUC) | 79 host.bridge.addSignal("mucRoomJoined", ".plugin", signature='sa{sa{ss}}sss') # args: room_jid, occupants, user_nick, subject, profile |
93 host.bridge.addSignal("roomJoined", ".plugin", signature='sa{sa{ss}}sss') # args: room_jid, occupants, user_nick, subject, profile | 80 host.bridge.addSignal("mucRoomLeft", ".plugin", signature='ss') # args: room_jid, profile |
94 host.bridge.addSignal("roomLeft", ".plugin", signature='ss') # args: room_jid, profile | 81 host.bridge.addSignal("mucRoomUserChangedNick", ".plugin", signature='ssss') # args: room_jid, old_nick, new_nick, profile |
95 host.bridge.addSignal("roomUserChangedNick", ".plugin", signature='ssss') # args: room_jid, old_nick, new_nick, profile | 82 host.bridge.addSignal("mucRoomNewSubject", ".plugin", signature='sss') # args: room_jid, subject, profile |
96 host.bridge.addSignal("roomNewSubject", ".plugin", signature='sss') # args: room_jid, subject, profile | |
97 self.__submit_conf_id = host.registerCallback(self._submitConfiguration, with_data=True) | 83 self.__submit_conf_id = host.registerCallback(self._submitConfiguration, with_data=True) |
98 host.importMenu((D_("MUC"), D_("configure")), self._configureRoomMenu, security_limit=0, help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM) | 84 host.importMenu((D_("MUC"), D_("configure")), self._configureRoomMenu, security_limit=0, help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM) |
99 try: | 85 try: |
100 self.host.plugins[C.TEXT_CMDS].registerTextCommands(self) | 86 txt_cmds = self.host.plugins[C.TEXT_CMDS] |
101 self.host.plugins[C.TEXT_CMDS].addWhoIsCb(self._whois, 100) | |
102 except KeyError: | 87 except KeyError: |
103 log.info(_("Text commands not available")) | 88 log.info(_(u"Text commands not available")) |
89 else: | |
90 txt_cmds.registerTextCommands(self) | |
91 txt_cmds.addWhoIsCb(self._whois, 100) | |
104 | 92 |
105 host.trigger.add("presence_available", self.presenceTrigger) | 93 host.trigger.add("presence_available", self.presenceTrigger) |
106 host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=1000000) | 94 host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=1000000) |
107 | 95 |
108 def profileConnected(self, profile): | 96 def profileConnected(self, profile): |
115 if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT: | 103 if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT: |
116 if message_elt.subject or message_elt.delay: | 104 if message_elt.subject or message_elt.delay: |
117 return False | 105 return False |
118 from_jid = jid.JID(message_elt['from']) | 106 from_jid = jid.JID(message_elt['from']) |
119 room_jid = from_jid.userhostJID() | 107 room_jid = from_jid.userhostJID() |
120 if room_jid in self.clients[client.profile].joined_rooms: | 108 if room_jid in client._muc_client.joined_rooms: |
121 room = self.clients[client.profile].joined_rooms[room_jid] | 109 room = client._muc_client.joined_rooms[room_jid] |
122 if not room._room_ok: | 110 if not room._room_ok: |
123 log.warning(u"Received non delayed message in a room before its initialisation: {}".format(message_elt.toXml())) | 111 log.warning(u"Received non delayed message in a room before its initialisation: {}".format(message_elt.toXml())) |
124 room._cache.append(message_elt) | 112 room._cache.append(message_elt) |
125 return False | 113 return False |
126 else: | 114 else: |
127 log.warning(u"Received groupchat message for a room which has not been joined, ignoring it: {}".format(message_elt.toXml())) | 115 log.warning(u"Received groupchat message for a room which has not been joined, ignoring it: {}".format(message_elt.toXml())) |
128 return False | 116 return False |
129 return True | 117 return True |
130 | 118 |
131 def checkClient(self, profile): | 119 def checkRoomJoined(self, client, room_jid): |
132 """Check if the profile is connected and has used the MUC feature. | 120 """Check that given room has been joined in current session |
133 | 121 |
134 If profile was using MUC feature but is now disconnected, remove it from the client list. | 122 @param room_jid (JID): room JID |
135 @param profile: profile to check | 123 """ |
136 @return: True if the profile is connected and has used the MUC feature, else False""" | 124 if room_jid not in client._muc_client.joined_rooms: |
137 if not profile or profile not in self.clients or not self.host.isConnected(profile): | 125 raise exceptions.NotFound(_(u"This room has not been joined")) |
138 log.error(_(u'Unknown or disconnected profile (%s)') % profile) | 126 |
139 if profile in self.clients: | 127 def isJoinedRoom(self, client, room_jid): |
140 del self.clients[profile] | 128 """Tell if a jid is a known and joined room |
129 | |
130 @room_jid(jid.JID): jid of the room | |
131 """ | |
132 try: | |
133 self.checkRoomJoined(client, room_jid) | |
134 except exceptions.NotFound: | |
141 return False | 135 return False |
142 return True | 136 else: |
143 | 137 return True |
144 def getProfileAssertInRoom(self, room_jid, profile_key): | 138 |
145 """Retrieve the profile name, assert that it's connected and participating in the given room. | 139 def _joinCb(self, room, client): |
146 | |
147 @param room_jid (JID): room JID | |
148 @param profile_key (str): %(doc_profile_key) | |
149 @return: the profile name | |
150 """ | |
151 profile = self.host.memory.getProfileName(profile_key) | |
152 if not self.checkClient(profile): | |
153 raise exceptions.ProfileUnknownError("Unknown or disconnected profile") | |
154 if room_jid not in self.clients[profile].joined_rooms: | |
155 raise UnknownRoom("This room has not been joined") | |
156 return profile | |
157 | |
158 def _joinCb(self, room, profile): | |
159 """Called when the user is in the requested room""" | 140 """Called when the user is in the requested room""" |
160 if room.locked: | 141 if room.locked: |
161 # FIXME: the current behaviour is to create an instant room | 142 # FIXME: the current behaviour is to create an instant room |
162 # and send the signal only when the room is unlocked | 143 # and send the signal only when the room is unlocked |
163 # a proper configuration management should be done | 144 # a proper configuration management should be done |
164 print "room locked !" | 145 log.debug(_(u"room locked !")) |
165 d = self.clients[profile].configure(room.roomJID, {}) | 146 d = client._muc_client.configure(room.roomJID, {}) |
166 d.addErrback(lambda dummy: log.error(_(u'Error while configuring the room'))) | 147 d.addErrback(lambda dummy: log.error(_(u'Error while configuring the room'))) |
167 return room | 148 return room |
168 | 149 |
169 def _joinEb(self, failure, room_jid, nick, password, profile): | 150 def _joinEb(self, failure, client, room_jid, nick, password): |
170 """Called when something is going wrong when joining the room""" | 151 """Called when something is going wrong when joining the room""" |
171 if hasattr(failure.value, "condition") and failure.value.condition == 'conflict': | |
172 # we have a nickname conflict, we try again with "_" suffixed to current nickname | |
173 nick += '_' | |
174 return self.clients[profile].join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, callbackKeywords={'profile': profile}, errbackArgs=[room_jid, nick, password, profile]) | |
175 mess = D_("Error while joining the room %s" % room_jid.userhost()) | |
176 try: | 152 try: |
177 mess += " with condition '%s'" % failure.value.condition | 153 condition = failure.value.condition |
178 except AttributeError: | 154 except AttributeError: |
179 pass | 155 msg_suffix = '' |
156 else: | |
157 if condition == 'conflict': | |
158 # we have a nickname conflict, we try again with "_" suffixed to current nickname | |
159 nick += '_' | |
160 return client.join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, callbackKeywords={client: client}, errbackArgs=[client, room_jid, nick, password]) | |
161 msg_suffix = ' with condition "{}"'.format(failure.value.condition) | |
162 | |
163 mess = D_(u"Error while joining the room {room}{suffix}".format( | |
164 room = room_jid.userhost(), suffix = msg_suffix)) | |
180 log.error(mess) | 165 log.error(mess) |
181 self.host.bridge.newAlert(mess, D_("Group chat error"), "ERROR", profile) | 166 xmlui = xml_tools.note(mess, D_(u"Group chat error"), level=C.XMLUI_DATA_LVL_ERROR) |
182 raise failure | 167 self.host.actionNew({'xmlui': xmlui.toXml()}, profile=client.profile) |
183 | 168 |
184 @staticmethod | 169 @staticmethod |
185 def _getOccupants(room): | 170 def _getOccupants(room): |
186 """Get occupants of a room in a form suitable for bridge""" | 171 """Get occupants of a room in a form suitable for bridge""" |
187 return {u.nick: {k:unicode(getattr(u,k) or '') for k in OCCUPANT_KEYS} for u in room.roster.values()} | 172 return {u.nick: {k:unicode(getattr(u,k) or '') for k in OCCUPANT_KEYS} for u in room.roster.values()} |
188 | 173 |
189 def isRoom(self, entity_bare, profile_key): | 174 def _getRoomsJoined(self, profile_key=C.PROF_KEY_NONE): |
190 """Tell if a bare entity is a MUC room. | 175 client = self.host.getClient(profile_key) |
191 | 176 return self.getRoomsJoined(client) |
192 @param entity_bare (jid.JID): bare entity | 177 |
193 @param profile_key (unicode): %(doc_profile_key)s | 178 def getRoomsJoined(self, client): |
194 @return bool | 179 """Return rooms where user is""" |
195 """ | |
196 profile = self.host.memory.getProfileName(profile_key) | |
197 return entity_bare in self.clients[profile].joined_rooms | |
198 | |
199 def getRoomsJoined(self, profile_key=C.PROF_KEY_NONE): | |
200 """Return room where user is""" | |
201 profile = self.host.memory.getProfileName(profile_key) | |
202 result = [] | 180 result = [] |
203 if not self.checkClient(profile): | 181 for room in client._muc_client.joined_rooms.values(): |
204 return result | |
205 for room in self.clients[profile].joined_rooms.values(): | |
206 if room._room_ok: | 182 if room._room_ok: |
207 result.append((room.roomJID.userhost(), self._getOccupants(room), room.nick, room.subject)) | 183 result.append((room.roomJID.userhost(), self._getOccupants(room), room.nick, room.subject)) |
208 return result | 184 return result |
209 | 185 |
210 def getRoomNick(self, room_jid, profile_key=C.PROF_KEY_NONE): | 186 def getRoomNick(self, client, room_jid): |
211 """return nick used in room by user | 187 """return nick used in room by user |
212 | 188 |
213 @param room_jid (jid.JID): JID of the room | 189 @param room_jid (jid.JID): JID of the room |
214 @profile_key: profile | 190 @profile_key: profile |
215 @return: nick or empty string in case of error""" | 191 @return: nick or empty string in case of error |
216 profile = self.host.memory.getProfileName(profile_key) | 192 """ |
217 if not self.checkClient(profile) or room_jid not in self.clients[profile].joined_rooms: | 193 try: |
218 return '' | 194 self.checkRoomJoined(client, room_jid) |
219 return self.clients[profile].joined_rooms[room_jid].nick | 195 except exceptions.NotFound: |
220 | 196 return '' # FIXME: should not return empty string but raise the error |
221 def getRoomNickOfUser(self, room, user_jid, secure=True): | 197 return client._muc_client.joined_rooms[room_jid].nick |
222 """Returns the nick of the given user in the room. | 198 |
223 | 199 # FIXME: broken, to be removed ! |
224 @param room (wokkel.muc.Room): the room | 200 # def getRoomEntityNick(self, client, room_jid, entity_jid, =True): |
225 @param user (jid.JID): bare JID of the user | 201 # """Returns the nick of the given user in the room. |
226 @param secure (bool): set to True for a secure check | 202 |
227 @return: unicode or None if the user didn't join the room. | 203 # @param room (wokkel.muc.Room): the room |
228 """ | 204 # @param user (jid.JID): bare JID of the user |
229 for user in room.roster.values(): | 205 # @param secure (bool): set to True for a secure check |
230 if user.entity is not None: | 206 # @return: unicode or None if the user didn't join the room. |
231 if user.entity.userhostJID() == user_jid.userhostJID(): | 207 # """ |
232 return user.nick | 208 # for user in room.roster.values(): |
233 elif not secure: | 209 # if user.entity is not None: |
234 # FIXME: this is NOT ENOUGH to check an identity!! | 210 # if user.entity.userhostJID() == user_jid.userhostJID(): |
235 # See in which conditions user.entity could be None. | 211 # return user.nick |
236 if user.nick == user_jid.user: | 212 # elif not secure: |
237 return user.nick | 213 # # FIXME: this is NOT ENOUGH to check an identity!! |
238 return None | 214 # # See in which conditions user.entity could be None. |
239 | 215 # if user.nick == user_jid.user: |
240 def getRoomNicksOfUsers(self, room, users=[], secure=True): | 216 # return user.nick |
241 """Returns the nicks of the given users in the room. | 217 # return None |
242 | 218 |
243 @param room (wokkel.muc.Room): the room | 219 # def getRoomNicksOfUsers(self, room, users=[], secure=True): |
244 @param users (list[jid.JID]): list of users | 220 # """Returns the nicks of the given users in the room. |
245 @param secure (True): set to True for a secure check | 221 |
246 @return: a couple (x, y) with: | 222 # @param room (wokkel.muc.Room): the room |
247 - x (list[unicode]): nicks of the users who are in the room | 223 # @param users (list[jid.JID]): list of users |
248 - y (list[jid.JID]): JID of the missing users. | 224 # @param secure (True): set to True for a secure check |
249 """ | 225 # @return: a couple (x, y) with: |
250 nicks = [] | 226 # - x (list[unicode]): nicks of the users who are in the room |
251 missing = [] | 227 # - y (list[jid.JID]): JID of the missing users. |
252 for user in users: | 228 # """ |
253 nick = self.getRoomNickOfUser(room, user, secure) | 229 # nicks = [] |
254 if nick is None: | 230 # missing = [] |
255 missing.append(user) | 231 # for user in users: |
256 else: | 232 # nick = self.getRoomNickOfUser(room, user, secure) |
257 nicks.append(nick) | 233 # if nick is None: |
258 return nicks, missing | 234 # missing.append(user) |
235 # else: | |
236 # nicks.append(nick) | |
237 # return nicks, missing | |
259 | 238 |
260 def _configureRoom(self, room_jid_s, profile_key=C.PROF_KEY_NONE): | 239 def _configureRoom(self, room_jid_s, profile_key=C.PROF_KEY_NONE): |
261 d = self.configureRoom(jid.JID(room_jid_s), profile_key) | 240 client = self.host.getClient(profile_key) |
241 d = self.configureRoom(client, jid.JID(room_jid_s)) | |
262 d.addCallback(lambda xmlui: xmlui.toXml()) | 242 d.addCallback(lambda xmlui: xmlui.toXml()) |
263 return d | 243 return d |
264 | 244 |
265 def _configureRoomMenu(self, menu_data, profile): | 245 def _configureRoomMenu(self, menu_data, profile): |
266 """Return room configuration form | 246 """Return room configuration form |
267 | 247 |
268 @param menu_data: %(menu_data)s | 248 @param menu_data: %(menu_data)s |
269 @param profile: %(doc_profile)s | 249 @param profile: %(doc_profile)s |
270 """ | 250 """ |
251 client = self.host.getClient(profile) | |
271 try: | 252 try: |
272 room_jid = jid.JID(menu_data['room_jid']) | 253 room_jid = jid.JID(menu_data['room_jid']) |
273 except KeyError: | 254 except KeyError: |
274 log.error(_("room_jid key is not present !")) | 255 log.error(_("room_jid key is not present !")) |
275 return defer.fail(exceptions.DataError) | 256 return defer.fail(exceptions.DataError) |
276 | 257 |
277 def xmluiReceived(xmlui): | 258 def xmluiReceived(xmlui): |
278 return {"xmlui": xmlui.toXml()} | 259 return {"xmlui": xmlui.toXml()} |
279 return self.configureRoom(room_jid, profile).addCallback(xmluiReceived) | 260 return self.configureRoom(client, room_jid).addCallback(xmluiReceived) |
280 | 261 |
281 def configureRoom(self, room_jid, profile_key=C.PROF_KEY_NONE): | 262 def configureRoom(self, client, room_jid): |
282 """return the room configuration form | 263 """return the room configuration form |
283 | 264 |
284 @param room: jid of the room to configure | 265 @param room: jid of the room to configure |
285 @param profile_key: %(doc_profile_key)s | |
286 @return: configuration form as XMLUI | 266 @return: configuration form as XMLUI |
287 """ | 267 """ |
288 profile = self.getProfileAssertInRoom(room_jid, profile_key) | 268 self.checkRoomJoined(client, room_jid) |
289 | 269 |
290 def config2XMLUI(result): | 270 def config2XMLUI(result): |
291 if not result: | 271 if not result: |
292 return "" | 272 return "" |
293 session_id, session_data = self._sessions.newSession(profile=profile) | 273 session_id, session_data = self._sessions.newSession(profile=client.profile) |
294 session_data["room_jid"] = room_jid | 274 session_data["room_jid"] = room_jid |
295 xmlui = xml_tools.dataForm2XMLUI(result, submit_id=self.__submit_conf_id) | 275 xmlui = xml_tools.dataForm2XMLUI(result, submit_id=self.__submit_conf_id) |
296 xmlui.session_id = session_id | 276 xmlui.session_id = session_id |
297 return xmlui | 277 return xmlui |
298 | 278 |
299 d = self.clients[profile].getConfiguration(room_jid) | 279 d = client._muc_client.getConfiguration(room_jid) |
300 d.addCallback(config2XMLUI) | 280 d.addCallback(config2XMLUI) |
301 return d | 281 return d |
302 | 282 |
303 def _submitConfiguration(self, raw_data, profile): | 283 def _submitConfiguration(self, raw_data, profile): |
284 client = self.host.getClient(profile) | |
304 try: | 285 try: |
305 session_data = self._sessions.profileGet(raw_data["session_id"], profile) | 286 session_data = self._sessions.profileGet(raw_data["session_id"], profile) |
306 except KeyError: | 287 except KeyError: |
307 log.warning(D_("Session ID doesn't exist, session has probably expired.")) | 288 log.warning(D_("Session ID doesn't exist, session has probably expired.")) |
308 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration failed')) | 289 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration failed')) |
309 _dialog.addText(D_("Session ID doesn't exist, session has probably expired.")) | 290 _dialog.addText(D_("Session ID doesn't exist, session has probably expired.")) |
310 return defer.succeed({'xmlui': _dialog.toXml()}) | 291 return defer.succeed({'xmlui': _dialog.toXml()}) |
311 | 292 |
312 data = xml_tools.XMLUIResult2DataFormResult(raw_data) | 293 data = xml_tools.XMLUIResult2DataFormResult(raw_data) |
313 d = self.clients[profile].configure(session_data['room_jid'], data) | 294 d = client._muc_client.configure(session_data['room_jid'], data) |
314 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration succeed')) | 295 _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration succeed')) |
315 _dialog.addText(D_("The new settings have been saved.")) | 296 _dialog.addText(D_("The new settings have been saved.")) |
316 d.addCallback(lambda ignore: {'xmlui': _dialog.toXml()}) | 297 d.addCallback(lambda ignore: {'xmlui': _dialog.toXml()}) |
317 del self._sessions[raw_data["session_id"]] | 298 del self._sessions[raw_data["session_id"]] |
318 return d | 299 return d |
319 | 300 |
320 def isNickInRoom(self, room_jid, nick, profile): | 301 def isNickInRoom(self, client, room_jid, nick): |
321 """Tell if a nick is currently present in a room""" | 302 """Tell if a nick is currently present in a room""" |
322 profile = self.getProfileAssertInRoom(room_jid, profile) | 303 self.checkRoomJoined(client, room_jid) |
323 return self.clients[profile].joined_rooms[room_jid].inRoster(muc.User(nick)) | 304 return client._muc_client.joined_rooms[room_jid].inRoster(muc.User(nick)) |
324 | |
325 def getRoomsSubjects(self, profile_key=C.PROF_KEY_NONE): | |
326 """Return received subjects of rooms""" | |
327 # FIXME: to be removed | |
328 profile = self.host.memory.getProfileName(profile_key) | |
329 if not self.checkClient(profile): | |
330 return [] | |
331 return self.clients[profile].rec_subjects.values() | |
332 | 305 |
333 @defer.inlineCallbacks | 306 @defer.inlineCallbacks |
334 def getMUCService(self, jid_=None, profile=C.PROF_KEY_NONE): | 307 def getMUCService(self, jid_=None, profile=C.PROF_KEY_NONE): |
335 """Return first found MUC service of an entity | 308 """Return first found MUC service of an entity |
336 | 309 |
347 muc_service = service | 320 muc_service = service |
348 break | 321 break |
349 defer.returnValue(muc_service) | 322 defer.returnValue(muc_service) |
350 | 323 |
351 def _getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE): | 324 def _getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE): |
352 return self.getUniqueName(muc_service or None, profile_key).full() | 325 client = self.host.getClient(profile_key) |
353 | 326 return self.getUniqueName(client, muc_service or None).full() |
354 def getUniqueName(self, muc_service=None, profile_key=C.PROF_KEY_NONE): | 327 |
328 def getUniqueName(self, client, muc_service=None): | |
355 """Return unique name for a room, avoiding collision | 329 """Return unique name for a room, avoiding collision |
356 | 330 |
357 @param muc_service (jid.JID) : leave empty string to use the default service | 331 @param muc_service (jid.JID) : leave empty string to use the default service |
358 @return: jid.JID (unique room bare JID) | 332 @return: jid.JID (unique room bare JID) |
359 """ | 333 """ |
360 # TODO: we should use #RFC-0045 10.1.4 when available here | 334 # TODO: we should use #RFC-0045 10.1.4 when available here |
361 client = self.host.getClient(profile_key) | 335 room_name = unicode(uuid.uuid4()) |
362 room_name = uuid.uuid1() | |
363 if muc_service is None: | 336 if muc_service is None: |
364 try: | 337 try: |
365 muc_service = client.muc_service | 338 muc_service = client.muc_service |
366 except AttributeError: | 339 except AttributeError: |
367 raise NotReadyYet("Main server MUC service has not been checked yet") | 340 raise exceptions.NotReady(u"Main server MUC service has not been checked yet") |
368 if muc_service is None: | 341 if muc_service is None: |
369 log.warning(_("No MUC service found on main server")) | 342 log.warning(_("No MUC service found on main server")) |
370 raise exceptions.FeatureNotFound | 343 raise exceptions.FeatureNotFound |
371 | 344 |
372 muc_service = muc_service.userhost() | 345 muc_service = muc_service.userhost() |
373 return jid.JID("%s@%s" % (room_name, muc_service)) | 346 return jid.JID(u"{}@{}".format(room_name, muc_service)) |
374 | 347 |
375 def getDefaultMUC(self): | 348 def getDefaultMUC(self): |
376 """Return the default MUC. | 349 """Return the default MUC. |
377 | 350 |
378 @return: unicode | 351 @return: unicode |
379 """ | 352 """ |
380 return self.host.memory.getConfig(CONFIG_SECTION, 'default_muc', default_conf['default_muc']) | 353 return self.host.memory.getConfig(CONFIG_SECTION, 'default_muc', default_conf['default_muc']) |
381 | 354 |
382 def join(self, client, room_jid, nick, options): | 355 def join(self, client, room_jid, nick, options): |
383 def _errDeferred(exc_obj=Exception, txt='Error while joining room'): | 356 def _errDeferred(exc_obj=Exception, txt=u'Error while joining room'): |
384 d = defer.Deferred() | 357 d = defer.Deferred() |
385 d.errback(exc_obj(txt)) | 358 d.errback(exc_obj(txt)) |
386 return d | 359 return d |
387 | 360 |
388 if room_jid in self.clients[client.profile].joined_rooms: | 361 if room_jid in client._muc_client.joined_rooms: |
389 log.warning(_(u'%(profile)s is already in room %(room_jid)s') % {'profile': client.profile, 'room_jid': room_jid.userhost()}) | 362 log.warning(_(u'{profile} is already in room {room_jid}').format(profile=client.profile, room_jid = room_jid.userhost())) |
390 return _errDeferred(AlreadyJoinedRoom, D_(u"The room has already been joined")) | 363 return defer.fail(exceptions.ConflictError(_(u"The room has already been joined"))) |
391 log.info(_(u"[%(profile)s] is joining room %(room)s with nick %(nick)s") % {'profile': client.profile, 'room': room_jid.userhost(), 'nick': nick}) | 364 log.info(_(u"[{profile}] is joining room {room} with nick {nick}").format(profile=client.profile, room=room_jid.userhost(), nick=nick)) |
392 | 365 |
393 password = options["password"] if "password" in options else None | 366 password = options["password"] if "password" in options else None |
394 | 367 |
395 return self.clients[client.profile].join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, callbackKeywords={'profile': client.profile}, errbackArgs=[room_jid, nick, password, client.profile]) | 368 return client._muc_client.join(room_jid, nick, password).addCallbacks(self._joinCb, self._joinEb, callbackKeywords={'client': client}, errbackArgs=[client, room_jid, nick, password]) |
396 | 369 |
397 def _join(self, room_jid_s, nick, options=None, profile_key=C.PROF_KEY_NONE): | 370 def _join(self, room_jid_s, nick, options=None, profile_key=C.PROF_KEY_NONE): |
398 """join method used by bridge | 371 """join method used by bridge |
399 | 372 |
400 @return: unicode (the room bare) | 373 @return: unicode (the room bare) |
414 room_jid = self.getUniqueName(profile_key=client.profile) | 387 room_jid = self.getUniqueName(profile_key=client.profile) |
415 # TODO: error management + signal in bridge | 388 # TODO: error management + signal in bridge |
416 d = self.join(client, room_jid, nick, options) | 389 d = self.join(client, room_jid, nick, options) |
417 return d.addCallback(lambda room: room.roomJID.userhost()) | 390 return d.addCallback(lambda room: room.roomJID.userhost()) |
418 | 391 |
419 def nick(self, room_jid, nick, profile_key): | 392 def _nick(self, room_jid_s, nick, profile_key=C.PROF_KEY_NONE): |
420 profile = self.getProfileAssertInRoom(room_jid, profile_key) | 393 client = self.host.getClient(profile_key) |
421 return self.clients[profile].nick(room_jid, nick) | 394 return self.nick(client, jid.JID(room_jid_s), nick) |
422 | 395 |
423 def leave(self, room_jid, profile_key): | 396 def nick(self, client, room_jid, nick): |
424 profile = self.getProfileAssertInRoom(room_jid, profile_key) | |
425 return self.clients[profile].leave(room_jid) | |
426 | |
427 def subject(self, room_jid, subject, profile_key): | |
428 profile = self.getProfileAssertInRoom(room_jid, profile_key) | |
429 return self.clients[profile].subject(room_jid, subject) | |
430 | |
431 def mucNick(self, room_jid_s, nick, profile_key=C.PROF_KEY_NONE): | |
432 """Change nickname in a room""" | 397 """Change nickname in a room""" |
433 return self.nick(jid.JID(room_jid_s), nick, profile_key) | 398 self.checkRoomJoined(client, room_jid) |
434 | 399 return client._muc_client.nick(room_jid, nick) |
435 def mucLeave(self, room_jid_s, profile_key=C.PROF_KEY_NONE): | 400 |
436 """Leave a room""" | 401 def _leave(self, room_jid, profile_key): |
437 return self.leave(jid.JID(room_jid_s), profile_key) | 402 client = self.host.getClient(profile_key) |
403 return self.leave(client, jid.JID(room_jid)) | |
404 | |
405 def leave(self, client, room_jid): | |
406 self.checkRoomJoined(client, room_jid) | |
407 return client._muc_client.leave(room_jid) | |
408 | |
409 def subject(self, client, room_jid, subject): | |
410 self.checkRoomJoined(client, room_jid) | |
411 return client._muc_client.subject(room_jid, subject) | |
438 | 412 |
439 def getHandler(self, profile): | 413 def getHandler(self, profile): |
440 self.clients[profile] = SatMUCClient(self) | 414 # create a MUC client and associate it with profile' session |
441 return self.clients[profile] | 415 client = self.host.getClient(profile) |
442 | 416 muc_client = client._muc_client = SatMUCClient(self) |
443 def profileDisconnected(self, profile): | 417 return muc_client |
444 try: | 418 |
445 del self.clients[profile] | 419 def kick(self, client, nick, room_jid, options=None): |
446 except KeyError: | |
447 pass | |
448 | |
449 def kick(self, nick, room_jid, options={}, profile_key=C.PROF_KEY_NONE): | |
450 """ | 420 """ |
451 Kick a participant from the room | 421 Kick a participant from the room |
452 @param nick (str): nick of the user to kick | 422 @param nick (str): nick of the user to kick |
453 @param room_jid_s (JID): jid of the room | 423 @param room_jid_s (JID): jid of the room |
454 @param options (dict): attribute with extra info (reason, password) as in #XEP-0045 | 424 @param options (dict): attribute with extra info (reason, password) as in #XEP-0045 |
455 @param profile_key (str): %(doc_profile_key)s | 425 """ |
456 """ | 426 if options is None: |
457 profile = self.getProfileAssertInRoom(room_jid, profile_key) | 427 options = {} |
458 return self.clients[profile].kick(room_jid, nick, reason=options.get('reason', None)) | 428 self.checkRoomJoined(client, room_jid) |
459 | 429 return client._muc_client.kick(room_jid, nick, reason=options.get('reason', None)) |
460 def ban(self, entity_jid, room_jid, options={}, profile_key=C.PROF_KEY_NONE): | 430 |
461 """ | 431 def ban(self, client, entity_jid, room_jid, options=None): |
462 Ban an entity from the room | 432 """Ban an entity from the room |
433 | |
463 @param entity_jid (JID): bare jid of the entity to be banned | 434 @param entity_jid (JID): bare jid of the entity to be banned |
464 @param room_jid_s (JID): jid of the room | 435 @param room_jid (JID): jid of the room |
465 @param options: attribute with extra info (reason, password) as in #XEP-0045 | 436 @param options: attribute with extra info (reason, password) as in #XEP-0045 |
466 @param profile_key (str): %(doc_profile_key)s | 437 """ |
467 """ | 438 self.checkRoomJoined(client, room_jid) |
468 assert(not entity_jid.resource) | 439 if options is None: |
469 assert(not room_jid.resource) | 440 options = {} |
470 profile = self.getProfileAssertInRoom(room_jid, profile_key) | 441 assert not entity_jid.resource |
471 return self.clients[profile].ban(room_jid, entity_jid, reason=options.get('reason', None)) | 442 assert not room_jid.resource |
472 | 443 return client._muc_client.ban(room_jid, entity_jid, reason=options.get('reason', None)) |
473 def affiliate(self, entity_jid, room_jid, options=None, profile_key=C.PROF_KEY_NONE): | 444 |
474 """ | 445 def affiliate(self, client, entity_jid, room_jid, options): |
475 Change the affiliation of an entity | 446 """Change the affiliation of an entity |
447 | |
476 @param entity_jid (JID): bare jid of the entity | 448 @param entity_jid (JID): bare jid of the entity |
477 @param room_jid_s (JID): jid of the room | 449 @param room_jid_s (JID): jid of the room |
478 @param options: attribute with extra info (reason, nick) as in #XEP-0045 | 450 @param options: attribute with extra info (reason, nick) as in #XEP-0045 |
479 @param profile_key (str): %(doc_profile_key)s | 451 """ |
480 """ | 452 self.checkRoomJoined(client, room_jid) |
481 assert(not entity_jid.resource) | 453 assert not entity_jid.resource |
482 assert(not room_jid.resource) | 454 assert not room_jid.resource |
483 assert('affiliation' in options) | 455 assert 'affiliation' in options |
484 profile = self.getProfileAssertInRoom(room_jid, profile_key) | |
485 # TODO: handles reason and nick | 456 # TODO: handles reason and nick |
486 return self.clients[profile].modifyAffiliationList(room_jid, [entity_jid], options['affiliation']) | 457 return client._muc_client.modifyAffiliationList(room_jid, [entity_jid], options['affiliation']) |
487 | 458 |
488 # Text commands # | 459 # Text commands # |
489 | 460 |
490 def cmd_nick(self, client, mess_data): | 461 def cmd_nick(self, client, mess_data): |
491 """change nickname | 462 """change nickname |
506 @command (all): JID | 477 @command (all): JID |
507 - JID: room to join (on the same service if full jid is not specified) | 478 - JID: room to join (on the same service if full jid is not specified) |
508 """ | 479 """ |
509 if mess_data["unparsed"].strip(): | 480 if mess_data["unparsed"].strip(): |
510 room_jid = self.host.plugins[C.TEXT_CMDS].getRoomJID(mess_data["unparsed"].strip(), mess_data["to"].host) | 481 room_jid = self.host.plugins[C.TEXT_CMDS].getRoomJID(mess_data["unparsed"].strip(), mess_data["to"].host) |
511 nick = (self.getRoomNick(room_jid, client.profile) or | 482 nick = (self.getRoomNick(client, room_jid) or |
512 self.host.getClient(client.profile).jid.user) | 483 self.host.getClient(client.profile).jid.user) |
513 self.join(client, room_jid, nick, {}) | 484 self.join(client, room_jid, nick, {}) |
514 | 485 |
515 return False | 486 return False |
516 | 487 |
544 - ROOM_NICK: the nick of the person to kick | 515 - ROOM_NICK: the nick of the person to kick |
545 """ | 516 """ |
546 options = mess_data["unparsed"].strip().split() | 517 options = mess_data["unparsed"].strip().split() |
547 try: | 518 try: |
548 nick = options[0] | 519 nick = options[0] |
549 assert(self.isNickInRoom(mess_data["to"], nick, client.profile)) | 520 assert self.isNickInRoom(client, mess_data["to"], nick) |
550 except (IndexError, AssertionError): | 521 except (IndexError, AssertionError): |
551 feedback = _(u"You must provide a member's nick to kick.") | 522 feedback = _(u"You must provide a member's nick to kick.") |
552 self.host.plugins[C.TEXT_CMDS].feedBack(client, feedback, mess_data) | 523 self.host.plugins[C.TEXT_CMDS].feedBack(client, feedback, mess_data) |
553 return False | 524 return False |
554 | 525 |
637 """ | 608 """ |
638 subject = mess_data["unparsed"].strip() | 609 subject = mess_data["unparsed"].strip() |
639 | 610 |
640 if subject: | 611 if subject: |
641 room = mess_data["to"] | 612 room = mess_data["to"] |
642 self.subject(room, subject, client.profile) | 613 self.subject(client, room, subject) |
643 | 614 |
644 return False | 615 return False |
645 | 616 |
646 def cmd_topic(self, client, mess_data): | 617 def cmd_topic(self, client, mess_data): |
647 """just a synonym of /title | 618 """just a synonym of /title |
653 | 624 |
654 def _whois(self, client, whois_msg, mess_data, target_jid): | 625 def _whois(self, client, whois_msg, mess_data, target_jid): |
655 """ Add MUC user information to whois """ | 626 """ Add MUC user information to whois """ |
656 if mess_data['type'] != "groupchat": | 627 if mess_data['type'] != "groupchat": |
657 return | 628 return |
658 if target_jid.userhostJID() not in self.clients[client.profile].joined_rooms: | 629 if target_jid.userhostJID() not in client._muc_client.joined_rooms: |
659 log.warning(_("This room has not been joined")) | 630 log.warning(_("This room has not been joined")) |
660 return | 631 return |
661 if not target_jid.resource: | 632 if not target_jid.resource: |
662 return | 633 return |
663 user = self.clients[client.profile].joined_rooms[target_jid.userhostJID()].getUser(target_jid.resource) | 634 user = client._muc_client.joined_rooms[target_jid.userhostJID()].getUser(target_jid.resource) |
664 whois_msg.append(_("Nickname: %s") % user.nick) | 635 whois_msg.append(_("Nickname: %s") % user.nick) |
665 if user.entity: | 636 if user.entity: |
666 whois_msg.append(_("Entity: %s") % user.entity) | 637 whois_msg.append(_("Entity: %s") % user.entity) |
667 if user.affiliation != 'none': | 638 if user.affiliation != 'none': |
668 whois_msg.append(_("Affiliation: %s") % user.affiliation) | 639 whois_msg.append(_("Affiliation: %s") % user.affiliation) |
673 if user.show: | 644 if user.show: |
674 whois_msg.append(_("Show: %s") % user.show) | 645 whois_msg.append(_("Show: %s") % user.show) |
675 | 646 |
676 def presenceTrigger(self, presence_elt, client): | 647 def presenceTrigger(self, presence_elt, client): |
677 # XXX: shouldn't it be done by the server ?!! | 648 # XXX: shouldn't it be done by the server ?!! |
678 muc_client = self.clients[client.profile] | 649 muc_client = client._muc_client |
679 for room_jid, room in muc_client.joined_rooms.iteritems(): | 650 for room_jid, room in muc_client.joined_rooms.iteritems(): |
680 elt = copy.deepcopy(presence_elt) | 651 elt = copy.deepcopy(presence_elt) |
681 elt['to'] = room_jid.userhost() + '/' + room.nick | 652 elt['to'] = room_jid.userhost() + '/' + room.nick |
682 client.presence.send(elt) | 653 client.presence.send(elt) |
683 return True | 654 return True |
684 | 655 |
685 | 656 |
686 class SatMUCClient (muc.MUCClient): | 657 class SatMUCClient(muc.MUCClient): |
687 implements(iwokkel.IDisco) | 658 implements(iwokkel.IDisco) |
688 | 659 |
689 def __init__(self, plugin_parent): | 660 def __init__(self, plugin_parent): |
690 self.plugin_parent = plugin_parent | 661 self.plugin_parent = plugin_parent |
691 self.host = plugin_parent.host | 662 self.host = plugin_parent.host |
821 # we left the room | 792 # we left the room |
822 room_jid_s = room.roomJID.userhost() | 793 room_jid_s = room.roomJID.userhost() |
823 log.info(_(u"Room ({room}) left ({profile})").format( | 794 log.info(_(u"Room ({room}) left ({profile})").format( |
824 room = room_jid_s, profile = self.parent.profile)) | 795 room = room_jid_s, profile = self.parent.profile)) |
825 self.host.memory.delEntityCache(room.roomJID, profile_key=self.parent.profile) | 796 self.host.memory.delEntityCache(room.roomJID, profile_key=self.parent.profile) |
826 self.host.bridge.roomLeft(room.roomJID.userhost(), self.parent.profile) | 797 self.host.bridge.mucRoomLeft(room.roomJID.userhost(), self.parent.profile) |
827 else: | 798 else: |
828 log.debug(_(u"user {nick} left room {room_id}").format(nick=user.nick, room_id=room.occupantJID.userhost())) | 799 log.debug(_(u"user {nick} left room {room_id}").format(nick=user.nick, room_id=room.occupantJID.userhost())) |
829 extra = {'info_type': ROOM_USER_LEFT, | 800 extra = {'info_type': ROOM_USER_LEFT, |
830 'user_affiliation': user.affiliation, | 801 'user_affiliation': user.affiliation, |
831 'user_role': user.role, | 802 'user_role': user.role, |
845 } | 816 } |
846 self.host.messageAddToHistory(mess_data, self.parent) | 817 self.host.messageAddToHistory(mess_data, self.parent) |
847 self.host.messageSendToBridge(mess_data, self.parent) | 818 self.host.messageSendToBridge(mess_data, self.parent) |
848 | 819 |
849 def userChangedNick(self, room, user, new_nick): | 820 def userChangedNick(self, room, user, new_nick): |
850 self.host.bridge.roomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile) | 821 self.host.bridge.mucRoomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile) |
851 | 822 |
852 def userUpdatedStatus(self, room, user, show, status): | 823 def userUpdatedStatus(self, room, user, show, status): |
853 self.host.bridge.presenceUpdate(room.roomJID.userhost() + '/' + user.nick, show or '', 0, {C.PRESENCE_STATUSES_DEFAULT: status or ''}, self.parent.profile) | 824 self.host.bridge.presenceUpdate(room.roomJID.userhost() + '/' + user.nick, show or '', 0, {C.PRESENCE_STATUSES_DEFAULT: status or ''}, self.parent.profile) |
854 | 825 |
855 ## messages ## | 826 ## messages ## |
921 | 892 |
922 def subject(self, room, subject): | 893 def subject(self, room, subject): |
923 return muc.MUCClientProtocol.subject(self, room, subject) | 894 return muc.MUCClientProtocol.subject(self, room, subject) |
924 | 895 |
925 def _historyCb(self, dummy, room): | 896 def _historyCb(self, dummy, room): |
926 self.host.bridge.roomJoined( | 897 self.host.bridge.mucRoomJoined( |
927 room.roomJID.userhost(), | 898 room.roomJID.userhost(), |
928 XEP_0045._getOccupants(room), | 899 XEP_0045._getOccupants(room), |
929 room.nick, | 900 room.nick, |
930 room.subject, | 901 room.subject, |
931 self.parent.profile) | 902 self.parent.profile) |
952 room._room_ok = False | 923 room._room_ok = False |
953 room._history_d.addCallbacks(self._historyCb, self._historyEb, [room], errbackArgs=[room]) | 924 room._history_d.addCallbacks(self._historyCb, self._historyEb, [room], errbackArgs=[room]) |
954 else: | 925 else: |
955 # the subject has been changed | 926 # the subject has been changed |
956 log.debug(_(u"New subject for room ({room_id}): {subject}").format(room_id = room.roomJID.full(), subject = subject)) | 927 log.debug(_(u"New subject for room ({room_id}): {subject}").format(room_id = room.roomJID.full(), subject = subject)) |
957 self.host.bridge.roomNewSubject(room.roomJID.userhost(), subject, self.parent.profile) | 928 self.host.bridge.mucRoomNewSubject(room.roomJID.userhost(), subject, self.parent.profile) |
958 | 929 |
959 ## disco ## | 930 ## disco ## |
960 | 931 |
961 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 932 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): |
962 return [disco.DiscoFeature(NS_MUC)] | 933 return [disco.DiscoFeature(NS_MUC)] |