# HG changeset patch # User Goffi # Date 1348784784 -7200 # Node ID f98bef71a9189ec05c132c758d6aa7068e77c3ed # Parent 2e43c74815ad4da5a4b512fc29b481e1001005ed 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 diff -r 2e43c74815ad -r f98bef71a918 frontends/src/primitivus/chat.py --- a/frontends/src/primitivus/chat.py Thu Sep 27 00:54:42 2012 +0200 +++ b/frontends/src/primitivus/chat.py Fri Sep 28 00:26:24 2012 +0200 @@ -203,10 +203,10 @@ self.present_wid.changeValues(nicks) self.host.redraw() - def replaceUser(self, param_nick): + def replaceUser(self, param_nick, show_info=True): """Add user if it is not in the group list""" nick = unicode(param_nick) #FIXME: should be done in DBus bridge - QuickChat.replaceUser(self, nick) + QuickChat.replaceUser(self, nick, show_info) presents = self.present_wid.getAllValues() if nick not in presents: presents.append(nick) @@ -214,10 +214,10 @@ self.present_wid.changeValues(presents) self.host.redraw() - def removeUser(self, param_nick): + def removeUser(self, param_nick, show_info=True): """Remove a user from the group list""" nick = unicode(param_nick) #FIXME: should be done in DBus bridge - QuickChat.removeUser(self, nick) + QuickChat.removeUser(self, nick, show_info) self.present_wid.deleteValue(nick) self.host.redraw() @@ -234,11 +234,11 @@ #as that mean that he is probably watching discussion history self.text_list.set_focus(len(self.content)-1) self.host.redraw() - if not self.host.notify.hasFocus(): + if not self.host.x_notify.hasFocus(): if self.type=="one2one": - self.host.notify.sendNotification(_("Primitivus: %s is talking to you") % from_jid) + self.host.x_notify.sendNotification(_("Primitivus: %s is talking to you") % from_jid) elif self.getUserNick().lower() in msg.lower(): - self.host.notify.sendNotification(_("Primitivus: Somebody pinged your name in %s room") % self.target) + self.host.x_notify.sendNotification(_("Primitivus: Somebody pinged your name in %s room") % self.target) def printInfo(self, msg, type='normal'): """Print general info @@ -253,11 +253,11 @@ #as that mean that he is probably watching discussion history self.text_list.set_focus(len(self.content)-1) self.host.redraw() - if not self.host.notify.hasFocus(): + if not self.host.x_notify.hasFocus(): if self.type=="one2one": - self.host.notify.sendNotification(_("Primitivus: there is a message about you")) + self.host.x_notify.sendNotification(_("Primitivus: there is a message about you")) elif self.getUserNick().lower() in msg.lower(): - self.host.notify.sendNotification(_("Primitivus: Somebody is talking about you in %s room") % self.target) + self.host.x_notify.sendNotification(_("Primitivus: Somebody is talking about you in %s room") % self.target) def startGame(self, game_type, referee, players): """Configure the chat window to start a game""" diff -r 2e43c74815ad -r f98bef71a918 frontends/src/primitivus/contact_list.py --- a/frontends/src/primitivus/contact_list.py Thu Sep 27 00:54:42 2012 +0200 +++ b/frontends/src/primitivus/contact_list.py Fri Sep 28 00:26:24 2012 +0200 @@ -239,6 +239,10 @@ groups_to_remove.append(group) for group in groups_to_remove: del self.groups[group] + try: + del self.special[jid.short] + except KeyError: + pass self.update() def add(self, jid, param_groups=[None]): diff -r 2e43c74815ad -r f98bef71a918 frontends/src/primitivus/primitivus --- a/frontends/src/primitivus/primitivus Thu Sep 27 00:54:42 2012 +0200 +++ b/frontends/src/primitivus/primitivus Fri Sep 28 00:26:24 2012 +0200 @@ -70,7 +70,7 @@ urwid.connect_signal(self.notBar.progress,'click',lambda x:self.addWindow(self.progress_wid)) self.__saved_overlay = None - self.notify = Notify() + self.x_notify = Notify() def debug(self): """convenient method to reset screen and launch p(u)db""" @@ -299,10 +299,13 @@ contact = self.contact_list.getContact() ###Based on the fact that there is currently only one contact selectableat once if contact: chat = self.chat_wins[contact] - self.bridge.sendMessage(contact, + try: + self.bridge.sendMessage(contact, editBar.get_edit_text(), mess_type = "groupchat" if chat.type == 'group' else "chat", profile_key=self.profile) + except: + self.notify(_("Error while sending message")) editBar.set_edit_text('') def newMessage(self, from_jid, msg, type, to_jid, profile): diff -r 2e43c74815ad -r f98bef71a918 frontends/src/quick_frontend/quick_app.py --- a/frontends/src/quick_frontend/quick_app.py Thu Sep 27 00:54:42 2012 +0200 +++ b/frontends/src/quick_frontend/quick_app.py Fri Sep 28 00:26:24 2012 +0200 @@ -57,8 +57,10 @@ self.bridge.register("actionResult", self.actionResult) self.bridge.register("actionResultExt", self.actionResult) self.bridge.register("roomJoined", self.roomJoined, "plugin") + self.bridge.register("roomLeft", self.roomLeft, "plugin") self.bridge.register("roomUserJoined", self.roomUserJoined, "plugin") self.bridge.register("roomUserLeft", self.roomUserLeft, "plugin") + self.bridge.register("roomUserChangedNick", self.roomUserChangedNick, "plugin") self.bridge.register("roomNewSubject", self.roomNewSubject, "plugin") self.bridge.register("tarotGameStarted", self.tarotGameStarted, "plugin") self.bridge.register("tarotGameNew", self.tarotGameNew, "plugin") @@ -290,6 +292,13 @@ self.chat_wins[room_jid].setPresents(list(set([user_nick]+room_nicks))) self.contact_list.setSpecial(JID(room_jid), "MUC") + def roomLeft(self, room_jid, profile): + """Called when a MUC room is left""" + if not self.check_profile(profile): + return + debug (_("Room [%(room_jid)s] left by %(profile)s") % {'room_jid':room_jid, 'profile': profile}) + del self.chat_wins[room_jid] + self.contact_list.remove(room_jid) def roomUserJoined(self, room_jid, user_nick, user_data, profile): """Called when an user joined a MUC room""" @@ -307,6 +316,14 @@ self.chat_wins[room_jid].removeUser(user_nick) debug (_("user [%(user_nick)s] left room [%(room_jid)s]") % {'user_nick':user_nick, 'room_jid':room_jid}) + def roomUserChangedNick(self, room_jid, old_nick, new_nick, profile): + """Called when an user joined a MUC room""" + if not self.check_profile(profile): + return + if self.chat_wins.has_key(room_jid): + self.chat_wins[room_jid].changeUserNick(old_nick, new_nick) + debug (_("user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]") % {'old_nick':old_nick, 'new_nick':new_nick, 'room_jid':room_jid}) + def roomNewSubject(self, room_jid, subject, profile): """Called when subject of MUC room change""" if not self.check_profile(profile): diff -r 2e43c74815ad -r f98bef71a918 frontends/src/quick_frontend/quick_chat.py --- a/frontends/src/quick_frontend/quick_chat.py Thu Sep 27 00:54:42 2012 +0200 +++ b/frontends/src/quick_frontend/quick_chat.py Fri Sep 28 00:26:24 2012 +0200 @@ -50,7 +50,7 @@ raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here self.occupants.update(nicks) - def replaceUser(self, nick): + def replaceUser(self, nick, show_info=True): """Add user if it is not in the group list""" debug (_("Replacing user %s") % nick) if self.type != "group": @@ -58,24 +58,35 @@ raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here len_before = len(self.occupants) self.occupants.add(nick) - if len_before != len(self.occupants): + if len_before != len(self.occupants) and show_info: self.printInfo("=> %s has joined the room" % nick) - + + def removeUser(self, nick, show_info=True): + """Remove a user from the group list""" + debug(_("Removing user %s") % nick) + if self.type != "group": + error (_("[INTERNAL] trying to remove user for a non group chat window")) + raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here + self.occupants.remove(nick) + if show_info: + self.printInfo("<= %s has left the room" % nick) + def setUserNick(self, nick): """Set the nick of the user, usefull for e.g. change the color of the user""" self.nick = nick def getUserNick(self): return unicode(self.nick) - - def removeUser(self, nick): - """Remove a user from the group list""" - debug(_("Removing user %s") % nick) + + def changeUserNick(self, old_nick, new_nick): + """Change nick of a user in group list""" + debug(_("Changing nick of user %(old_nick)s to %(new_nick)s") % {"old_nick": old_nick, "new_nick": new_nick}) if self.type != "group": - error (_("[INTERNAL] trying to remove user for a non group chat window")) + error (_("[INTERNAL] trying to change user nick for a non group chat window")) raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here - self.occupants.remove(nick) - self.printInfo("<= %s has left the room" % nick) + self.removeUser(old_nick, show_info=False) + self.replaceUser(new_nick, show_info=False) + self.printInfo("%s is now known as %s" % (old_nick, new_nick)) def setSubject(self, subject): """Set title for a group chat""" diff -r 2e43c74815ad -r f98bef71a918 frontends/src/wix/chat.py --- a/frontends/src/wix/chat.py Thu Sep 27 00:54:42 2012 +0200 +++ b/frontends/src/wix/chat.py Fri Sep 28 00:26:24 2012 +0200 @@ -142,18 +142,18 @@ for nick in nicks: self.present_panel.presents.replace(nick) - def replaceUser(self, nick): + def replaceUser(self, nick, show_info=True): """Add user if it is not in the group list""" debug (_("Replacing user %s") % nick) if self.type != "group": error (_("[INTERNAL] trying to replace user for a non group chat window")) return - QuickChat.replaceUser(self, nick) + QuickChat.replaceUser(self, nick, show_info) self.present_panel.presents.replace(nick) - def removeUser(self, nick): + def removeUser(self, nick, show_info=True): """Remove a user from the group list""" - QuickChat.removeUser(self, nick) + QuickChat.removeUser(self, nick, show_info) self.present_panel.presents.remove(nick) def setSubject(self, subject): diff -r 2e43c74815ad -r f98bef71a918 src/memory/memory.py --- a/src/memory/memory.py Thu Sep 27 00:54:42 2012 +0200 +++ b/src/memory/memory.py Fri Sep 28 00:26:24 2012 +0200 @@ -750,6 +750,18 @@ ret[key] = entity_data[key] return ret + def delEntityCache(self, entity_jid, profile_key): + """Remove cached data for entity + @param entity_jid: JID of the entity + """ + profile = self.getProfileName(profile_key) + try: + del self.entitiesCache[profile][entity_jid.userhost()] + except KeyError: + pass + + + def addWaitingSub(self, _type, entity_jid, profile_key): """Called when a subcription request is received""" profile = self.getProfileName(profile_key) diff -r 2e43c74815ad -r f98bef71a918 src/plugins/plugin_xep_0045.py --- 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