diff 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
line wrap: on
line diff
--- a/src/plugins/plugin_xep_0045.py	Thu Sep 27 00:54:42 2012 +0200
+++ b/src/plugins/plugin_xep_0045.py	Fri Sep 28 00:26:24 2012 +0200
@@ -20,24 +20,15 @@
 """
 
 from logging import debug, info, warning, error
-from twisted.words.xish import domish
-from twisted.internet import protocol, defer, threads, reactor
-from twisted.words.protocols.jabber import client, jid, xmlstream
-from twisted.words.protocols.jabber import error as jab_error
-from twisted.words.protocols.jabber.xmlstream import IQ
+from twisted.internet import defer
+from twisted.words.protocols.jabber import jid
 
 from sat.core import exceptions
 
-import os.path
 import uuid
 
-from zope.interface import implements
-
-from wokkel import disco, iwokkel, muc
+from wokkel import muc
 
-from base64 import b64decode
-from hashlib import sha1
-from time import sleep
 
 try:
     from twisted.words.protocols.xmlstream import XMPPHandler
@@ -65,15 +56,19 @@
         self.host = host
         self.clients={}
         host.bridge.addMethod("joinMUC", ".plugin", in_sign='ssa{ss}s', out_sign='', method=self._join)
-        host.bridge.addMethod("changeNick", ".plugin", in_sign='sss', out_sign='', method=self.changeNick)
+        host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick)
+        host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.leave)
         host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sass)', method=self.getRoomsJoined)
         host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects)
         host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='s', out_sign='s', method=self.getUniqueName)
         host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') #args: room_jid, room_nicks, user_nick, profile
+        host.bridge.addSignal("roomLeft", ".plugin", signature='ss') #args: room_jid, profile
         host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') #args: room_jid, user_nick, user_data, profile
         host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') #args: room_jid, user_nick, user_data, profile
+        host.bridge.addSignal("roomUserChangedNick", ".plugin", signature='ssss') #args: room_jid, old_nick, new_nick, profile
         host.bridge.addSignal("roomNewSubject", ".plugin", signature='sss') #args: room_jid, subject, profile
 
+
     def __check_profile(self, profile):
         """check if profile is used and connected
         if profile known but disconnected, remove it from known profiles
@@ -179,18 +174,30 @@
         d = self.join(room_jid, nick, options, profile)
         d.addErrback(lambda x: warning(_('Error while joining room'))) #TODO: error management + signal in bridge
 
-    def nick(self, room_jid, nick, profile_key='@DEFAULT@'):
+    def nick(self, room_jid, nick, profile_key):
         profile = self.host.memory.getProfileName(profile_key)
         if not self.__check_profile(profile):
             raise exceptions.UnknownProfileError("Unknown or disconnected profile")
         if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()):
             raise UnknownRoom("This room has not been joined") 
         return self.clients[profile].nick(room_jid, nick)
-   
-    def changeNick(self, room_jid_s, nick, profile_key='@DEFAULT@'):
+  
+    def leave(self, room_jid, profile_key): 
+        profile = self.host.memory.getProfileName(profile_key)
+        if not self.__check_profile(profile):
+            raise exceptions.UnknownProfileError("Unknown or disconnected profile")
+        if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()):
+            raise UnknownRoom("This room has not been joined") 
+        return self.clients[profile].leave(room_jid)
+
+    def mucNick(self, room_jid_s, nick, profile_key='@DEFAULT@'):
         """Change nickname in a room"""
         return self.nick(jid.JID(room_jid_s), nick, profile_key)
 
+    def mucLeave(self, room_jid_s, profile_key='@DEFAULT@'):
+        """Leave a room"""
+        return self.leave(jid.JID(room_jid_s), profile_key)
+
     def getHandler(self, profile):
         self.clients[profile] = SatMUCClient(self)
         return self.clients[profile]
@@ -206,22 +213,62 @@
         muc.MUCClient.__init__(self)
         self.joined_rooms = {}
         self.rec_subjects = {}
+        self.__changing_nicks = set() # used to keep trace of who is changing nick,
+                                      # and to discard userJoinedRoom signal in this case
         print "init SatMUCClient OK"
     
+    def unavailableReceived(self, presence):
+        #XXX: we override this method to manage nickname change
+        #TODO: feed this back to Wokkel
+        """
+        Unavailable presence was received.
+
+        If this was received from a MUC room occupant JID, that occupant has
+        left the room.
+        """
+        room, user = self._getRoomUser(presence)
+
+        if room is None or user is None:
+            return
+
+        room.removeUser(user)
+
+        if muc.STATUS_CODE.NEW_NICK in presence.mucStatuses:
+            self.__changing_nicks.add(presence.nick)
+            self.userChangedNick(room, user, presence.nick)
+        else:
+            self.__changing_nicks.discard(presence.nick)
+            self.userLeftRoom(room, user)
+
     def receivedGroupChat(self, room, user, body):
         debug('receivedGroupChat: room=%s user=%s body=%s', room, user, body)
 
     def userJoinedRoom(self, room, user):
-        debug (_("user %(nick)s has joined room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
-        if not self.host.trigger.point("MUC user joined", room, user, self.parent.profile):
-            return
-        user_data={'entity':user.entity.full() if user.entity else  '', 'affiliation':user.affiliation, 'role':user.role} 
-        self.host.bridge.roomUserJoined(room.roomJID.userhost(), user.nick, user_data, self.parent.profile)
+        if user.nick in self.__changing_nicks:
+            self.__changing_nicks.remove(user.nick)
+        else:
+            debug (_("user %(nick)s has joined room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
+            if not self.host.trigger.point("MUC user joined", room, user, self.parent.profile):
+                return
+            user_data={'entity':user.entity.full() if user.entity else  '', 'affiliation':user.affiliation, 'role':user.role} 
+            self.host.bridge.roomUserJoined(room.roomJID.userhost(), user.nick, user_data, self.parent.profile)
     
     def userLeftRoom(self, room, user):
-        debug (_("user %(nick)s left room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
-        user_data={'entity':user.entity.full() if user.entity else  '', 'affiliation':user.affiliation, 'role':user.role} 
-        self.host.bridge.roomUserLeft(room.roomJID.userhost(), user.nick, user_data, self.parent.profile)
+        if user.nick == room.nick:
+            # we left the room
+            room_jid_s = room.roomJID.userhost()
+            info (_("Room [%(room)s] left (%(profile)s))") % { "room": room_jid_s,
+                                                               "profile": self.parent.profile })
+            self.host.memory.delEntityCache(room.roomJID, self.parent.profile)
+            del self.plugin_parent.clients[self.parent.profile].joined_rooms[room_jid_s]
+            self.host.bridge.roomLeft(room.roomJID.userhost(), self.parent.profile)
+        else:
+            debug (_("user %(nick)s left room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
+            user_data={'entity':user.entity.full() if user.entity else  '', 'affiliation':user.affiliation, 'role':user.role} 
+            self.host.bridge.roomUserLeft(room.roomJID.userhost(), user.nick, user_data, self.parent.profile)
+
+    def userChangedNick(self, room, user, new_nick):
+            self.host.bridge.roomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile)
 
     def userUpdatedStatus(self, room, user, show, status):
         print("FIXME: MUC status not managed yet")
@@ -232,6 +279,7 @@
         self.rec_subjects[room.roomJID.userhost()] = (room.roomJID.userhost(), subject)
         self.host.bridge.roomNewSubject(room.roomJID.userhost(), subject, self.parent.profile)
 
+    
     #def connectionInitialized(self):
         #pass