]>
code.delx.au - pymsnt/blob - src/tlib/msn/msnw.py
1 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
5 from twisted
.internet
import reactor
6 from twisted
.internet
.defer
import Deferred
7 from twisted
.internet
.protocol
import ClientFactory
10 import math
, base64
, binascii
, math
13 from debug
import LogEvent
, INFO
, WARN
, ERROR
14 from tlib
.msn
import msn
18 SWITCHBOARDTIMEOUT
= 30.0*60.0
23 All interaction should be with the MSNConnection and MultiSwitchboardSession classes.
24 You should not directly instantiate any objects of other classes.
28 """ Manages all the Twisted factories, etc """
29 def __init__(self
, username
, password
, ident
):
30 """ Connects to the MSN servers.
31 @param username: the MSN passport to connect with.
32 @param password: the password for this account.
33 @param ident: a unique identifier to use in logging.
35 self
.username
= username
36 self
.password
= password
39 self
.notificationClient
= None
41 LogEvent(INFO
, self
.ident
)
44 """ Automatically called by the constructor """
46 self
.switchboardSessions
= {}
47 self
.savedEvents
= SavedEvents() # Save any events that occur before connect
48 self
._getNotificationReferral
()
50 def _getNotificationReferral(self
):
52 if not d
.called
: d
.errback()
53 self
.timeout
= reactor
.callLater(30, timeout
)
54 dispatchFactory
= msn
.DispatchFactory()
55 dispatchFactory
.userHandle
= self
.username
56 dispatchFactory
.protocol
= DispatchClient
59 d
.addCallbacks(self
._gotNotificationReferral
, self
.connectionFailed
)
60 self
.connectors
.append(reactor
.connectTCP("messenger.hotmail.com", 1863, dispatchFactory
))
61 LogEvent(INFO
, self
.ident
)
63 def _gotNotificationReferral(self
, (host
, port
)):
65 # Create the NotificationClient
66 self
.notificationFactory
= msn
.NotificationFactory()
67 self
.notificationFactory
.userHandle
= self
.username
68 self
.notificationFactory
.password
= self
.password
69 self
.notificationFactory
.msncon
= self
70 self
.notificationFactory
.protocol
= NotificationClient
71 self
.connectors
.append(reactor
.connectTCP(host
, port
, self
.notificationFactory
))
72 LogEvent(INFO
, self
.ident
)
74 def _sendSavedEvents(self
):
75 self
.savedEvents
.send(self
)
76 self
.savedEvents
= None
78 def _notificationClientReady(self
, notificationClient
):
79 self
.notificationClient
= notificationClient
81 def _ensureSwitchboardSession(self
, userHandle
):
82 if not self
.switchboardSessions
.has_key(userHandle
):
83 sb
= OneSwitchboardSession(self
, userHandle
)
85 self
.switchboardSessions
[userHandle
] = sb
90 def getContacts(self
):
91 """ Gets the contact list.
93 @return an instance of MSNContactList (do not modify) if connected,
96 if self
.notificationFactory
:
97 return self
.notificationFactory
.contacts
101 def sendMessage(self
, userHandle
, text
, noerror
=False):
103 Sends a message to a contact. Can only be called after listSynchronized().
105 @param userHandle: the contact's MSN passport.
106 @param text: the text to send.
107 @param noerror: Set this to True if you don't want failed messages to bounce.
109 LogEvent(INFO
, self
.ident
)
110 if self
.notificationClient
:
111 self
._ensureSwitchboardSession
(userHandle
)
112 self
.switchboardSessions
[userHandle
].sendMessage(text
, noerror
)
114 self
.failedMessage(userHandle
, text
)
116 def sendAvatarRequest(self
, userHandle
):
118 Requests the avatar of a contact.
120 @param userHandle: the contact to request an avatar from.
121 @return: a Deferred() if the avatar can be fetched at this time.
122 This will fire with an argument of a tuple with the PNG
123 image data as the only element.
124 Otherwise returns None
127 LogEvent(INFO
, self
.ident
)
128 if not self
.notificationClient
: return
130 self
._ensureSwitchboardSession
(userHandle
)
131 sb
= self
.switchboardSessions
.get(userHandle
)
132 if sb
: return sb
.sendAvatarRequest()
134 def sendFile(self
, userHandle
, filename
, filesize
):
136 Used to send a file to a contact.
138 @param username: the passport of the contact to send a file to.
139 @param filename: the name of the file to send.
140 @param filesize: the size of the file to send.
142 @return: A Deferred, which will fire with an argument of:
143 (fileSend, d) A FileSend object and a Deferred.
144 The Deferred will pass one argument in a tuple,
145 whether or not the transfer is accepted. If you
146 receive a True, then you can call write() on the
147 fileSend object to send your file. Call close()
148 when the file is done.
149 NOTE: You MUST write() exactly as much as you
152 msnContact
= self
.getContacts().getContact(userHandle
)
154 raise ValueError, "Contact not found"
155 self
._ensureSwitchboardSession
(userHandle
)
156 return self
.switchboardSessions
[userHandle
].sendFile(msnContact
, filename
, filesize
)
158 def sendTypingToContact(self
, userHandle
):
160 Sends typing notification to a contact. Should send every 5secs.
161 @param userHandle: the contact to notify of our typing.
164 sb
= self
.switchboardSessions
.get(userHandle
)
165 if sb
: return sb
.sendTypingNotification()
167 def changeAvatar(self
, imageData
):
169 Changes the user's avatar.
170 @param imageData: the new PNG avatar image data.
172 if self
.notificationClient
:
173 LogEvent(INFO
, self
.ident
)
174 self
.notificationClient
.changeAvatar(imageData
, push
=True)
176 self
.savedEvents
.avatarImageData
= imageData
178 def changeStatus(self
, statusCode
, screenName
, personal
):
180 Changes your status details. All details must be given with
181 each call. This can be called before connection if you wish.
183 @param statusCode: the user's new status (look in msn.statusCodes).
184 @param screenName: the user's new screenName (up to 127 characters).
185 @param personal: the user's new personal message.
188 if not screenName
: screenName
= self
.username
189 if self
.notificationClient
:
190 changeCount
= [0] # Hack
191 def cb(ignored
=None):
193 if changeCount
[0] == 3:
194 self
.ourStatusChanged(statusCode
, screenName
, personal
)
196 LogEvent(INFO
, self
.ident
)
197 self
.notificationClient
.changeStatus(statusCode
.encode("utf-8")).addCallback(cb
)
198 self
.notificationClient
.changeScreenName(screenName
.encode("utf-8")).addCallback(cb
)
199 if not personal
: personal
= ""
200 self
.notificationClient
.changePersonalMessage(personal
.encode("utf-8")).addCallback(cb
)
202 self
.savedEvents
.statusCode
= statusCode
203 self
.savedEvents
.screenName
= screenName
204 self
.savedEvents
.personal
= personal
206 def addContact(self
, listType
, userHandle
):
207 """ See msn.NotificationClient.addContact """
208 if self
.notificationClient
:
209 return self
.notificationClient
.addContact(listType
, str(userHandle
))
211 self
.savedEvents
.addContacts
.append((listType
, str(userHandle
)))
213 def remContact(self
, listType
, userHandle
):
214 """ See msn.NotificationClient.remContact """
215 if self
.notificationClient
:
216 return self
.notificationClient
.remContact(listType
, str(userHandle
))
218 self
.savedEvents
.remContacts
.append((listType
, str(userHandle
)))
221 """ Shuts down the whole connection. Don't try to call any
222 other methods after this one. """
223 if self
.notificationClient
:
224 self
.notificationClient
.logOut()
225 for c
in self
.connectors
:
227 if self
.notificationFactory
:
228 self
.notificationFactory
.msncon
= None
230 self
.switchboardSessions
= {}
231 LogEvent(INFO
, self
.ident
)
235 def connectionFailed(self
, reason
=''):
236 """ Called when the connection to the server failed. """
238 def loginFailed(self
, reason
=''):
239 """ Called when the account could not be logged in. """
241 def connectionLost(self
, reason
=''):
242 """ Called when we are disconnected. """
244 def multipleLogin(self
):
245 """ Called when the server says there has been another login
246 for this account. """
248 def serverGoingDown(self
):
249 """ Called when the server says that it will be going down. """
251 def accountNotVerified(self
):
252 """ Called if this passport has not been verified. Certain
253 functions are not available. """
255 def userMapping(self
, passport
, jid
):
256 """ Called when it is brought to our attention that one of the
257 MSN contacts has a Jabber ID. You should communicate with Jabber. """
260 """ Called when we have authenticated, but before we receive
261 the contact list. """
263 def listSynchronized(self
):
264 """ Called when we have received the contact list. All methods
265 in this class are now valid. """
267 def ourStatusChanged(self
, statusCode
, screenName
, personal
):
268 """ Called when the user's status has changed. """
270 def gotMessage(self
, userHandle
, text
):
271 """ Called when a contact sends us a message """
273 def gotGroupchat(self
, msnGroupchat
, userHandle
):
274 """ Called when a conversation with more than one contact begins.
275 userHandle is the person who invited us.
276 The overriding method is expected to set msnGroupchat.groupchat to an object
277 that implements the following methods:
278 contactJoined(userHandle)
279 contactLeft(userHandle)
280 gotMessage(userHandle, text)
282 The object received as 'msnGroupchat' is an instance of MultiSwitchboardSession.
285 def gotContactTyping(self
, userHandle
):
286 """ Called when a contact sends typing notification.
287 Will be called once every 5 seconds. """
289 def failedMessage(self
, userHandle
, text
):
290 """ Called when a message we sent has been bounced back. """
292 def contactAvatarChanged(self
, userHandle
, hash):
293 """ Called when we receive a changed avatar hash for a contact.
294 You should call sendAvatarRequest(). """
296 def contactStatusChanged(self
, userHandle
):
297 """ Called when we receive status information for a contact. """
299 def gotFileReceive(self
, fileReceive
):
300 """ Called when a contact sends the user a file.
301 Call accept(fileHandle) or reject() on the object. """
303 def contactAddedMe(self
, userHandle
):
304 """ Called when a contact adds the user to their list. """
306 def contactRemovedMe(self
, userHandle
):
307 """ Called when a contact removes the user from their list. """
309 def gotInitialEmailNotification(self
, inboxunread
, foldersunread
):
310 """ Received at login to tell about the user's Hotmail status """
312 def gotRealtimeEmailNotification(self
, mailfrom
, fromaddr
, subject
):
313 """ Received in realtime whenever an email comes into the hotmail account """
315 def gotMSNAlert(self
, body
, action
, subscr
):
316 """ An MSN Alert (http://alerts.msn.com) was received. Body is the
317 text of the alert. 'action' is a url for more information,
318 'subscr' is a url to modify your your alerts subscriptions. """
320 def gotAvatarImageData(self
, userHandle
, imageData
):
321 """ An contact's avatar has been received because a switchboard
322 session with them was started. """
330 self
.avatarImageData
= ""
331 self
.addContacts
= []
332 self
.remContacts
= []
334 def send(self
, msncon
):
335 if self
.avatarImageData
:
336 msncon
.notificationClient
.changeAvatar(self
.avatarImageData
, push
=False)
337 if self
.nickname
or self
.statusCode
or self
.personal
:
338 msncon
.changeStatus(self
.statusCode
, self
.nickname
, self
.personal
)
339 for listType
, userHandle
in self
.addContacts
:
340 msncon
.addContact(listType
, userHandle
)
341 for listType
, userHandle
in self
.remContacts
:
342 msncon
.remContact(listType
, userHandle
)
346 class DispatchClient(msn
.DispatchClient
):
347 def gotNotificationReferral(self
, host
, port
):
348 if self
.factory
.d
.called
: return # Too slow! We've already timed out
349 self
.factory
.d
.callback((host
, port
))
352 class NotificationClient(msn
.NotificationClient
):
353 def loginFailure(self
, message
):
354 self
.factory
.msncon
.loginFailed(message
)
356 def loggedIn(self
, userHandle
, verified
):
357 LogEvent(INFO
, self
.factory
.msncon
.ident
)
358 msn
.NotificationClient
.loggedIn(self
, userHandle
, verified
)
359 self
.factory
.msncon
._notificationClientReady
(self
)
360 self
.factory
.msncon
.loggedIn()
362 self
.factory
.msncon
.accountNotVerified()
365 msn
.NotificationClient
.logOut(self
)
367 def connectionLost(self
, reason
):
368 if not self
.factory
.msncon
: return # If we called logOut
370 LogEvent(INFO
, self
.factory
.msncon
.ident
)
371 msn
.NotificationClient
.connectionLost(self
, reason
)
372 self
.factory
.msncon
.connectionLost(reason
)
373 # Make sure this event is handled after any others
374 reactor
.callLater(0, wait
)
376 def gotMSNAlert(self
, body
, action
, subscr
):
377 LogEvent(INFO
, self
.factory
.msncon
.ident
)
378 self
.factory
.msncon
.gotMSNAlert(body
, action
, subscr
)
380 def gotInitialEmailNotification(self
, inboxunread
, foldersunread
):
381 LogEvent(INFO
, self
.factory
.msncon
.ident
)
382 self
.factory
.msncon
.gotInitialEmailNotification(inboxunread
, foldersunread
)
384 def gotRealtimeEmailNotification(self
, mailfrom
, fromaddr
, subject
):
385 LogEvent(INFO
, self
.factory
.msncon
.ident
)
386 self
.factory
.msncon
.gotRealtimeEmailNotification(mailfrom
, fromaddr
, subject
)
388 def userAddedMe(self
, userGuid
, userHandle
, screenName
):
389 LogEvent(INFO
, self
.factory
.msncon
.ident
)
390 self
.factory
.msncon
.contactAddedMe(userHandle
)
392 def userRemovedMe(self
, userHandle
):
393 LogEvent(INFO
, self
.factory
.msncon
.ident
)
394 self
.factory
.msncon
.contactRemovedMe(userHandle
)
396 def listSynchronized(self
, *args
):
397 LogEvent(INFO
, self
.factory
.msncon
.ident
)
398 self
.factory
.msncon
._sendSavedEvents
()
399 self
.factory
.msncon
.listSynchronized()
401 def contactAvatarChanged(self
, userHandle
, hash):
402 LogEvent(INFO
, self
.factory
.msncon
.ident
)
403 self
.factory
.msncon
.contactAvatarChanged(userHandle
, hash)
405 def gotContactStatus(self
, userHandle
, statusCode
, screenName
):
406 LogEvent(INFO
, self
.factory
.msncon
.ident
)
407 self
.factory
.msncon
.contactStatusChanged(userHandle
)
409 def contactStatusChanged(self
, userHandle
, statusCode
, screenName
):
410 LogEvent(INFO
, self
.factory
.msncon
.ident
)
411 self
.factory
.msncon
.contactStatusChanged(userHandle
)
413 def contactPersonalChanged(self
, userHandle
, personal
):
414 LogEvent(INFO
, self
.factory
.msncon
.ident
)
415 self
.factory
.msncon
.contactStatusChanged(userHandle
)
417 def contactOffline(self
, userHandle
):
418 LogEvent(INFO
, self
.factory
.msncon
.ident
)
419 self
.factory
.msncon
.contactStatusChanged(userHandle
)
421 def gotSwitchboardInvitation(self
, sessionID
, host
, port
, key
, userHandle
, screenName
):
422 LogEvent(INFO
, self
.factory
.msncon
.ident
)
423 sb
= self
.factory
.msncon
.switchboardSessions
.get(userHandle
)
424 if sb
and sb
.transport
:
425 sb
.transport
.loseConnection()
427 sb
= OneSwitchboardSession(self
.factory
.msncon
, userHandle
)
428 self
.factory
.msncon
.switchboardSessions
[userHandle
] = sb
429 sb
.connectReply(host
, port
, key
, sessionID
)
431 def multipleLogin(self
):
432 LogEvent(INFO
, self
.factory
.msncon
.ident
)
433 self
.factory
.msncon
.multipleLogin()
435 def serverGoingDown(self
):
436 LogEvent(INFO
, self
.factory
.msncon
.ident
)
437 self
.factory
.msncon
.serverGoingDown()
441 class SwitchboardSessionBase(msn
.SwitchboardClient
):
442 def __init__(self
, msncon
):
443 msn
.SwitchboardClient
.__init
__(self
)
445 self
.msnobj
= msncon
.notificationClient
.msnobj
446 self
.userHandle
= msncon
.username
447 self
.ident
= (msncon
.ident
, "INVALID!!")
448 self
.messageBuffer
= []
453 LogEvent(INFO
, self
.ident
)
455 self
.transport
.disconnect()
458 LogEvent(INFO
, self
.ident
)
463 LogEvent(INFO
, self
.ident
)
465 def sbRequestAccepted((host
, port
, key
)):
466 LogEvent(INFO
, self
.ident
)
469 factory
= ClientFactory()
470 factory
.buildProtocol
= lambda addr
: self
471 reactor
.connectTCP(host
, port
, factory
)
472 def sbRequestFailed(ignored
=None):
473 LogEvent(INFO
, self
.ident
)
474 del self
.msncon
.switchboardSessions
[self
.remoteUser
]
475 d
= self
.msncon
.notificationClient
.requestSwitchboardServer()
476 d
.addCallbacks(sbRequestAccepted
, sbRequestFailed
)
478 def connectReply(self
, host
, port
, key
, sessionID
):
479 LogEvent(INFO
, self
.ident
)
482 self
.sessionID
= sessionID
484 factory
= ClientFactory()
485 factory
.buildProtocol
= lambda addr
: self
486 reactor
.connectTCP(host
, port
, factory
)
488 def flushBuffer(self
):
489 for message
, noerror
in self
.messageBuffer
[:]:
490 self
.messageBuffer
.remove((message
, noerror
))
491 self
.sendMessage(message
, noerror
)
492 for f
in self
.funcBuffer
[:]:
493 self
.funcBuffer
.remove(f
)
496 def failedMessage(self
, *ignored
):
497 raise NotImplementedError
499 def sendClientCaps(self
):
500 message
= msn
.MSNMessage()
501 message
.setHeader("Content-Type", "text/x-clientcaps")
502 message
.setHeader("Client-Name", "PyMSNt")
503 if hasattr(self
.msncon
, "jabberID"):
504 message
.setHeader("JabberID", str(self
.msncon
.jabberID
))
505 self
.sendMessage(message
)
507 def sendMessage(self
, message
, noerror
=False):
508 # Check to make sure that clientcaps only gets sent after
509 # the first text type message.
510 if isinstance(message
, msn
.MSNMessage
) and message
.getHeader("Content-Type").startswith("text"):
511 self
.sendMessage
= self
.sendMessageReal
512 self
.sendClientCaps()
513 return self
.sendMessage(message
, noerror
)
515 return self
.sendMessageReal(message
, noerror
)
517 def sendMessageReal(self
, text
, noerror
=False):
518 if not isinstance(text
, basestring
):
519 msn
.SwitchboardClient
.sendMessage(self
, text
)
522 self
.messageBuffer
.append((text
, noerror
))
524 LogEvent(INFO
, self
.ident
)
525 def failedMessage(ignored
):
527 self
.failedMessage(text
)
529 if len(text
) < MAXMESSAGESIZE
:
530 message
= msn
.MSNMessage(message
=str(text
.replace("\n", "\r\n").encode("utf-8")))
531 message
.setHeader("Content-Type", "text/plain; charset=UTF-8")
532 message
.ack
= msn
.MSNMessage
.MESSAGE_NACK
534 d
= msn
.SwitchboardClient
.sendMessage(self
, message
)
536 d
.addCallback(failedMessage
)
539 chunks
= int(math
.ceil(len(text
) / float(MAXMESSAGESIZE
)))
541 guid
= msn
.random_guid()
542 while chunk
< chunks
:
543 offset
= chunk
* MAXMESSAGESIZE
544 text
= message
[offset
: offset
+ MAXMESSAGESIZE
]
545 message
= msn
.MSNMessage(message
=str(text
.replace("\n", "\r\n").encode("utf-8")))
546 message
.ack
= msn
.MSNMessage
.MESSAGE_NACK
548 message
.setHeader("Content-Type", "text/plain; charset=UTF-8")
549 message
.setHeader("Chunks", str(chunks
))
551 message
.setHeader("Chunk", str(chunk
))
553 d
= msn
.SwitchboardClient
.sendMessage(self
, message
)
555 d
.addCallback(failedMessage
)
560 class MultiSwitchboardSession(SwitchboardSessionBase
):
561 """ Create one of me to chat to multiple contacts """
563 def __init__(self
, msncon
):
564 """ Automatically creates a new switchboard connection to the server """
565 SwitchboardSessionBase
.__init
__(self
, msncon
)
566 self
.ident
= (self
.msncon
.ident
, self
)
567 self
.contactCount
= 0
568 self
.groupchat
= None
571 def failedMessage(self
, text
):
572 self
.groupchat
.gotMessage("BOUNCE", text
)
574 def sendMessage(self
, text
, noerror
=False):
575 """ Used to send a mesage to the groupchat. Can be called immediately
576 after instantiation. """
577 if self
.contactCount
> 0:
578 SwitchboardSessionBase
.sendMessage(self
, text
, noerror
)
580 #self.messageBuffer.append((message, noerror))
581 pass # They're sending messages to an empty room. Ignore.
583 def inviteUser(self
, userHandle
):
584 """ Used to invite a contact to the groupchat. Can be called immediately
585 after instantiation. """
586 userHandle
= str(userHandle
)
588 LogEvent(INFO
, self
.ident
, "immediate")
589 msn
.SwitchboardClient
.inviteUser(self
, userHandle
)
591 LogEvent(INFO
, self
.ident
, "pending")
592 self
.funcBuffer
.append(lambda: msn
.SwitchboardClient
.inviteUser(self
, userHandle
))
594 def gotMessage(self
, message
):
595 self
.groupchat
.gotMessage(message
.userHandle
, message
.getMessage())
597 def userJoined(self
, userHandle
, screenName
=''):
598 LogEvent(INFO
, self
.ident
)
599 self
.contactCount
+= 1
600 self
.groupchat
.contactJoined(userHandle
)
602 def userLeft(self
, userHandle
):
603 LogEvent(INFO
, self
.ident
)
604 self
.contactCount
-= 1
605 self
.groupchat
.contactLeft(userHandle
)
609 class OneSwitchboardSession(SwitchboardSessionBase
):
610 def __init__(self
, msncon
, remoteUser
):
611 SwitchboardSessionBase
.__init
__(self
, msncon
)
612 self
.remoteUser
= str(remoteUser
)
613 self
.ident
= (self
.msncon
.ident
, self
.remoteUser
)
614 self
.chattingUsers
= []
618 for message
, noerror
in self
.messageBuffer
:
620 self
.failedMessage(self
.remoteUser
, message
)
623 LogEvent(INFO
, self
.ident
)
625 for user
in self
.chattingUsers
:
626 self
.userJoined(user
)
628 self
.timeout
.cancel()
632 def _switchToMulti(self
, userHandle
):
633 LogEvent(INFO
, self
.ident
)
634 del self
.msncon
.switchboardSessions
[self
.remoteUser
]
635 self
.__class
__ = MultiSwitchboardSession
637 self
.contactCount
= 0
638 self
.msncon
.gotGroupchat(self
, userHandle
)
639 if not self
.groupchat
:
640 LogEvent(ERROR
, self
.ident
)
641 raise Exception("YouNeedAGroupchat-WeHaveAProblemError") # FIXME
643 def failedMessage(self
, text
):
644 self
.msncon
.failedMessage(self
.remoteUser
, text
)
648 LogEvent(INFO
, self
.ident
)
650 def failCB(arg
=None):
651 LogEvent(INFO
, self
.ident
, "User has not joined after 30 seconds.")
652 del self
.msncon
.switchboardSessions
[self
.remoteUser
]
653 d
= self
.inviteUser(self
.remoteUser
)
655 self
.timeout
= reactor
.callLater(30.0, failCB
)
659 def gotChattingUsers(self
, users
):
660 for userHandle
in users
.keys():
661 self
.chattingUsers
.append(userHandle
)
663 def userJoined(self
, userHandle
, screenName
=''):
664 LogEvent(INFO
, self
.ident
)
667 if userHandle
!= self
.remoteUser
:
668 # Another user has joined, so we now have three participants.
669 remoteUser
= self
.remoteUser
670 self
._switchToMulti
(remoteUser
)
671 self
.userJoined(remoteUser
)
672 self
.userJoined(userHandle
)
674 def updateAvatarCB((imageData
, )):
676 self
.msncon
.gotAvatarImageData(self
.remoteUser
, imageData
)
677 d
= self
.sendAvatarRequest()
679 d
.addCallback(updateAvatarCB
)
681 def userLeft(self
, userHandle
):
683 if userHandle
== self
.remoteUser
:
684 del self
.msncon
.switchboardSessions
[self
.remoteUser
]
685 reactor
.callLater(0, wait
) # Make sure this is handled after everything else
687 def gotMessage(self
, message
):
688 LogEvent(INFO
, self
.ident
)
689 cTypes
= [s
.strip() for s
in message
.getHeader("Content-Type").split(';')]
690 if "text/plain" == cTypes
[0]:
692 if len(cTypes
) > 1 and cTypes
[1].lower().find("utf-8") >= 0:
693 text
= message
.getMessage().decode("utf-8")
695 text
= message
.getMessage()
696 self
.msncon
.gotMessage(self
.remoteUser
, text
)
698 self
.msncon
.gotMessage("A message was lost.")
700 elif "text/x-clientcaps" == cTypes
[0]:
701 if message
.hasHeader("JabberID"):
702 jid
= message
.getHeader("JabberID")
703 self
.switchboardSession
.msncon
.userMapping(message
.userHandle
, jid
)
705 LogEvent(INFO
, self
.ident
, "Discarding unknown message type.")
707 def gotFileReceive(self
, fileReceive
):
708 LogEvent(INFO
, self
.ident
)
709 self
.msncon
.gotFileReceive(fileReceive
)
711 def gotContactTyping(self
, message
):
712 LogEvent(INFO
, self
.ident
)
713 self
.msncon
.gotContactTyping(message
.userHandle
)
715 def sendTypingNotification(self
):
716 LogEvent(INFO
, self
.ident
)
718 msn
.SwitchboardClient
.sendTypingNotification(self
)
720 CAPS
= msn
.MSNContact
.MSNC1 | msn
.MSNContact
.MSNC2 | msn
.MSNContact
.MSNC3 | msn
.MSNContact
.MSNC4
721 def sendAvatarRequest(self
):
722 if not self
.ready
: return
723 msnContacts
= self
.msncon
.getContacts()
724 if not msnContacts
: return
725 msnContact
= msnContacts
.getContact(self
.remoteUser
)
726 if not (msnContact
and msnContact
.caps
& self
.CAPS
and msnContact
.msnobj
): return
727 if msnContact
.msnobjGot
: return
728 msnContact
.msnobjGot
= True # This is deliberately set before we get the avatar. So that we don't try to reget failed avatars over & over
729 return msn
.SwitchboardClient
.sendAvatarRequest(self
, msnContact
)
731 def sendFile(self
, msnContact
, filename
, filesize
):
732 def doSendFile(ignored
=None):
733 d
.callback(msn
.SwitchboardClient
.sendFile(self
, msnContact
, filename
, filesize
))
736 reactor
.callLater(0, doSendFile
)
738 self
.funcBuffer
.append(doSendFile
)