"""
Irker protocol implementation.
"""
from twisted.internet import protocol, defer
from twisted.protocols import basic
from twisted.python import log
import json, urlparse
class Irk (basic.LineOnlyReceiver) :
"""
A connected Irk client.
"""
delimiter = '\n'
def connectionMade (self) :
self.transport.logPrefix = self.logPrefix
log.msg("connected", self)
def connectionLost (self, reason) :
log.err("connection lost", reason)
def error (self, *args) :
log.err(*args)
self.transport.loseConnection()
def lineReceived (self, line) :
"""
JSON -> in
"""
try :
irk = json.loads(line)
except ValueError as ex :
# invalid
return self.error(ex, line)
# dispatch
self.factory.irkReceived(irk).addErrback(self.error, line)
def __str__ (self) :
if not self.transport : return 'Irk'
host = self.transport.getHost()
peer = self.transport.getPeer()
return "{host.host}:{host.port}:{peer.host}".format(host=host, peer=peer)
logPrefix = __str__
class IrkFactory (protocol.ServerFactory) :
"""
Manage connected Irk clients.
"""
MAXATTR = 16
protocol = Irk
def __init__ (self, irc) :
self.irc = irc
@defer.inlineCallbacks
def irkReceived (self, irk) :
"""
Deffered to handle lookup of target, and then sending message.
Errbacks on failures.
"""
log.msg(str(irk))
if not 'to' in irk :
raise ValueError("missing target: to")
# MUST NOT be unicode
# XXX: ValueError?
url = urlparse.urlparse(str(irk.pop('to')))
# connect, register, join
target = yield self.irc.target(url)
# dispatch attrs
for attr, value in irk.iteritems() :
if len(attr) >= self.MAXATTR or not attr.islower() :
raise ValueError("invalid attr: %s" % (attr, ))
method = getattr(self, 'irk_' + attr, None)
if not method :
raise ValueError("unknown attr: %s" % (attr, ))
if value :
value = unicode(value)
else :
value = None
method(target, value)
# XXX: explicitly enable?
def irk_privmsg (self, target, value) :
"""
Send PRIVMSG to target.
"""
if not value :
# legacy
return
target.privmsg(value)
def irk_notice (self, target, value) :
"""
Send NOTICE to target.
"""
if not value :
raise ValueError("empty notice")
target.notice(value)
# TODO: refcounting vs join!
def irk_part (self, target, value) :
"""
PART target.
"""
# value is optional
target.part(value)