import main from twisted.internet.ssl import DefaultOpenSSLContextFactory from twisted.internet import reactor from twisted.words.protocols.irc import IRCClient from twisted.internet.protocol import ReconnectingClientFactory from utils.parsing import parsen from utils.logging.log import log, error from utils.logging.send import sendAll from modules import userinfo from datetime import datetime from core.relay import sendRelayNotification from utils.get import getRelay # TODO: strip out non-relay functionality class IRCRelay(IRCClient): def __init__(self, num, relayCommands, user, stage2): self.isconnected = False self.buffer = "" if user is None: self.user = main.config["Relay"]["User"] else: self.user = user.lower() password = main.config["Relay"]["Password"] self.nickname = "relay" self.realname = "relay" self.username = self.user self.password = self.user + ":" + password self.relayCommands = relayCommands self.num = num self.stage2 = stage2 self.loop = None def privmsg(self, user, channel, msg): nick, ident, host = parsen(user) for i in main.ZNCErrors: if i in msg: error("ZNC issue:", msg) if nick[0] == main.config["Tweaks"]["ZNC"]["Prefix"]: nick = nick[1:] if nick in self.relayCommands.keys(): sendAll("[%s] %s -> %s" % (self.num, nick, msg)) def irc_ERR_PASSWDMISMATCH(self, prefix, params): log("%s: relay password mismatch" % self.num) sendAll("%s: relay password mismatch" % self.num) def sendStage2(self): # [["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.num, commands, user, self.stage2) def signedOn(self): if not self.isconnected: self.isconnected = True # log("signed on as a relay: %s" % self.num) sleeptime = 0 increment = 0.8 for i in self.relayCommands.keys(): for x in self.relayCommands[i]: reactor.callLater( sleeptime, self.msg, main.config["Tweaks"]["ZNC"]["Prefix"] + i, x, ) sleeptime += increment increment += 0.8 if self.stage2 is not None: reactor.callLater(sleeptime, self.sendStage2) reactor.callLater(sleeptime + 5, self.transport.loseConnection) return class IRCRelayFactory(ReconnectingClientFactory): def __init__(self, net, num=None, relayCommands=None, user=None, stage2=None): if net is None: self.num = num self.net = None self.name = "relay - %i" % num self.relay = True else: self.name = net + str(num) self.num = num self.net = net self.relay = False self.client = None 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.relayCommands, self.user, self.stage2 = relayCommands, user, stage2 def buildProtocol(self, addr): entry = IRCRelay(self.num, self.relayCommands, self.user, self.stage2) self.client = entry return entry def clientConnectionLost(self, connector, reason): if not self.relay: userinfo.delChannels(self.net, self.client.channels) if self.client is not None: self.client.isconnected = False self.client.authenticated = False self.client.channels = [] error = reason.getErrorMessage() if not self.relay: log("%s - %i: connection lost: %s" % (self.net, self.num, error)) sendAll("%s - %i: connection lost: %s" % (self.net, self.num, error)) ctime = str(datetime.now().isoformat()) sendRelayNotification( { "type": "conn", "net": self.net, "num": self.num, "status": "lost", "message": error, "ts": ctime, } ) self.retry(connector) # ReconnectingClientFactory.clientConnectionLost(self, connector, reason) def clientConnectionFailed(self, connector, reason): if self.client is not None: self.client.isconnected = False self.client.authenticated = False self.client.channels = [] error = reason.getErrorMessage() log("%s - %i: connection failed: %s" % (self.net, self.num, error)) if not self.relay: sendAll("%s - %s: connection failed: %s" % (self.net, self.num, error)) ctime = str(datetime.now().isoformat()) sendRelayNotification( { "type": "conn", "net": self.net, "num": self.num, "status": "failed", "message": error, "ts": ctime, } ) self.retry(connector) # ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) def deliverRelayCommands(num, 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 = IRCRelayFactory(net=None, num=num, relayCommands=relayCommands, user=user, stage2=stage2) host, port = getRelay(num) reactor.connectSSL(host, port, bot, contextFactory)