#!/usr/bin/env python from twisted.internet import reactor from twisted.internet.defer import Deferred from twisted.internet.ssl import DefaultOpenSSLContextFactory from twisted.internet.protocol import Protocol, Factory, ClientFactory, ReconnectingClientFactory from twisted.words.protocols.irc import IRCClient #from twisted.python import log #from sys import stdout #log.startLogging(stdout) from json import load, dump, loads from sys import exit from subprocess import run, PIPE numbers = "0123456789" listener = None connections = {} IRCPool = {} ReactorPool = {} FactoryPool = {} MonitorPool = [] configPath = "conf/" certPath = "cert/" filemap = { "config": ["config.json", "configuration"], "keyconf": ["keyword.json", "keyword lists"], "pool": ["pool.json", "pool"], "help": ["help.json", "command help"], "wholist": ["wholist.json", "WHO lists"], } def log(data): print("[LOG]", data) def debug(data): print("[DEBUG]", data) def warn(data): print("[WARNING]", data) def error(data): print("[ERROR]", data) exit(1) def sendData(addr, data): connections[addr].send(data) def sendSuccess(addr, data): sendData(addr, "[y] " + data) def sendFailure(addr, data): sendData(addr, "[n] " + data) def sendInfo(addr, data): sendData(addr, "[i] " + data) class IRCBot(IRCClient): def __init__(self, name): self.connected = False self.channels = [] self.name = name instance = pool[name] self.nickname = instance["nickname"] self.realname = instance["realname"] self.username = instance["username"] self.userinfo = instance["userinfo"] self.fingerReply = instance["finger"] self.versionName = instance["version"] self.versionNum = None self.versionEnv = None self.sourceURL = instance["source"] self.autojoin = instance["autojoin"] self._who = {} self._getWho = {} self.authtype = instance["authtype"] if self.authtype == "ns": self.authpass = instance["password"] self.authentity = instance["authentity"] else: self.password = instance["password"] def refresh(self): instance = pool[self.name] if not instance["nickname"] == self.nickname: self.nickname = instance["nickname"] self.setNick(self.nickname) self.userinfo = instance["userinfo"] self.fingerReply = instance["finger"] self.versionName = instance["version"] self.versionNum = None self.versionEnv = None self.sourceURL = instance["source"] 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) helper.setWhoSingle(self.name, nick, ident, host) helper.actKeyword(user, channel, msg, self.nickname, "PRV", self.name) def noticed(self, user, channel, msg): nick, ident, host = self.parsen(user) helper.setWhoSingle(self.name, nick, ident, host) helper.actKeyword(user, channel, msg, self.nickname, "NOT", self.name) def action(self, user, channel, msg): nick, ident, host = self.parsen(user) helper.setWhoSingle(self.name, nick, ident, host) helper.actKeyword(user, channel, msg, self.nickname, "ACT", self.name) def get(self, var): try: result = getattr(self, var) except AttributeError: result = None return result def setNick(self, nickname): self._attemptedNick = nickname self.sendLine("NICK %s" % nickname) self.nickname = nickname def alterCollidedNick(self, nickname): newnick = nickname + "_" return newnick def irc_ERR_NICKNAMEINUSE(self, prefix, params): self._attemptedNick = self.alterCollidedNick(self._attemptedNick) self.setNick(self._attemptedNick) def irc_ERR_PASSWDMISMATCH(self, prefix, params): log("%s: password mismatch" % self.name) helper.sendAll("%s: password mismatch" % self.name) def who(self, channel): channel = channel d = Deferred() if channel not in self._who: self._who[channel] = ([], {}) self._who[channel][0].append(d) self.sendLine("WHO %s" % channel) return d def irc_RPL_WHOREPLY(self, prefix, params): channel = params[1] user = params[2] host = params[3] server = params[4] nick = params[5] status = params[6] realname = params[7] if channel not in self._who: return n = self._who[channel][1] n[nick] = [nick, user, host, server, status, realname] def irc_RPL_ENDOFWHO(self, prefix, params): channel = params[1] if channel not in self._who: return callbacks, info = self._who[channel] for cb in callbacks: cb.callback((channel, info)) del self._who[channel] def got_who(self, whoinfo): global wholist helper.setWho(self.name, whoinfo[1]) def signedOn(self): self.connected = True log("signed on: %s" % self.name) if config["ConnectionNotifications"]: helper.sendMaster("SIGNON: %s" % self.name) if self.authtype == "ns": self.msg(self.authentity, "IDENTIFY %s" % self.nspass) for i in self.autojoin: self.join(i) def joined(self, channel): if not channel in self.channels: self.channels.append(channel) self.who(channel).addCallback(self.got_who) def left(self, channel): if channel in self.channels: self.channels.remove(channel) def kickedFrom(self, channel, kicker, message): if channel in self.channels: self.channels.remove(channel) helper.sendMaster("KICK %s: (%s/%s) %s" % (self.name, kicker, channel, message)) def userJoined(self, user, channel): nick, ident, host = self.parsen(user) helper.setWhoSingle(self.name, nick, ident, host) def userLeft(self, user, channel): nick, ident, host = self.parsen(user) helper.setWhoSingle(self.name, nick, ident, host) def userQuit(self, user, quitMessage): nick, ident, host = self.parsen(user) helper.setWhoSingle(self.name, nick, ident, host) helper.actKeyword(user, None, quitMessage, self.nickname, "QUT", self.name) def userKicked(self, kickee, channel, kicker, message): nick, ident, host = self.parsen(kicker) helper.setWhoSingle(self.name, nick, ident, host) helper.actKeyword(kicker, channel, message, self.nickname, "KCK", self.name) def userRenamed(self, oldname, newname): nick, ident, host = self.parsen(oldname) helper.setWhoSingle(self.name, nick, ident, host) helper.setWhoSingle(self.name, newname, ident, host) def topicUpdated(self, user, channel, newTopic): nick, ident, host = self.parsen(user) helper.setWhoSingle(self.name, nick, ident, host) helper.actKeyword(user, channel, newTopic, self.nickname, "TOP", self.name) def modeChanged(self, user, channel, toset, modes, args): nick, ident, host = self.parsen(user) helper.setWhoSingle(self.name, nick, ident, host) class IRCBotFactory(ReconnectingClientFactory): def __init__(self, name): self.instance = pool[name] self.name = name self.client = None self.maxDelay = self.instance["maxdelay"] self.initialDelay = self.instance["initialdelay"] self.factor = self.instance["factor"] self.jitter = self.instance["jitter"] def buildProtocol(self, addr): global IRCPool entry = IRCBot(self.name) IRCPool[self.name] = entry self.client = entry return entry def clientConnectionLost(self, connector, reason): if not self.client == None: self.client.connected = False self.client.channels = [] error = reason.getErrorMessage() log("%s: connection lost: %s" % (self.name, error)) helper.sendAll("%s: connection lost: %s" % (self.name, error)) if config["ConnectionNotifications"]: helper.sendMaster("CONNLOST %s: %s" % (self.name, error)) self.retry(connector) #ReconnectingClientFactory.clientConnectionLost(self, connector, reason) def clientConnectionFailed(self, connector, reason): if not self.client == None: self.client.connected = False self.client.channels = [] error = reason.getErrorMessage() log("%s: connection failed: %s" % (self.name, error)) helper.sendAll("%s: connection failed: %s" % (self.name, error)) if config["ConnectionNotifications"]: helper.sendMaster("CONNFAIL %s: %s" % (self.name, error)) self.retry(connector) #ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) class Base(Protocol): def __init__(self, addr): self.addr = addr self.authed = False if config["UsePassword"] == False: self.authed = True def send(self, data): data += "\r\n" data = data.encode("utf-8", "replace") self.transport.write(data) def dataReceived(self, data): data = data.decode("utf-8", "replace") #log("Data received from %s:%s -- %s" % (self.addr.host, self.addr.port, repr(data))) if "\n" in data: splitData = [x for x in data.split("\n") if x] if "\n" in data: for i in splitData: helper.parseCommand(self.addr, self.authed, i) return helper.parseCommand(self.addr, self.authed, data) def connectionMade(self): log("Connection from %s:%s" % (self.addr.host, self.addr.port)) self.send("Hello.") def connectionLost(self, reason): global connections, MonitorPool self.authed = False log("Connection lost from %s:%s -- %s" % (self.addr.host, self.addr.port, reason.getErrorMessage())) if not listener == None: if self.addr in connections.keys(): del connections[self.addr] else: warn("Tried to remove a non-existant connection.") else: warn("Tried to remove a connection from a listener that wasn't running.") if self.addr in MonitorPool: MonitorPool.remove(self.addr) class BaseFactory(Factory): def buildProtocol(self, addr): global connections entry = Base(addr) connections[addr] = entry return entry def send(self, addr, data): global connections if addr in connections.keys(): connection = connections[addr] connection.send(data) else: return class Helper(object): def setWho(self, network, newObjects): global wholist network = "".join([x for x in network if not x in numbers]) if not network in wholist.keys(): wholist[network] = {} for i in newObjects.keys(): wholist[network][i] = newObjects[i] return def setWhoSingle(self, network, nick, ident, host): global wholist network = "".join([x for x in network if not x in numbers]) if network in wholist.keys(): if nick in wholist[network].keys(): wholist[network][nick][1] = ident wholist[network][nick][2] = host else: wholist[network][nick] = [nick, ident, host, None, None, None] def getWho(self, nick): result = {} for i in wholist.keys(): for x in wholist[i].keys(): if nick.lower() == x.lower(): if not i in result.keys(): result[i] = [] result[i].append(wholist[i][x]) return result def save(self, var): with open(configPath+filemap[var][0], "w") as f: dump(globals()[var], f, indent=4) return def load(self, var): with open(configPath+filemap[var][0], "r") as f: globals()[var] = load(f) def incorrectUsage(self, addr, mode): if mode == None: sendFailure(addr, "Incorrect usage") return if mode in help.keys(): sendFailure(addr, "Usage: " + help[mode]) return def sendAll(self, data): global connections for i in connections: connections[i].send(data) return def sendMaster(self, data): if config["Master"][0] in IRCPool.keys(): IRCPool[config["Master"][0]].msg(config["Master"][1], data) else: warn("Master with no IRC instance defined") for i in MonitorPool: connections[i].send(data) def isKeyword(self, msg): message = msg.lower() messageDuplicate = message toUndo = False uniqueNum = 0 totalNum = 0 for i in keyconf["Keywords"]: if i in message: if i in keyconf["KeywordsExcept"].keys(): for x in keyconf["KeywordsExcept"][i]: if x in message: toUndo = True messageDuplicate = messageDuplicate.replace(x, "\0\r\n\n\0") for y in keyconf["Keywords"]: if i in messageDuplicate: totalNum += messageDuplicate.count(i) message = messageDuplicate.replace(i, "{"+i+"}") message = message.replace("\0\r\n\n\0", x) uniqueNum += 1 if toUndo == False: totalNum += message.count(i) message = message.replace(i, "{"+i+"}") uniqueNum += 1 toUndo = False if totalNum == 0: return False else: return [message, uniqueNum, totalNum] def actKeyword(self, user, channel, message, nickname, actType, name): toSend = self.isKeyword(message) if name == config["Master"][0] and channel == config["Master"][1]: pass else: if config["HighlightNotifications"]: msgLower = message.lower() nickLower = nickname.lower() if nickLower in msgLower: msgLower = msgLower.replace(nickLower, "{"+nickLower+"}") self.sendMaster("NICK %s %s (T:%s): (%s/%s) %s" % (actType, name, msgLower.count(nickLower), user, channel, msgLower)) if toSend: helper.sendMaster("MATCH %s %s (U:%s T:%s): (%s/%s) %s" % (actType, name, toSend[1], toSend[2], user, channel, toSend[0])) def addBot(self, name): global IRCPool, certPath instance = 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": if instance["bind"] == None: bot = IRCBotFactory(name) rct = reactor.connectTCP(instance["host"], instance["port"], bot, timeout=int(instance["timeout"])) ReactorPool[name] = rct FactoryPool[name] = bot return else: bot = IRCBotFactory(name) rct = reactor.connectTCP(instance["host"], instance["port"], bot, timeout=int(instance["timeout"]), bindAddress=instance["bind"]) ReactorPool[name] = rct FactoryPool[name] = bot return elif instance["protocol"] == "ssl": keyFN = certPath+instance["key"] certFN = 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) ReactorPool[name] = rct FactoryPool[name] = bot return else: bot = IRCBotFactory(name) rct = reactor.connectSSL(instance["host"], int(instance["port"]), bot, contextFactory, bindAddress=instance["bind"]) ReactorPool[name] = rct FactoryPool[name] = bot return def addKeyword(self, keyword): if keyword in keyconf["Keywords"]: return "EXISTS" else: for i in keyconf["Keywords"]: if i in keyword or keyword in i: return "ISIN" keyconf["Keywords"].append(keyword) self.save("keyconf") return True def delKeyword(self, keyword): if not keyword in keyconf["Keywords"]: return "NOKEY" keyconf["Keywords"].remove(keyword) self.save("keyconf") return True def parseCommand(self, addr, authed, data): global pool, MonitorPool, wholist spl = data.split() if addr in connections.keys(): obj = connections[addr] else: warn("Got connection object with no instance in the address pool") return success = lambda data: sendSuccess(addr, data) failure = lambda data: sendFailure(addr, data) info = lambda data: sendInfo(addr, data) incUsage = lambda mode: self.incorrectUsage(addr, mode) length = len(spl) if len(spl) > 0: cmd = spl[0] else: failure("No text was sent") return if authed == True: if cmd == "help": helpMap = [] for i in help.keys(): helpMap.append("%s: %s" % (i, help[i])) info("\n".join(helpMap)) return elif cmd == "save": if length == 2: if spl[1] in filemap.keys(): self.save(spl[1]) success("Saved %s to %s" % (spl[1], filemap[spl[1]][0])) return elif spl[1] == "all": for i in filemap.keys(): helper.save(i) success("Saved %s from %s" % (i, filemap[i][0])) return else: incUsage("save") return else: incUsage("save") return elif cmd == "load": if length == 2: if spl[1] in filemap.keys(): self.load(spl[1]) success("Loaded %s from %s" % (spl[1], filemap[spl[1]][0])) return elif spl[1] == "all": for i in filemap.keys(): helper.load(i) success("Loaded %s from %s" % (i, filemap[i][0])) return else: incUsage("load") return else: incUsage("load") return elif cmd == "dist": if config["Dist"]["Enabled"]: rtrn = run([config["Dist"]["File"]], shell=True, stdout=PIPE) if config["Dist"]["SendOutput"]: info("Exit code: %s -- Stdout: %s" % (rtrn.returncode, rtrn.stdout)) else: info("Exit code: %s" % rtrn.returncode) else: failure("The dist command is not enabled") return elif cmd == "pass": info("You are already authenticated") return elif cmd == "logout": obj.authed = False success("Logged out") return elif cmd == "list": poolMap = [] for i in pool.keys(): poolMap.append("Server: %s" % i) for x in pool[i].keys(): poolMap.append("%s: %s" % (x, pool[i][x])) poolMap.append("\n") info("\n".join(poolMap)) return elif cmd == "stats": if length == 1: stats = [] numChannels = 0 numWhoEntries = 0 for i in IRCPool.keys(): numChannels += len(IRCPool[i].channels) for i in wholist.keys(): numWhoEntries += len(wholist[i].keys()) stats.append("Servers: %s" % len(IRCPool.keys())) stats.append("Channels: %s" % numChannels) stats.append("User records: %s" % numWhoEntries) info("\n".join(stats)) return elif length == 2: stats = [] numChannels = 0 numWhoEntries = 0 failures = 0 if spl[1] in IRCPool.keys(): numChannels += len(IRCPool[spl[1]].channels) else: failure("Name does not exist: %s" % spl[1]) failures += 1 if spl[1] in wholist.keys(): numWhoEntries += len(wholist[spl[1]].keys()) else: failure("Who entry does not exist: %s" % spl[1]) failures += 1 if failures == 2: failure("No information found, aborting") return stats.append("Channels: %s" % numChannels) stats.append("User records: %s" % numWhoEntries) info("\n".join(stats)) return else: incUsage("stats") elif cmd == "enable": if length == 2: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return pool[spl[1]]["enabled"] = True self.save("pool") if not spl[1] in IRCPool.keys(): self.addBot(spl[1]) else: pass success("Successfully enabled bot %s" % spl[1]) return else: incUsage("enable") return elif cmd == "disable": if length == 2: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return pool[spl[1]]["enabled"] = False self.save("pool") if spl[1] in ReactorPool.keys(): if spl[1] in FactoryPool.keys(): FactoryPool[spl[1]].stopTrying() ReactorPool[spl[1]].disconnect() if spl[1] in IRCPool.keys(): del IRCPool[spl[1]] del ReactorPool[spl[1]] del FactoryPool[spl[1]] success("Successfully disabled bot %s" % spl[1]) return else: incUsage("disable") return elif cmd == "join": if length == 3: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return if not spl[1] in IRCPool.keys(): failure("Name has no instance: %s" % spl[1]) return IRCPool[spl[1]].join(spl[2]) success("Joined %s" % spl[2]) return elif length == 4: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return if not spl[1] in IRCPool.keys(): failure("Name has no instance: %s" % spl[1]) return IRCPool[spl[1]].join(spl[2], spl[3]) success("Joined %s with key %s" % (spl[2], spl[3])) return else: incUsage("join") return elif cmd == "part": if length == 3: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return if not spl[1] in IRCPool.keys(): failure("Name has no instance: %s" % spl[1]) return IRCPool[spl[1]].part(spl[2]) success("Left %s" % spl[2]) return else: incUsage("part") return elif cmd == "get": if length == 3: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return if not spl[1] in IRCPool.keys(): failure("Name has no instance: %s" % spl[1]) return info(str(IRCPool[spl[1]].get(spl[2]))) return else: incUsage("get") return elif cmd == "who": if length == 2: result = self.getWho(spl[1]) rtrn = "" for i in result.keys(): rtrn += "Matches from: %s" % i rtrn += "\n" for x in result[i]: x = [y for y in x if not y == None] rtrn += str((", ".join(x))) rtrn += "\n" info(rtrn) return else: incUsage("who") return elif cmd == "key": if data.startswith("key add"): if not data in ["key add ", "key add"] and data[3] == " ": keywordsToAdd = data[8:] keywords = keywordsToAdd.split(",") for keyword in keywords: rtrn = self.addKeyword(keyword) if rtrn == "EXISTS": failure("Keyword already exists: %s" % keyword) elif rtrn == "ISIN": failure("Keyword already matched: %s" % keyword) elif rtrn == True: success("Keyword added: %s" % keyword) return else: incUsage("key") return elif data.startswith("key del"): if not data in ["key del ", "key del"] and data[3] == " ": keywordsToDel = data[8:] keywords = keywordsToDel.split(",") for keyword in keywords: rtrn = self.delKeyword(keyword) if rtrn == "NOKEY": failure("Keyword does not exist: %s" % keyword) elif rtrn == True: success("Keyword deleted: %s" % keyword) return else: incUsage("key") return if length == 4: if spl[1] == "except": if not spl[2] in keyconf["Keywords"]: failure("No such keyword: %s" % spl[2]) return if spl[2] in keyconf["KeywordsExcept"].keys(): if spl[3] in keyconf["KeywordsExcept"][spl[2]]: failure("Exception exists: %s" % spl[3]) return else: if not spl[2] in spl[3]: failure("Keyword %s not in exception %s. This won't work" % (spl[2], spl[3])) return keyconf["KeywordsExcept"][spl[2]] = [] keyconf["KeywordsExcept"][spl[2]].append(spl[3]) self.save("keyconf") success("Successfully added exception %s for keyword %s" % (spl[3], spl[2])) return elif spl[1] == "master": if not spl[2] in pool.keys(): failure("Name does not exist: %s" % spl[2]) return if spl[2] in IRCPool.keys(): if not spl[3] in IRCPool[spl[2]].channels: info("Bot not on channel: %s" % spl[3]) config["Master"] = [spl[2], spl[3]] self.save("config") success("Master set to %s on %s" % (spl[3], spl[2])) return elif spl[1] == "unexcept": if not spl[2] in keyconf["KeywordsExcept"].keys(): failure("No such exception: %s" % spl[2]) return if not spl[3] in keyconf["KeywordsExcept"][spl[2]]: failure("Exception %s has no attribute %s" % (spl[2], spl[3])) return keyconf["KeywordsExcept"][spl[2]].remove(spl[3]) if keyconf["KeywordsExcept"][spl[2]] == []: del keyconf["KeywordsExcept"][spl[2]] self.save("keyconf") success("Successfully removed exception %s for keyword %s" % (spl[3], spl[2])) return else: incUsage("key") return elif length == 3: if spl[1] == "unexcept": if not spl[2] in keyconf["KeywordsExcept"].keys(): failure("No such exception: %s" % spl[2]) return del keyconf["KeywordsExcept"][spl[2]] self.save("keyconf") success("Successfully removed exception list of %s" % spl[2]) return elif spl[1] == "monitor": if spl[2] == "on": if not obj.addr in MonitorPool: MonitorPool.append(obj.addr) success("Keyword monitoring enabled") return else: failure("Keyword monitoring is already enabled") return elif spl[2] == "off": if obj.addr in MonitorPool: MonitorPool.remove(obj.addr) success("Keyword monitoring disabled") return else: failure("Keyword monitoring is already disabled") return else: incUsage("key") return else: incUsage("key") return elif length == 2: if spl[1] == "show": info(",".join(keyconf["Keywords"])) return elif spl[1] == "showexcept": exceptMap = [] for i in keyconf["KeywordsExcept"].keys(): exceptMap.append("Key: %s" % i) exceptMap.append("%s: %s" % (i, ",".join(keyconf["KeywordsExcept"][i]))) exceptMap.append("\n") info("\n".join(exceptMap)) return elif spl[1] == "master": info(" - ".join(config["Master"])) return elif spl[1] == "monitor": if obj.addr in MonitorPool: info("Keyword monitoring is enabled on this connection") return else: info("Keyword monitoring is disabled on this connection") return else: incUsage("key") return else: incUsage("key") return elif cmd == "add": if length > 6: failure("Too many arguments") return if length > 1: name = spl[1] else: incUsage("add") return if length > 2: host = spl[2] if length > 3: port = spl[3] if length > 4: protocol = spl[4] if length > 5: nickname = spl[5] toFail = False if length < 6: if config["Default"]["nickname"] == None: failure("Choose a nickname, or configure one in defaults") toFail = True else: nickname = config["Default"]["nickname"] if length < 5: if config["Default"]["protocol"] == None: failure("Choose a protocol, or configure one in defaults") toFail = True else: protocol = config["Default"]["protocol"] if length < 4: if config["Default"]["port"] == None: failure("Choose a port, or configure one in defaults") toFail = True else: port = config["Default"]["port"] if length < 3: if config["Default"]["host"] == None: failure("Choose a host, or configure one in defaults") toFail = True else: host = config["Default"]["host"] if toFail: failure("Stopping due to previous error(s)") return if length < 2: incUsage("add") return if name in pool.keys(): failure("Name already exists: %s" % name) return protocol = protocol.lower() if not protocol in ["ssl", "plain"]: failure("Protocol must be ssl or plain, not %s" % protocol) return try: int(port) except: failure("Port must be an integer, not %s" % port) return pool[name] = { "host": host, "port": port, "protocol": protocol, "bind": config["Default"]["bind"], "timeout": config["Default"]["timeout"], "maxdelay": config["Default"]["maxdelay"], "initialdelay": config["Default"]["initialdelay"], "factor": config["Default"]["factor"], "jitter": config["Default"]["jitter"], "nickname": nickname, "username": config["Default"]["username"], "realname": config["Default"]["realname"], "userinfo": config["Default"]["userinfo"], "finger": config["Default"]["finger"], "version": config["Default"]["version"], "source": config["Default"]["source"], "autojoin": config["Default"]["autojoin"], "authtype": config["Default"]["authtype"], "password": config["Default"]["password"], "authentity": config["Default"]["authentity"], "key": config["Default"]["key"], "certificate": config["Default"]["certificate"], "enabled": config["ConnectOnCreate"], } if config["ConnectOnCreate"] == True: self.addBot(name) success("Successfully created bot") self.save("pool") return elif cmd == "del": if length == 2: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return del pool[spl[1]] if spl[1] in ReactorPool.keys(): if spl[1] in FactoryPool.keys(): FactoryPool[spl[1]].stopTrying() ReactorPool[spl[1]].disconnect() if spl[1] in IRCPool.keys(): del IRCPool[spl[1]] del ReactorPool[spl[1]] del FactoryPool[spl[1]] success("Successfully removed bot") self.save("pool") return else: incUsage("del") return elif cmd == "default": toUnset = False if length == 1: optionMap = ["Viewing defaults"] for i in config["Default"].keys(): optionMap.append("%s: %s" % (i, config["Default"][i])) info("\n".join(optionMap)) return elif length == 2: if not spl[1] in config["Default"].keys(): failure("No such key: %s" % spl[1]) return info("%s: %s" % (spl[1], config["Default"][spl[1]])) return elif length == 3: if not spl[1] in config["Default"].keys(): failure("No such key: %s" % spl[1]) return if spl[2].lower() in ["none", "nil"]: spl[2] = None toUnset = True if spl[1] in ["port", "timeout", "maxdelay"]: try: spl[2] = int(spl[2]) except: failure("Value must be an integer, not %s" % spl[2]) return if spl[2] in ["initialdelay", "factor", "jitter"]: try: spl[3] = float(spl[3]) except: failure("Value must be a floating point integer, not %s" % spl[3]) return if spl[1] == "protocol": if not toUnset: if not spl[2] in ["ssl", "plain"]: failure("Protocol must be ssl or plain, not %s" % spl[2]) return if spl[2] == config["Default"][spl[1]]: failure("Value already exists: %s" % spl[2]) return if spl[1] == "authtype": if not toUnset: if not spl[2] in ["sp", "ns"]: failure("Authtype must be sp or ns, not %s" % spl[2]) return if spl[1] == "enabled": failure("Use the ConnectOnCreate config parameter to set this") return if spl[1] == "autojoin": if not toUnset: spl[2] = spl[2].split(",") else: spl[2] = [] config["Default"][spl[1]] = spl[2] self.save("config") if toUnset: success("Successfully unset key %s" % spl[1]) else: success("Successfully set key %s to %s" % (spl[1], spl[2])) return else: incUsage("default") return elif cmd == "mod": toUnset = False if length == 2: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return optionMap = ["Viewing options for %s" % spl[1]] for i in pool[spl[1]].keys(): optionMap.append("%s: %s" % (i, pool[spl[1]][i])) info("\n".join(optionMap)) return elif length == 3: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return if not spl[2] in pool[spl[1]].keys(): failure("No such key: %s" % spl[2]) return info("%s: %s" % (spl[2], pool[spl[1]][spl[2]])) return elif length == 4: if not spl[1] in pool.keys(): failure("Name does not exist: %s" % spl[1]) return if not spl[2] in pool[spl[1]].keys(): failure("No such key: %s" % spl[2]) return if spl[2] == "protocol": if not spl[3] in ["ssl", "plain"]: failure("Protocol must be ssl or plain, not %s" % spl[3]) return if spl[3] == pool[spl[1]][spl[2]]: failure("Value already exists: %s" % spl[3]) return if spl[3].lower() in ["none", "nil"]: spl[3] = None toUnset = True if spl[2] in ["port", "timeout", "maxdelay"]: try: spl[3] = int(spl[3]) except: failure("Value must be an integer, not %s" % spl[3]) return if spl[2] in ["initialdelay", "factor", "jitter"]: try: spl[3] = float(spl[3]) except: failure("Value must be a floating point integer, not %s" % spl[3]) return if spl[2] == "authtype": if not toUnset: if not spl[3] in ["sp", "ns"]: failure("Authtype must be sp or ns, not %s" % spl[3]) return if spl[2] == "enabled": failure("Use the enable and disable commands to manage this") return if spl[2] == "autojoin": spl[3] = spl[3].split(",") pool[spl[1]][spl[2]] = spl[3] if spl[1] in IRCPool.keys(): IRCPool[spl[1]].refresh() self.save("pool") if toUnset: success("Successfully unset key %s on %s" % (spl[2], spl[1])) else: success("Successfully set key %s to %s on %s" % (spl[2], spl[3], spl[1])) return else: incUsage("mod") return else: incUsage(None) return else: if cmd == "pass" and length == 2: if spl[1] == config["Password"]: success("Authenticated successfully") obj.authed = True return else: failure("Password incorrect") obj.transport.loseConnection() return else: incUsage(None) return if __name__ == "__main__": helper = Helper() for i in filemap.keys(): helper.load(i) for i in pool.keys(): if pool[i]["enabled"] == True: helper.addBot(i) listener = BaseFactory() if config["Listener"]["UseSSL"] == True: reactor.listenSSL(config["Listener"]["Port"], listener, DefaultOpenSSLContextFactory(certPath+config["Listener"]["Key"], certPath+config["Listener"]["Certificate"]), interface=config["Listener"]["Address"]) log("Threshold running with SSL on %s:%s" % (config["Listener"]["Address"], config["Listener"]["Port"])) else: reactor.listenTCP(config["Listener"]["Port"], listener, interface=config["Listener"]["Address"]) log("Threshold running on %s:%s" % (config["Listener"]["Address"], config["Listener"]["Port"])) reactor.run()