Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0045.py @ 944:e1842ebcb2f3
core, plugin XEP-0115: discovery refactoring:
- hashing algorithm of XEP-0115 has been including in core
- our own hash is still calculated by XEP-0115 and can be regenerated with XEP_0115.recalculateHash
- old discovery methods have been removed. Now the following methods are used:
- hasFeature: tell if a feature is available for an entity
- getDiscoInfos: self explaining
- getDiscoItems: self explaining
- findServiceEntities: return all available items of an entity which given (category, type)
- findFeaturesSet: search for a set of features in entity + entity's items
all these methods are asynchronous, and manage cache automatically
- XEP-0115 manage in a better way hashes, and now use a trigger for presence instead of monkey patch
- new FeatureNotFound exception, when we want to do something which is not available
- refactored client initialisation sequence, removed client.initialized Deferred
- added constant APP_URL
- test_plugin_xep_0033.py has been temporarly deactivated, the time to adapt it
- lot of cleaning
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 28 Mar 2014 18:07:22 +0100 |
parents | 73873e9b56f7 |
children | 5e8e8a034411 |
comparison
equal
deleted
inserted
replaced
943:71926ec2114d | 944:e1842ebcb2f3 |
---|---|
29 import uuid | 29 import uuid |
30 | 30 |
31 from wokkel import muc | 31 from wokkel import muc |
32 from sat.tools import xml_tools | 32 from sat.tools import xml_tools |
33 | 33 |
34 | |
35 try: | |
36 from twisted.words.protocols.xmlstream import XMPPHandler | |
37 except ImportError: | |
38 from wokkel.subprotocols import XMPPHandler | |
39 | 34 |
40 PLUGIN_INFO = { | 35 PLUGIN_INFO = { |
41 "name": "XEP 0045 Plugin", | 36 "name": "XEP 0045 Plugin", |
42 "import_name": "XEP-0045", | 37 "import_name": "XEP-0045", |
43 "type": "XEP", | 38 "type": "XEP", |
51 | 46 |
52 | 47 |
53 class UnknownRoom(Exception): | 48 class UnknownRoom(Exception): |
54 pass | 49 pass |
55 | 50 |
51 class NotReadyYet(Exception): | |
52 pass | |
56 | 53 |
57 class XEP_0045(object): | 54 class XEP_0045(object): |
58 # TODO: this plugin is messy, need a big cleanup/refactoring | 55 # TODO: this plugin is messy, need a big cleanup/refactoring |
59 | 56 |
60 def __init__(self, host): | 57 def __init__(self, host): |
65 host.bridge.addMethod("joinMUC", ".plugin", in_sign='ssa{ss}s', out_sign='s', method=self._join) | 62 host.bridge.addMethod("joinMUC", ".plugin", in_sign='ssa{ss}s', out_sign='s', method=self._join) |
66 host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick) | 63 host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick) |
67 host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.mucLeave, async=True) | 64 host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.mucLeave, async=True) |
68 host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sass)', method=self.getRoomsJoined) | 65 host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sass)', method=self.getRoomsJoined) |
69 host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects) | 66 host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects) |
70 host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self.getUniqueName) | 67 host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self._getUniqueName) |
71 host.bridge.addMethod("configureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async=True) | 68 host.bridge.addMethod("configureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async=True) |
72 host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') # args: room_jid, room_nicks, user_nick, profile | 69 host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') # args: room_jid, room_nicks, user_nick, profile |
73 host.bridge.addSignal("roomLeft", ".plugin", signature='ss') # args: room_jid, profile | 70 host.bridge.addSignal("roomLeft", ".plugin", signature='ss') # args: room_jid, profile |
74 host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile | 71 host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile |
75 host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile | 72 host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile |
80 self.host.plugins[C.TEXT_CMDS].registerTextCommands(self) | 77 self.host.plugins[C.TEXT_CMDS].registerTextCommands(self) |
81 self.host.plugins[C.TEXT_CMDS].addWhoIsCb(self._whois, 100) | 78 self.host.plugins[C.TEXT_CMDS].addWhoIsCb(self._whois, 100) |
82 except KeyError: | 79 except KeyError: |
83 info(_("Text commands not available")) | 80 info(_("Text commands not available")) |
84 | 81 |
82 def profileConnected(self, profile): | |
83 def assign_service(service): | |
84 client = self.host.getClient(profile) | |
85 client.muc_service = service | |
86 self.getMUCService(profile_key=profile).addCallback(assign_service) | |
87 | |
85 def __check_profile(self, profile): | 88 def __check_profile(self, profile): |
86 """check if profile is used and connected | 89 """check if profile is used and connected |
90 | |
87 if profile known but disconnected, remove it from known profiles | 91 if profile known but disconnected, remove it from known profiles |
88 @param profile: profile to check | 92 @param profile: profile to check |
89 @return: True if the profile is known and connected, else False""" | 93 @return: True if the profile is known and connected, else False""" |
90 if not profile or profile not in self.clients or not self.host.isConnected(profile): | 94 if not profile or profile not in self.clients or not self.host.isConnected(profile): |
91 error(_('Unknown or disconnected profile (%s)') % profile) | 95 error(_('Unknown or disconnected profile (%s)') % profile) |
134 result.append((room.roomJID.userhost(), [user.nick for user in room.roster.values()], room.nick)) | 138 result.append((room.roomJID.userhost(), [user.nick for user in room.roster.values()], room.nick)) |
135 return result | 139 return result |
136 | 140 |
137 def getRoomNick(self, room_jid_s, profile_key=C.PROF_KEY_NONE): | 141 def getRoomNick(self, room_jid_s, profile_key=C.PROF_KEY_NONE): |
138 """return nick used in room by user | 142 """return nick used in room by user |
143 | |
139 @param room_jid_s: unicode room id | 144 @param room_jid_s: unicode room id |
140 @profile_key: profile | 145 @profile_key: profile |
141 @return: nick or empty string in case of error""" | 146 @return: nick or empty string in case of error""" |
142 profile = self.host.memory.getProfileName(profile_key) | 147 profile = self.host.memory.getProfileName(profile_key) |
143 if not self.__check_profile(profile) or room_jid_s not in self.clients[profile].joined_rooms: | 148 if not self.__check_profile(profile) or room_jid_s not in self.clients[profile].joined_rooms: |
144 return '' | 149 return '' |
145 return self.clients[profile].joined_rooms[room_jid_s].nick | 150 return self.clients[profile].joined_rooms[room_jid_s].nick |
146 | 151 |
147 def getRoomNickOfUser(self, room, user_jid, secure=True): | 152 def getRoomNickOfUser(self, room, user_jid, secure=True): |
148 """Returns the nick of the given user in the room. | 153 """Returns the nick of the given user in the room. |
154 | |
149 @room: instance of wokkel.muc.Room | 155 @room: instance of wokkel.muc.Room |
150 @user: JID or unicode (JID userhost). | 156 @user: JID or unicode (JID userhost). |
151 @param secure: set to True for a secure check | 157 @param secure: set to True for a secure check |
152 @return: the nick or None if the user didn't join the room. | 158 @return: the nick or None if the user didn't join the room. |
153 """ | 159 """ |
164 return user.nick | 170 return user.nick |
165 return None | 171 return None |
166 | 172 |
167 def getRoomNicksOfUsers(self, room, users=[], secure=True): | 173 def getRoomNicksOfUsers(self, room, users=[], secure=True): |
168 """Returns the nicks of the given users in the room. | 174 """Returns the nicks of the given users in the room. |
175 | |
169 @room: instance of wokkel.muc.Room | 176 @room: instance of wokkel.muc.Room |
170 @users: list of JID or unicode (JID userhost). | 177 @users: list of JID or unicode (JID userhost). |
171 @param secure: set to True for a secure check | 178 @param secure: set to True for a secure check |
172 @return: (x, y) with x a list containing the nicks of | 179 @return: (x, y) with x a list containing the nicks of |
173 the users who are in the room, and y the missing users. | 180 the users who are in the room, and y the missing users. |
187 d.addCallback(lambda xmlui: xmlui.toXml()) | 194 d.addCallback(lambda xmlui: xmlui.toXml()) |
188 return d | 195 return d |
189 | 196 |
190 def configureRoom(self, room_jid, profile_key=C.PROF_KEY_NONE): | 197 def configureRoom(self, room_jid, profile_key=C.PROF_KEY_NONE): |
191 """ return the room configuration form | 198 """ return the room configuration form |
199 | |
192 @param room: jid of the room to configure | 200 @param room: jid of the room to configure |
193 @param profile_key: %(doc_profile_key)s | 201 @param profile_key: %(doc_profile_key)s |
194 @return: configuration form as XMLUI | 202 @return: configuration form as XMLUI |
195 | |
196 """ | 203 """ |
197 profile = self.host.memory.getProfileName(profile_key) | 204 profile = self.host.memory.getProfileName(profile_key) |
198 if not self.__check_profile(profile): | 205 if not self.__check_profile(profile): |
199 raise exceptions.ProfileUnknownError("Unknown or disconnected profile") | 206 raise exceptions.ProfileUnknownError("Unknown or disconnected profile") |
200 if room_jid.userhost() not in self.clients[profile].joined_rooms: | 207 if room_jid.userhost() not in self.clients[profile].joined_rooms: |
241 profile = self.host.memory.getProfileName(profile_key) | 248 profile = self.host.memory.getProfileName(profile_key) |
242 if not self.__check_profile(profile): | 249 if not self.__check_profile(profile): |
243 return [] | 250 return [] |
244 return self.clients[profile].rec_subjects.values() | 251 return self.clients[profile].rec_subjects.values() |
245 | 252 |
246 def getMUCService(self, profile): | 253 @defer.inlineCallbacks |
247 """Return the MUC service or None""" | 254 def getMUCService(self, jid_=None, profile_key=C.PROF_KEY_NONE): |
255 """Return first found MUC service of an entity | |
256 | |
257 @param jid_: entity which may have a MUC service, or None for our own server | |
258 @param profile_key: %(doc_profile_key)s | |
259 """ | |
248 muc_service = None | 260 muc_service = None |
249 for service in self.host.memory.getServerServiceEntities("conference", "text", profile=profile): | 261 services = yield self.host.findServiceEntities("conference", "text", jid_, profile_key=profile_key) |
262 for service in services: | |
250 if not ".irc." in service.userhost(): | 263 if not ".irc." in service.userhost(): |
251 #FIXME: | 264 # FIXME: |
252 #This awfull ugly hack is here to avoid an issue with openfire: the irc gateway | 265 # This ugly hack is here to avoid an issue with openfire: the IRC gateway |
253 #use "conference/text" identity (instead of "conference/irc"), there is certainly a better way | 266 # use "conference/text" identity (instead of "conference/irc") |
254 #to manage this, but this hack fill do it for test purpose | |
255 muc_service = service | 267 muc_service = service |
256 break | 268 break |
257 return muc_service | 269 defer.returnValue(muc_service) |
258 | 270 |
259 def getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE): | 271 def _getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE): |
260 """Return unique name for room, avoiding collision | 272 return self.getUniqueName(muc_service or None, profile_key).full() |
273 | |
274 def getUniqueName(self, muc_service=None, profile_key=C.PROF_KEY_NONE): | |
275 """Return unique name for a room, avoiding collision | |
276 | |
261 @param muc_service: leave empty string to use the default service | 277 @param muc_service: leave empty string to use the default service |
262 @return: unique room userhost, or '' if an error occured. | 278 @return: unique room userhost, or '' if an error occured. |
263 """ | 279 """ |
264 #TODO: we should use #RFC-0045 10.1.4 when available here | 280 #TODO: we should use #RFC-0045 10.1.4 when available here |
265 profile = self.host.memory.getProfileName(profile_key) | 281 client = self.host.getClient(profile_key) |
266 if not profile: | |
267 error(_("Unknown profile")) | |
268 return "" | |
269 room_name = uuid.uuid1() | 282 room_name = uuid.uuid1() |
270 print "\n\n===> room_name:", room_name | 283 if muc_service is None: |
271 if muc_service == "": | 284 try: |
272 muc_service = self.getMUCService(profile) | 285 muc_service = client.muc_service |
273 if not muc_service: | 286 except AttributeError: |
274 error(_("Can't find a MUC service")) | 287 raise NotReadyYet("Main server MUC service has not been checked yet") |
275 return "" | 288 if muc_service is None: |
276 muc_service = muc_service.userhost() | 289 warning(_("No MUC service found on main server")) |
277 return "%s@%s" % (room_name, muc_service) | 290 raise exceptions.FeatureNotFound |
291 | |
292 muc_service = muc_service.userhost() | |
293 return jid.JID("%s@%s" % (room_name, muc_service)) | |
278 | 294 |
279 def join(self, room_jid, nick, options, profile_key=C.PROF_KEY_NONE): | 295 def join(self, room_jid, nick, options, profile_key=C.PROF_KEY_NONE): |
280 def _errDeferred(exc_obj=Exception, txt='Error while joining room'): | 296 def _errDeferred(exc_obj=Exception, txt='Error while joining room'): |
281 d = defer.Deferred() | 297 d = defer.Deferred() |
282 d.errback(exc_obj(txt)) | 298 d.errback(exc_obj(txt)) |
313 except: | 329 except: |
314 mess = _("Invalid room jid: %s") % room_jid_s | 330 mess = _("Invalid room jid: %s") % room_jid_s |
315 warning(mess) | 331 warning(mess) |
316 self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile) | 332 self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile) |
317 return | 333 return |
318 d = self.join(room_jid, nick, options, profile) | 334 self.join(room_jid, nick, options, profile) |
319 # TODO: error management + signal in bridge | 335 # TODO: error management + signal in bridge |
320 return room_jid_s | 336 return room_jid_s |
321 | 337 |
322 def nick(self, room_jid, nick, profile_key): | 338 def nick(self, room_jid, nick, profile_key): |
323 profile = self.host.memory.getProfileName(profile_key) | 339 profile = self.host.memory.getProfileName(profile_key) |