| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 | |
| 5 """L{twisted.words} support for Instance Messenger.""" | |
| 6 | |
| 7 from __future__ import nested_scopes | |
| 8 | |
| 9 from twisted.internet import defer | |
| 10 from twisted.internet import error | |
| 11 from twisted.python import log | |
| 12 from twisted.python.failure import Failure | |
| 13 from twisted.spread import pb | |
| 14 | |
| 15 from twisted.words.im.locals import ONLINE, OFFLINE, AWAY | |
| 16 | |
| 17 from twisted.words.im import basesupport, interfaces | |
| 18 from zope.interface import implements | |
| 19 | |
| 20 | |
| 21 class TwistedWordsPerson(basesupport.AbstractPerson): | |
| 22 """I a facade for a person you can talk to through a twisted.words service. | |
| 23 """ | |
| 24 def __init__(self, name, wordsAccount): | |
| 25 basesupport.AbstractPerson.__init__(self, name, wordsAccount) | |
| 26 self.status = OFFLINE | |
| 27 | |
| 28 def isOnline(self): | |
| 29 return ((self.status == ONLINE) or | |
| 30 (self.status == AWAY)) | |
| 31 | |
| 32 def getStatus(self): | |
| 33 return self.status | |
| 34 | |
| 35 def sendMessage(self, text, metadata): | |
| 36 """Return a deferred... | |
| 37 """ | |
| 38 if metadata: | |
| 39 d=self.account.client.perspective.directMessage(self.name, | |
| 40 text, metadata) | |
| 41 d.addErrback(self.metadataFailed, "* "+text) | |
| 42 return d | |
| 43 else: | |
| 44 return self.account.client.perspective.callRemote('directMessage',se
lf.name, text) | |
| 45 | |
| 46 def metadataFailed(self, result, text): | |
| 47 print "result:",result,"text:",text | |
| 48 return self.account.client.perspective.directMessage(self.name, text) | |
| 49 | |
| 50 def setStatus(self, status): | |
| 51 self.status = status | |
| 52 self.chat.getContactsList().setContactStatus(self) | |
| 53 | |
| 54 class TwistedWordsGroup(basesupport.AbstractGroup): | |
| 55 implements(interfaces.IGroup) | |
| 56 def __init__(self, name, wordsClient): | |
| 57 basesupport.AbstractGroup.__init__(self, name, wordsClient) | |
| 58 self.joined = 0 | |
| 59 | |
| 60 def sendGroupMessage(self, text, metadata=None): | |
| 61 """Return a deferred. | |
| 62 """ | |
| 63 #for backwards compatibility with older twisted.words servers. | |
| 64 if metadata: | |
| 65 d=self.account.client.perspective.callRemote( | |
| 66 'groupMessage', self.name, text, metadata) | |
| 67 d.addErrback(self.metadataFailed, "* "+text) | |
| 68 return d | |
| 69 else: | |
| 70 return self.account.client.perspective.callRemote('groupMessage', | |
| 71 self.name, text) | |
| 72 | |
| 73 def setTopic(self, text): | |
| 74 self.account.client.perspective.callRemote( | |
| 75 'setGroupMetadata', | |
| 76 {'topic': text, 'topic_author': self.client.name}, | |
| 77 self.name) | |
| 78 | |
| 79 def metadataFailed(self, result, text): | |
| 80 print "result:",result,"text:",text | |
| 81 return self.account.client.perspective.callRemote('groupMessage', | |
| 82 self.name, text) | |
| 83 | |
| 84 def joining(self): | |
| 85 self.joined = 1 | |
| 86 | |
| 87 def leaving(self): | |
| 88 self.joined = 0 | |
| 89 | |
| 90 def leave(self): | |
| 91 return self.account.client.perspective.callRemote('leaveGroup', | |
| 92 self.name) | |
| 93 | |
| 94 | |
| 95 | |
| 96 class TwistedWordsClient(pb.Referenceable, basesupport.AbstractClientMixin): | |
| 97 """In some cases, this acts as an Account, since it a source of text | |
| 98 messages (multiple Words instances may be on a single PB connection) | |
| 99 """ | |
| 100 def __init__(self, acct, serviceName, perspectiveName, chatui, | |
| 101 _logonDeferred=None): | |
| 102 self.accountName = "%s (%s:%s)" % (acct.accountName, serviceName, perspe
ctiveName) | |
| 103 self.name = perspectiveName | |
| 104 print "HELLO I AM A PB SERVICE", serviceName, perspectiveName | |
| 105 self.chat = chatui | |
| 106 self.account = acct | |
| 107 self._logonDeferred = _logonDeferred | |
| 108 | |
| 109 def getPerson(self, name): | |
| 110 return self.chat.getPerson(name, self) | |
| 111 | |
| 112 def getGroup(self, name): | |
| 113 return self.chat.getGroup(name, self) | |
| 114 | |
| 115 def getGroupConversation(self, name): | |
| 116 return self.chat.getGroupConversation(self.getGroup(name)) | |
| 117 | |
| 118 def addContact(self, name): | |
| 119 self.perspective.callRemote('addContact', name) | |
| 120 | |
| 121 def remote_receiveGroupMembers(self, names, group): | |
| 122 print 'received group members:', names, group | |
| 123 self.getGroupConversation(group).setGroupMembers(names) | |
| 124 | |
| 125 def remote_receiveGroupMessage(self, sender, group, message, metadata=None): | |
| 126 print 'received a group message', sender, group, message, metadata | |
| 127 self.getGroupConversation(group).showGroupMessage(sender, message, metad
ata) | |
| 128 | |
| 129 def remote_memberJoined(self, member, group): | |
| 130 print 'member joined', member, group | |
| 131 self.getGroupConversation(group).memberJoined(member) | |
| 132 | |
| 133 def remote_memberLeft(self, member, group): | |
| 134 print 'member left' | |
| 135 self.getGroupConversation(group).memberLeft(member) | |
| 136 | |
| 137 def remote_notifyStatusChanged(self, name, status): | |
| 138 self.chat.getPerson(name, self).setStatus(status) | |
| 139 | |
| 140 def remote_receiveDirectMessage(self, name, message, metadata=None): | |
| 141 self.chat.getConversation(self.chat.getPerson(name, self)).showMessage(m
essage, metadata) | |
| 142 | |
| 143 def remote_receiveContactList(self, clist): | |
| 144 for name, status in clist: | |
| 145 self.chat.getPerson(name, self).setStatus(status) | |
| 146 | |
| 147 def remote_setGroupMetadata(self, dict_, groupName): | |
| 148 if dict_.has_key("topic"): | |
| 149 self.getGroupConversation(groupName).setTopic(dict_["topic"], dict_.
get("topic_author", None)) | |
| 150 | |
| 151 def joinGroup(self, name): | |
| 152 self.getGroup(name).joining() | |
| 153 return self.perspective.callRemote('joinGroup', name).addCallback(self._
cbGroupJoined, name) | |
| 154 | |
| 155 def leaveGroup(self, name): | |
| 156 self.getGroup(name).leaving() | |
| 157 return self.perspective.callRemote('leaveGroup', name).addCallback(self.
_cbGroupLeft, name) | |
| 158 | |
| 159 def _cbGroupJoined(self, result, name): | |
| 160 groupConv = self.chat.getGroupConversation(self.getGroup(name)) | |
| 161 groupConv.showGroupMessage("sys", "you joined") | |
| 162 self.perspective.callRemote('getGroupMembers', name) | |
| 163 | |
| 164 def _cbGroupLeft(self, result, name): | |
| 165 print 'left',name | |
| 166 groupConv = self.chat.getGroupConversation(self.getGroup(name), 1) | |
| 167 groupConv.showGroupMessage("sys", "you left") | |
| 168 | |
| 169 def connected(self, perspective): | |
| 170 print 'Connected Words Client!', perspective | |
| 171 if self._logonDeferred is not None: | |
| 172 self._logonDeferred.callback(self) | |
| 173 self.perspective = perspective | |
| 174 self.chat.getContactsList() | |
| 175 | |
| 176 | |
| 177 pbFrontEnds = { | |
| 178 "twisted.words": TwistedWordsClient, | |
| 179 "twisted.reality": None | |
| 180 } | |
| 181 | |
| 182 | |
| 183 class PBAccount(basesupport.AbstractAccount): | |
| 184 implements(interfaces.IAccount) | |
| 185 gatewayType = "PB" | |
| 186 _groupFactory = TwistedWordsGroup | |
| 187 _personFactory = TwistedWordsPerson | |
| 188 | |
| 189 def __init__(self, accountName, autoLogin, username, password, host, port, | |
| 190 services=None): | |
| 191 """ | |
| 192 @param username: The name of your PB Identity. | |
| 193 @type username: string | |
| 194 """ | |
| 195 basesupport.AbstractAccount.__init__(self, accountName, autoLogin, | |
| 196 username, password, host, port) | |
| 197 self.services = [] | |
| 198 if not services: | |
| 199 services = [('twisted.words', 'twisted.words', username)] | |
| 200 for serviceType, serviceName, perspectiveName in services: | |
| 201 self.services.append([pbFrontEnds[serviceType], serviceName, | |
| 202 perspectiveName]) | |
| 203 | |
| 204 def logOn(self, chatui): | |
| 205 """ | |
| 206 @returns: this breaks with L{interfaces.IAccount} | |
| 207 @returntype: DeferredList of L{interfaces.IClient}s | |
| 208 """ | |
| 209 # Overriding basesupport's implementation on account of the | |
| 210 # fact that _startLogOn tends to return a deferredList rather | |
| 211 # than a simple Deferred, and we need to do registerAccountClient. | |
| 212 if (not self._isConnecting) and (not self._isOnline): | |
| 213 self._isConnecting = 1 | |
| 214 d = self._startLogOn(chatui) | |
| 215 d.addErrback(self._loginFailed) | |
| 216 def registerMany(results): | |
| 217 for success, result in results: | |
| 218 if success: | |
| 219 chatui.registerAccountClient(result) | |
| 220 self._cb_logOn(result) | |
| 221 else: | |
| 222 log.err(result) | |
| 223 d.addCallback(registerMany) | |
| 224 return d | |
| 225 else: | |
| 226 raise error.ConnectionError("Connection in progress") | |
| 227 | |
| 228 | |
| 229 def _startLogOn(self, chatui): | |
| 230 print 'Connecting...', | |
| 231 d = pb.getObjectAt(self.host, self.port) | |
| 232 d.addCallbacks(self._cbConnected, self._ebConnected, | |
| 233 callbackArgs=(chatui,)) | |
| 234 return d | |
| 235 | |
| 236 def _cbConnected(self, root, chatui): | |
| 237 print 'Connected!' | |
| 238 print 'Identifying...', | |
| 239 d = pb.authIdentity(root, self.username, self.password) | |
| 240 d.addCallbacks(self._cbIdent, self._ebConnected, | |
| 241 callbackArgs=(chatui,)) | |
| 242 return d | |
| 243 | |
| 244 def _cbIdent(self, ident, chatui): | |
| 245 if not ident: | |
| 246 print 'falsely identified.' | |
| 247 return self._ebConnected(Failure(Exception("username or password inc
orrect"))) | |
| 248 print 'Identified!' | |
| 249 dl = [] | |
| 250 for handlerClass, sname, pname in self.services: | |
| 251 d = defer.Deferred() | |
| 252 dl.append(d) | |
| 253 handler = handlerClass(self, sname, pname, chatui, d) | |
| 254 ident.callRemote('attach', sname, pname, handler).addCallback(handle
r.connected) | |
| 255 return defer.DeferredList(dl) | |
| 256 | |
| 257 def _ebConnected(self, error): | |
| 258 print 'Not connected.' | |
| 259 return error | |
| 260 | |
| OLD | NEW |