]> code.delx.au - pymsnt/commitdiff
Integrating new msnw more. Groupchat. About to test..
authorjamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>
Sun, 1 Jan 2006 05:00:31 +0000 (05:00 +0000)
committerjamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>
Sun, 1 Jan 2006 05:00:31 +0000 (05:00 +0000)
git-svn-id: http://delx.cjb.net/svn/pymsnt/trunk@67 55fbd22a-6204-0410-b2f0-b6c764c7e90a

committer: jamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>

src/lang.py
src/legacy/glue.py
src/tlib/msn/msnw.py

index 8a131d8cd42e7988c864d2406b9683a9db1eca69..fe4abaebd71dfd575e555a14f7a4bd2bae8274e9 100644 (file)
@@ -40,7 +40,8 @@ class strings:
                msnDroppedMessage = u"(Automated message)\nA message from this person did not get delivered to you. Please report this to your Jabber server administrator."
                msnInitialMail = u"Hotmail notification\n\nUnread message in inbox: %s\nUnread messages in folders: %s"
                msnRealtimeMail = u"Hotmail notification\n\nFrom: %s <%s>\n Subject: %s"
-               msnDisconnected = u"Disconnection from MSN servers: %s"
+               msnDisconnected = u"Disconnected from MSN servers: %s"
+               msnConnectFailed = u"Failed to connect to MSN servers: %s"
 
                command_CommandList = u"PyMSNt Commands"
                command_Done = "Command completed."
@@ -86,6 +87,7 @@ class strings:
                msnInitialMail = u"Hotmail notification\n\nUnread message in inbox: %s\nUnread messages in folders: %s"
                msnRealtimeMail = u"Hotmail notification\n\nFrom: %s <%s>\n Subject: %s"
                msnDisconnected = u"Desligado dos servidores MSN: %s"
+               msnConnectFailed = u"Failed to connect to MSN servers: %s"
 
                command_CommandList = u"PyMSNt Commands"
                command_Done = "Command completed."
@@ -127,6 +129,7 @@ class strings:
                msnInitialMail = u"Hotmail-meldingen\n\nAantal ongelezen berichten in postvak in: %s\nAantal ongelezen berichten in mappen: %s"
                msnRealtimeMail = u"Hotmail-meldingen\n\nVan: %s <%s>\n Onderwerp: %s"
                msnDisconnected = u"De verbinding met de MSN-servers werd verbroken: %s"
+               msnConnectFailed = u"Failed to connect to MSN servers: %s"
 
                command_CommandList = u"Commando's voor PyMSNt"
                command_Done = "Commando beëindigd."
@@ -172,6 +175,7 @@ class strings:
                msnInitialMail = u"Hotmail notification\n\nUngelesene Nachrichten in der Inbox: %s\nUngelesene Nachrichten in anderen Ordnern: %s"
                msnRealtimeMail = u"Hotmail notification\n\nNeue Nachricht von %s <%s>\n Subject: %s"
                msnDisconnected = u"Die Verbindung zum MSN-Server wurde getrennt: %s"
+               msnConnectFailed = u"Failed to connect to MSN servers: %s"
 
                command_CommandList = u"PyMSNt Commands"
                command_Done = "Command completed."
@@ -214,6 +218,7 @@ class strings:
                msnInitialMail = u"Notification Hotmail\n\n Message(s) non lu(s) dans votre boîte de réception : %s\nMessage(s) non lu(s) dans le dossier : %s"
                msnRealtimeMail = u"Notification Hotmail\n\nDe: %s <%s>\n Sujet: %s"
                msnDisconnected = u"Déconnecté du serveur MSN: %s"
+               msnConnectFailed = u"Failed to connect to MSN servers: %s"
                
                command_CommandList = u"Commandes PyMSNt"
                command_Done = "Command completed."
@@ -261,6 +266,7 @@ class strings:
                msnInitialMail = u"Notificación de Hotmail\n\nMensajes sin leer en la bandeja de entrada: %s\nMensajes sin leer en otras carpetas: %s"
                msnRealtimeMail = u"Notificación de Hotmail\n\nDe: %s <%s>\nAsunto: %s"
                msnDisconnected = u"Desconexión de los servidores MSN: %s"
+               msnConnectFailed = u"Failed to connect to MSN servers: %s"
 
                command_CommandList = u"PyMSNt Commands"
                command_Done = "Command completed."
@@ -321,6 +327,7 @@ class strings:
                msnInitialMail = u"Powiadomienie Hotmail\n\nNieprzeczytane wiadomości w skrzynce odbiorczej: %s\nNieprzeczytane wiadomości w folderach: %s"
                msnRealtimeMail = u"Powiadomienie Hotmail\n\nOd: %s <%s>\n Temat: %s"
                msnDisconnected = u"Rozłączenie z sieci MSN: %s"
+               msnConnectFailed = u"Failed to connect to MSN servers: %s"
 
                command_CommandList = u"Polecenia PyMSNt"
                command_Done = "Polecenie zakończone."
index 939751f4b08bf0f1653949fc6b0412fab7593eb8..917ba65d1e3ea862d3173332fdfdf7ff503d7a05 100644 (file)
@@ -174,25 +174,22 @@ def jabsub2msnlist(sub):
 
 # This class handles groupchats with the legacy protocol
 class LegacyGroupchat(groupchat.BaseGroupchat):
-       def __init__(self, session, resource, ID=None, existing=False, switchboardSession=None):
+       def __init__(self, session, resource, ID=None, switchboardSession=None):
                """ Possible entry points for groupchat
                        - User starts an empty switchboard session by sending presence to a blank room
                        - An existing switchboard session is joined by another MSN user
                        - User invited to an existing switchboard session with more than one user
                """
                groupchat.BaseGroupchat.__init__(self, session, resource, ID)
-               if not existing:
-                       self.switchboardSession = msn.GroupchatSwitchboardSession(self, makeSwitchboard=True)
-               else:
+               if switchboardSession:
                        self.switchboardSession = switchboardSession
+               else:
+                       self.switchboardSession = msn.MultiSwitchboardSession(self)
                        
-               assert(self.switchboardSession != None)
-               
                LogEvent(INFO, self.roomJID())
        
        def removeMe(self):
-               self.switchboardSession.removeMe()
-               self.switchboardSession = None
+               del self.switchboardSession
                groupchat.BaseGroupchat.removeMe(self)
                LogEvent(INFO, self.roomJID())
        
@@ -204,6 +201,10 @@ class LegacyGroupchat(groupchat.BaseGroupchat):
                LogEvent(INFO, self.roomJID())
                userHandle = jid2msn(contactJID)
                self.switchboardSession.inviteUser(userHandle)
+       
+       def gotMessage(self, userHandle, text)
+               LogEvent(INFO, self.roomJID())
+               self.messageReceived(userHandle, text)
 
 
 
@@ -243,6 +244,15 @@ class LegacyConnection(msn.MSNConnection):
                self.legacyList = None
                self.session = None
        
+       def _sendShowStatus(self):
+               if not self.session: return
+               source = config.jid
+               to = self.session.jabberID
+               self.session.sendPresence(to=to, fro=source, show=self.remoteShow, status=self.remoteStatus, nickname=self.remoteNick)
+       
+       
+
+       # Implemented from baseproto
        def resourceOffline(self, resource):
                pass
 
@@ -261,28 +271,6 @@ class LegacyConnection(msn.MSNConnection):
                        self.failedMessage(dest, body)
                        raise
        
-       def msnAlert(self, text, actionurl, subscrurl):
-               if not self.session: return
-
-               el = Element((None, "message"))
-               el.attributes["to"] = self.session.jabberID
-               el.attributes["from"] = config.jid
-               el.attributes["type"] = "headline"
-               body = el.addElement("body")
-               body.addContent(text)
-               
-               x = el.addElement("x")
-               x.attributes["xmlns"] = "jabber:x:oob"
-               x.addElement("desc").addContent("More information on this notice.")
-               x.addElement("url").addContent(actionurl)
-
-               x = el.addElement("x")
-               x.attributes["xmlns"] = "jabber:x:oob"
-               x.addElement("desc").addContent("Manage subscriptions to alerts.")
-               x.addElement("url").addContent(subscrurl)
-
-               self.session.pytrans.send(el)
-       
        def setStatus(self, nickname, show, status):
                statusCode = presence2state(show, None)
                msn.MSNConnection.changeStatus(self, statusCode, nickname, status)
@@ -310,15 +298,6 @@ class LegacyConnection(msn.MSNConnection):
                                self.session.sendTypingNotification(self.session.jabberID, msn2jid(contact), False)
                                del self.contactTyping[contact]
        
-       def gotContactTyping(self, contact):
-               if not self.session: return
-               # Check if the contact has only just started typing
-               if not self.contactTyping.has_key(contact):
-                       self.session.sendTypingNotification(self.session.jabberID, msn2jid(contact), True)
-
-               # Reset the counter
-               self.contactTyping[contact] = 0
-       
        def userTypingNotification(self, dest, resource, composing):
                if not self.session: return
                dest = jid2msn(dest)
@@ -326,60 +305,114 @@ class LegacyConnection(msn.MSNConnection):
                if composing: # Make it instant
                        self.sendTypingToContact(dest)
        
+
+
+       # Implement callbacks from msn.MSNConnection
+       def connectionFailed(self, reason):
+               LogEvent(INFO, self.session.jabberID)
+               text = lang.get(self.session.lang).msnConnectFailed % reason
+               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text)
+               self.session.removeMe()
+
+       def loginFailed(self, reason):
+               LogEvent(INFO, self.session.jabberID)
+               text = lang.get(self.session.lang).msnLoginFailure % (self.session.username)
+               self.session.sendErrorMessage(to=self.session.jabberID, fro=config.jid, etype="auth", condition="not-authorized", explanation=text, body="Login Failure")
+               self.session.removeMe()
+       
+       def connectionLost(self, reason):
+               LogEvent(INFO, self.session.jabberID)
+               text = lang.get(self.session.lang).msnDisconnected % reason
+               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text)
+               self.session.removeMe() # Tear down the session
+
+       def multipleLogin(self):
+               LogEvent(INFO, self.session.jabberID)
+               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMultipleLogin)
+               self.session.removeMe()
+       
+       def serverGoingDown(self):
+               LogEvent(INFO, self.session.jabberID)
+               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance)
+       
+       def accountNotVerified(self):
+               LogEvent(INFO, self.session.jabberID)
+               text = lang.get(self.session.lang).msnNotVerified % (self.session.username)
+               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text)
+       
+       def userMapping(self, passport, jid):
+               LogEvent(INFO, self.session.jabberID)
+               text = lang.get(self.session.lang).userMapping % (passport, jid)
+               self.session.sendMessage(to=self.session.jabberID, fro=msn2jid(passport), body=text)
+       
+       def loggedIn(self):
+               LogEvent(INFO, self.session.jabberID)
+               self.session.ready = True
+       
        def listSynchronized(self):
-               if not self.session: return
+               LogEvent(INFO, self.session.jabberID)
                self.session.sendPresence(to=self.session.jabberID, fro=config.jid)
                self.legacyList.syncJabberLegacyLists()
                self.listSynced = True
                #self.legacyList.flushSubscriptionBuffer()
        
+       def ourStatusChanged(self, statusCode, screenName, personal):
+               # Send out a new presence packet to the Jabber user so that the transport icon changes
+               LogEvent(INFO, self.session.jabberID)
+               self.remoteShow, ptype = state2presence(statusCode)
+               self.remoteStatus = personal
+               self.remoteNick = screenName
+               self._sendShowStatus()
+
        def gotMessage(self, remoteUser, text):
-               if not self.session: return
+               LogEvent(INFO, self.session.jabberID)
                source = msn2jid(remoteUser)
                self.session.sendMessage(self.session.jabberID, fro=source, body=text, mtype="chat")
                self.session.pytrans.statistics.stats["MessageCount"] += 1
        
-       def avatarHashChanged(self, userHandle, hash):
-               if not self.session: return
+       def gotGroupchat(self, msnGroupchat):
+               LogEvent(INFO, self.session.jabberID)
+               msnGroupchat.groupchat = LegacyGroupchat(self.session, switchboardSession=msnGroupchat)
+       
+       def gotContactTyping(self, contact):
+               LogEvent(INFO, self.session.jabberID)
+               # Check if the contact has only just started typing
+               if not self.contactTyping.has_key(contact):
+                       self.session.sendTypingNotification(self.session.jabberID, msn2jid(contact), True)
 
-               if not hash:
-                       # They've turned off their avatar
-                       c = self.session.contactList.findContact(jid)
-                       if not c: return
-                       c.updateAvatar(av)
-               else:
+               # Reset the counter
+               self.contactTyping[contact] = 0
+       
+       def failedMessage(self, remoteUser, message):
+               LogEvent(INFO, self.session.jabberID)
+               self.session.pytrans.statistics.stats["FailedMessageCount"] += 1
+               fro = msn2jid(remoteUser)
+               self.session.sendErrorMessage(to=self.session.jabberID, fro=fro, etype="wait", condition="recipient-unavailable", explanation=lang.get(self.session.lang).msnFailedMessage, body=message)
+       
+       def contactAvatarChanged(self, userHandle, hash):
+               LogEvent(INFO, self.session.jabberID)
+               c = self.session.contactList.findContact(jid)
+               if not c: return
+
+               if hash:
                        # New avatar
                        av = self.session.pytrans.avatarCache.getAvatar(hash)
                        if av:
                                msnContact = self.getContacts().getContact(userHandle)
                                msnContact.msnobjGot = True
                                jid = msn2jid(userHandle)
-                               c = self.session.contactList.findContact(jid)
-                               if not c: return
                                c.updateAvatar(av)
                        else:
-                               self.requestAvatar(userHandle)
-       
-       def gotAvatarImage(self, userHandle, imageData):
-               if not self.session: return
-               jid = msn2jid(userHandle)
-               c = self.session.contactList.findContact(jid)
-               if not c: return
-               av = self.session.pytrans.avatarCache.setAvatar(imageData)
-               c.updateAvatar(av)
-       
-       def gotSendRequest(self, fileReceive):
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               ft.FTReceive(self.session, msn2jid(fileReceive.userHandle), fileReceive)
-       
-       def loggedIn(self):
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               self.session.ready = True
+                               def updateAvatar((imageData,)):
+                                       av = self.session.pytrans.avatarCache.setAvatar(imageData)
+                                       c.updateAvatar(av)
+                               self.sendAvatarRequest(userHandle).addCallback(updateAvatar)
+               else:
+                       # They've turned off their avatar
+                       global defaultAvatar
+                       c.updateAvatar(defaultAvatar)
        
-       def contactStatusChanged(self, remoteUser, statusCode, screenName):
-               if not (self.session and self.getContacts()): return
+       def contactStatusChanged(self, remoteUser):
                LogEvent(INFO, self.session.jabberID)
                
                msnContact = self.getContacts().getContact(remoteUser)
@@ -393,88 +426,53 @@ class LegacyConnection(msn.MSNConnection):
                c.updateNickname(screenName, push=False)
                c.updatePresence(show, status, ptype, force=True)
        
-       def ourStatusChanged(self, statusCode):
-               # Send out a new presence packet to the Jabber user so that the MSN-t icon changes
-               if not self.session: return
+       def gotFileReceive(self, fileReceive):
                LogEvent(INFO, self.session.jabberID)
-               self.remoteShow, ptype = state2presence(statusCode)
-               self.sendShowStatus()
+               pass # FIXME
        
-       def ourPersonalChanged(self, statusMessage):
-               if not self.session: return
+       def contactAddedMe(self, userHandle):
                LogEvent(INFO, self.session.jabberID)
-               self.remoteStatus = statusMessage
-               self.sendShowStatus()
-       
-       def ourNickChanged(self, nick):
-               if not self.session: return
-               LogEvent(INFO, self.session.jabberID)
-               self.remoteNick = nick
-               self.sendShowStatus()
-       
-       def sendShowStatus(self):
-               if not self.session: return
-               source = config.jid
-               to = self.session.jabberID
-               self.session.sendPresence(to=to, fro=source, show=self.remoteShow, status=self.remoteStatus, nickname=self.remoteNick)
-       
-       def userMapping(self, passport, jid):
-               if not self.session: return
-               text = lang.get(self.session.lang).userMapping % (passport, jid)
-               self.session.sendMessage(to=self.session.jabberID, fro=msn2jid(passport), body=text)
-       
-       def userAddedMe(self, userHandle):
-               if not self.session: return
                self.session.contactList.getContact(msn2jid(userHandle)).contactRequestsAuth()
        
-       def userRemovedMe(self, userHandle):
-               if not self.session: return
+       def contactRemovedMe(self, userHandle):
+               LogEvent(INFO, self.session.jabberID)
                c = self.session.contactList.getContact(msn2jid(userHandle))
                c.contactDerequestsAuth()
                c.contactRemovesAuth()
        
-       def serverGoingDown(self):
-               if not self.session: return
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance)
-       
-       def multipleLogin(self):
-               if not self.session: return
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMultipleLogin)
-               self.session.removeMe()
-       
-       def accountNotVerified(self):
-               if not self.session: return
-               text = lang.get(self.session.lang).msnNotVerified % (self.session.username)
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text)
-       
-       def loginFailure(self, message):
-               if not self.session: return
-               text = lang.get(self.session.lang).msnLoginFailure % (self.session.username)
-               self.session.sendErrorMessage(to=self.session.jabberID, fro=config.jid, etype="auth", condition="not-authorized", explanation=text, body="Login Failure")
-               self.session.removeMe()
-       
-       def failedMessage(self, remoteUser, message):
-               if not self.session: return
-               self.session.pytrans.statistics.stats["FailedMessageCount"] += 1
-               fro = msn2jid(remoteUser)
-               self.session.sendErrorMessage(to=self.session.jabberID, fro=fro, etype="wait", condition="recipient-unavailable", explanation=lang.get(self.session.lang).msnFailedMessage, body=message)
-       
-       def initialEmailNotification(self, inboxunread, foldersunread):
-               if not self.session: return
+       def gotInitialEmailNotification(self, inboxunread, foldersunread):
+               LogEvent(INFO, self.session.jabberID)
                text = lang.get(self.session.lang).msnInitialMail % (inboxunread, foldersunread)
                self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text, mtype="headline")
        
-       def realtimeEmailNotification(self, mailfrom, fromaddr, subject):
-               if not self.session: return
+       def gotRealtimeEmailNotification(self, mailfrom, fromaddr, subject):
+               LogEvent(INFO, self.session.jabberID)
                text = lang.get(self.session.lang).msnRealtimeMail % (mailfrom, fromaddr, subject)
                self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text, mtype="headline")
                
-       def connectionLost(self, reason):
-               if not self.session: return
+       def gotMSNAlert(self, text, actionurl, subscrurl):
                LogEvent(INFO, self.session.jabberID)
-               text = lang.get(self.session.lang).msnDisconnected % ("Error") # FIXME, a better error would be nice =P
-               self.session.sendMessage(to=self.session.jabberID, fro=config.jid, body=text)
-               self.session.removeMe() # Tear down the session
+
+               el = Element((None, "message"))
+               el.attributes["to"] = self.session.jabberID
+               el.attributes["from"] = config.jid
+               el.attributes["type"] = "headline"
+               body = el.addElement("body")
+               body.addContent(text)
+               
+               x = el.addElement("x")
+               x.attributes["xmlns"] = "jabber:x:oob"
+               x.addElement("desc").addContent("More information on this notice.")
+               x.addElement("url").addContent(actionurl)
+
+               x = el.addElement("x")
+               x.attributes["xmlns"] = "jabber:x:oob"
+               x.addElement("desc").addContent("Manage subscriptions to alerts.")
+               x.addElement("url").addContent(subscrurl)
+
+               self.session.pytrans.send(el)
+       
+       
 
 
 class LegacyList:
index 1eed6acb0569d678f9a01c0d819b77f15fe1c08a..950b8692e04a915f95402d788f380c4230fcfd1c 100644 (file)
@@ -157,7 +157,7 @@ class MSNConnection:
 
        def sendTypingToContact(self, userHandle):
                """
-               Sends typing notification to a contact.
+               Sends typing notification to a contact. Should send every 5secs.
                @param userHandle: the contact to notify of our typing.
                """
 
@@ -232,6 +232,9 @@ class MSNConnection:
        def connectionFailed(self, reason=''):
                """ Called when the connection to the server failed. """
        
+       def loginFailed(self, reason=''):
+               """ Called when the account could not be logged in. """
+       
        def connectionLost(self, reason=''):
                """ Called when we are disconnected. """
        
@@ -264,19 +267,20 @@ class MSNConnection:
        def gotMessage(self, userHandle, text):
                """ Called when a contact sends us a message """
        
-       def gotGroupchat(self, groupchat):
+       def gotGroupchat(self, msnGroupchat):
                """ Called when a conversation with more than one contact begins.
-               The overriding method is expected to set groupchat.groupchat to an object
+               The overriding method is expected to set msnGroupchat.groupchat to an object
                that implements the following methods:
                        contactJoined(userHandle)
                        contactLeft(userHandle)
                        gotMessage(userHandle, text)
 
-               The object received as 'groupchat' is an instance of MultiSwitchboardSession.
+               The object received as 'msnGroupchat' is an instance of MultiSwitchboardSession.
                """
        
        def gotContactTyping(self, userHandle):
-               """ Called when a contact sends typing notification """
+               """ Called when a contact sends typing notification.
+                   Will be called once every 5 seconds. """
        
        def failedMessage(self, userHandle, text):
                """ Called when a message we sent has been bounced back. """
@@ -285,12 +289,9 @@ class MSNConnection:
                """ Called when we receive a changed avatar hash for a contact.
                You should call sendAvatarRequest(). """
        
-       def contactStatusChanged(self, userHandle, statusCode, screenName):
+       def contactStatusChanged(self, userHandle):
                """ Called when we receive status information for a contact. """
        
-       def contactPersonalChanged(self, personal):
-               """ Called when we receive a new personal message for a contact. """
-       
        def gotFileReceive(self, fileReceive):
                """ Called when a contact sends the user a file.
                Call accept(fileHandle) or reject() on the object. """
@@ -300,6 +301,17 @@ class MSNConnection:
        
        def contactRemovedMe(self, userHandle):
                """ Called when a contact removes the user from their list. """
+       
+       def gotInitialEmailNotification(self, inboxunread, foldersunread):
+               """ Received at login to tell about the user's Hotmail status """
+       
+       def gotRealtimeEmailNotification(self, mailfrom, fromaddr, subject):
+               """ Received in realtime whenever an email comes into the hotmail account """
+       
+       def gotMSNAlert(self, body, action, subscr):
+               """ An MSN Alert (http://alerts.msn.com) was received. Body is the
+               text of the alert. 'action' is a url for more information,
+               'subscr' is a url to modify your your alerts subscriptions. """
 
 
 class SavedEvents:
@@ -331,7 +343,7 @@ class DispatchClient(msn.DispatchClient):
 
 class NotificationClient(msn.NotificationClient):
        def loginFailure(self, message):
-               self.factory.msncon.connectionFailed(message)
+               self.factory.msncon.loginFailed(message)
        
        def loggedIn(self, userHandle, verified):
                LogEvent(INFO, self.factory.msncon.ident)
@@ -384,16 +396,20 @@ class NotificationClient(msn.NotificationClient):
        
        def gotContactStatus(self, userHandle, statusCode, screenName): 
                LogEvent(INFO, self.factory.msncon.ident)
-               self.factory.msncon.contactStatusChanged(userHandle, statusCode, screenName)
+               self.factory.msncon.contactStatusChanged(userHandle)
        
        def contactStatusChanged(self, userHandle, statusCode, screenName):
                LogEvent(INFO, self.factory.msncon.ident)
-               self.factory.msncon.contactStatusChanged(userHandle, statusCode, screenName)
+               self.factory.msncon.contactStatusChanged(userHandle)
        
-       def contactOffline(self, userHandle):
+       def contactPersonalChanged(self, userHandle, personal):
                LogEvent(INFO, self.factory.msncon.ident)
-               self.factory.msncon.contactStatusChanged(userHandle, msn.STATUS_OFFLINE, "")
+               self.factory.msncon.contactStatusChanged(userHandle)
 
+       def contactOffline(self, userHandle):
+               LogEvent(INFO, self.factory.msncon.ident)
+               self.factory.msncon.contactStatusChanged(userHandle)
+       
        def gotSwitchboardInvitation(self, sessionID, host, port, key, userHandle, screenName):
                LogEvent(INFO, self.factory.msncon.ident)
                sb = self.factory.msncon.switchboardSessions.get(userHandle)
@@ -414,16 +430,6 @@ class NotificationClient(msn.NotificationClient):
 
        
 
-def switchToMulti(switchboardSession, msncon, userHandle):
-       LogEvent(INFO, switchboardSession.ident)
-       switchboardSession.__class__ = MultiSwitchboardSession
-       del switchboardSession.remoteUser
-       switchboardSession.userJoined(userHandle)
-       msncon.gotGroupchat(switchboardSession)
-       if not switchboardSession.groupchat:
-               raise Exception("YouNeedAGroupchat-WeHaveAProblemError") # FIXME
-
-
 class SwitchboardSessionBase(msn.SwitchboardClient):
        def __init__(self, msncon):
                msn.SwitchboardClient.__init__(self)
@@ -548,7 +554,7 @@ class MultiSwitchboardSession(SwitchboardSessionBase):
                        LogEvent(INFO, self.ident)
                        msn.SwitchboardClient.inviteUser(userHandle)
                else:
-                       self.funcBuffer.append(lamba: msn.SwitchboardClient.inviteUser(userHandle))
+                       self.funcBuffer.append(lambda: msn.SwitchboardClient.inviteUser(userHandle))
        
        def gotMessage(self, message):
                self.groupchat.gotMessage(message.userHandle, message.getMessage())
@@ -587,6 +593,16 @@ class OneSwitchboardSession(SwitchboardSessionBase):
                        self.timeout.cancel()
                self.timeout = None
                self.flushBuffer()
+
+       def _switchToMulti(self):
+               LogEvent(INFO, self.ident)
+               del self.switchboardSessions[self.remoteUser]
+               self.__class__ = MultiSwitchboardSession
+               del self.remoteUser
+               self.msncon.gotGroupchat(switchboardSession)
+               if not self.groupchat:
+                       LogEvent(ERROR, self.ident)
+                       raise Exception("YouNeedAGroupchat-WeHaveAProblemError") # FIXME
        
        def failedMessage(self, text):
                self.msncon.failedMessage(self.remoteUser, text)
@@ -614,7 +630,8 @@ class OneSwitchboardSession(SwitchboardSessionBase):
                        self._ready()
                if userHandle != self.remoteUser:
                        # Another user has joined, so we now have three participants.
-                       switchToMulti(self, msncon, userHandle)
+                       self.switchToMulti(self)
+                       self.userJoined(userHandle)
 
        def userLeft(self, userHandle):
                def wait():