~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/words/im/pbsupport.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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',self.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, perspectiveName)
 
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, metadata)
 
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(message, 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 incorrect")))
 
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(handler.connected)
 
255
        return defer.DeferredList(dl)
 
256
 
 
257
    def _ebConnected(self, error):
 
258
        print 'Not connected.'
 
259
        return error
 
260