1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
6
"""Instance Messenger base classes for protocol support.
8
You will find these useful if you're adding a new protocol to IM.
11
# Abstract representation of chat "model" classes
13
from twisted.words.im.locals import ONLINE, OFFLINE, OfflineError
14
from twisted.words.im import interfaces
16
from twisted.internet.protocol import Protocol
18
from twisted.python.reflect import prefixedMethods
19
from twisted.persisted import styles
21
from twisted.internet import error
24
def __init__(self, name, account):
26
self.account = account
28
def getGroupCommands(self):
29
"""finds group commands
31
these commands are methods on me that start with imgroup_; they are
32
called with no arguments
34
return prefixedMethods(self, "imgroup_")
36
def getTargetCommands(self, target):
37
"""finds group commands
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
42
you may want to override this in your group in order to filter for
43
appropriate commands on the given user
45
return prefixedMethods(self, "imtarget_")
48
if not self.account.client:
50
self.account.client.joinGroup(self.name)
53
if not self.account.client:
55
self.account.client.leaveGroup(self.name)
58
return '<%s %r>' % (self.__class__, self.name)
61
return '%s@%s' % (self.name, self.account.accountName)
64
def __init__(self, name, baseAccount):
66
self.account = baseAccount
69
def getPersonCommands(self):
70
"""finds person commands
72
these commands are methods on me that start with imperson_; they are
73
called with no arguments
75
return prefixedMethods(self, "imperson_")
77
def getIdleTime(self):
84
return '<%s %r/%s>' % (self.__class__, self.name, self.status)
87
return '%s@%s' % (self.name, self.account.accountName)
89
class AbstractClientMixin:
90
"""Designed to be mixed in to a Protocol implementing class.
92
Inherit from me first.
94
@ivar _logonDeferred: Fired when I am done logging in.
96
def __init__(self, account, chatui, logonDeferred):
97
for base in self.__class__.__bases__:
98
if issubclass(base, Protocol):
99
self.__class__._protoBase = base
103
self.account = account
105
self._logonDeferred = logonDeferred
107
def connectionMade(self):
108
self._protoBase.connectionMade(self)
110
def connectionLost(self, reason):
111
self.account._clientLost(self, reason)
112
self.unregisterAsAccountClient()
113
return self._protoBase.connectionLost(self, reason)
115
def unregisterAsAccountClient(self):
116
"""Tell the chat UI that I have `signed off'.
118
self.chat.unregisterAccountClient(self)
121
class AbstractAccount(styles.Versioned):
122
"""Base class for Accounts.
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.
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.
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.
151
_groupFactory = AbstractGroup
152
_personFactory = AbstractPerson
154
persistanceVersion = 2
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
167
def upgrateToVersion2(self):
168
# Added in CVS revision 1.16.
169
for k in ('_groups', '_persons'):
170
if not hasattr(self, k):
173
def __getstate__(self):
174
state = styles.Versioned.__getstate__(self)
175
for k in ('client', '_isOnline', '_isConnecting'):
183
return self._isOnline
185
def logOn(self, chatui):
186
"""Log on to this account.
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.
193
@returntype: Deferred L{interfaces.IClient}
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)
206
raise error.ConnectError("Connection in progress")
208
def getGroup(self, name):
211
@param name: Name of the group on this account.
214
group = self._groups.get(name)
216
group = self._groupFactory(name, self)
217
self._groups[name] = group
220
def getPerson(self, name):
223
@param name: Name of the person on this account.
226
person = self._persons.get(name)
228
person = self._personFactory(name, self)
229
self._persons[name] = person
232
def _startLogOn(self, chatui):
233
"""Start the sign on process.
235
Factored out of L{logOn}.
237
@returntype: Deferred L{interfaces.IClient}
239
raise NotImplementedError()
241
def _cb_logOn(self, client):
242
self._isConnecting = 0
247
def _loginFailed(self, reason):
248
"""Errorback for L{logOn}.
250
@type reason: Failure
252
@returns: I{reason}, for further processing in the callback chain.
255
self._isConnecting = 0
256
self._isOnline = 0 # just in case
259
def _clientLost(self, client, reason):
261
self._isConnecting = 0
266
return "<%s: %s (%s@%s:%s)>" % (self.__class__,