1 # Do everything properly, and componentize
2 from twisted.application import internet, service
3 from twisted.internet import protocol, reactor, defer, utils
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
13 class IFingerService(Interface):
17 Return a deferred returning a string.
22 Return a deferred returning a list of strings.
26 class IFingerSetterService(Interface):
28 def setUser(user, status):
30 Set the user's status to something.
34 class IFingerSetterService(Interface):
36 def setUser(user, status):
38 Set the user's status to something.
43 return "Internal error in server"
46 class FingerProtocol(basic.LineReceiver):
48 def lineReceived(self, user):
49 d = self.factory.getUser(user)
50 d.addErrback(catchError)
51 def writeValue(value):
52 self.transport.write(value+'\r\n')
53 self.transport.loseConnection()
54 d.addCallback(writeValue)
57 class IFingerFactory(Interface):
61 Return a deferred returning a string.
64 def buildProtocol(addr):
66 Return a protocol returning a string.
70 class FingerFactoryFromService(protocol.ServerFactory):
72 implements(IFingerFactory)
74 protocol = FingerProtocol
76 def __init__(self, service):
77 self.service = service
79 def getUser(self, user):
80 return self.service.getUser(user)
82 components.registerAdapter(FingerFactoryFromService,
87 class FingerSetterProtocol(basic.LineReceiver):
89 def connectionMade(self):
92 def lineReceived(self, line):
93 self.lines.append(line)
95 def connectionLost(self, reason):
96 if len(self.lines) == 2:
97 self.factory.setUser(*self.lines)
100 class IFingerSetterFactory(Interface):
102 def setUser(user, status):
104 Return a deferred returning a string.
107 def buildProtocol(addr):
109 Return a protocol returning a string.
113 class FingerSetterFactoryFromService(protocol.ServerFactory):
115 implements(IFingerSetterFactory)
117 protocol = FingerSetterProtocol
119 def __init__(self, service):
120 self.service = service
122 def setUser(self, user, status):
123 self.service.setUser(user, status)
126 components.registerAdapter(FingerSetterFactoryFromService,
127 IFingerSetterService,
128 IFingerSetterFactory)
131 class IRCReplyBot(irc.IRCClient):
133 def connectionMade():
134 self.nickname = self.factory.nickname
135 irc.IRCClient.connectionMade(self)
137 def privmsg(self, user, channel, msg):
138 user = user.split('!')[0]
139 if self.nickname.lower() == channel.lower():
140 d = self.factory.getUser(msg)
141 d.addErrback(catchError)
142 d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
143 d.addCallback(lambda m: self.msg(user, m))
146 class IIRCClientFactory(Interface):
154 Return a deferred returning a string.
157 def buildProtocol(addr):
163 class IRCClientFactoryFromService(protocol.ClientFactory):
165 implements(IIRCClientFactory)
167 protocol = IRCReplyBot
170 def __init__(self, service):
171 self.service = service
173 def getUser(self, user):
174 return self.service.getUser(user)
176 components.registerAdapter(IRCClientFactoryFromService,
181 class UserStatusTree(resource.Resource):
183 implements(resource.IResource)
185 def __init__(self, service):
186 resource.Resource.__init__(self)
187 self.service = service
188 self.putChild('RPC2', UserStatusXR(self.service))
190 def render_GET(self, request):
191 d = self.service.getUsers()
192 def formatUsers(users):
193 l = ['<li><a href="%s">%s</a></li>' % (user, user)
195 return '<ul>'+''.join(l)+'</ul>'
196 d.addCallback(formatUsers)
197 d.addCallback(request.write)
198 d.addCallback(lambda _: request.finish())
199 return server.NOT_DONE_YET
201 def getChild(self, path, request):
203 return UserStatusTree(self.service)
205 return UserStatus(path, self.service)
207 components.registerAdapter(UserStatusTree, IFingerService,
211 class UserStatus(resource.Resource):
213 def __init__(self, user, service):
214 resource.Resource.__init__(self)
216 self.service = service
218 def render_GET(self, request):
219 d = self.service.getUser(self.user)
220 d.addCallback(cgi.escape)
221 d.addCallback(lambda m:
222 '<h1>%s</h1>'%self.user+'<p>%s</p>'%m)
223 d.addCallback(request.write)
224 d.addCallback(lambda _: request.finish())
225 return server.NOT_DONE_YET
228 class UserStatusXR(xmlrpc.XMLRPC):
230 def __init__(self, service):
231 xmlrpc.XMLRPC.__init__(self)
232 self.service = service
234 def xmlrpc_getUser(self, user):
235 return self.service.getUser(user)
238 class FingerService(service.Service):
240 implements(IFingerService)
242 def __init__(self, filename):
243 self.filename = filename
248 for line in file(self.filename):
249 user, status = line.split(':', 1)
251 status = status.strip()
252 self.users[user] = status
253 self.call = reactor.callLater(30, self._read)
255 def getUser(self, user):
256 return defer.succeed(self.users.get(user, "No such user"))
259 return defer.succeed(self.users.keys())
261 def startService(self):
263 service.Service.startService(self)
265 def stopService(self):
266 service.Service.stopService(self)
270 # Yet another back-end
272 class LocalFingerService(service.Service):
274 implements(IFingerService)
276 def getUser(self, user):
279 entry = pwd.getpwnam(user)
281 return defer.succeed("No such user")
283 f = file(os.path.join(entry[5],'.plan'))
284 except (IOError, OSError):
285 return defer.succeed("No such user")
289 return defer.succeed(data)
292 return defer.succeed([])
295 application = service.Application('finger', uid=1, gid=1)
296 f = LocalFingerService()
297 serviceCollection = service.IServiceCollection(application)
298 internet.TCPServer(79, IFingerFactory(f)
299 ).setServiceParent(serviceCollection)
300 internet.TCPServer(8000, server.Site(resource.IResource(f))
301 ).setServiceParent(serviceCollection)
302 i = IIRCClientFactory(f)
303 i.nickname = 'fingerbot'
304 internet.TCPClient('irc.freenode.org', 6667, i
305 ).setServiceParent(serviceCollection)