1 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
4 from twisted
.internet
import reactor
5 from twisted
.internet
.protocol
import ClientFactory
6 from twisted
.python
import log
7 from debug
import LogEvent
, INFO
, WARN
, ERROR
21 """ Manages all the Twisted factories, etc """
22 def __init__(self
, username
, password
):
23 self
.username
= username
24 self
.password
= password
28 LogEvent(INFO
, self
.session
.jabberID
)
32 MSNConnection
.removeMe(self
)
34 self
.switchboardSessions
= {}
35 dispatchFactory
= DispatchFactory(self
)
36 reactor
.connectTCP('messenger.hotmail.com', 1863, dispatchFactory
)
37 self
.notificationFactory
= msn
.NotificationFactory()
38 self
.notificationFactory
.userHandle
= self
.username
39 self
.notificationFactory
.password
= self
.password
40 self
.notificationFactory
.msncon
= self
41 self
.notificationFactory
.protocol
= Notification
42 self
.notificationFactory
.initialListVersion
= self
.initialListVersion
43 self
.notificationProtocol
= None
45 self
.savedEvents
= SavedEvents()
49 LogEvent(INFO
, self
.session
.jabberID
)
52 LogEvent(INFO
, self
.session
.jabberID
)
53 if self
.notificationProtocol
:
54 self
.notificationProtocol
.removeMe()
55 if self
.notificationFactory
:
56 self
.notificationFactory
.msncon
= None
57 self
.notificationFactory
= None
58 self
.notificationProtocol
= None
59 self
.savedEvents
= SavedEvents()
60 for userHandle
in self
.switchboardSessions
.copy():
61 self
.switchboardSessions
[userHandle
].removeMe()
62 self
.switchboardSessions
= {}
64 def resourceOffline(self
, offlineResource
):
65 for contact
in self
.switchboardSessions
.keys():
66 if self
.switchboardSessions
[contact
].resource
== offlineResource
:
67 self
.switchboardSessions
[contact
].resource
= self
.highestResource()
69 def getContacts(self
):
70 if self
.notificationFactory
:
71 return self
.notificationFactory
.contacts
76 def sendMessage(self
, remoteUser
, resource
, text
, noerror
):
77 LogEvent(INFO
, self
.session
.jabberID
)
78 if self
.notificationProtocol
:
79 if not self
.switchboardSessions
.has_key(remoteUser
):
80 self
.switchboardSessions
[remoteUser
] = SwitchboardSession(self
, remoteUser
, resource
)
81 self
.switchboardSessions
[remoteUser
].resource
= resource
82 self
.switchboardSessions
[remoteUser
].sendMessage(text
.replace("\n", "\r\n"), noerror
)
84 self
.failedMessage(remoteUser
, text
)
86 def requestAvatar(self
, userHandle
):
87 LogEvent(INFO
, self
.session
.jabberID
)
88 resource
= self
.session
.highestResource()
89 if config
.getAllAvatars
:
90 if not self
.switchboardSessions
.has_key(userHandle
):
91 self
.switchboardSessions
[userHandle
] = SwitchboardSession(self
, userHandle
, resource
)
93 self
.switchboardSessions
[userHandle
].requestAvatar()
95 if self
.switchboardSessions
.has_key(userHandle
): # Only request avatars for open switchboards
96 self
.switchboardSessions
[userHandle
].requestAvatar()
98 def sendTypingToContact(self
, remoteUser
):
99 if self
.switchboardSessions
.has_key(remoteUser
):
100 self
.switchboardSessions
[remoteUser
].sendTypingNofication()
102 def notificationProtocolReady(self
, notificationProtocol
):
103 self
.notificationProtocol
= notificationProtocol
107 def sendSavedEvents(self
):
108 # Hack for events sent before we're logged in
109 self
.savedEvents
.send(self
)
110 self
.savedEvents
= None
112 def changeAvatar(self
, imageData
):
113 if self
.notificationProtocol
:
114 self
.notificationProtocol
.changeAvatar(imageData
, push
=True)
116 self
.savedEvents
.avatarImageData
= imageData
118 def changeStatus(self
, statusCode
, screenName
, personal
):
119 if self
.notificationProtocol
:
121 self
.ourStatusChanged(arg
[0])
123 self
.ourNickChanged(arg
[0])
125 self
.ourPersonalChanged(personal
)
126 LogEvent(INFO
, self
.session
.jabberID
)
128 statusCode
= str(statusCode
.encode("utf-8"))
129 self
.notificationProtocol
.changeStatus(statusCode
).addCallback(cb1
)
131 screenName
= str(screenName
.encode("utf-8"))
132 self
.notificationProtocol
.changeScreenName(screenName
).addCallback(cb2
)
134 personal
= str(personal
.encode("utf-8"))
137 self
.notificationProtocol
.changePersonalMessage(personal
).addCallback(cb3
)
139 self
.savedEvents
.statusCode
= statusCode
140 self
.savedEvents
.screenName
= screenName
141 self
.savedEvents
.personal
= personal
143 def connectionLostBase(self
, reason
):
144 # Attempts to reconnect
145 if self
.tries
< 5 and self
.session
:
146 reactor
.callLater(2 ** self
.tries
, self
.initMe
)
149 self
.connectionLost(self
)
151 def addContact(self
, listType
, userHandle
):
152 if self
.notificationProtocol
:
153 return self
.notificationProtocol
.addContact(listType
, str(userHandle
))
155 self
.savedEvents
.addContacts
.append((listType
, str(userHandle
)))
157 def remContact(self
, listType
, userHandle
, groupID
=0):
158 if self
.notificationProtocol
:
159 return self
.notificationProtocol
.remContact(listType
, str(userHandle
))
161 self
.savedEvents
.remContacts
.append((listType
, str(userHandle
)))
165 def initialEmailNotification(self
, inboxunread
, foldersunread
):
168 def realtimeEmailNotification(self
, mailfrom
, fromaddr
, subject
):
174 def loginFailure(self
, message
):
177 def multipleLogin(self
):
180 def gotMessage(self
, remoteUser
, resource
, text
):
183 def avatarHashChanged(self
, userHandle
, hash):
186 def gotAvatarImage(self
, to
, image
):
189 def listSynchronized(self
):
192 def contactStatusChanged(self
, remoteUser
):
195 def ourStatusChanged(self
, statusCode
):
198 def ourNickChanged(self
, nick
):
201 def ourPersonalChanged(self
, personal
):
204 def userMapping(self
, passport
, jid
):
207 def gotContactTyping(self
, remoteUser
, resource
):
210 def serverGoingDown(self
):
213 def accountNotVerified(self
):
216 def userAddedMe(self
, userHandle
):
219 def userRemovedMe(self
, userHandle
):
222 def failedMessage(self
, remoteUser
, message
):
225 def connectionLost(self
):
235 self
.avatarImageData
= ""
236 self
.addContacts
= []
237 self
.remContacts
= []
239 def send(self
, msncon
):
240 if self
.avatarImageData
:
241 msncon
.notificationProtocol
.changeAvatar(self
.avatarImageData
, push
=False)
242 if self
.nickname
or self
.statusCode
or self
.personal
:
243 msncon
.changeStatus(self
.statusCode
, self
.nickname
, self
.personal
)
244 for listType
, userHandle
in self
.addContacts
:
245 msncon
.addContact(listType
, userHandle
)
246 for listType
, userHandle
in self
.remContacts
:
247 msncon
.remContact(listType
, userHandle
)
251 def switchToGroupchat(switchboardSession
, user1
, user2
):
252 gcsbs
= GroupchatSwitchboardSession()
253 from glue
import LegacyGroupchat
, msn2jid
254 groupchat
= LegacyGroupchat(session
=switchboardSession
.msncon
.session
, resource
=None, existing
=True, switchboardSession
=gcsbs
)
255 gcsbs
.groupchat
= groupchat
256 gcsbs
.msncon
= switchboardSession
.msncon
257 gcsbs
.switchboard
= switchboardSession
.switchboard
258 gcsbs
.switchboard
.switchboardSession
= gcsbs
260 gcsbs
.userJoined(user1
)
261 gcsbs
.userJoined(user2
)
262 groupchat
.sendUserInvite(msn2jid(switchboardSession
.remoteUser
))
263 switchboardSession
.removeMe(False)
264 LogEvent(INFO
, gcsbs
.msncon
.session
.jabberID
)
268 class SwitchboardSessionBase
:
269 def sendMessage(self
, message
, noerror
):
271 def failedMessage(ignored
):
272 if self
.__class
__ == GroupchatSwitchboardSession
:
273 tempmsncon
.failedMessage(self
.groupchat
.roomJID(), message
)
275 tempmsncon
.failedMessage(self
.remoteUser
, message
)
277 tempmsncon
= self
.msncon
# In case MSN tells us the message failed after removeMe()
279 LogEvent(INFO
, self
.ident
)
280 message
= str(message
.encode("utf-8"))
282 if len(message
) < MAXMESSAGESIZE
:
283 msnmessage
= msn
.MSNMessage(message
=message
)
284 msnmessage
.setHeader("Content-Type", "text/plain; charset=UTF-8")
285 msnmessage
.ack
= msn
.MSNMessage
.MESSAGE_NACK
287 d
= self
.switchboard
.sendMessage(msnmessage
)
289 d
.addCallback(failedMessage
)
291 chunks
= int(math
.ceil(len(message
) / float(MAXMESSAGESIZE
)))
293 guid
= utils
.random_guid()
294 while chunk
< chunks
:
295 offset
= chunk
* MAXMESSAGESIZE
296 text
= message
[offset
: offset
+ MAXMESSAGESIZE
]
298 msnmessage
= msn
.MSNMessage(message
=text
)
299 msnmessage
.setHeader("Message-ID", guid
)
301 msnmessage
.setHeader("Content-Type", "text/plain; charset=UTF-8")
302 msnmessage
.setHeader("Chunks", str(chunks
))
304 msnmessage
.setHeader("Chunk", str(chunk
))
305 msnmessage
.ack
= msn
.MSNMessage
.MESSAGE_NACK
307 d
= self
.switchboard
.sendMessage(msnmessage
)
309 d
.addCallback(failedMessage
)
314 self
.messageBuffer
.append((message
, noerror
))
316 def gotAvatarImage(self
, to
, image
):
317 self
.msncon
.gotAvatarImage(to
, image
)
319 def switchboardReady(self
, switchboard
):
320 LogEvent(INFO
, self
.ident
)
322 self
.switchboard
= switchboard
325 def resetTimer(self
):
329 class GroupchatSwitchboardSession(SwitchboardSessionBase
):
330 def __init__(self
, groupchat
=None, makeSwitchboard
=False):
335 self
.ident
= groupchat
.roomJID()
336 self
.groupchat
= groupchat
337 self
.msncon
= self
.groupchat
.session
.legacycon
339 self
.ident
= str(self
)
340 self
.groupchat
= None
341 self
.switchboard
= None
343 self
.messageBuffer
= []
344 self
.invitedUsers
= []
345 self
.oneUserHasJoined
= False
347 if makeSwitchboard
and groupchat
:
348 LogEvent(INFO
, self
.ident
, "Requesting switchboard.")
349 d
= self
.msncon
.notificationProtocol
.requestSwitchboardServer()
350 d
.addCallback(self
.sbRequestAccepted
)
351 d
.addErrback(self
.removeMe
)
354 LogEvent(INFO
, self
.ident
, "Created groupchat for " + self
.msncon
.username
)
358 log
.err("removeMe called more than once!")
362 LogEvent(INFO
, self
.ident
)
365 self
.switchboard
.removeMe()
366 self
.switchboard
= None
367 self
.groupchat
= None
370 utils
.mutilateMe(self
)
372 def sbRequestAccepted(self
, (host
, port
, key
)):
373 # Connect to the switchboard server
374 LogEvent(INFO
, self
.ident
)
375 reactor
.connectTCP(host
, port
, SwitchboardFactory(self
, key
))
377 def sendMessage(self
, message
, noerror
):
378 if self
.oneUserHasJoined
:
379 SwitchboardSessionBase
.sendMessage(self
, message
, noerror
)
381 self
.messageBuffer
.append((message
, noerror
))
383 def inviteUser(self
, userHandle
):
384 userHandle
= str(userHandle
)
386 LogEvent(INFO
, self
.ident
)
387 self
.switchboard
.inviteUser(userHandle
)
389 self
.invitedUsers
.append(userHandle
)
391 def gotMessage(self
, message
):
392 self
.groupchat
.messageReceived(message
.userHandle
, message
.getMessage())
394 def flushBuffer(self
):
395 for m
, noerror
in self
.messageBuffer
[:]:
396 self
.messageBuffer
.remove((m
, noerror
))
397 self
.sendMessage(m
, noerror
)
399 for i
in self
.invitedUsers
[:]:
400 self
.invitedUsers
.remove(i
)
403 def userJoined(self
, userHandle
):
404 LogEvent(INFO
, self
.ident
)
405 self
.oneUserHasJoined
= True
407 self
.groupchat
.contactJoined(userHandle
)
409 def userLeft(self
, userHandle
):
410 LogEvent(INFO
, self
.ident
)
411 self
.groupchat
.contactLeft(userHandle
)
415 class SwitchboardSession(SwitchboardSessionBase
):
416 def __init__(self
, msncon
, remoteUser
, resource
, reply
=False, host
=None, port
=None, key
=None, sessionID
=None):
419 self
.ident
= (msncon
.session
.jabberID
, remoteUser
)
421 self
.remoteUser
= str(remoteUser
)
422 self
.resource
= str(resource
)
424 self
.killTimer
= reactor
.callLater(30.0*60.0, self
.removeMe
)
426 self
.switchboard
= None # The SwitchboardClient class
427 self
.messageBuffer
= [] # Any messages sent before the switchboard is ready are buffered
428 self
.ready
= False # Is True when we are connected to the switchboard, and the remote user has accepted our invite
431 # Request a switchboard
432 d
= self
.msncon
.notificationProtocol
.requestSwitchboardServer()
433 d
.addCallback(self
.sbRequestAccepted
)
434 d
.addErrback(self
.removeMe
)
436 reactor
.connectTCP(host
, port
, SwitchboardFactory(self
, key
, sessionID
, reply
))
438 LogEvent(INFO
, self
.ident
)
440 def removeMe(self
, sbflag
=True):
442 log
.err("removeMe called more than once!")
446 LogEvent(INFO
, self
.ident
)
447 for message
, noerror
in self
.messageBuffer
:
449 self
.msncon
.failedMessage(self
.remoteUser
, message
)
450 self
.messageBuffer
= []
452 del self
.msncon
.switchboardSessions
[self
.remoteUser
]
454 if sbflag
and self
.switchboard
:
455 self
.switchboard
.removeMe()
456 self
.switchboard
= None
458 if self
.killTimer
and not self
.killTimer
.called
:
459 self
.killTimer
.cancel()
460 self
.killTimer
= None
462 utils
.mutilateMe(self
)
464 def resetTimer(self
):
465 # Sets a count down timer to kill this switchboard session in 30 minutes
466 self
.killTimer
.cancel()
467 self
.killTimer
= reactor
.callLater(30.0*60.0, self
.removeMe
)
469 def sbRequestAccepted(self
, (host
, port
, key
)):
470 # Connect to the switchboard server
471 reactor
.connectTCP(host
, port
, SwitchboardFactory(self
, key
))
473 def sendTypingNofication(self
):
475 self
.switchboard
.sendTypingNotification()
477 def contactTyping(self
):
478 self
.msncon
.gotContactTyping(self
.remoteUser
, self
.resource
)
480 def flushBuffer(self
):
481 for m
, noerror
in self
.messageBuffer
[:]:
482 self
.messageBuffer
.remove((m
, noerror
))
483 self
.sendMessage(m
, noerror
)
485 def gotMessage(self
, message
):
486 self
.msncon
.gotMessage(self
.remoteUser
, self
.resource
, message
.getMessage())
489 CAPS
= msn
.MSNContact
.MSNC1 | msn
.MSNContact
.MSNC2 | msn
.MSNContact
.MSNC3 | msn
.MSNContact
.MSNC4
490 def requestAvatar(self
):
491 if not self
.switchboard
: return
492 msnContacts
= self
.msncon
.getContacts()
493 if not msnContacts
: return
494 msnContact
= msnContacts
.getContact(self
.remoteUser
)
495 if not (msnContact
and msnContact
.caps
& self
.CAPS
and msnContact
.msnobj
): return
496 if msnContact
.msnobjGot
: return
497 msnContact
.msnobjGot
= True # This is deliberately set before we get the avatar. So that we don't try to reget failed avatars over & over
498 self
.switchboard
.sendAvatarRequest(self
.remoteUser
, msnContact
.msnobj
)
500 def userJoined(self
, userHandle
):
501 if userHandle
!= self
.remoteUser
:
502 # Another user has joined, so we now have three participants (these two and ourself)
503 switchToGroupchat(self
, self
.remoteUser
, userHandle
)
507 def userLeft(self
, userHandle
):
508 if userHandle
== self
.remoteUser
:
513 class DispatchFactory(ClientFactory
):
514 def __init__(self
, msncon
):
517 def buildProtocol(self
, addr
):
518 p
= Dispatch(self
.msncon
)
519 del self
.msncon
# No longer needed
522 def clientConnectionFailed(self
, connector
, reason
):
523 self
.msncon
.connectionLostBase(reason
)
526 class Dispatch(msn
.DispatchClient
):
527 def __init__(self
, msncon
):
528 msn
.DispatchClient
.__init
__(self
)
530 self
.userHandle
= self
.msncon
.username
536 def gotNotificationReferral(self
, host
, port
):
537 self
.transport
.loseConnection()
538 if self
.msncon
and self
.msncon
.session
and self
.msncon
.session
.alive
:
539 reactor
.connectTCP(host
, port
, self
.msncon
.notificationFactory
)
543 class Notification(msn
.NotificationClient
):
547 msn
.NotificationClient
.__init
__(self
)
551 log
.err("removeMe called more than once!")
556 self
.transport
.loseConnection()
557 if self
.factory
.msncon
:
558 self
.factory
.msncon
.notificationProtocol
= None
559 self
.factory
.msncon
= None
562 utils
.mutilateMe(self
)
564 def badConditions(self
):
565 if not (self
.factory
and self
.factory
.msncon
and self
.factory
.msncon
.session
and self
.factory
.msncon
.session
.alive
):
572 def loginFailure(self
, message
):
573 if self
.badConditions(): return
574 self
.factory
.msncon
.loginFailure(message
)
576 def loggedIn(self
, userHandle
, verified
):
577 if self
.badConditions(): return
579 self
.factory
.msncon
.notificationProtocolReady(self
)
581 self
.factory
.msncon
.accountNotVerified()
583 msn
.NotificationClient
.loggedIn(self
, userHandle
, verified
)
585 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
587 def msnAlertReceived(self
, body
, action
, subscr
):
588 if self
.badConditions(): return
589 self
.factory
.msncon
.msnAlert(body
, action
, subscr
)
591 def initialEmailNotification(self
, inboxunread
, foldersunread
):
592 if self
.badConditions() or not config
.mailNotifications
: return
593 self
.factory
.msncon
.initialEmailNotification(inboxunread
, foldersunread
)
595 def realtimeEmailNotification(self
, mailfrom
, fromaddr
, subject
):
596 if self
.badConditions() or not config
.mailNotifications
: return
597 self
.factory
.msncon
.realtimeEmailNotification(mailfrom
, fromaddr
, subject
)
599 def connectionLost(self
, reason
):
600 if self
.badConditions(): return
602 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
603 msn
.NotificationClient
.connectionLost(self
, reason
)
604 self
.factory
.msncon
.connectionLostBase(reason
)
605 # Make sure this event is handled after any others
606 reactor
.callLater(0, wait
)
608 def listSynchronized(self
, *args
):
609 if self
.badConditions(): return
610 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
611 self
.factory
.msncon
.listSynchronized()
612 if self
.badConditions(): return # Just in case the session is deregistered
613 self
.factory
.msncon
.sendSavedEvents()
614 self
.setPrivacyMode(False)
616 def gotSwitchboardInvitation(self
, sessionID
, host
, port
, key
, remoteUser
, screenName
):
617 if self
.badConditions(): return
618 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
619 sbs
= SwitchboardSession(self
.factory
.msncon
, remoteUser
, self
.factory
.msncon
.session
.highestResource(), True, host
, port
, key
, sessionID
)
620 if self
.factory
.msncon
.switchboardSessions
.has_key(remoteUser
):
621 self
.factory
.msncon
.switchboardSessions
[remoteUser
].removeMe()
622 self
.factory
.msncon
.switchboardSessions
[remoteUser
] = sbs
624 def avatarHashChanged(self
, userHandle
, hash):
625 if self
.badConditions(): return
626 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
627 hash = base64
.decodestring(hash)
628 hash = binascii
.hexlify(hash)
629 self
.factory
.msncon
.avatarHashChanged(userHandle
, hash)
631 def contactStatusChanged(self
, statusCode
, userHandle
, screenName
):
632 if self
.badConditions(): return
633 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
635 self
.factory
.msncon
.contactStatusChanged(userHandle
)
637 def contactPersonalChanged(self
, userHandle
, personal
):
638 if self
.badConditions(): return
639 msn
.NotificationClient
.contactPersonalChanged(self
, userHandle
, personal
)
640 self
.factory
.msncon
.contactStatusChanged(userHandle
)
642 def contactOffline(self
, userHandle
):
643 if self
.badConditions(): return
644 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
645 msn
.NotificationClient
.contactOffline(self
, userHandle
)
647 self
.factory
.msncon
.contactStatusChanged(userHandle
)
649 def userAddedMe(self
, userGuid
, userHandle
, screenName
):
650 if self
.badConditions(): return
651 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
652 msn
.NotificationClient
.userAddedMe(self
, userGuid
, userHandle
, screenName
)
653 self
.factory
.msncon
.userAddedMe(userHandle
)
655 def userRemovedMe(self
, userGuid
, userHandle
):
656 if self
.badConditions(): return
657 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
658 msn
.NotificationClient
.userRemovedMe(self
, userGuid
, userHandle
)
659 self
.factory
.msncon
.userRemovedMe(userHandle
)
661 def multipleLogin(self
):
662 if self
.badConditions(): return
663 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
664 self
.factory
.msncon
.multipleLogin()
668 class SwitchboardFactory(ClientFactory
):
669 def __init__(self
, switchboardSession
, key
, sessionID
=None, reply
=False):
670 self
.switchboardSession
= switchboardSession
672 self
.sessionID
= sessionID
675 def buildProtocol(self
, addr
):
676 p
= Switchboard(self
.switchboardSession
)
677 if p
.badConditions(): return p
679 p
.sessionID
= self
.sessionID
681 p
.userHandle
= self
.switchboardSession
.msncon
.username
685 class Switchboard(msn
.SwitchboardClient
):
686 def __init__(self
, switchboardSession
):
689 self
.switchboardSession
= switchboardSession
690 self
.chattingUsers
= []
692 msn
.SwitchboardClient
.__init
__(self
)
693 if self
.badConditions(): return
694 self
.msnobj
= self
.switchboardSession
.msncon
.notificationProtocol
.msnobj
695 LogEvent(INFO
, self
.switchboardSession
.ident
)
699 log
.err("removeMe called more than once!")
703 self
.transport
.loseConnection()
704 LogEvent(INFO
, self
.switchboardSession
.ident
)
705 self
.switchboardSession
= None
706 self
.factory
.switchboardSession
= None
709 if self
.callid
and not self
.callid
.called
:
710 self
.callid
.cancel() # Cancel the invite fail message
713 utils
.mutilateMe(self
)
715 def badConditions(self
):
716 if not (self
.switchboardSession
and self
.switchboardSession
.msncon
and self
.switchboardSession
.msncon
.session
and self
.switchboardSession
.msncon
.session
.alive
):
717 if self
.switchboardSession
:
718 if not self
.switchboardSession
.removed
:
719 self
.switchboardSession
.removeMe()
720 elif not self
.removed
:
726 if self
.badConditions(): return
727 if (not self
.reply
) and self
.switchboardSession
.__class
__ == SwitchboardSession
:
728 def failCB(arg
=None):
729 LogEvent(INFO
, ident
, "User has not joined after 30 seconds.")
730 self
.switchboardSession
.removeMe()
731 d
= self
.inviteUser(self
.switchboardSession
.remoteUser
)
733 ident
= self
.switchboardSession
.ident
734 # If the user doesn't join then we want to tear down the SwitchboardSession
735 self
.callid
= reactor
.callLater(30.0, failCB
)
738 self
.readySwitchboardSession()
740 def readySwitchboardSession(self
):
741 self
.switchboardSession
.switchboardReady(self
)
742 for user
in self
.chattingUsers
:
743 self
.switchboardSession
.userJoined(user
)
744 if self
.callid
and not self
.callid
.called
:
745 self
.callid
.cancel() # Cancel the invite fail message (only applies if we needed to invite the user)
748 def gotChattingUsers(self
, users
):
750 self
.chattingUsers
.append(user
)
752 def userJoined(self
, userHandle
, screenName
):
753 if self
.badConditions(): return
754 # FIXME - check this is correct
755 if (not self
.reply
) and isinstance(self
.switchboardSession
, SwitchboardSession
):
756 self
.readySwitchboardSession()
757 LogEvent(INFO
, self
.switchboardSession
.ident
)
758 self
.switchboardSession
.userJoined(userHandle
)
760 def userLeft(self
, userHandle
):
761 if self
.badConditions(): return
762 LogEvent(INFO
, self
.switchboardSession
.ident
)
764 if self
.badConditions(): return
765 self
.switchboardSession
.userLeft(userHandle
)
766 # Make sure this event is handled after any others (eg, gotMessage)
767 reactor
.callLater(0, wait
)
769 def gotMessage(self
, message
):
770 if self
.badConditions():
771 LogEvent(WARN
, self
.switchboardSession
.ident
, "gotMessage() called too late. Dropped a message!")
774 LogEvent(INFO
, self
.switchboardSession
.ident
)
775 cTypes
= [s
.lstrip() for s
in message
.getHeader("Content-Type").split(';')]
776 if "text/plain" in cTypes
:
778 if len(cTypes
) > 1 and cTypes
[1].find("UTF-8") >= 0:
779 message
.message
= message
.message
.decode("utf-8")
780 self
.switchboardSession
.gotMessage(message
)
782 self
.switchboardSession
.gotMessage(lang
.get(self
.switchboardSession
.msncon
.session
.lang
).msnDroppedMessage
) # FIXME, this is a little deep
785 if "text/x-clientcaps" in cTypes
:
786 if message
.hasHeader("JabberID"):
787 jid
= message
.getHeader("JabberID")
788 self
.switchboardSession
.msncon
.userMapping(message
.userHandle
, jid
)
790 LogEvent(INFO
, self
.switchboardSession
.ident
, "Discarding unknown message type.")
792 def userTyping(self
, message
):
793 if self
.badConditions(): return
794 if self
.switchboardSession
.__class
__ == SwitchboardSession
: # Ignore typing in groupchats
795 if message
.userHandle
== self
.switchboardSession
.remoteUser
:
796 self
.switchboardSession
.contactTyping()
798 def sendClientCaps(self
):
799 message
= msn
.MSNMessage()
800 message
.setHeader("Content-Type", "text/x-clientcaps")
801 message
.setHeader("Client-Name", "PyMSNt")
802 message
.setHeader("JabberID", str(self
.switchboardSession
.msncon
.session
.jabberID
)) # FIXME, this is a little deep
803 self
.sendMessage(message
)
805 def sendMessage(self
, message
):
806 # A little bit of fancyness to make sure that clientcaps
807 # only gets sent after the first text message.
808 if message
.getHeader("Content-Type").startswith("text"):
809 self
.sendMessage
= type(self
.sendMessage
)(msn
.SwitchboardClient
.sendMessage
, self
, Switchboard
)
810 self
.sendClientCaps()
811 return self
.sendMessage(message
)
813 return msn
.SwitchboardClient
.sendMessage(self
, message
)
815 def gotAvatarImage(self
, to
, image
):
816 if self
.badConditions(): return
817 self
.switchboardSession
.gotAvatarImage(to
, image
)