2012-03-02 04:31:57 +00:00
from twisted . words . protocols import irc
2015-02-02 01:02:53 +00:00
from twisted . internet import reactor , protocol , ssl
2016-11-10 19:55:25 +00:00
from twisted . internet . protocol import ReconnectingClientFactory , ServerFactory
2015-02-02 01:21:05 +00:00
from twisted . python import log , reflect , util
2016-11-10 19:55:25 +00:00
from twisted . internet . endpoints import clientFromString , serverFromString
2015-02-02 01:21:05 +00:00
from twisted . internet . error import VerifyError , CertificateError
2016-11-10 19:55:25 +00:00
from twisted . internet . defer import Deferred , succeed
2013-01-20 05:31:23 +00:00
from twisted . internet . task import LoopingCall
2012-03-03 03:16:43 +00:00
from twisted . application import service
2016-11-10 19:55:25 +00:00
from twisted . web import client , http_headers , iweb , resource , server
from zope . interface import implementer
2016-07-01 09:42:51 +00:00
from hashlib import md5
2015-02-02 01:21:05 +00:00
from OpenSSL import SSL , crypto
2012-03-02 17:24:25 +00:00
from signal import signal , SIGINT
2012-03-03 06:41:51 +00:00
from ConfigParser import SafeConfigParser
2016-11-10 19:55:25 +00:00
import re , sys , itertools , json
2012-03-02 04:31:57 +00:00
2012-03-03 03:54:28 +00:00
#
# RelayBot is a derivative of http://code.google.com/p/relaybot/
#
2012-03-02 04:31:57 +00:00
log . startLogging ( sys . stdout )
2012-03-03 03:56:25 +00:00
__version__ = " 0.1 "
2012-03-03 03:16:43 +00:00
application = service . Application ( " RelayBot " )
2012-03-02 04:31:57 +00:00
2015-02-02 01:21:05 +00:00
_sessionCounter = itertools . count ( ) . next
2012-03-03 01:53:32 +00:00
def main ( ) :
2012-03-03 06:41:51 +00:00
config = SafeConfigParser ( )
config . read ( " relaybot.config " )
defaults = config . defaults ( )
2012-07-16 22:06:00 +00:00
2016-11-10 19:55:25 +00:00
# Webhook stuff
webhooks = resource . Resource ( )
pool = client . HTTPConnectionPool ( reactor )
agent = client . Agent ( reactor , pool = pool )
2012-03-03 06:41:51 +00:00
for section in config . sections ( ) :
2012-07-16 22:06:00 +00:00
2012-03-04 19:51:06 +00:00
def get ( option ) :
2012-07-04 00:14:14 +00:00
if option in defaults or config . has_option ( section , option ) :
return config . get ( section , option ) or defaults [ option ]
else :
return None
2012-07-16 22:06:00 +00:00
2016-07-01 09:44:13 +00:00
options = { ' servername ' : section }
2016-07-13 03:49:22 +00:00
for option in [ " timeout " , " host " , " port " , " nick " , " channel " , " channels " , " heartbeat " , " password " , " username " , " realname " , " mode " , " ssl " , " fingerprint " , " nickcolor " , " topicsync " ] :
2012-03-11 05:55:48 +00:00
options [ option ] = get ( option )
2012-07-16 22:06:00 +00:00
2012-03-11 05:55:48 +00:00
mode = get ( " mode " )
2012-07-16 22:06:00 +00:00
2012-03-03 01:53:32 +00:00
#Not using endpoints pending http://twistedmatrix.com/trac/ticket/4735
#(ReconnectingClientFactory equivalent for endpoints.)
factory = None
2012-03-11 07:10:29 +00:00
if mode == " Default " :
2012-03-04 19:42:46 +00:00
factory = RelayFactory
2012-03-11 07:10:29 +00:00
elif mode == " FLIP " :
factory = FLIPFactory
elif mode == " NickServ " :
factory = NickServFactory
options [ " nickServPassword " ] = get ( " nickServPassword " )
2013-01-20 06:16:24 +00:00
elif mode == " ReadOnly " :
factory = ReadOnlyFactory
options [ " nickServPassword " ] = get ( " nickServPassword " )
2015-02-02 00:56:00 +00:00
# RelayByCommand: only messages with <nickname>: will be relayed.
elif mode == " RelayByCommand " :
factory = CommandFactory
2016-11-10 19:55:25 +00:00
elif mode == " Webhooks " :
options [ ' webhookNonce ' ] = get ( ' webhookNonce ' )
options [ ' outgoingWebhook ' ] = get ( ' outgoingWebhook ' )
webhooks . putChild ( options [ ' webhookNonce ' ] , Webhook ( agent , options ) )
continue
2012-07-16 22:06:00 +00:00
2012-03-11 05:55:48 +00:00
factory = factory ( options )
2015-02-02 01:02:53 +00:00
if options [ ' ssl ' ] == " True " :
2015-02-02 01:21:05 +00:00
if options [ ' fingerprint ' ] :
ctx = certoptions ( fingerprint = options [ ' fingerprint ' ] , verifyDepth = 0 )
reactor . connectSSL ( options [ ' host ' ] , int ( options [ ' port ' ] ) , factory , ctx , int ( options [ ' timeout ' ] ) )
else :
reactor . connectSSL ( options [ ' host ' ] , int ( options [ ' port ' ] ) , factory , ssl . ClientContextFactory ( ) , int ( options [ ' timeout ' ] ) )
2015-02-02 01:02:53 +00:00
else :
reactor . connectTCP ( options [ ' host ' ] , int ( options [ ' port ' ] ) , factory , int ( options [ ' timeout ' ] ) )
2012-07-16 22:06:00 +00:00
2016-11-10 19:55:25 +00:00
# Start incoming webhook server
if ' webhook ' in defaults :
serverFromString ( reactor , defaults [ ' webhook ' ] ) . listen ( server . Site ( webhooks ) )
2012-03-03 01:53:32 +00:00
reactor . callWhenRunning ( signal , SIGINT , handler )
2015-02-02 01:21:05 +00:00
class certoptions ( object ) :
_context = None
_OP_ALL = getattr ( SSL , ' OP_ALL ' , 0x0000FFFF )
_OP_NO_TICKET = 0x00004000
method = SSL . TLSv1_METHOD
def __init__ ( self , privateKey = None , certificate = None , method = None , verify = False , caCerts = None , verifyDepth = 9 , requireCertificate = True , verifyOnce = True , enableSingleUseKeys = True , enableSessions = True , fixBrokenPeers = False , enableSessionTickets = False , fingerprint = True ) :
assert ( privateKey is None ) == ( certificate is None ) , " Specify neither or both of privateKey and certificate "
self . privateKey = privateKey
self . certificate = certificate
if method is not None :
self . method = method
self . verify = verify
assert ( ( verify and caCerts ) or
( not verify ) ) , " Specify client CA certificate information if and only if enabling certificate verification "
self . caCerts = caCerts
self . verifyDepth = verifyDepth
self . requireCertificate = requireCertificate
self . verifyOnce = verifyOnce
self . enableSingleUseKeys = enableSingleUseKeys
self . enableSessions = enableSessions
self . fixBrokenPeers = fixBrokenPeers
self . enableSessionTickets = enableSessionTickets
self . fingerprint = fingerprint
def __getstate__ ( self ) :
d = self . __dict__ . copy ( )
try :
del d [ ' _context ' ]
except KeyError :
pass
return d
def __setstate__ ( self , state ) :
self . __dict__ = state
def getContext ( self ) :
if self . _context is None :
self . _context = self . _makeContext ( )
return self . _context
def _makeContext ( self ) :
ctx = SSL . Context ( self . method )
if self . certificate is not None and self . privateKey is not None :
ctx . use_certificate ( self . certificate )
ctx . use_privatekey ( self . privateKey )
ctx . check_privatekey ( )
verifyFlags = SSL . VERIFY_NONE
if self . verify or self . fingerprint :
verifyFlags = SSL . VERIFY_PEER
if self . requireCertificate :
verifyFlags | = SSL . VERIFY_FAIL_IF_NO_PEER_CERT
if self . verifyOnce :
verifyFlags | = SSL . VERIFY_CLIENT_ONCE
if self . caCerts :
store = ctx . get_cert_store ( )
for cert in self . caCerts :
store . add_cert ( cert )
def _verifyCallback ( conn , cert , errno , depth , preverify_ok ) :
if self . fingerprint :
digest = cert . digest ( " sha1 " )
if digest != self . fingerprint :
log . msg ( " Remote server fingerprint mismatch. Got: %s Expect: %s " % ( digest , self . fingerprint ) )
return False
else :
log . msg ( " Remote server fingerprint match: %s " % ( digest ) )
return True
return preverify_ok
ctx . set_verify ( verifyFlags , _verifyCallback )
if self . verifyDepth is not None :
ctx . set_verify_depth ( self . verifyDepth )
if self . enableSingleUseKeys :
ctx . set_options ( SSL . OP_SINGLE_DH_USE )
if self . fixBrokenPeers :
ctx . set_options ( self . _OP_ALL )
if self . enableSessions :
sessionName = md5 ( " %s - %d " % ( reflect . qual ( self . __class__ ) , _sessionCounter ( ) ) ) . hexdigest ( )
ctx . set_session_id ( sessionName )
if not self . enableSessionTickets :
ctx . set_options ( self . _OP_NO_TICKET )
return ctx
2012-03-02 18:09:00 +00:00
class Communicator :
def __init__ ( self ) :
self . protocolInstances = { }
def register ( self , protocol ) :
self . protocolInstances [ protocol . identifier ] = protocol
2012-03-11 22:14:32 +00:00
def isRegistered ( self , protocol ) :
return protocol . identifier in self . protocolInstances
2012-03-02 18:09:00 +00:00
def unregister ( self , protocol ) :
2012-03-11 13:52:59 +00:00
if protocol . identifier not in self . protocolInstances :
log . msg ( " No protocol instance with identifier %s . " % protocol . identifier )
return
2012-03-02 18:09:00 +00:00
del self . protocolInstances [ protocol . identifier ]
2016-07-13 03:49:22 +00:00
def relay ( self , protocol , channel , message ) :
2012-03-02 18:09:00 +00:00
for identifier in self . protocolInstances . keys ( ) :
if identifier == protocol . identifier :
continue
instance = self . protocolInstances [ identifier ]
2016-07-13 03:49:22 +00:00
instance . say ( channel , message )
2016-07-01 09:44:56 +00:00
2016-07-13 03:49:22 +00:00
def relayTopic ( self , protocol , channel , newTopic ) :
2016-07-01 09:44:56 +00:00
for identifier in self . protocolInstances . keys ( ) :
if identifier == protocol . identifier :
continue
instance = self . protocolInstances [ identifier ]
2016-07-13 03:49:22 +00:00
instance . topic ( channel , newTopic )
2012-03-02 18:09:00 +00:00
#Global scope: all protocol instances will need this.
communicator = Communicator ( )
2012-03-02 04:31:57 +00:00
class IRCRelayer ( irc . IRCClient ) :
2012-07-16 22:06:00 +00:00
2012-03-11 05:55:48 +00:00
def __init__ ( self , config ) :
2016-07-01 09:44:13 +00:00
self . servername = config [ ' servername ' ]
2012-03-11 05:55:48 +00:00
self . network = config [ ' host ' ]
2012-07-04 00:14:14 +00:00
self . password = config [ ' password ' ]
2016-07-13 03:49:22 +00:00
self . channels = config [ ' channels ' ]
2012-03-11 05:55:48 +00:00
self . nickname = config [ ' nick ' ]
self . identifier = config [ ' identifier ' ]
2012-03-14 15:57:29 +00:00
self . heartbeatInterval = float ( config [ ' heartbeat ' ] )
2013-01-20 07:14:18 +00:00
self . username = config [ ' username ' ]
self . realname = config [ ' realname ' ]
2015-02-02 00:56:00 +00:00
self . mode = config [ ' mode ' ]
2015-02-04 19:48:55 +00:00
self . nickcolor = config [ ' nickcolor ' ]
2015-02-04 20:20:02 +00:00
self . topicsync = config [ ' topicsync ' ]
2016-07-13 03:49:22 +00:00
2013-01-20 17:32:53 +00:00
# IRC RFC: https://tools.ietf.org/html/rfc2812#page-4
if len ( self . nickname ) > 9 :
log . msg ( " Nickname %s is %d characters long, which exceeds the RFC maximum of 9 characters. This may cause connection problems. " % ( self . nickname , len ( self . nickname ) ) )
2012-03-02 17:24:25 +00:00
2016-07-13 03:49:22 +00:00
# Backward compatibility with deprecated single-channel setting
if not self . channels :
self . channels = config [ ' channel ' ]
# Read comma separated string as list
self . channels = [ channel . strip ( ) for channel in self . channels . split ( ' , ' ) ]
log . msg ( " IRC Relay created. Name: %s | Host: %s | Channels: %s " % (
self . nickname , self . network , " , " . join ( self . channels ) ) )
2012-03-02 20:59:26 +00:00
def formatUsername ( self , username ) :
2012-03-04 01:50:55 +00:00
return username . split ( " ! " ) [ 0 ]
2012-03-02 20:59:26 +00:00
2016-07-13 03:49:22 +00:00
def relay ( self , channel , message ) :
communicator . relay ( self , channel , message )
def relayTopic ( self , channel , newTopic ) :
communicator . relayTopic ( self , channel , newTopic )
2012-03-02 14:31:43 +00:00
2016-07-13 03:49:22 +00:00
def joinChannel ( self , channel ) :
self . join ( channel , " " )
def joinChannels ( self ) :
[ self . joinChannel ( channel ) for channel in self . channels ]
2016-07-01 09:44:56 +00:00
2012-03-02 04:31:57 +00:00
def signedOn ( self ) :
2012-03-02 20:59:26 +00:00
log . msg ( " [ %s ] Connected to network. " % self . network )
2012-03-14 15:57:29 +00:00
self . startHeartbeat ( )
2016-07-13 03:49:22 +00:00
self . joinChannels ( )
2012-07-16 22:06:00 +00:00
2012-03-02 04:31:57 +00:00
def connectionLost ( self , reason ) :
2012-03-02 20:59:26 +00:00
log . msg ( " [ %s ] Connection lost, unregistering. " % self . network )
2012-03-02 18:09:00 +00:00
communicator . unregister ( self )
2012-07-16 22:06:00 +00:00
2012-03-02 04:31:57 +00:00
def joined ( self , channel ) :
2012-03-02 20:59:26 +00:00
log . msg ( " Joined channel %s , registering. " % channel )
2012-03-02 18:09:00 +00:00
communicator . register ( self )
2012-07-16 22:06:00 +00:00
2015-02-02 12:02:38 +00:00
def formatMessage ( self , message ) :
2015-02-06 19:24:37 +00:00
return message . replace ( self . nickname + " : " , " " , 1 )
2015-02-02 12:02:38 +00:00
2015-02-04 19:48:55 +00:00
def formatNick ( self , user ) :
2016-07-01 09:44:13 +00:00
nick = " [ " + self . servername + " / " + self . formatUsername ( user ) + " ] "
2015-02-04 19:48:55 +00:00
if self . nickcolor == " True " :
2016-07-01 09:44:13 +00:00
nick = " [ " + self . servername + " / \x03 03 " + self . formatUsername ( user ) + " \x03 ] "
2015-02-04 19:48:55 +00:00
return nick
2012-03-02 04:31:57 +00:00
def privmsg ( self , user , channel , message ) :
2015-02-04 19:55:40 +00:00
if self . mode != " RelayByCommand " :
2016-07-13 03:49:22 +00:00
self . relay ( channel , " %s %s " % ( self . formatNick ( user ) , message ) )
2015-02-04 19:55:40 +00:00
elif message . startswith ( self . nickname + ' : ' ) :
2016-07-13 03:49:22 +00:00
self . relay ( channel , " %s %s " % ( self . formatNick ( user ) , self . formatMessage ( message ) ) )
2012-07-16 22:06:00 +00:00
2012-03-02 04:31:57 +00:00
def kickedFrom ( self , channel , kicker , message ) :
2012-03-02 18:09:00 +00:00
log . msg ( " Kicked by %s . Message \" %s \" " % ( kicker , message ) )
communicator . unregister ( self )
2012-07-16 22:06:00 +00:00
2015-02-04 20:11:04 +00:00
def action ( self , user , channel , message ) :
2015-02-02 20:15:35 +00:00
if self . mode != " RelayByCommand " :
2016-07-13 03:49:22 +00:00
self . relay ( channel , " %s %s " % ( self . formatNick ( user ) , message ) )
2012-07-16 22:06:00 +00:00
2015-02-04 20:11:04 +00:00
def topicUpdated ( self , user , channel , newTopic ) :
if self . mode != " RelayByCommand " :
self . topic ( user , channel , newTopic )
def topic ( self , user , channel , newTopic ) :
2015-02-04 20:20:02 +00:00
if self . topicsync == " True " :
2016-07-13 03:49:22 +00:00
self . relayTopic ( channel , newTopic )
2015-02-04 20:11:04 +00:00
2012-03-03 01:42:01 +00:00
class RelayFactory ( ReconnectingClientFactory ) :
2012-03-02 04:31:57 +00:00
protocol = IRCRelayer
2012-03-03 01:42:01 +00:00
#Log information which includes reconnection status.
noisy = True
2012-07-16 22:06:00 +00:00
2012-03-11 05:55:48 +00:00
def __init__ ( self , config ) :
2012-03-11 21:59:40 +00:00
config [ " identifier " ] = " {0} {1} {2} " . format ( config [ " host " ] , config [ " port " ] , config [ " channel " ] )
2012-03-11 05:55:48 +00:00
self . config = config
2012-07-16 22:06:00 +00:00
2012-03-02 04:31:57 +00:00
def buildProtocol ( self , addr ) :
2012-03-03 01:42:01 +00:00
#Connected - reset reconnect attempt delay.
2015-02-02 11:07:08 +00:00
self . maxDelay = 900
2012-03-11 05:55:48 +00:00
x = self . protocol ( self . config )
2012-03-02 04:31:57 +00:00
x . factory = self
return x
2012-03-02 22:58:11 +00:00
#Remove the _<numbers> that FLIP puts on the end of usernames.
2015-02-02 13:19:11 +00:00
class FLIPRelayer ( IRCRelayer ) :
2012-03-02 22:58:11 +00:00
def formatUsername ( self , username ) :
2012-03-04 01:50:55 +00:00
return re . sub ( " _ \ d+$ " , " " , IRCRelayer . formatUsername ( self , username ) )
2012-03-02 22:58:11 +00:00
class FLIPFactory ( RelayFactory ) :
protocol = FLIPRelayer
2015-02-02 13:19:11 +00:00
class NickServRelayer ( IRCRelayer ) :
2012-03-11 07:10:29 +00:00
NickServ = " nickserv "
2013-01-20 05:25:31 +00:00
NickPollInterval = 30
2012-03-11 22:02:17 +00:00
2012-03-11 07:10:29 +00:00
def signedOn ( self ) :
2012-03-11 22:02:17 +00:00
log . msg ( " [ %s ] Connected to network. " % self . network )
2012-03-14 15:57:29 +00:00
self . startHeartbeat ( )
2016-07-13 03:49:22 +00:00
self . joinChannels ( )
2013-01-20 05:25:31 +00:00
self . checkDesiredNick ( )
def checkDesiredNick ( self ) :
"""
Checks that the nick is as desired , and if not attempts to retrieve it with
NickServ GHOST and trying again to change it after a polling interval .
"""
2012-07-16 22:25:10 +00:00
if self . nickname != self . desiredNick :
2012-03-14 20:57:47 +00:00
log . msg ( " [ %s ] Using GHOST to reclaim nick %s . " % ( self . network , self . desiredNick ) )
2012-03-11 22:02:17 +00:00
self . msg ( NickServRelayer . NickServ , " GHOST %s %s " % ( self . desiredNick , self . password ) )
2013-01-20 05:25:31 +00:00
# If NickServ does not respond try to regain nick anyway.
self . nickPoll . start ( self . NickPollInterval )
def regainNickPoll ( self ) :
if self . nickname != self . desiredNick :
log . msg ( " [ %s ] Reclaiming desired nick in polling. " % ( self . network ) )
self . setNick ( self . desiredNick )
else :
2013-01-20 06:00:50 +00:00
log . msg ( " [ %s ] Have desired nick. " % ( self . network ) )
2013-01-20 05:25:31 +00:00
self . nickPoll . stop ( )
def nickChanged ( self , nick ) :
log . msg ( " [ %s ] Nick changed from %s to %s . " % ( self . network , self . nickname , nick ) )
self . nickname = nick
self . checkDesiredNick ( )
2012-07-16 22:25:10 +00:00
2012-03-11 07:10:29 +00:00
def noticed ( self , user , channel , message ) :
2012-07-16 22:25:10 +00:00
log . msg ( " [ %s ] Recieved notice \" %s \" from %s . " % ( self . network , message , user ) )
#Identify with nickserv if requested
if IRCRelayer . formatUsername ( self , user ) . lower ( ) == NickServRelayer . NickServ :
msg = message . lower ( )
2016-07-01 09:46:12 +00:00
if msg . startswith ( " this nickname is registered " ) :
2012-07-16 22:25:10 +00:00
log . msg ( " [ %s ] Password requested; identifying with %s . " % ( self . network , NickServRelayer . NickServ ) )
self . msg ( NickServRelayer . NickServ , " IDENTIFY %s " % self . password )
elif msg == " ghost with your nickname has been killed. " or msg == " ghost with your nick has been killed. " :
2012-05-25 14:08:12 +00:00
log . msg ( " [ %s ] GHOST successful, reclaiming nick %s . " % ( self . network , self . desiredNick ) )
2012-03-11 22:02:17 +00:00
self . setNick ( self . desiredNick )
2012-07-18 11:41:37 +00:00
elif msg . endswith ( " isn ' t currently in use. " ) :
2012-07-29 04:32:20 +00:00
log . msg ( " [ %s ] GHOST not needed, reclaiming nick %s . " % ( self . network , self . desiredNick ) )
2012-07-18 11:41:37 +00:00
self . setNick ( self . desiredNick )
2012-07-16 22:06:00 +00:00
2012-03-11 07:10:29 +00:00
def __init__ ( self , config ) :
IRCRelayer . __init__ ( self , config )
self . password = config [ ' nickServPassword ' ]
2012-05-15 00:47:23 +00:00
self . desiredNick = config [ ' nick ' ]
2013-01-20 05:25:31 +00:00
self . nickPoll = LoopingCall ( self . regainNickPoll )
2012-03-11 07:10:29 +00:00
2013-01-20 06:16:24 +00:00
class ReadOnlyRelayer ( NickServRelayer ) :
2015-02-04 20:00:21 +00:00
pass
2013-01-20 06:16:24 +00:00
2015-02-02 13:19:11 +00:00
class CommandRelayer ( IRCRelayer ) :
2015-02-02 00:56:00 +00:00
pass
2013-01-20 06:16:24 +00:00
class ReadOnlyFactory ( RelayFactory ) :
protocol = ReadOnlyRelayer
2012-03-11 07:10:29 +00:00
class NickServFactory ( RelayFactory ) :
protocol = NickServRelayer
2015-02-02 00:56:00 +00:00
class CommandFactory ( RelayFactory ) :
protocol = CommandRelayer
2016-11-10 19:55:25 +00:00
@implementer ( iweb . IBodyProducer )
class StringProducer ( object ) :
def __init__ ( self , body ) :
self . body = body
self . length = len ( body )
def startProducing ( self , consumer ) :
consumer . write ( self . body )
return succeed ( None )
def pauseProducing ( self ) :
pass
def stopProducing ( self ) :
pass
class Webhook ( resource . Resource ) :
def __init__ ( self , agent , config ) :
self . agent = agent
self . servername = config [ ' servername ' ]
self . identifier = ' Webhook: %s ' % config [ ' webhookNonce ' ]
self . outgoingWebhook = config [ ' outgoingWebhook ' ]
self . nickcolor = config [ ' nickcolor ' ]
communicator . register ( self )
def render_POST ( self , request ) :
"""
Process the contents of a request ( i . e . a post from Rocket . Chat ' s
webhook service )
"""
action = request . getHeader ( " X-IRC-Action " )
obj = json . loads ( request . content . read ( ) )
if action == ' say ' :
user = str ( obj [ ' username ' ] )
channel = str ( obj [ ' channel ' ] )
message = str ( obj [ ' message ' ] )
self . relay ( channel , " %s %s " % ( self . formatNick ( user ) , message ) )
def formatUsername ( self , username ) :
return username . split ( " ! " ) [ 0 ]
def formatNick ( self , user ) :
nick = " [ " + self . servername + " / " + self . formatUsername ( user ) + " ] "
if self . nickcolor == " True " :
nick = " [ " + self . servername + " / \x03 03 " + self . formatUsername ( user ) + " \x03 ] "
return nick
def relay ( self , channel , message ) :
communicator . relay ( self , channel , message )
def relayTopic ( self , channel , newTopic ) :
communicator . relayTopic ( self , channel , newTopic )
def post ( self , action , user , channel , message ) :
obj = {
' username ' : user ,
' channel ' : channel ,
' message ' : message ,
}
d = self . agent . request (
' POST ' ,
self . outgoingWebhook ,
http_headers . Headers ( {
' Content-Type ' : [ ' application/json ' ] ,
' X-IRC-Action ' : [ action ] ,
} ) ,
StringProducer ( json . dumps ( obj ) ) )
def cbResponse ( response ) :
log . msg ( ' Outgoing webhook response: %d ' % response . code )
d . addCallback ( cbResponse )
def say ( self , channel , message , length = None ) :
if self . outgoingWebhook :
user , msg = message . split ( ' ' , 1 )
self . post ( ' say ' , user , channel , msg )
def topic ( self , user , channel , newTopic ) :
pass
2012-03-02 17:24:25 +00:00
def handler ( signum , frame ) :
2012-07-16 22:06:00 +00:00
reactor . stop ( )
2012-03-02 04:31:57 +00:00
2012-03-03 03:16:43 +00:00
#Main if run as script, builtin for twistd.
if __name__ in [ " __main__ " , " __builtin__ " ] :
2012-03-03 01:53:32 +00:00
main ( )