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 |