OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 # | |
5 | |
6 """Instance Messenger base classes for protocol support. | |
7 | |
8 You will find these useful if you're adding a new protocol to IM. | |
9 """ | |
10 | |
11 # Abstract representation of chat "model" classes | |
12 | |
13 from twisted.words.im.locals import ONLINE, OFFLINE, OfflineError | |
14 from twisted.words.im import interfaces | |
15 | |
16 from twisted.internet.protocol import Protocol | |
17 | |
18 from twisted.python.reflect import prefixedMethods | |
19 from twisted.persisted import styles | |
20 | |
21 from twisted.internet import error | |
22 | |
23 class AbstractGroup: | |
24 def __init__(self, name, account): | |
25 self.name = name | |
26 self.account = account | |
27 | |
28 def getGroupCommands(self): | |
29 """finds group commands | |
30 | |
31 these commands are methods on me that start with imgroup_; they are | |
32 called with no arguments | |
33 """ | |
34 return prefixedMethods(self, "imgroup_") | |
35 | |
36 def getTargetCommands(self, target): | |
37 """finds group commands | |
38 | |
39 these commands are methods on me that start with imgroup_; they are | |
40 called with a user present within this room as an argument | |
41 | |
42 you may want to override this in your group in order to filter for | |
43 appropriate commands on the given user | |
44 """ | |
45 return prefixedMethods(self, "imtarget_") | |
46 | |
47 def join(self): | |
48 if not self.account.client: | |
49 raise OfflineError | |
50 self.account.client.joinGroup(self.name) | |
51 | |
52 def leave(self): | |
53 if not self.account.client: | |
54 raise OfflineError | |
55 self.account.client.leaveGroup(self.name) | |
56 | |
57 def __repr__(self): | |
58 return '<%s %r>' % (self.__class__, self.name) | |
59 | |
60 def __str__(self): | |
61 return '%s@%s' % (self.name, self.account.accountName) | |
62 | |
63 class AbstractPerson: | |
64 def __init__(self, name, baseAccount): | |
65 self.name = name | |
66 self.account = baseAccount | |
67 self.status = OFFLINE | |
68 | |
69 def getPersonCommands(self): | |
70 """finds person commands | |
71 | |
72 these commands are methods on me that start with imperson_; they are | |
73 called with no arguments | |
74 """ | |
75 return prefixedMethods(self, "imperson_") | |
76 | |
77 def getIdleTime(self): | |
78 """ | |
79 Returns a string. | |
80 """ | |
81 return '--' | |
82 | |
83 def __repr__(self): | |
84 return '<%s %r/%s>' % (self.__class__, self.name, self.status) | |
85 | |
86 def __str__(self): | |
87 return '%s@%s' % (self.name, self.account.accountName) | |
88 | |
89 class AbstractClientMixin: | |
90 """Designed to be mixed in to a Protocol implementing class. | |
91 | |
92 Inherit from me first. | |
93 | |
94 @ivar _logonDeferred: Fired when I am done logging in. | |
95 """ | |
96 def __init__(self, account, chatui, logonDeferred): | |
97 for base in self.__class__.__bases__: | |
98 if issubclass(base, Protocol): | |
99 self.__class__._protoBase = base | |
100 break | |
101 else: | |
102 pass | |
103 self.account = account | |
104 self.chat = chatui | |
105 self._logonDeferred = logonDeferred | |
106 | |
107 def connectionMade(self): | |
108 self._protoBase.connectionMade(self) | |
109 | |
110 def connectionLost(self, reason): | |
111 self.account._clientLost(self, reason) | |
112 self.unregisterAsAccountClient() | |
113 return self._protoBase.connectionLost(self, reason) | |
114 | |
115 def unregisterAsAccountClient(self): | |
116 """Tell the chat UI that I have `signed off'. | |
117 """ | |
118 self.chat.unregisterAccountClient(self) | |
119 | |
120 | |
121 class AbstractAccount(styles.Versioned): | |
122 """Base class for Accounts. | |
123 | |
124 I am the start of an implementation of L{IAccount<interfaces.IAccount>}, I | |
125 implement L{isOnline} and most of L{logOn}, though you'll need to implement | |
126 L{_startLogOn} in a subclass. | |
127 | |
128 @cvar _groupFactory: A Callable that will return a L{IGroup} appropriate | |
129 for this account type. | |
130 @cvar _personFactory: A Callable that will return a L{IPerson} appropriate | |
131 for this account type. | |
132 | |
133 @type _isConnecting: boolean | |
134 @ivar _isConnecting: Whether I am in the process of establishing a | |
135 connection to the server. | |
136 @type _isOnline: boolean | |
137 @ivar _isOnline: Whether I am currently on-line with the server. | |
138 | |
139 @ivar accountName: | |
140 @ivar autoLogin: | |
141 @ivar username: | |
142 @ivar password: | |
143 @ivar host: | |
144 @ivar port: | |
145 """ | |
146 | |
147 _isOnline = 0 | |
148 _isConnecting = 0 | |
149 client = None | |
150 | |
151 _groupFactory = AbstractGroup | |
152 _personFactory = AbstractPerson | |
153 | |
154 persistanceVersion = 2 | |
155 | |
156 def __init__(self, accountName, autoLogin, username, password, host, port): | |
157 self.accountName = accountName | |
158 self.autoLogin = autoLogin | |
159 self.username = username | |
160 self.password = password | |
161 self.host = host | |
162 self.port = port | |
163 | |
164 self._groups = {} | |
165 self._persons = {} | |
166 | |
167 def upgrateToVersion2(self): | |
168 # Added in CVS revision 1.16. | |
169 for k in ('_groups', '_persons'): | |
170 if not hasattr(self, k): | |
171 setattr(self, k, {}) | |
172 | |
173 def __getstate__(self): | |
174 state = styles.Versioned.__getstate__(self) | |
175 for k in ('client', '_isOnline', '_isConnecting'): | |
176 try: | |
177 del state[k] | |
178 except KeyError: | |
179 pass | |
180 return state | |
181 | |
182 def isOnline(self): | |
183 return self._isOnline | |
184 | |
185 def logOn(self, chatui): | |
186 """Log on to this account. | |
187 | |
188 Takes care to not start a connection if a connection is | |
189 already in progress. You will need to implement | |
190 L{_startLogOn} for this to work, and it would be a good idea | |
191 to override L{_loginFailed} too. | |
192 | |
193 @returntype: Deferred L{interfaces.IClient} | |
194 """ | |
195 if (not self._isConnecting) and (not self._isOnline): | |
196 self._isConnecting = 1 | |
197 d = self._startLogOn(chatui) | |
198 d.addCallback(self._cb_logOn) | |
199 # if chatui is not None: | |
200 # (I don't particularly like having to pass chatUI to this function, | |
201 # but we haven't factored it out yet.) | |
202 d.addCallback(chatui.registerAccountClient) | |
203 d.addErrback(self._loginFailed) | |
204 return d | |
205 else: | |
206 raise error.ConnectError("Connection in progress") | |
207 | |
208 def getGroup(self, name): | |
209 """Group factory. | |
210 | |
211 @param name: Name of the group on this account. | |
212 @type name: string | |
213 """ | |
214 group = self._groups.get(name) | |
215 if group is None: | |
216 group = self._groupFactory(name, self) | |
217 self._groups[name] = group | |
218 return group | |
219 | |
220 def getPerson(self, name): | |
221 """Person factory. | |
222 | |
223 @param name: Name of the person on this account. | |
224 @type name: string | |
225 """ | |
226 person = self._persons.get(name) | |
227 if person is None: | |
228 person = self._personFactory(name, self) | |
229 self._persons[name] = person | |
230 return person | |
231 | |
232 def _startLogOn(self, chatui): | |
233 """Start the sign on process. | |
234 | |
235 Factored out of L{logOn}. | |
236 | |
237 @returntype: Deferred L{interfaces.IClient} | |
238 """ | |
239 raise NotImplementedError() | |
240 | |
241 def _cb_logOn(self, client): | |
242 self._isConnecting = 0 | |
243 self._isOnline = 1 | |
244 self.client = client | |
245 return client | |
246 | |
247 def _loginFailed(self, reason): | |
248 """Errorback for L{logOn}. | |
249 | |
250 @type reason: Failure | |
251 | |
252 @returns: I{reason}, for further processing in the callback chain. | |
253 @returntype: Failure | |
254 """ | |
255 self._isConnecting = 0 | |
256 self._isOnline = 0 # just in case | |
257 return reason | |
258 | |
259 def _clientLost(self, client, reason): | |
260 self.client = None | |
261 self._isConnecting = 0 | |
262 self._isOnline = 0 | |
263 return reason | |
264 | |
265 def __repr__(self): | |
266 return "<%s: %s (%s@%s:%s)>" % (self.__class__, | |
267 self.accountName, | |
268 self.username, | |
269 self.host, | |
270 self.port) | |
OLD | NEW |