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
12 class IFingerService(Interface):
16 Return a deferred returning a string.
21 Return a deferred returning a list of strings.
25 class IFingerSetterService(Interface):
27 def setUser(user, status):
29 Set the user's status to something.
33 class IFingerSetterService(Interface):
35 def setUser(user, status):
37 Set the user's status to something.
42 return "Internal error in server"
45 class FingerProtocol(basic.LineReceiver):
47 def lineReceived(self, user):
48 d = self.factory.getUser(user)
49 d.addErrback(catchError)
50 def writeValue(value):
51 self.transport.write(value+'\r\n')
52 self.transport.loseConnection()
53 d.addCallback(writeValue)
56 class IFingerFactory(Interface):
60 Return a deferred returning a string.
63 def buildProtocol(addr):
65 Return a protocol returning a string.
69 class FingerFactoryFromService(protocol.ServerFactory):
71 implements(IFingerFactory)
73 protocol = FingerProtocol
75 def __init__(self, service):
76 self.service = service
78 def getUser(self, user):
79 return self.service.getUser(user)
81 components.registerAdapter(FingerFactoryFromService,
86 class FingerSetterProtocol(basic.LineReceiver):
88 def connectionMade(self):
91 def lineReceived(self, line):
92 self.lines.append(line)
94 def connectionLost(self, reason):
95 if len(self.lines) == 2:
96 self.factory.setUser(*self.lines)
99 class IFingerSetterFactory(Interface):
101 def setUser(user, status):
103 Return a deferred returning a string.
106 def buildProtocol(addr):
108 Return a protocol returning a string.
112 class FingerSetterFactoryFromService(protocol.ServerFactory):
114 implements(IFingerSetterFactory)
116 protocol = FingerSetterProtocol
118 def __init__(self, service):
119 self.service = service
121 def setUser(self, user, status):
122 self.service.setUser(user, status)
125 components.registerAdapter(FingerSetterFactoryFromService,
126 IFingerSetterService,
127 IFingerSetterFactory)
129 class IRCReplyBot(irc.IRCClient):
131 def connectionMade(self):
132 self.nickname = self.factory.nickname
133 irc.IRCClient.connectionMade(self)
135 def privmsg(self, user, channel, msg):
136 user = user.split('!')[0]
137 if self.nickname.lower() == channel.lower():
138 d = self.factory.getUser(msg)
139 d.addErrback(catchError)
140 d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
141 d.addCallback(lambda m: self.msg(user, m))
144 class IIRCClientFactory(Interface):
152 Return a deferred returning a string.
155 def buildProtocol(addr):
161 class IRCClientFactoryFromService(protocol.ClientFactory):
163 implements(IIRCClientFactory)
165 protocol = IRCReplyBot
168 def __init__(self, service):
169 self.service = service
171 def getUser(self, user):
172 return self.service.getUser(user)
174 components.registerAdapter(IRCClientFactoryFromService,
179 class UserStatusTree(resource.Resource):
181 implements(resource.IResource)
183 def __init__(self, service):
184 resource.Resource.__init__(self)
185 self.service = service
186 self.putChild('RPC2', UserStatusXR(self.service))
188 def render_GET(self, request):
189 d = self.service.getUsers()
190 def formatUsers(users):
191 l = ['<li><a href="%s">%s</a></li>' % (user, user)
193 return '<ul>'+''.join(l)+'</ul>'
194 d.addCallback(formatUsers)
195 d.addCallback(request.write)
196 d.addCallback(lambda _: request.finish())
197 return server.NOT_DONE_YET
199 def getChild(self, path, request):
201 return UserStatusTree(self.service)
203 return UserStatus(path, self.service)
205 components.registerAdapter(UserStatusTree, IFingerService,
209 class UserStatus(resource.Resource):
211 def __init__(self, user, service):
212 resource.Resource.__init__(self)
214 self.service = service
216 def render_GET(self, request):
217 d = self.service.getUser(self.user)
218 d.addCallback(cgi.escape)
219 d.addCallback(lambda m:
220 '<h1>%s</h1>'%self.user+'<p>%s</p>'%m)
221 d.addCallback(request.write)
222 d.addCallback(lambda _: request.finish())
223 return server.NOT_DONE_YET
226 class UserStatusXR(xmlrpc.XMLRPC):
228 def __init__(self, service):
229 xmlrpc.XMLRPC.__init__(self)
230 self.service = service
232 def xmlrpc_getUser(self, user):
233 return self.service.getUser(user)
236 class FingerService(service.Service):
238 implements(IFingerService)
240 def __init__(self, filename):
241 self.filename = filename
246 for line in file(self.filename):
247 user, status = line.split(':', 1)
249 status = status.strip()
250 self.users[user] = status
251 self.call = reactor.callLater(30, self._read)
253 def getUser(self, user):
254 return defer.succeed(self.users.get(user, "No such user"))
257 return defer.succeed(self.users.keys())
259 def startService(self):
261 service.Service.startService(self)
263 def stopService(self):
264 service.Service.stopService(self)
270 class LocalFingerService(service.Service):
272 implements(IFingerService)
274 def getUser(self, user):
275 # need a local finger daemon running for this to work
276 return utils.getProcessOutput("finger", [user])
279 return defer.succeed([])
282 application = service.Application('finger', uid=1, gid=1)
283 f = LocalFingerService()
284 serviceCollection = service.IServiceCollection(application)
285 internet.TCPServer(79, IFingerFactory(f)
286 ).setServiceParent(serviceCollection)
287 internet.TCPServer(8000, server.Site(resource.IResource(f))
288 ).setServiceParent(serviceCollection)
289 i = IIRCClientFactory(f)
290 i.nickname = 'fingerbot'
291 internet.TCPClient('irc.freenode.org', 6667, i
292 ).setServiceParent(serviceCollection)