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
8 from zope.interface import Interface, implements
11 class IFingerService(Interface):
15 Return a deferred returning a string.
20 Return a deferred returning a list of strings.
24 class IFingerSetterService(Interface):
26 def setUser(user, status):
28 Set the user's status to something.
33 return "Internal error in server"
36 class FingerProtocol(basic.LineReceiver):
38 def lineReceived(self, user):
39 d = self.factory.getUser(user)
40 d.addErrback(catchError)
41 def writeValue(value):
42 self.transport.write(value+'\r\n')
43 self.transport.loseConnection()
44 d.addCallback(writeValue)
47 class IFingerFactory(Interface):
51 Return a deferred returning a string.
54 def buildProtocol(addr):
56 Return a protocol returning a string.
60 class FingerFactoryFromService(protocol.ServerFactory):
62 implements(IFingerFactory)
64 protocol = FingerProtocol
66 def __init__(self, service):
67 self.service = service
69 def getUser(self, user):
70 return self.service.getUser(user)
72 components.registerAdapter(FingerFactoryFromService,
77 class FingerSetterProtocol(basic.LineReceiver):
79 def connectionMade(self):
82 def lineReceived(self, line):
83 self.lines.append(line)
85 def connectionLost(self, reason):
86 if len(self.lines) == 2:
87 self.factory.setUser(*self.lines)
90 class IFingerSetterFactory(Interface):
92 def setUser(user, status):
94 Return a deferred returning a string.
97 def buildProtocol(addr):
99 Return a protocol returning a string.
103 class FingerSetterFactoryFromService(protocol.ServerFactory):
105 implements(IFingerSetterFactory)
107 protocol = FingerSetterProtocol
109 def __init__(self, service):
110 self.service = service
112 def setUser(self, user, status):
113 self.service.setUser(user, status)
116 components.registerAdapter(FingerSetterFactoryFromService,
117 IFingerSetterService,
118 IFingerSetterFactory)
121 class IRCReplyBot(irc.IRCClient):
123 def connectionMade(self):
124 self.nickname = self.factory.nickname
125 irc.IRCClient.connectionMade(self)
127 def privmsg(self, user, channel, msg):
128 user = user.split('!')[0]
129 if self.nickname.lower() == channel.lower():
130 d = self.factory.getUser(msg)
131 d.addErrback(catchError)
132 d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
133 d.addCallback(lambda m: self.msg(user, m))
136 class IIRCClientFactory(Interface):
143 Return a deferred returning a string.
146 def buildProtocol(addr):
152 class IRCClientFactoryFromService(protocol.ClientFactory):
154 implements(IIRCClientFactory)
156 protocol = IRCReplyBot
159 def __init__(self, service):
160 self.service = service
162 def getUser(self, user):
163 return self.service.getUser(user)
165 components.registerAdapter(IRCClientFactoryFromService,
170 class UserStatusTree(resource.Resource):
172 implements(resource.IResource)
174 def __init__(self, service):
175 resource.Resource.__init__(self)
176 self.service = service
177 self.putChild('RPC2', UserStatusXR(self.service))
179 def render_GET(self, request):
180 d = self.service.getUsers()
181 def formatUsers(users):
182 l = ['<li><a href="%s">%s</a></li>' % (user, user)
184 return '<ul>'+''.join(l)+'</ul>'
185 d.addCallback(formatUsers)
186 d.addCallback(request.write)
187 d.addCallback(lambda _: request.finish())
188 return server.NOT_DONE_YET
190 def getChild(self, path, request):
192 return UserStatusTree(self.service)
194 return UserStatus(path, self.service)
196 components.registerAdapter(UserStatusTree, IFingerService,
200 class UserStatus(resource.Resource):
202 def __init__(self, user, service):
203 resource.Resource.__init__(self)
205 self.service = service
207 def render_GET(self, request):
208 d = self.service.getUser(self.user)
209 d.addCallback(cgi.escape)
210 d.addCallback(lambda m:
211 '<h1>%s</h1>'%self.user+'<p>%s</p>'%m)
212 d.addCallback(request.write)
213 d.addCallback(lambda _: request.finish())
214 return server.NOT_DONE_YET
217 class UserStatusXR(xmlrpc.XMLRPC):
219 def __init__(self, service):
220 xmlrpc.XMLRPC.__init__(self)
221 self.service = service
223 def xmlrpc_getUser(self, user):
224 return self.service.getUser(user)
227 class FingerService(service.Service):
229 implements(IFingerService)
231 def __init__(self, filename):
232 self.filename = filename
237 for line in file(self.filename):
238 user, status = line.split(':', 1)
240 status = status.strip()
241 self.users[user] = status
242 self.call = reactor.callLater(30, self._read)
244 def getUser(self, user):
245 return defer.succeed(self.users.get(user, "No such user"))
248 return defer.succeed(self.users.keys())
250 def startService(self):
252 service.Service.startService(self)
254 def stopService(self):
255 service.Service.stopService(self)
259 application = service.Application('finger', uid=1, gid=1)
260 f = FingerService('/etc/users')
261 serviceCollection = service.IServiceCollection(application)
262 f.setServiceParent(serviceCollection)
263 internet.TCPServer(79, IFingerFactory(f)
264 ).setServiceParent(serviceCollection)
265 internet.TCPServer(8000, server.Site(resource.IResource(f))
266 ).setServiceParent(serviceCollection)
267 i = IIRCClientFactory(f)
268 i.nickname = 'fingerbot'
269 internet.TCPClient('irc.freenode.org', 6667, i
270 ).setServiceParent(serviceCollection)