Implement the backend for automatically provisioning relays
This commit is contained in:
115
core/bot.py
115
core/bot.py
@@ -1,7 +1,8 @@
|
||||
from twisted.internet.protocol import ReconnectingClientFactory
|
||||
from twisted.words.protocols.irc import IRCClient
|
||||
from twisted.internet.defer import Deferred
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.internet.task import LoopingCall, deferLater
|
||||
from twisted.internet import reactor
|
||||
|
||||
from string import digits
|
||||
from random import randint
|
||||
@@ -15,6 +16,76 @@ import main
|
||||
from utils.logging.log import *
|
||||
from utils.logging.send import *
|
||||
|
||||
from twisted.internet.ssl import DefaultOpenSSLContextFactory
|
||||
|
||||
def deliverRelayCommands(relay, relayCommands, user=None, stage2=None):
|
||||
keyFN = main.certPath+main.config["Key"]
|
||||
certFN = main.certPath+main.config["Certificate"]
|
||||
contextFactory = DefaultOpenSSLContextFactory(keyFN.encode("utf-8", "replace"),
|
||||
certFN.encode("utf-8", "replace"))
|
||||
bot = IRCBotFactory(None, relay, relayCommands, user, stage2)
|
||||
rct = reactor.connectSSL(main.relay[relay]["host"],
|
||||
int(main.relay[relay]["port"]),
|
||||
bot, contextFactory)
|
||||
|
||||
class IRCRelay(IRCClient):
|
||||
def __init__(self, relay, relayCommands, user, stage2):
|
||||
self.connected = False
|
||||
self.buffer = ""
|
||||
if user == None:
|
||||
self.user = main.relay[relay]["user"]
|
||||
else:
|
||||
self.user = user
|
||||
password = main.relay[relay]["password"]
|
||||
self.nickname = self.user
|
||||
self.realname = self.user
|
||||
self.username = self.user
|
||||
self.password = self.user+":"+password
|
||||
|
||||
self.relayCommands = relayCommands
|
||||
self.relay = relay
|
||||
self.stage2 = stage2
|
||||
|
||||
def parsen(self, user):
|
||||
step = user.split("!")
|
||||
nick = step[0]
|
||||
if len(step) == 2:
|
||||
step2 = step[1].split("@")
|
||||
ident, host = step2
|
||||
else:
|
||||
ident = nick
|
||||
host = nick
|
||||
|
||||
return [nick, ident, host]
|
||||
|
||||
def privmsg(self, user, channel, msg):
|
||||
nick, ident, host = self.parsen(user)
|
||||
if nick[0] == main.config["Tweaks"]["ZNC"]["Prefix"]:
|
||||
nick = nick[1:]
|
||||
if nick in self.relayCommands.keys():
|
||||
sendAll("[%s] %s -> %s" % (self.relay, nick, msg))
|
||||
|
||||
def irc_ERR_PASSWDMISMATCH(self, prefix, params):
|
||||
log("%s: relay password mismatch" % self.relay)
|
||||
sendAll("%s: relay password mismatch" % self.relay)
|
||||
|
||||
def signedOn(self):
|
||||
self.connected = True
|
||||
log("signed on as a relay: %s" % self.relay)
|
||||
if main.config["Notifications"]["Connection"]:
|
||||
keyword.sendMaster("SIGNON: %s" % self.relay)
|
||||
for i in self.relayCommands.keys():
|
||||
for x in self.relayCommands[i]:
|
||||
self.msg(main.config["Tweaks"]["ZNC"]["Prefix"]+i, x)
|
||||
if not self.stage2 == None: # [["user", {"sasl": ["message1", "message2"]}], []]
|
||||
if not len(self.stage2) == 0:
|
||||
user = self.stage2[0].pop(0)
|
||||
commands = self.stage2[0].pop(0)
|
||||
del self.stage2[0]
|
||||
deliverRelayCommands(self.relay, commands, user, self.stage2)
|
||||
deferLater(reactor, 1, self.transport.loseConnection)
|
||||
return
|
||||
|
||||
class IRCBot(IRCClient):
|
||||
def __init__(self, name):
|
||||
self.connected = False
|
||||
@@ -380,33 +451,46 @@ class IRCBot(IRCClient):
|
||||
monitor.event(self.net, channel, {"type": "mode", "exact": user, "nick": nick, "ident": ident, "host": host, "modes": m, "modeargs": a})
|
||||
|
||||
class IRCBotFactory(ReconnectingClientFactory):
|
||||
def __init__(self, name):
|
||||
self.instance = main.pool[name]
|
||||
self.name = name
|
||||
self.net = "".join([x for x in self.name if not x in digits])
|
||||
def __init__(self, name, relay=None, relayCommands=None, user=None, stage2=None):
|
||||
if not name == None:
|
||||
self.name = name
|
||||
self.instance = main.pool[name]
|
||||
self.net = "".join([x for x in self.name if not x in digits])
|
||||
else:
|
||||
self.name = "Relay to "+relay
|
||||
self.client = None
|
||||
self.maxDelay = self.instance["maxdelay"]
|
||||
self.initialDelay = self.instance["initialdelay"]
|
||||
self.factor = self.instance["factor"]
|
||||
self.jitter = self.instance["jitter"]
|
||||
self.maxDelay = main.config["Tweaks"]["Delays"]["MaxDelay"]
|
||||
self.initialDelay = main.config["Tweaks"]["Delays"]["InitialDelay"]
|
||||
self.factor = main.config["Tweaks"]["Delays"]["Factor"]
|
||||
self.jitter = main.config["Tweaks"]["Delays"]["Jitter"]
|
||||
|
||||
self.relay, self.relayCommands, self.user, self.stage2 = relay, relayCommands, user, stage2
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
entry = IRCBot(self.name)
|
||||
main.IRCPool[self.name] = entry
|
||||
|
||||
if self.relay == None:
|
||||
entry = IRCBot(self.name)
|
||||
main.IRCPool[self.name] = entry
|
||||
else:
|
||||
entry = IRCRelay(self.relay, self.relayCommands, self.user, self.stage2)
|
||||
|
||||
self.client = entry
|
||||
return entry
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
userinfo.delNetwork(self.net, self.client.channels)
|
||||
if not self.relay:
|
||||
userinfo.delNetwork(self.net, self.client.channels)
|
||||
if not self.client == None:
|
||||
self.client.connected = False
|
||||
self.client.channels = []
|
||||
error = reason.getErrorMessage()
|
||||
log("%s: connection lost: %s" % (self.name, error))
|
||||
sendAll("%s: connection lost: %s" % (self.name, error))
|
||||
if not self.relay:
|
||||
sendAll("%s: connection lost: %s" % (self.name, error))
|
||||
if main.config["Notifications"]["Connection"]:
|
||||
keyword.sendMaster("CONNLOST %s: %s" % (self.name, error))
|
||||
self.retry(connector)
|
||||
if not self.relay:
|
||||
self.retry(connector)
|
||||
#ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
@@ -418,6 +502,7 @@ class IRCBotFactory(ReconnectingClientFactory):
|
||||
sendAll("%s: connection failed: %s" % (self.name, error))
|
||||
if main.config["Notifications"]["Connection"]:
|
||||
keyword.sendMaster("CONNFAIL %s: %s" % (self.name, error))
|
||||
self.retry(connector)
|
||||
if not self.relay:
|
||||
self.retry(connector)
|
||||
#ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
|
||||
|
||||
|
||||
@@ -1,35 +1,27 @@
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet.ssl import DefaultOpenSSLContextFactory
|
||||
|
||||
from core.bot import IRCBot, IRCBotFactory
|
||||
import main
|
||||
from utils.logging.log import *
|
||||
|
||||
def addBot(name):
|
||||
instance = main.pool[name]
|
||||
log("Started bot %s to %s:%s protocol %s nickname %s" % (name, instance["host"], instance["port"], instance["protocol"], instance["nickname"]))
|
||||
if instance["protocol"] == "plain":
|
||||
log("Started bot %s to %s:%s protocol %s nickname %s" % (name,
|
||||
instance["host"],
|
||||
instance["port"],
|
||||
instance["protocol"],
|
||||
instance["nickname"]))
|
||||
|
||||
|
||||
if instance["protocol"] == "ssl":
|
||||
keyFN = main.certPath+main.config["Key"]
|
||||
certFN = main.certPath+main.config["Certificate"]
|
||||
contextFactory = DefaultOpenSSLContextFactory(keyFN.encode("utf-8", "replace"),
|
||||
certFN.encode("utf-8", "replace"))
|
||||
if instance["bind"] == None:
|
||||
bot = IRCBotFactory(name)
|
||||
rct = reactor.connectTCP(instance["host"], instance["port"], bot, timeout=int(instance["timeout"]))
|
||||
|
||||
main.ReactorPool[name] = rct
|
||||
main.FactoryPool[name] = bot
|
||||
return
|
||||
else:
|
||||
bot = IRCBotFactory(name)
|
||||
rct = reactor.connectTCP(instance["host"], instance["port"], bot, timeout=int(instance["timeout"]), bindAddress=instance["bind"])
|
||||
|
||||
main.ReactorPool[name] = rct
|
||||
main.FactoryPool[name] = bot
|
||||
return
|
||||
elif instance["protocol"] == "ssl":
|
||||
keyFN = main.certPath+instance["key"]
|
||||
certFN = main.certPath+instance["certificate"]
|
||||
contextFactory = DefaultOpenSSLContextFactory(keyFN.encode("utf-8", "replace"), certFN.encode("utf-8", "replace"))
|
||||
if instance["bind"] == None:
|
||||
bot = IRCBotFactory(name)
|
||||
rct = reactor.connectSSL(instance["host"], int(instance["port"]), bot, contextFactory)
|
||||
rct = reactor.connectSSL(instance["host"],
|
||||
int(instance["port"]),
|
||||
bot, contextFactory)
|
||||
|
||||
main.ReactorPool[name] = rct
|
||||
main.FactoryPool[name] = bot
|
||||
@@ -37,7 +29,10 @@ def addBot(name):
|
||||
else:
|
||||
|
||||
bot = IRCBotFactory(name)
|
||||
rct = reactor.connectSSL(instance["host"], int(instance["port"]), bot, contextFactory, bindAddress=instance["bind"])
|
||||
rct = reactor.connectSSL(instance["host"],
|
||||
int(instance["port"]),
|
||||
bot, contextFactory,
|
||||
bindAddress=instance["bind"])
|
||||
|
||||
main.ReactorPool[name] = rct
|
||||
main.FactoryPool[name] = bot
|
||||
|
||||
Reference in New Issue
Block a user