1
# Do everything properly, and componentize
2
from twisted.application import internet, service
3
from twisted.internet import protocol, reactor, defer
4
from twisted.words.protocols import irc
5
from twisted.protocols import basic
6
from twisted.python import components
7
from twisted.web import resource, server, static, xmlrpc, microdom
8
from twisted.spread import pb
9
from zope.interface import Interface, implements
12
class IFingerService(Interface):
15
"""Return a deferred returning a string"""
18
"""Return a deferred returning a list of strings"""
20
class IFingerSetterService(Interface):
22
def setUser(user, status):
23
"""Set the user's status to something"""
26
return "Internal error in server"
28
class FingerProtocol(basic.LineReceiver):
30
def lineReceived(self, user):
31
d = self.factory.getUser(user)
32
d.addErrback(catchError)
33
def writeValue(value):
34
self.transport.write(value+'\r\n')
35
self.transport.loseConnection()
36
d.addCallback(writeValue)
39
class IFingerFactory(Interface):
42
"""Return a deferred returning a string"""
44
def buildProtocol(addr):
45
"""Return a protocol returning a string"""
48
class FingerFactoryFromService(protocol.ServerFactory):
50
implements(IFingerFactory)
52
protocol = FingerProtocol
54
def __init__(self, service):
55
self.service = service
57
def getUser(self, user):
58
return self.service.getUser(user)
60
components.registerAdapter(FingerFactoryFromService,
64
class FingerSetterProtocol(basic.LineReceiver):
66
def connectionMade(self):
69
def lineReceived(self, line):
70
self.lines.append(line)
72
def connectionLost(self, reason):
73
if len(self.lines) == 2:
74
self.factory.setUser(*self.lines)
77
class IFingerSetterFactory(Interface):
79
def setUser(user, status):
80
"""Return a deferred returning a string"""
82
def buildProtocol(addr):
83
"""Return a protocol returning a string"""
86
class FingerSetterFactoryFromService(protocol.ServerFactory):
88
implements(IFingerSetterFactory)
90
protocol = FingerSetterProtocol
92
def __init__(self, service):
93
self.service = service
95
def setUser(self, user, status):
96
self.service.setUser(user, status)
99
components.registerAdapter(FingerSetterFactoryFromService,
100
IFingerSetterService,
101
IFingerSetterFactory)
103
class IRCReplyBot(irc.IRCClient):
105
def connectionMade(self):
106
self.nickname = self.factory.nickname
107
irc.IRCClient.connectionMade(self)
109
def privmsg(self, user, channel, msg):
110
user = user.split('!')[0]
111
if self.nickname.lower() == channel.lower():
112
d = self.factory.getUser(msg)
113
d.addErrback(catchError)
114
d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
115
d.addCallback(lambda m: self.msg(user, m))
118
class IIRCClientFactory(Interface):
125
"""Return a deferred returning a string"""
127
def buildProtocol(addr):
128
"""Return a protocol"""
131
class IRCClientFactoryFromService(protocol.ClientFactory):
133
implements(IIRCClientFactory)
135
protocol = IRCReplyBot
138
def __init__(self, service):
139
self.service = service
141
def getUser(self, user):
142
return self.service.getUser(user)
144
components.registerAdapter(IRCClientFactoryFromService,
148
class UserStatusTree(resource.Resource):
150
def __init__(self, service):
151
resource.Resource.__init__(self)
154
# add a specific child for the path "RPC2"
155
self.putChild("RPC2", UserStatusXR(self.service))
157
# need to do this for resources at the root of the site
158
self.putChild("", self)
160
def _cb_render_GET(self, users, request):
161
userOutput = ''.join(["<li><a href=\"%s\">%s</a></li>" % (user, user)
164
<html><head><title>Users</title></head><body>
168
</ul></body></html>""" % userOutput)
171
def render_GET(self, request):
172
d = self.service.getUsers()
173
d.addCallback(self._cb_render_GET, request)
175
# signal that the rendering is not complete
176
return server.NOT_DONE_YET
178
def getChild(self, path, request):
179
return UserStatus(user=path, service=self.service)
181
components.registerAdapter(UserStatusTree, IFingerService, resource.IResource)
183
class UserStatus(resource.Resource):
185
def __init__(self, user, service):
186
resource.Resource.__init__(self)
188
self.service = service
190
def _cb_render_GET(self, status, request):
191
request.write("""<html><head><title>%s</title></head>
194
</body></html>""" % (self.user, self.user, status))
197
def render_GET(self, request):
198
d = self.service.getUser(self.user)
199
d.addCallback(self._cb_render_GET, request)
201
# signal that the rendering is not complete
202
return server.NOT_DONE_YET
204
class UserStatusXR(xmlrpc.XMLRPC):
206
def __init__(self, service):
207
xmlrpc.XMLRPC.__init__(self)
208
self.service = service
210
def xmlrpc_getUser(self, user):
211
return self.service.getUser(user)
213
def xmlrpc_getUsers(self):
214
return self.service.getUsers()
217
class IPerspectiveFinger(Interface):
219
def remote_getUser(username):
220
"""return a user's status"""
222
def remote_getUsers():
223
"""return a user's status"""
225
class PerspectiveFingerFromService(pb.Root):
227
implements(IPerspectiveFinger)
229
def __init__(self, service):
230
self.service = service
232
def remote_getUser(self, username):
233
return self.service.getUser(username)
235
def remote_getUsers(self):
236
return self.service.getUsers()
238
components.registerAdapter(PerspectiveFingerFromService,
243
class FingerService(service.Service):
245
implements(IFingerService)
247
def __init__(self, filename):
248
self.filename = filename
254
for line in file(self.filename):
255
user, status = line.split(':', 1)
257
status = status.strip()
258
self.users[user] = status
259
self.call = reactor.callLater(30, self._read)
261
def getUser(self, user):
262
return defer.succeed(self.users.get(user, "No such user"))
265
return defer.succeed(self.users.keys())
268
application = service.Application('finger', uid=1, gid=1)
269
f = FingerService('/etc/users')
270
serviceCollection = service.IServiceCollection(application)
271
internet.TCPServer(79, IFingerFactory(f)
272
).setServiceParent(serviceCollection)
273
internet.TCPServer(8000, server.Site(resource.IResource(f))
274
).setServiceParent(serviceCollection)
275
i = IIRCClientFactory(f)
276
i.nickname = 'fingerbot'
277
internet.TCPClient('irc.freenode.org', 6667, i
278
).setServiceParent(serviceCollection)
279
internet.TCPServer(8889, pb.PBServerFactory(IPerspectiveFinger(f))
280
).setServiceParent(serviceCollection)