Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0045.py @ 507:f98bef71a918
frontends, core, plugin XEP-0045: leave implementation + better nick change
- memory: individual entity cache can be deleted
- plugin XEP-0045: nick change are now detected and userChangedNick signal is sent instead of joined/left
- plugin XEP-0045: leave implementation
- frontends: userChangedNick signal management
- Primitivus: an alert is shown in notification bar in case of error in sendMessage
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 28 Sep 2012 00:26:24 +0200 |
parents | 2402668b5d05 |
children | 64ff046dc201 |
comparison
equal
deleted
inserted
replaced
506:2e43c74815ad | 507:f98bef71a918 |
---|---|
18 You should have received a copy of the GNU Affero General Public License | 18 You should have received a copy of the GNU Affero General Public License |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 """ | 20 """ |
21 | 21 |
22 from logging import debug, info, warning, error | 22 from logging import debug, info, warning, error |
23 from twisted.words.xish import domish | 23 from twisted.internet import defer |
24 from twisted.internet import protocol, defer, threads, reactor | 24 from twisted.words.protocols.jabber import jid |
25 from twisted.words.protocols.jabber import client, jid, xmlstream | |
26 from twisted.words.protocols.jabber import error as jab_error | |
27 from twisted.words.protocols.jabber.xmlstream import IQ | |
28 | 25 |
29 from sat.core import exceptions | 26 from sat.core import exceptions |
30 | 27 |
31 import os.path | |
32 import uuid | 28 import uuid |
33 | 29 |
34 from zope.interface import implements | 30 from wokkel import muc |
35 | 31 |
36 from wokkel import disco, iwokkel, muc | |
37 | |
38 from base64 import b64decode | |
39 from hashlib import sha1 | |
40 from time import sleep | |
41 | 32 |
42 try: | 33 try: |
43 from twisted.words.protocols.xmlstream import XMPPHandler | 34 from twisted.words.protocols.xmlstream import XMPPHandler |
44 except ImportError: | 35 except ImportError: |
45 from wokkel.subprotocols import XMPPHandler | 36 from wokkel.subprotocols import XMPPHandler |
63 def __init__(self, host): | 54 def __init__(self, host): |
64 info(_("Plugin XEP_0045 initialization")) | 55 info(_("Plugin XEP_0045 initialization")) |
65 self.host = host | 56 self.host = host |
66 self.clients={} | 57 self.clients={} |
67 host.bridge.addMethod("joinMUC", ".plugin", in_sign='ssa{ss}s', out_sign='', method=self._join) | 58 host.bridge.addMethod("joinMUC", ".plugin", in_sign='ssa{ss}s', out_sign='', method=self._join) |
68 host.bridge.addMethod("changeNick", ".plugin", in_sign='sss', out_sign='', method=self.changeNick) | 59 host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick) |
60 host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.leave) | |
69 host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sass)', method=self.getRoomsJoined) | 61 host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sass)', method=self.getRoomsJoined) |
70 host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects) | 62 host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects) |
71 host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='s', out_sign='s', method=self.getUniqueName) | 63 host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='s', out_sign='s', method=self.getUniqueName) |
72 host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') #args: room_jid, room_nicks, user_nick, profile | 64 host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') #args: room_jid, room_nicks, user_nick, profile |
65 host.bridge.addSignal("roomLeft", ".plugin", signature='ss') #args: room_jid, profile | |
73 host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') #args: room_jid, user_nick, user_data, profile | 66 host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') #args: room_jid, user_nick, user_data, profile |
74 host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') #args: room_jid, user_nick, user_data, profile | 67 host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') #args: room_jid, user_nick, user_data, profile |
68 host.bridge.addSignal("roomUserChangedNick", ".plugin", signature='ssss') #args: room_jid, old_nick, new_nick, profile | |
75 host.bridge.addSignal("roomNewSubject", ".plugin", signature='sss') #args: room_jid, subject, profile | 69 host.bridge.addSignal("roomNewSubject", ".plugin", signature='sss') #args: room_jid, subject, profile |
70 | |
76 | 71 |
77 def __check_profile(self, profile): | 72 def __check_profile(self, profile): |
78 """check if profile is used and connected | 73 """check if profile is used and connected |
79 if profile known but disconnected, remove it from known profiles | 74 if profile known but disconnected, remove it from known profiles |
80 @param profile: profile to check | 75 @param profile: profile to check |
177 self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile) | 172 self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile) |
178 return | 173 return |
179 d = self.join(room_jid, nick, options, profile) | 174 d = self.join(room_jid, nick, options, profile) |
180 d.addErrback(lambda x: warning(_('Error while joining room'))) #TODO: error management + signal in bridge | 175 d.addErrback(lambda x: warning(_('Error while joining room'))) #TODO: error management + signal in bridge |
181 | 176 |
182 def nick(self, room_jid, nick, profile_key='@DEFAULT@'): | 177 def nick(self, room_jid, nick, profile_key): |
183 profile = self.host.memory.getProfileName(profile_key) | 178 profile = self.host.memory.getProfileName(profile_key) |
184 if not self.__check_profile(profile): | 179 if not self.__check_profile(profile): |
185 raise exceptions.UnknownProfileError("Unknown or disconnected profile") | 180 raise exceptions.UnknownProfileError("Unknown or disconnected profile") |
186 if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()): | 181 if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()): |
187 raise UnknownRoom("This room has not been joined") | 182 raise UnknownRoom("This room has not been joined") |
188 return self.clients[profile].nick(room_jid, nick) | 183 return self.clients[profile].nick(room_jid, nick) |
189 | 184 |
190 def changeNick(self, room_jid_s, nick, profile_key='@DEFAULT@'): | 185 def leave(self, room_jid, profile_key): |
186 profile = self.host.memory.getProfileName(profile_key) | |
187 if not self.__check_profile(profile): | |
188 raise exceptions.UnknownProfileError("Unknown or disconnected profile") | |
189 if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()): | |
190 raise UnknownRoom("This room has not been joined") | |
191 return self.clients[profile].leave(room_jid) | |
192 | |
193 def mucNick(self, room_jid_s, nick, profile_key='@DEFAULT@'): | |
191 """Change nickname in a room""" | 194 """Change nickname in a room""" |
192 return self.nick(jid.JID(room_jid_s), nick, profile_key) | 195 return self.nick(jid.JID(room_jid_s), nick, profile_key) |
196 | |
197 def mucLeave(self, room_jid_s, profile_key='@DEFAULT@'): | |
198 """Leave a room""" | |
199 return self.leave(jid.JID(room_jid_s), profile_key) | |
193 | 200 |
194 def getHandler(self, profile): | 201 def getHandler(self, profile): |
195 self.clients[profile] = SatMUCClient(self) | 202 self.clients[profile] = SatMUCClient(self) |
196 return self.clients[profile] | 203 return self.clients[profile] |
197 | 204 |
204 self.plugin_parent = plugin_parent | 211 self.plugin_parent = plugin_parent |
205 self.host = plugin_parent.host | 212 self.host = plugin_parent.host |
206 muc.MUCClient.__init__(self) | 213 muc.MUCClient.__init__(self) |
207 self.joined_rooms = {} | 214 self.joined_rooms = {} |
208 self.rec_subjects = {} | 215 self.rec_subjects = {} |
216 self.__changing_nicks = set() # used to keep trace of who is changing nick, | |
217 # and to discard userJoinedRoom signal in this case | |
209 print "init SatMUCClient OK" | 218 print "init SatMUCClient OK" |
210 | 219 |
220 def unavailableReceived(self, presence): | |
221 #XXX: we override this method to manage nickname change | |
222 #TODO: feed this back to Wokkel | |
223 """ | |
224 Unavailable presence was received. | |
225 | |
226 If this was received from a MUC room occupant JID, that occupant has | |
227 left the room. | |
228 """ | |
229 room, user = self._getRoomUser(presence) | |
230 | |
231 if room is None or user is None: | |
232 return | |
233 | |
234 room.removeUser(user) | |
235 | |
236 if muc.STATUS_CODE.NEW_NICK in presence.mucStatuses: | |
237 self.__changing_nicks.add(presence.nick) | |
238 self.userChangedNick(room, user, presence.nick) | |
239 else: | |
240 self.__changing_nicks.discard(presence.nick) | |
241 self.userLeftRoom(room, user) | |
242 | |
211 def receivedGroupChat(self, room, user, body): | 243 def receivedGroupChat(self, room, user, body): |
212 debug('receivedGroupChat: room=%s user=%s body=%s', room, user, body) | 244 debug('receivedGroupChat: room=%s user=%s body=%s', room, user, body) |
213 | 245 |
214 def userJoinedRoom(self, room, user): | 246 def userJoinedRoom(self, room, user): |
215 debug (_("user %(nick)s has joined room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()}) | 247 if user.nick in self.__changing_nicks: |
216 if not self.host.trigger.point("MUC user joined", room, user, self.parent.profile): | 248 self.__changing_nicks.remove(user.nick) |
217 return | 249 else: |
218 user_data={'entity':user.entity.full() if user.entity else '', 'affiliation':user.affiliation, 'role':user.role} | 250 debug (_("user %(nick)s has joined room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()}) |
219 self.host.bridge.roomUserJoined(room.roomJID.userhost(), user.nick, user_data, self.parent.profile) | 251 if not self.host.trigger.point("MUC user joined", room, user, self.parent.profile): |
252 return | |
253 user_data={'entity':user.entity.full() if user.entity else '', 'affiliation':user.affiliation, 'role':user.role} | |
254 self.host.bridge.roomUserJoined(room.roomJID.userhost(), user.nick, user_data, self.parent.profile) | |
220 | 255 |
221 def userLeftRoom(self, room, user): | 256 def userLeftRoom(self, room, user): |
222 debug (_("user %(nick)s left room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()}) | 257 if user.nick == room.nick: |
223 user_data={'entity':user.entity.full() if user.entity else '', 'affiliation':user.affiliation, 'role':user.role} | 258 # we left the room |
224 self.host.bridge.roomUserLeft(room.roomJID.userhost(), user.nick, user_data, self.parent.profile) | 259 room_jid_s = room.roomJID.userhost() |
260 info (_("Room [%(room)s] left (%(profile)s))") % { "room": room_jid_s, | |
261 "profile": self.parent.profile }) | |
262 self.host.memory.delEntityCache(room.roomJID, self.parent.profile) | |
263 del self.plugin_parent.clients[self.parent.profile].joined_rooms[room_jid_s] | |
264 self.host.bridge.roomLeft(room.roomJID.userhost(), self.parent.profile) | |
265 else: | |
266 debug (_("user %(nick)s left room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()}) | |
267 user_data={'entity':user.entity.full() if user.entity else '', 'affiliation':user.affiliation, 'role':user.role} | |
268 self.host.bridge.roomUserLeft(room.roomJID.userhost(), user.nick, user_data, self.parent.profile) | |
269 | |
270 def userChangedNick(self, room, user, new_nick): | |
271 self.host.bridge.roomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile) | |
225 | 272 |
226 def userUpdatedStatus(self, room, user, show, status): | 273 def userUpdatedStatus(self, room, user, show, status): |
227 print("FIXME: MUC status not managed yet") | 274 print("FIXME: MUC status not managed yet") |
228 #FIXME: gof | 275 #FIXME: gof |
229 | 276 |
230 def receivedSubject(self, room, user, subject): | 277 def receivedSubject(self, room, user, subject): |
231 debug (_("New subject for room (%(room_id)s): %(subject)s") % {'room_id':room.roomJID.full(),'subject':subject}) | 278 debug (_("New subject for room (%(room_id)s): %(subject)s") % {'room_id':room.roomJID.full(),'subject':subject}) |
232 self.rec_subjects[room.roomJID.userhost()] = (room.roomJID.userhost(), subject) | 279 self.rec_subjects[room.roomJID.userhost()] = (room.roomJID.userhost(), subject) |
233 self.host.bridge.roomNewSubject(room.roomJID.userhost(), subject, self.parent.profile) | 280 self.host.bridge.roomNewSubject(room.roomJID.userhost(), subject, self.parent.profile) |
234 | 281 |
282 | |
235 #def connectionInitialized(self): | 283 #def connectionInitialized(self): |
236 #pass | 284 #pass |
237 | 285 |
238 #def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 286 #def getDiscoInfo(self, requestor, target, nodeIdentifier=''): |
239 #return [disco.DiscoFeature(NS_VCARD)] | 287 #return [disco.DiscoFeature(NS_VCARD)] |