Index: third_party/twisted_8_1/twisted/words/service.py |
diff --git a/third_party/twisted_8_1/twisted/words/service.py b/third_party/twisted_8_1/twisted/words/service.py |
deleted file mode 100644 |
index f85d87422c9ed77d15e157fe6775ef27fe07931e..0000000000000000000000000000000000000000 |
--- a/third_party/twisted_8_1/twisted/words/service.py |
+++ /dev/null |
@@ -1,1195 +0,0 @@ |
-# -*- test-case-name: twisted.words.test.test_service -*- |
-# Copyright (c) 2001-2005 Twisted Matrix Laboratories. |
-# See LICENSE for details. |
- |
-""" |
-A module that needs a better name. |
- |
-Implements new cred things for words. |
- |
-How does this thing work? |
- |
- - Network connection on some port expecting to speak some protocol |
- |
- - Protocol-specific authentication, resulting in some kind of credentials object |
- |
- - twisted.cred.portal login using those credentials for the interface |
- IUser and with something implementing IChatClient as the mind |
- |
- - successful login results in an IUser avatar the protocol can call |
- methods on, and state added to the realm such that the mind will have |
- methods called on it as is necessary |
- |
- - protocol specific actions lead to calls onto the avatar; remote events |
- lead to calls onto the mind |
- |
- - protocol specific hangup, realm is notified, user is removed from active |
- play, the end. |
-""" |
- |
-from time import time, ctime |
- |
-from zope.interface import implements |
- |
-from twisted.words import iwords, ewords |
- |
-from twisted.python.components import registerAdapter |
-from twisted.cred import portal, credentials, error as ecred |
-from twisted.spread import pb |
-from twisted.words.protocols import irc |
-from twisted.internet import defer, protocol |
-from twisted.python import log, failure, reflect |
-from twisted import copyright |
- |
- |
-class Group(object): |
- implements(iwords.IGroup) |
- |
- def __init__(self, name): |
- self.name = name |
- self.users = {} |
- self.meta = { |
- "topic": "", |
- "topic_author": "", |
- } |
- |
- |
- def _ebUserCall(self, err, p): |
- return failure.Failure(Exception(p, err)) |
- |
- |
- def _cbUserCall(self, results): |
- for (success, result) in results: |
- if not success: |
- user, err = result.value # XXX |
- self.remove(user, err.getErrorMessage()) |
- |
- |
- def add(self, user): |
- assert iwords.IChatClient.providedBy(user), "%r is not a chat client" % (user,) |
- if user.name not in self.users: |
- additions = [] |
- self.users[user.name] = user |
- for p in self.users.itervalues(): |
- if p is not user: |
- d = defer.maybeDeferred(p.userJoined, self, user) |
- d.addErrback(self._ebUserCall, p=p) |
- additions.append(d) |
- defer.DeferredList(additions).addCallback(self._cbUserCall) |
- return defer.succeed(None) |
- |
- |
- def remove(self, user, reason=None): |
- assert reason is None or isinstance(reason, unicode) |
- try: |
- del self.users[user.name] |
- except KeyError: |
- pass |
- else: |
- removals = [] |
- for p in self.users.itervalues(): |
- if p is not user: |
- d = defer.maybeDeferred(p.userLeft, self, user, reason) |
- d.addErrback(self._ebUserCall, p=p) |
- removals.append(d) |
- defer.DeferredList(removals).addCallback(self._cbUserCall) |
- return defer.succeed(None) |
- |
- |
- def size(self): |
- return defer.succeed(len(self.users)) |
- |
- |
- def receive(self, sender, recipient, message): |
- assert recipient is self |
- receives = [] |
- for p in self.users.itervalues(): |
- if p is not sender: |
- d = defer.maybeDeferred(p.receive, sender, self, message) |
- d.addErrback(self._ebUserCall, p=p) |
- receives.append(d) |
- defer.DeferredList(receives).addCallback(self._cbUserCall) |
- return defer.succeed(None) |
- |
- |
- def setMetadata(self, meta): |
- self.meta = meta |
- sets = [] |
- for p in self.users.itervalues(): |
- d = defer.maybeDeferred(p.groupMetaUpdate, self, meta) |
- d.addErrback(self._ebUserCall, p=p) |
- sets.append(d) |
- defer.DeferredList(sets).addCallback(self._cbUserCall) |
- return defer.succeed(None) |
- |
- |
- def iterusers(self): |
- # XXX Deferred? |
- return iter(self.users.values()) |
- |
- |
-class User(object): |
- implements(iwords.IUser) |
- |
- realm = None |
- mind = None |
- |
- def __init__(self, name): |
- self.name = name |
- self.groups = [] |
- self.lastMessage = time() |
- |
- |
- def loggedIn(self, realm, mind): |
- self.realm = realm |
- self.mind = mind |
- self.signOn = time() |
- |
- |
- def join(self, group): |
- def cbJoin(result): |
- self.groups.append(group) |
- return result |
- return group.add(self.mind).addCallback(cbJoin) |
- |
- |
- def leave(self, group, reason=None): |
- def cbLeave(result): |
- self.groups.remove(group) |
- return result |
- return group.remove(self.mind, reason).addCallback(cbLeave) |
- |
- |
- def send(self, recipient, message): |
- self.lastMessage = time() |
- return recipient.receive(self.mind, recipient, message) |
- |
- |
- def itergroups(self): |
- return iter(self.groups) |
- |
- |
- def logout(self): |
- for g in self.groups[:]: |
- self.leave(g) |
- |
- |
-NICKSERV = 'NickServ!NickServ@services' |
-class IRCUser(irc.IRC): |
- implements(iwords.IChatClient) |
- |
- # A list of IGroups in which I am participating |
- groups = None |
- |
- # A no-argument callable I should invoke when I go away |
- logout = None |
- |
- # An IUser we use to interact with the chat service |
- avatar = None |
- |
- # To whence I belong |
- realm = None |
- |
- # How to handle unicode (TODO: Make this customizable on a per-user basis) |
- encoding = 'utf-8' |
- |
- # Twisted callbacks |
- def connectionMade(self): |
- self.irc_PRIVMSG = self.irc_NICKSERV_PRIVMSG |
- |
- |
- def connectionLost(self, reason): |
- if self.logout is not None: |
- self.logout() |
- self.avatar = None |
- |
- |
- # Make sendMessage a bit more useful to us |
- def sendMessage(self, command, *parameter_list, **kw): |
- if not kw.has_key('prefix'): |
- kw['prefix'] = self.hostname |
- if not kw.has_key('to'): |
- kw['to'] = self.name.encode(self.encoding) |
- |
- arglist = [self, command, kw['to']] + list(parameter_list) |
- irc.IRC.sendMessage(*arglist, **kw) |
- |
- |
- # IChatClient implementation |
- def userJoined(self, group, user): |
- self.join( |
- "%s!%s@%s" % (user.name, user.name, self.hostname), |
- '#' + group.name) |
- |
- |
- def userLeft(self, group, user, reason=None): |
- assert reason is None or isinstance(reason, unicode) |
- self.part( |
- "%s!%s@%s" % (user.name, user.name, self.hostname), |
- '#' + group.name, |
- (reason or u"leaving").encode(self.encoding, 'replace')) |
- |
- |
- def receive(self, sender, recipient, message): |
- #>> :glyph!glyph@adsl-64-123-27-108.dsl.austtx.swbell.net PRIVMSG glyph_ :hello |
- |
- # omg??????????? |
- if iwords.IGroup.providedBy(recipient): |
- recipientName = '#' + recipient.name |
- else: |
- recipientName = recipient.name |
- |
- text = message.get('text', '<an unrepresentable message>') |
- for L in text.splitlines(): |
- self.privmsg( |
- '%s!%s@%s' % (sender.name, sender.name, self.hostname), |
- recipientName, |
- L) |
- |
- |
- def groupMetaUpdate(self, group, meta): |
- if 'topic' in meta: |
- topic = meta['topic'] |
- author = meta.get('topic_author', '') |
- self.topic( |
- self.name, |
- '#' + group.name, |
- topic, |
- '%s!%s@%s' % (author, author, self.hostname) |
- ) |
- |
- # irc.IRC callbacks - starting with login related stuff. |
- nickname = None |
- password = None |
- |
- def irc_PASS(self, prefix, params): |
- """Password message -- Register a password. |
- |
- Parameters: <password> |
- |
- [REQUIRED] |
- |
- Note that IRC requires the client send this *before* NICK |
- and USER. |
- """ |
- self.password = params[-1] |
- |
- |
- def irc_NICK(self, prefix, params): |
- """Nick message -- Set your nickname. |
- |
- Parameters: <nickname> |
- |
- [REQUIRED] |
- """ |
- try: |
- nickname = params[0].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.privmsg( |
- NICKSERV, |
- nickname, |
- 'Your nickname is cannot be decoded. Please use ASCII or UTF-8.') |
- self.transport.loseConnection() |
- return |
- |
- if self.password is None: |
- self.nickname = nickname |
- self.privmsg( |
- NICKSERV, |
- nickname, |
- 'Password?') |
- else: |
- password = self.password |
- self.password = None |
- self.logInAs(nickname, password) |
- |
- |
- def irc_USER(self, prefix, params): |
- """User message -- Set your realname. |
- |
- Parameters: <user> <mode> <unused> <realname> |
- """ |
- # Note: who gives a crap about this? The IUser has the real |
- # information we care about. Save it anyway, I guess, just |
- # for fun. |
- self.realname = params[-1] |
- |
- |
- def irc_NICKSERV_PRIVMSG(self, prefix, params): |
- """Send a (private) message. |
- |
- Parameters: <msgtarget> <text to be sent> |
- """ |
- target = params[0] |
- password = params[-1] |
- |
- if self.nickname is None: |
- # XXX Send an error response here |
- self.transport.loseConnection() |
- elif target.lower() != "nickserv": |
- self.privmsg( |
- NICKSERV, |
- self.nickname, |
- "Denied. Please send me (NickServ) your password.") |
- else: |
- nickname = self.nickname |
- self.nickname = None |
- self.logInAs(nickname, password) |
- |
- |
- def logInAs(self, nickname, password): |
- d = self.factory.portal.login( |
- credentials.UsernamePassword(nickname, password), |
- self, |
- iwords.IUser) |
- d.addCallbacks(self._cbLogin, self._ebLogin, errbackArgs=(nickname,)) |
- |
- |
- _welcomeMessages = [ |
- (irc.RPL_WELCOME, |
- ":connected to Twisted IRC"), |
- (irc.RPL_YOURHOST, |
- ":Your host is %(serviceName)s, running version %(serviceVersion)s"), |
- (irc.RPL_CREATED, |
- ":This server was created on %(creationDate)s"), |
- |
- # "Bummer. This server returned a worthless 004 numeric. |
- # I'll have to guess at all the values" |
- # -- epic |
- (irc.RPL_MYINFO, |
- # w and n are the currently supported channel and user modes |
- # -- specify this better |
- "%(serviceName)s %(serviceVersion)s w n"), |
- ] |
- |
- |
- def _cbLogin(self, (iface, avatar, logout)): |
- assert iface is iwords.IUser, "Realm is buggy, got %r" % (iface,) |
- |
- # Let them send messages to the world |
- del self.irc_PRIVMSG |
- |
- self.avatar = avatar |
- self.logout = logout |
- self.realm = avatar.realm |
- self.hostname = self.realm.name |
- |
- info = { |
- "serviceName": self.hostname, |
- "serviceVersion": copyright.version, |
- "creationDate": ctime(), # XXX |
- } |
- for code, text in self._welcomeMessages: |
- self.sendMessage(code, text % info) |
- |
- |
- def _ebLogin(self, err, nickname): |
- if err.check(ewords.AlreadyLoggedIn): |
- self.privmsg( |
- NICKSERV, |
- nickname, |
- "Already logged in. No pod people allowed!") |
- elif err.check(ecred.UnauthorizedLogin): |
- self.privmsg( |
- NICKSERV, |
- nickname, |
- "Login failed. Goodbye.") |
- else: |
- log.msg("Unhandled error during login:") |
- log.err(err) |
- self.privmsg( |
- NICKSERV, |
- nickname, |
- "Server error during login. Sorry.") |
- self.transport.loseConnection() |
- |
- |
- # Great, now that's out of the way, here's some of the interesting |
- # bits |
- def irc_PING(self, prefix, params): |
- """Ping message |
- |
- Parameters: <server1> [ <server2> ] |
- """ |
- if self.realm is not None: |
- self.sendMessage('PONG', self.hostname) |
- |
- |
- def irc_QUIT(self, prefix, params): |
- """Quit |
- |
- Parameters: [ <Quit Message> ] |
- """ |
- self.transport.loseConnection() |
- |
- |
- def _channelMode(self, group, modes=None, *args): |
- if modes: |
- self.sendMessage( |
- irc.ERR_UNKNOWNMODE, |
- ":Unknown MODE flag.") |
- else: |
- self.channelMode(self.name, '#' + group.name, '+') |
- |
- |
- def _userMode(self, user, modes=None): |
- if modes: |
- self.sendMessage( |
- irc.ERR_UNKNOWNMODE, |
- ":Unknown MODE flag.") |
- elif user is self.avatar: |
- self.sendMessage( |
- irc.RPL_UMODEIS, |
- "+") |
- else: |
- self.sendMessage( |
- irc.ERR_USERSDONTMATCH, |
- ":You can't look at someone else's modes.") |
- |
- |
- def irc_MODE(self, prefix, params): |
- """User mode message |
- |
- Parameters: <nickname> |
- *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) ) |
- |
- """ |
- try: |
- channelOrUser = params[0].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.ERR_NOSUCHNICK, params[0], |
- ":No such nickname (could not decode your unicode!)") |
- return |
- |
- if channelOrUser.startswith('#'): |
- def ebGroup(err): |
- err.trap(ewords.NoSuchGroup) |
- self.sendMessage( |
- irc.ERR_NOSUCHCHANNEL, params[0], |
- ":That channel doesn't exist.") |
- d = self.realm.lookupGroup(channelOrUser[1:]) |
- d.addCallbacks( |
- self._channelMode, |
- ebGroup, |
- callbackArgs=tuple(params[1:])) |
- else: |
- def ebUser(err): |
- self.sendMessage( |
- irc.ERR_NOSUCHNICK, |
- ":No such nickname.") |
- |
- d = self.realm.lookupUser(channelOrUser) |
- d.addCallbacks( |
- self._userMode, |
- ebUser, |
- callbackArgs=tuple(params[1:])) |
- |
- |
- def irc_USERHOST(self, prefix, params): |
- """Userhost message |
- |
- Parameters: <nickname> *( SPACE <nickname> ) |
- |
- [Optional] |
- """ |
- pass |
- |
- |
- def irc_PRIVMSG(self, prefix, params): |
- """Send a (private) message. |
- |
- Parameters: <msgtarget> <text to be sent> |
- """ |
- try: |
- targetName = params[0].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.ERR_NOSUCHNICK, targetName, |
- ":No such nick/channel (could not decode your unicode!)") |
- return |
- |
- messageText = params[-1] |
- if targetName.startswith('#'): |
- target = self.realm.lookupGroup(targetName[1:]) |
- else: |
- target = self.realm.lookupUser(targetName).addCallback(lambda user: user.mind) |
- |
- def cbTarget(targ): |
- if targ is not None: |
- return self.avatar.send(targ, {"text": messageText}) |
- |
- def ebTarget(err): |
- self.sendMessage( |
- irc.ERR_NOSUCHNICK, targetName, |
- ":No such nick/channel.") |
- |
- target.addCallbacks(cbTarget, ebTarget) |
- |
- |
- def irc_JOIN(self, prefix, params): |
- """Join message |
- |
- Parameters: ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) |
- """ |
- try: |
- groupName = params[0].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.IRC_NOSUCHCHANNEL, params[0], |
- ":No such channel (could not decode your unicode!)") |
- return |
- |
- if groupName.startswith('#'): |
- groupName = groupName[1:] |
- |
- def cbGroup(group): |
- def cbJoin(ign): |
- self.userJoined(group, self) |
- self.names( |
- self.name, |
- '#' + group.name, |
- [user.name for user in group.iterusers()]) |
- self._sendTopic(group) |
- return self.avatar.join(group).addCallback(cbJoin) |
- |
- def ebGroup(err): |
- self.sendMessage( |
- irc.ERR_NOSUCHCHANNEL, '#' + groupName, |
- ":No such channel.") |
- |
- self.realm.getGroup(groupName).addCallbacks(cbGroup, ebGroup) |
- |
- |
- def irc_PART(self, prefix, params): |
- """Part message |
- |
- Parameters: <channel> *( "," <channel> ) [ <Part Message> ] |
- """ |
- try: |
- groupName = params[0].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.ERR_NOTONCHANNEL, params[0], |
- ":Could not decode your unicode!") |
- return |
- |
- if groupName.startswith('#'): |
- groupName = groupName[1:] |
- |
- if len(params) > 1: |
- reason = params[1].decode('utf-8') |
- else: |
- reason = None |
- |
- def cbGroup(group): |
- def cbLeave(result): |
- self.userLeft(group, self, reason) |
- return self.avatar.leave(group, reason).addCallback(cbLeave) |
- |
- def ebGroup(err): |
- err.trap(ewords.NoSuchGroup) |
- self.sendMessage( |
- irc.ERR_NOTONCHANNEL, |
- '#' + groupName, |
- ":" + err.getErrorMessage()) |
- |
- self.realm.lookupGroup(groupName).addCallbacks(cbGroup, ebGroup) |
- |
- |
- def irc_NAMES(self, prefix, params): |
- """Names message |
- |
- Parameters: [ <channel> *( "," <channel> ) [ <target> ] ] |
- """ |
- #<< NAMES #python |
- #>> :benford.openprojects.net 353 glyph = #python :Orban ... @glyph ... Zymurgy skreech |
- #>> :benford.openprojects.net 366 glyph #python :End of /NAMES list. |
- try: |
- channel = params[-1].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.ERR_NOSUCHCHANNEL, params[-1], |
- ":No such channel (could not decode your unicode!)") |
- return |
- |
- if channel.startswith('#'): |
- channel = channel[1:] |
- |
- def cbGroup(group): |
- self.names( |
- self.name, |
- '#' + group.name, |
- [user.name for user in group.iterusers()]) |
- |
- def ebGroup(err): |
- err.trap(ewords.NoSuchGroup) |
- # No group? Fine, no names! |
- self.names( |
- self.name, |
- '#' + channel, |
- []) |
- |
- self.realm.lookupGroup(channel).addCallbacks(cbGroup, ebGroup) |
- |
- |
- def irc_TOPIC(self, prefix, params): |
- """Topic message |
- |
- Parameters: <channel> [ <topic> ] |
- """ |
- try: |
- channel = params[0].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.ERR_NOSUCHCHANNEL, |
- ":That channel doesn't exist (could not decode your unicode!)") |
- return |
- |
- if channel.startswith('#'): |
- channel = channel[1:] |
- |
- if len(params) > 1: |
- self._setTopic(channel, params[1]) |
- else: |
- self._getTopic(channel) |
- |
- |
- def _sendTopic(self, group): |
- topic = group.meta.get("topic") |
- author = group.meta.get("topic_author") or "<noone>" |
- date = group.meta.get("topic_date", 0) |
- self.topic(self.name, '#' + group.name, topic) |
- self.topicAuthor(self.name, '#' + group.name, author, date) |
- |
- |
- def _getTopic(self, channel): |
- #<< TOPIC #python |
- #>> :benford.openprojects.net 332 glyph #python :<churchr> I really did. I sprained all my toes. |
- #>> :benford.openprojects.net 333 glyph #python itamar|nyc 994713482 |
- def ebGroup(err): |
- err.trap(ewords.NoSuchGroup) |
- self.sendMessage( |
- irc.ERR_NOSUCHCHANNEL, '=', channel, |
- ":That channel doesn't exist.") |
- |
- self.realm.lookupGroup(channel).addCallbacks(self._sendTopic, ebGroup) |
- |
- |
- def _setTopic(self, channel, topic): |
- #<< TOPIC #divunal :foo |
- #>> :glyph!glyph@adsl-64-123-27-108.dsl.austtx.swbell.net TOPIC #divunal :foo |
- |
- def cbGroup(group): |
- newMeta = group.meta.copy() |
- newMeta['topic'] = topic |
- newMeta['topic_author'] = self.name |
- newMeta['topic_date'] = int(time()) |
- |
- def ebSet(err): |
- self.sendMessage( |
- irc.ERR_CHANOPRIVSNEEDED, |
- "#" + group.name, |
- ":You need to be a channel operator to do that.") |
- |
- return group.setMetadata(newMeta).addErrback(ebSet) |
- |
- def ebGroup(err): |
- err.trap(ewords.NoSuchGroup) |
- self.sendMessage( |
- irc.ERR_NOSUCHCHANNEL, '=', channel, |
- ":That channel doesn't exist.") |
- |
- self.realm.lookupGroup(channel).addCallbacks(cbGroup, ebGroup) |
- |
- |
- def list(self, channels): |
- """Send a group of LIST response lines |
- |
- @type channel: C{list} of C{(str, int, str)} |
- @param channel: Information about the channels being sent: |
- their name, the number of participants, and their topic. |
- """ |
- for (name, size, topic) in channels: |
- self.sendMessage(irc.RPL_LIST, name, str(size), ":" + topic) |
- self.sendMessage(irc.RPL_LISTEND, ":End of /LIST") |
- |
- |
- def irc_LIST(self, prefix, params): |
- """List query |
- |
- Return information about the indicated channels, or about all |
- channels if none are specified. |
- |
- Parameters: [ <channel> *( "," <channel> ) [ <target> ] ] |
- """ |
- #<< list #python |
- #>> :orwell.freenode.net 321 exarkun Channel :Users Name |
- #>> :orwell.freenode.net 322 exarkun #python 358 :The Python programming language |
- #>> :orwell.freenode.net 323 exarkun :End of /LIST |
- if params: |
- # Return information about indicated channels |
- try: |
- channels = params[0].decode(self.encoding).split(',') |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.ERR_NOSUCHCHANNEL, params[0], |
- ":No such channel (could not decode your unicode!)") |
- return |
- |
- groups = [] |
- for ch in channels: |
- if ch.startswith('#'): |
- ch = ch[1:] |
- groups.append(self.realm.lookupGroup(ch)) |
- |
- groups = defer.DeferredList(groups, consumeErrors=True) |
- groups.addCallback(lambda gs: [r for (s, r) in gs if s]) |
- else: |
- # Return information about all channels |
- groups = self.realm.itergroups() |
- |
- def cbGroups(groups): |
- def gotSize(size, group): |
- return group.name, size, group.meta.get('topic') |
- d = defer.DeferredList([ |
- group.size().addCallback(gotSize, group) for group in groups]) |
- d.addCallback(lambda results: self.list([r for (s, r) in results if s])) |
- return d |
- groups.addCallback(cbGroups) |
- |
- |
- def _channelWho(self, group): |
- self.who(self.name, '#' + group.name, |
- [(m.name, self.hostname, self.realm.name, m.name, "H", 0, m.name) for m in group.iterusers()]) |
- |
- |
- def _userWho(self, user): |
- self.sendMessage(irc.RPL_ENDOFWHO, |
- ":User /WHO not implemented") |
- |
- |
- def irc_WHO(self, prefix, params): |
- """Who query |
- |
- Parameters: [ <mask> [ "o" ] ] |
- """ |
- #<< who #python |
- #>> :x.opn 352 glyph #python aquarius pc-62-31-193-114-du.blueyonder.co.uk y.opn Aquarius H :3 Aquarius |
- # ... |
- #>> :x.opn 352 glyph #python foobar europa.tranquility.net z.opn skreech H :0 skreech |
- #>> :x.opn 315 glyph #python :End of /WHO list. |
- ### also |
- #<< who glyph |
- #>> :x.opn 352 glyph #python glyph adsl-64-123-27-108.dsl.austtx.swbell.net x.opn glyph H :0 glyph |
- #>> :x.opn 315 glyph glyph :End of /WHO list. |
- if not params: |
- self.sendMessage(irc.RPL_ENDOFWHO, ":/WHO not supported.") |
- return |
- |
- try: |
- channelOrUser = params[0].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.RPL_ENDOFWHO, params[0], |
- ":End of /WHO list (could not decode your unicode!)") |
- return |
- |
- if channelOrUser.startswith('#'): |
- def ebGroup(err): |
- err.trap(ewords.NoSuchGroup) |
- self.sendMessage( |
- irc.RPL_ENDOFWHO, channelOrUser, |
- ":End of /WHO list.") |
- d = self.realm.lookupGroup(channelOrUser[1:]) |
- d.addCallbacks(self._channelWho, ebGroup) |
- else: |
- def ebUser(err): |
- err.trap(ewords.NoSuchUser) |
- self.sendMessage( |
- irc.RPL_ENDOFWHO, channelOrUser, |
- ":End of /WHO list.") |
- d = self.realm.lookupUser(channelOrUser) |
- d.addCallbacks(self._userWho, ebUser) |
- |
- |
- |
- def irc_WHOIS(self, prefix, params): |
- """Whois query |
- |
- Parameters: [ <target> ] <mask> *( "," <mask> ) |
- """ |
- def cbUser(user): |
- self.whois( |
- self.name, |
- user.name, user.name, self.realm.name, |
- user.name, self.realm.name, 'Hi mom!', False, |
- int(time() - user.lastMessage), user.signOn, |
- ['#' + group.name for group in user.itergroups()]) |
- |
- def ebUser(err): |
- err.trap(ewords.NoSuchUser) |
- self.sendMessage( |
- irc.ERR_NOSUCHNICK, |
- params[0], |
- ":No such nick/channel") |
- |
- try: |
- user = params[0].decode(self.encoding) |
- except UnicodeDecodeError: |
- self.sendMessage( |
- irc.ERR_NOSUCHNICK, |
- params[0], |
- ":No such nick/channel") |
- return |
- |
- self.realm.lookupUser(user).addCallbacks(cbUser, ebUser) |
- |
- |
- # Unsupported commands, here for legacy compatibility |
- def irc_OPER(self, prefix, params): |
- """Oper message |
- |
- Parameters: <name> <password> |
- """ |
- self.sendMessage(irc.ERR_NOOPERHOST, ":O-lines not applicable") |
- |
- |
-class IRCFactory(protocol.ServerFactory): |
- protocol = IRCUser |
- |
- def __init__(self, realm, portal): |
- self.realm = realm |
- self.portal = portal |
- |
- |
-class PBMind(pb.Referenceable): |
- def __init__(self): |
- pass |
- |
- def jellyFor(self, jellier): |
- return reflect.qual(PBMind), jellier.invoker.registerReference(self) |
- |
- def remote_userJoined(self, user, group): |
- pass |
- |
- def remote_userLeft(self, user, group, reason): |
- pass |
- |
- def remote_receive(self, sender, recipient, message): |
- pass |
- |
- def remote_groupMetaUpdate(self, group, meta): |
- pass |
- |
- |
-class PBMindReference(pb.RemoteReference): |
- implements(iwords.IChatClient) |
- |
- def receive(self, sender, recipient, message): |
- if iwords.IGroup.providedBy(recipient): |
- rec = PBGroup(self.realm, self.avatar, recipient) |
- else: |
- rec = PBUser(self.realm, self.avatar, recipient) |
- return self.callRemote( |
- 'receive', |
- PBUser(self.realm, self.avatar, sender), |
- rec, |
- message) |
- |
- def groupMetaUpdate(self, group, meta): |
- return self.callRemote( |
- 'groupMetaUpdate', |
- PBGroup(self.realm, self.avatar, group), |
- meta) |
- |
- def userJoined(self, group, user): |
- return self.callRemote( |
- 'userJoined', |
- PBGroup(self.realm, self.avatar, group), |
- PBUser(self.realm, self.avatar, user)) |
- |
- def userLeft(self, group, user, reason=None): |
- assert reason is None or isinstance(reason, unicode) |
- return self.callRemote( |
- 'userLeft', |
- PBGroup(self.realm, self.avatar, group), |
- PBUser(self.realm, self.avatar, user), |
- reason) |
-pb.setUnjellyableForClass(PBMind, PBMindReference) |
- |
- |
-class PBGroup(pb.Referenceable): |
- def __init__(self, realm, avatar, group): |
- self.realm = realm |
- self.avatar = avatar |
- self.group = group |
- |
- |
- def processUniqueID(self): |
- return hash((self.realm.name, self.avatar.name, self.group.name)) |
- |
- |
- def jellyFor(self, jellier): |
- return reflect.qual(self.__class__), self.group.name.encode('utf-8'), jellier.invoker.registerReference(self) |
- |
- |
- def remote_leave(self, reason=None): |
- return self.avatar.leave(self.group, reason) |
- |
- |
- def remote_send(self, message): |
- return self.avatar.send(self.group, message) |
- |
- |
-class PBGroupReference(pb.RemoteReference): |
- implements(iwords.IGroup) |
- |
- def unjellyFor(self, unjellier, unjellyList): |
- clsName, name, ref = unjellyList |
- self.name = name.decode('utf-8') |
- return pb.RemoteReference.unjellyFor(self, unjellier, [clsName, ref]) |
- |
- def leave(self, reason=None): |
- return self.callRemote("leave", reason) |
- |
- def send(self, message): |
- return self.callRemote("send", message) |
-pb.setUnjellyableForClass(PBGroup, PBGroupReference) |
- |
-class PBUser(pb.Referenceable): |
- def __init__(self, realm, avatar, user): |
- self.realm = realm |
- self.avatar = avatar |
- self.user = user |
- |
- def processUniqueID(self): |
- return hash((self.realm.name, self.avatar.name, self.user.name)) |
- |
- |
-class ChatAvatar(pb.Referenceable): |
- implements(iwords.IChatClient) |
- |
- def __init__(self, avatar): |
- self.avatar = avatar |
- |
- |
- def jellyFor(self, jellier): |
- return reflect.qual(self.__class__), jellier.invoker.registerReference(self) |
- |
- |
- def remote_join(self, groupName): |
- assert isinstance(groupName, unicode) |
- def cbGroup(group): |
- def cbJoin(ignored): |
- return PBGroup(self.avatar.realm, self.avatar, group) |
- d = self.avatar.join(group) |
- d.addCallback(cbJoin) |
- return d |
- d = self.avatar.realm.getGroup(groupName) |
- d.addCallback(cbGroup) |
- return d |
-registerAdapter(ChatAvatar, iwords.IUser, pb.IPerspective) |
- |
-class AvatarReference(pb.RemoteReference): |
- def join(self, groupName): |
- return self.callRemote('join', groupName) |
- |
- def quit(self): |
- d = defer.Deferred() |
- self.broker.notifyOnDisconnect(lambda: d.callback(None)) |
- self.broker.transport.loseConnection() |
- return d |
- |
-pb.setUnjellyableForClass(ChatAvatar, AvatarReference) |
- |
- |
-class WordsRealm(object): |
- implements(portal.IRealm, iwords.IChatService) |
- |
- _encoding = 'utf-8' |
- |
- def __init__(self, name): |
- self.name = name |
- |
- |
- def userFactory(self, name): |
- return User(name) |
- |
- |
- def groupFactory(self, name): |
- return Group(name) |
- |
- |
- def logoutFactory(self, avatar, facet): |
- def logout(): |
- # XXX Deferred support here |
- getattr(facet, 'logout', lambda: None)() |
- avatar.realm = avatar.mind = None |
- return logout |
- |
- |
- def requestAvatar(self, avatarId, mind, *interfaces): |
- if isinstance(avatarId, str): |
- avatarId = avatarId.decode(self._encoding) |
- |
- def gotAvatar(avatar): |
- if avatar.realm is not None: |
- raise ewords.AlreadyLoggedIn() |
- for iface in interfaces: |
- facet = iface(avatar, None) |
- if facet is not None: |
- avatar.loggedIn(self, mind) |
- mind.name = avatarId |
- mind.realm = self |
- mind.avatar = avatar |
- return iface, facet, self.logoutFactory(avatar, facet) |
- raise NotImplementedError(self, interfaces) |
- |
- return self.getUser(avatarId).addCallback(gotAvatar) |
- |
- |
- # IChatService, mostly. |
- createGroupOnRequest = False |
- createUserOnRequest = True |
- |
- def lookupUser(self, name): |
- raise NotImplementedError |
- |
- |
- def lookupGroup(self, group): |
- raise NotImplementedError |
- |
- |
- def addUser(self, user): |
- """Add the given user to this service. |
- |
- This is an internal method intented to be overridden by |
- L{WordsRealm} subclasses, not called by external code. |
- |
- @type user: L{IUser} |
- |
- @rtype: L{twisted.internet.defer.Deferred} |
- @return: A Deferred which fires with C{None} when the user is |
- added, or which fails with |
- L{twisted.words.ewords.DuplicateUser} if a user with the |
- same name exists already. |
- """ |
- raise NotImplementedError |
- |
- |
- def addGroup(self, group): |
- """Add the given group to this service. |
- |
- @type group: L{IGroup} |
- |
- @rtype: L{twisted.internet.defer.Deferred} |
- @return: A Deferred which fires with C{None} when the group is |
- added, or which fails with |
- L{twisted.words.ewords.DuplicateGroup} if a group with the |
- same name exists already. |
- """ |
- raise NotImplementedError |
- |
- |
- def getGroup(self, name): |
- assert isinstance(name, unicode) |
- if self.createGroupOnRequest: |
- def ebGroup(err): |
- err.trap(ewords.DuplicateGroup) |
- return self.lookupGroup(name) |
- return self.createGroup(name).addErrback(ebGroup) |
- return self.lookupGroup(name) |
- |
- |
- def getUser(self, name): |
- assert isinstance(name, unicode) |
- if self.createUserOnRequest: |
- def ebUser(err): |
- err.trap(ewords.DuplicateUser) |
- return self.lookupUser(name) |
- return self.createUser(name).addErrback(ebUser) |
- return self.lookupUser(name) |
- |
- |
- def createUser(self, name): |
- assert isinstance(name, unicode) |
- def cbLookup(user): |
- return failure.Failure(ewords.DuplicateUser(name)) |
- def ebLookup(err): |
- err.trap(ewords.NoSuchUser) |
- return self.userFactory(name) |
- |
- name = name.lower() |
- d = self.lookupUser(name) |
- d.addCallbacks(cbLookup, ebLookup) |
- d.addCallback(self.addUser) |
- return d |
- |
- |
- def createGroup(self, name): |
- assert isinstance(name, unicode) |
- def cbLookup(group): |
- return failure.Failure(ewords.DuplicateGroup(name)) |
- def ebLookup(err): |
- err.trap(ewords.NoSuchGroup) |
- return self.groupFactory(name) |
- |
- name = name.lower() |
- d = self.lookupGroup(name) |
- d.addCallbacks(cbLookup, ebLookup) |
- d.addCallback(self.addGroup) |
- return d |
- |
- |
-class InMemoryWordsRealm(WordsRealm): |
- def __init__(self, *a, **kw): |
- super(InMemoryWordsRealm, self).__init__(*a, **kw) |
- self.users = {} |
- self.groups = {} |
- |
- |
- def itergroups(self): |
- return defer.succeed(self.groups.itervalues()) |
- |
- |
- def addUser(self, user): |
- if user.name in self.users: |
- return defer.fail(failure.Failure(ewords.DuplicateUser())) |
- self.users[user.name] = user |
- return defer.succeed(user) |
- |
- |
- def addGroup(self, group): |
- if group.name in self.groups: |
- return defer.fail(failure.Failure(ewords.DuplicateGroup())) |
- self.groups[group.name] = group |
- return defer.succeed(group) |
- |
- |
- def lookupUser(self, name): |
- assert isinstance(name, unicode) |
- name = name.lower() |
- try: |
- user = self.users[name] |
- except KeyError: |
- return defer.fail(failure.Failure(ewords.NoSuchUser(name))) |
- else: |
- return defer.succeed(user) |
- |
- |
- def lookupGroup(self, name): |
- assert isinstance(name, unicode) |
- name = name.lower() |
- try: |
- group = self.groups[name] |
- except KeyError: |
- return defer.fail(failure.Failure(ewords.NoSuchGroup(name))) |
- else: |
- return defer.succeed(group) |
- |
-__all__ = [ |
- 'Group', 'User', |
- |
- 'WordsRealm', 'InMemoryWordsRealm', |
- ] |