From 15ca45e5dfdcdfc2235c55b015166c2d11caade2 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Sat, 28 Sep 2019 19:46:10 +0100 Subject: [PATCH] Implement Ctrl-C handling and fix a large number of small bugs --- commands/auto.py | 6 +++--- commands/del.py | 20 ++++++++++++-------- commands/disable.py | 32 +++++++++++++++++++------------- commands/stats.py | 2 +- commands/swho.py | 9 ++++++--- conf/help.json | 5 +++-- core/bot.py | 29 ++++++++++++++++------------- main.py | 8 -------- modules/userinfo.py | 8 ++++---- threshold | 9 ++++++--- utils/cleanup.py | 15 +++++++++++++++ utils/loaders/single_loader.py | 6 +++--- 12 files changed, 88 insertions(+), 61 deletions(-) create mode 100644 utils/cleanup.py diff --git a/commands/auto.py b/commands/auto.py index 3d29a85..05e8d6c 100644 --- a/commands/auto.py +++ b/commands/auto.py @@ -14,11 +14,11 @@ class AutoCommand: if not spl[2].isdigit(): failure("Must be integer, not %s" % spl[2]) return - - id, alias = main.network[spl[1]].add_relay(int(spl[2])) + relayNum = int(spl[2]) + id, alias = main.network[spl[1]].add_relay(relayNum) success("Successfully created relay %s on network %s with alias %s" % (str(id), spl[1], alias)) main.saveConf("network") - rtrn = provision.provisionRelay(int(spl[2]), spl[1]) + rtrn = provision.provisionRelay(relayNum, spl[1]) success("Started provisioning network %s on relay %s for alias %s" % (spl[1], spl[2], rtrn)) return diff --git a/commands/del.py b/commands/del.py index eb59eaa..e1108ad 100644 --- a/commands/del.py +++ b/commands/del.py @@ -10,19 +10,23 @@ class DelCommand: if not spl[1] in main.network.keys(): failure("No such network: %s" % spl[1]) return + if not spl[2].isdigit(): + failure("Must be integer, not %s" % spl[2]) + return if not int(spl[2]) in main.network[spl[1]].relays.keys(): failure("No such relay: %s in network %s" % (spl[2], spl[1])) return main.network[spl[1]].delete_relay(int(spl[2])) - if spl[1]+spl[2] in main.ReactorPool.keys(): - if spl[1]+spl[2] in main.FactoryPool.keys(): - main.FactoryPool[spl[1]+spl[2]].stopTrying() - main.ReactorPool[spl[1]+spl[2]].disconnect() - if spl[1]+spl[2] in main.IRCPool.keys(): - del main.IRCPool[spl[1]+spl[2]] - del main.ReactorPool[spl[1]+spl[2]] - del main.FactoryPool[spl[1]+spl[2]] + name = spl[1]+spl[2] + if name in main.ReactorPool.keys(): + if name in main.FactoryPool.keys(): + main.FactoryPool[name].stopTrying() + main.ReactorPool[name].disconnect() + if name in main.IRCPool.keys(): + del main.IRCPool[name] + del main.ReactorPool[name] + del main.FactoryPool[name] success("Successfully removed bot: %s" % spl[1]) main.saveConf("network") return diff --git a/commands/disable.py b/commands/disable.py index 20ace48..7abfec0 100644 --- a/commands/disable.py +++ b/commands/disable.py @@ -11,25 +11,31 @@ class DisableCommand: if not spl[1] in main.network.keys(): failure("No such network: %s" % spl[1]) return - if not int(spl[2]) in main.network[spl[1]].relays.keys(): + if not spl[2].isdigit(): + failure("Must be integer, not %s" % spl[2]) + return + relayNum = int(spl[2]) + name = spl[1]+spl[2] + if not spl[1] in main.IRCPool.keys(): + info("Note - instance not running, proceeding anyway") + if not relayNum in main.network[spl[1]].relays.keys(): failure("No such relay: %s in network %s" % (spl[2], spl[1])) return - - main.network[spl[1]].relays[int(spl[2])]["enabled"] = False - user = main.network[spl[1]].aliases[int(spl[2])] + main.network[spl[1]].relays[relayNum]["enabled"] = False + user = main.network[spl[1]].aliases[relayNum]["nick"] network = spl[1] - relay = main.network[spl[1]].relays[int(spl[2])] + relay = main.network[spl[1]].relays[relayNum] commands = {"status": ["Disconnect"]} - deliverRelayCommands(relay, commands, user=user+"/"+network) + deliverRelayCommands(relayNum, commands, user=user+"/"+network) main.saveConf("network") - if spl[1]+spl[2] in main.ReactorPool.keys(): - if spl[1]+spl[2] in main.FactoryPool.keys(): - main.FactoryPool[spl[1]+spl[2]].stopTrying() - main.ReactorPool[spl[1]+spl[2]].disconnect() + if name in main.ReactorPool.keys(): + if name in main.FactoryPool.keys(): + main.FactoryPool[name].stopTrying() + main.ReactorPool[name].disconnect() if spl[1] in main.IRCPool.keys(): - del main.IRCPool[spl[1]+spl[2]] - del main.ReactorPool[spl[1]+spl[2]] - del main.FactoryPool[spl[1]+spl[2]] + del main.IRCPool[name] + del main.ReactorPool[name] + del main.FactoryPool[name] success("Successfully disabled bot %s on network %s" % (spl[2], spl[1])) return else: diff --git a/commands/stats.py b/commands/stats.py index e205d02..0db414a 100644 --- a/commands/stats.py +++ b/commands/stats.py @@ -17,7 +17,7 @@ class StatsCommand: numChannels += len(main.IRCPool[i].channels) numWhoEntries += userinfo.getNumTotalWhoEntries() stats.append("Registered servers:") - stats.append(" Total: %s" % len(main.network.keys())) + stats.append(" Unique: %s" % len(main.network.keys())) stats.append("Online servers:") stats.append(" Total: %s" % len(main.IRCPool.keys())) stats.append(" Unique: %s" % len(main.liveNets())) diff --git a/commands/swho.py b/commands/swho.py index b03ca61..49c71d7 100644 --- a/commands/swho.py +++ b/commands/swho.py @@ -11,18 +11,21 @@ class SwhoCommand: failure("Network does not exist: %s" % spl[1]) return for i in main.IRCPool.keys(): - if spl[1] in i: + if spl[1] == main.IRCPool[i].net: for x in main.IRCPool[i].channels: main.IRCPool[i].who(x) - success("Sent WHO to all channels on all networks on %s" % spl[1]) + success("Sent WHO to all channels on all networks for %s" % spl[1]) return elif length == 3: if not spl[1] in main.network.keys(): failure("Network does not exist: %s" % spl[1]) return matches = [] + + # This loop gets all networks where the core network matches spl[1] + # where there is also a currently joined channel matching spl[2] for i in main.IRCPool.keys(): - if spl[1] in i: + if spl[1] == main.IRCPool[i].net: for x in main.IRCPool[i].channels: if x == spl[2]: main.IRCPool[i].who(x) diff --git a/conf/help.json b/conf/help.json index e58bfe1..34eca54 100644 --- a/conf/help.json +++ b/conf/help.json @@ -1,7 +1,7 @@ { "pass": "pass ", "logout": "logout", - "del": "del ", + "del": "del ", "mod": "mod [] []", "who": "who ", "join": "join []", @@ -24,5 +24,6 @@ "cmd": "cmd ", "token": "token [] []", "all": "all ", - "allc": "allc <(network)|(alias)> " + "allc": "allc <(network)|(alias)> ", + "swho": "swho []" } diff --git a/core/bot.py b/core/bot.py index 52c8044..466a2a4 100644 --- a/core/bot.py +++ b/core/bot.py @@ -72,7 +72,6 @@ class IRCRelay(IRCClient): sendAll("[%s] %s -> %s" % (self.num, nick, msg)) def irc_ERR_PASSWDMISMATCH(self, prefix, params): - print(', '.join("%s: %s" % item for item in vars(self).items())) log("%s: relay password mismatch" % self.num) sendAll("%s: relay password mismatch" % self.num) @@ -113,7 +112,7 @@ class IRCBot(IRCClient): self.versionEnv = None self.sourceURL = None - self._who = {} + self._tempWho = {} self._getWho = {} self._names = {} @@ -227,14 +226,17 @@ class IRCBot(IRCClient): log("%s: password mismatch" % self.name) sendAll("%s: password mismatch" % self.name) - def who(self, channel): + def _who(self, channel): d = Deferred() - if channel not in self._who: - self._who[channel] = ([], []) - self._who[channel][0].append(d) + if channel not in self._tempWho: + self._tempWho[channel] = ([], []) + self._tempWho[channel][0].append(d) self.sendLine("WHO %s" % channel) return d + def who(self, channel): + self._who(channel).addCallback(self.got_who) + def irc_RPL_WHOREPLY(self, prefix, params): channel = params[1] ident = params[2] @@ -243,20 +245,20 @@ class IRCBot(IRCClient): nick = params[5] status = params[6] realname = params[7] - if channel not in self._who: + if channel not in self._tempWho: return - n = self._who[channel][1] + n = self._tempWho[channel][1] n.append([nick, nick, host, server, status, realname]) self.event(type="who", nick=nick, ident=ident, host=host, realname=realname, target=channel, server=server, status=status) def irc_RPL_ENDOFWHO(self, prefix, params): channel = params[1] - if channel not in self._who: + if channel not in self._tempWho: return - callbacks, info = self._who[channel] + callbacks, info = self._tempWho[channel] for cb in callbacks: cb.callback((channel, info)) - del self._who[channel] + del self._tempWho[channel] def got_who(self, whoinfo): userinfo.initialUsers(self.net, whoinfo[0], whoinfo[1]) @@ -382,7 +384,8 @@ class IRCBot(IRCClient): self.channels.append(channel) self.names(channel).addCallback(self.got_names) if main.config["Toggles"]["Who"]: - self.who(channel).addCallback(self.got_who) + #self.who(channel).addCallback(self.got_who) + #self.who(channel) lc = LoopingCall(self.who, channel) self._getWho[channel] = lc intrange = main.config["Tweaks"]["Delays"]["WhoRange"] @@ -466,7 +469,7 @@ class IRCBotFactory(ReconnectingClientFactory): self.relayCommands, self.user, self.stage2 = relayCommands, user, stage2 def buildProtocol(self, addr): - if self.net == None: + if self.relay == None: entry = IRCRelay(self.num, self.relayCommands, self.user, self.stage2) else: entry = IRCBot(self.net, self.num) diff --git a/main.py b/main.py index 28633ea..c9e45e3 100644 --- a/main.py +++ b/main.py @@ -39,14 +39,6 @@ lastMinuteSample = 0 hashKey = urandom(16) lastEvents = {} -def nets(): - if not "pool" in globals(): - return - networks = set() - for i in pool.keys(): - networks.add("".join([x for x in i if not x in digits])) - return networks - def liveNets(): networks = set() for i in IRCPool.keys(): diff --git a/modules/userinfo.py b/modules/userinfo.py index 29da9b0..e4035e8 100644 --- a/modules/userinfo.py +++ b/modules/userinfo.py @@ -13,7 +13,7 @@ def getWhoSingle(name, query): def getWho(query): result = {} - for i in main.nets(): + for i in main.network.keys(): f = getWhoSingle(i, query) if f: result[i] = f @@ -35,7 +35,7 @@ def getChanList(name, nick): def getChans(nick): result = {} - for i in main.nets(): + for i in main.network.keys(): f = getChansSingle(i, nick) if f: result[i] = f @@ -50,7 +50,7 @@ def getUsersSingle(name, nick): def getUsers(nick): result = {} - for i in main.nets(): + for i in main.network.keys(): f = getUsersSingle(i, nick) if f: result[i] = f @@ -61,7 +61,7 @@ def getNumWhoEntries(name): def getNumTotalWhoEntries(): total = 0 - for i in main.nets(): + for i in main.network.keys(): total += getNumWhoEntries(i) return total diff --git a/threshold b/threshold index c5a3cc3..9425e60 100755 --- a/threshold +++ b/threshold @@ -1,18 +1,21 @@ #!/usr/bin/env python from twisted.internet import reactor from twisted.internet.ssl import DefaultOpenSSLContextFactory -from sys import argv, stdout, stderr +import sys +from signal import signal, SIGINT #from twisted.python import log #from sys import stdout #log.startLogging(stdout) +from sys import stdout, stderr # Import again because we want to override from codecs import getwriter # fix printing odd shit to the terminal stdout = getwriter("utf8")(stdout) # this is a generic fix but we all know stderr = getwriter("utf8")(stderr) # it's just for the retards on Rizon using # unicode quit messages for no reason import main - main.initMain() -if "--debug" in argv: # yes really +from utils.cleanup import handler +signal(SIGINT, handler) # Handle Ctrl-C and run the cleanup routine +if "--debug" in sys.argv: # yes really main.config["Debug"] = True from utils.logging.log import * from utils.loaders.command_loader import loadCommands diff --git a/utils/cleanup.py b/utils/cleanup.py new file mode 100644 index 0000000..3b818f3 --- /dev/null +++ b/utils/cleanup.py @@ -0,0 +1,15 @@ +import main +from twisted.internet import reactor +from utils.logging.debug import debug +from utils.logging.log import * +import sys + +def handler(sig, frame): + log("Received SIGINT, cleaning up") + cleanup() + +def cleanup(): + debug("Flushing Redis database") + main.r.flushdb() + reactor.stop() + #sys.exit(1) diff --git a/utils/loaders/single_loader.py b/utils/loaders/single_loader.py index dc28252..81e5c76 100644 --- a/utils/loaders/single_loader.py +++ b/utils/loaders/single_loader.py @@ -1,6 +1,6 @@ from os import listdir from importlib import reload -from sys import modules +import sys from utils.logging.debug import debug from utils.logging.log import * @@ -13,8 +13,8 @@ def loadSingle(commandName): className = commandName.capitalize()+"Command" try: if commandName in CommandMap.keys(): - reload(modules["commands."+commandName]) - CommandMap[commandName] = getattr(modules["commands."+commandName], className) + reload(sys.modules["commands."+commandName]) + CommandMap[commandName] = getattr(sys.modules["commands."+commandName], className) debug("Reloaded command: %s" % commandName) return "RELOAD" module = __import__('commands.%s' % commandName)