| 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',
|
| - ]
|
|
|