From 2757256d4f4c79fd1c77e0877206f42e289e96bf Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Sun, 11 Aug 2019 20:52:10 +0100 Subject: [PATCH] Implement a single function for all callbacks from IRC hooks and send a seperate notification if an action takes place that concerns a bot --- .gitignore | 1 - commands/cmd.py | 1 - conf/example/config.json | 15 ++-- conf/help.json | 1 - core/bot.py | 181 ++++++++++++++++++++------------------- core/relay.py | 6 +- main.py | 1 - modules/monitor.py | 5 -- modules/provision.py | 3 + modules/userinfo.py | 5 +- 10 files changed, 106 insertions(+), 113 deletions(-) diff --git a/.gitignore b/.gitignore index e23d8a6..e81873f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ conf/config.json conf/pool.json conf/wholist.json conf/counters.json -conf/masterbuf.json conf/monitor.json conf/alias.json conf/relay.json diff --git a/commands/cmd.py b/commands/cmd.py index 5908bda..68a63ce 100644 --- a/commands/cmd.py +++ b/commands/cmd.py @@ -13,7 +13,6 @@ class Cmd: return commands = {spl[3]: [" ".join(spl[4:])]} - print(" ".join(spl[4:])) success("Sending commands to relay %s as user %s" % (spl[1], spl[2])) deliverRelayCommands(spl[1], commands, user=spl[2]) return diff --git a/conf/example/config.json b/conf/example/config.json index 27e79fb..bc67bdf 100644 --- a/conf/example/config.json +++ b/conf/example/config.json @@ -15,19 +15,15 @@ "RedisSocket": "/tmp/redis.sock", "UsePassword": true, "ConnectOnCreate": false, - "Notifications": { - "Highlight": true, - "Connection": true, - "Query": true - }, - "Compat": { - "ZNC": true - }, "Dist": { "Enabled": true, "SendOutput": false, "File": "conf/dist.sh" }, + "Toggles": { + "Who": false, + "CycleChans": true + }, "Password": "s", "Tweaks": { "ZNC": { @@ -42,6 +38,5 @@ "Factor": 2.718281828459045, "Jitter": 0.11962656472 } - }, - "Master": [null, null] + } } diff --git a/conf/help.json b/conf/help.json index 13ee310..0211171 100644 --- a/conf/help.json +++ b/conf/help.json @@ -4,7 +4,6 @@ "del": "del ", "mod": "mod [] []", "get": "get ", - "key": "key [] [] [] []", "who": "who ", "join": "join []", "part": "part ", diff --git a/core/bot.py b/core/bot.py index bb91bc7..bc7a6f5 100644 --- a/core/bot.py +++ b/core/bot.py @@ -6,6 +6,7 @@ from twisted.internet import reactor from string import digits from random import randint +from copy import deepcopy from modules import userinfo from modules import counters as count @@ -73,8 +74,7 @@ class IRCRelay(IRCClient): def signedOn(self): self.connected = True log("signed on as a relay: %s" % self.relay) - if main.config["Notifications"]["Connection"]: - sendRelayNotification("Relay", {"type": "conn", "status": "connected"}) + #sendRelayNotification("Relay", {"type": "conn", "status": "connected"}) nobody actually cares for i in self.relayCommands.keys(): for x in self.relayCommands[i]: self.msg(main.config["Tweaks"]["ZNC"]["Prefix"]+i, x) @@ -133,29 +133,69 @@ class IRCBot(IRCClient): return [nick, ident, host] + def event(self, **cast): + for i in list(cast.keys()): # Make a copy of the .keys() as Python 3 cannot handle iterating over + if cast[i] == "": # a dictionary that changes length with each iteration + del cast[i] + if "muser" in cast.keys(): + cast["nick"], cast["ident"], cast["host"] = self.parsen(cast["muser"]) + del cast["muser"] + if set(["nick", "ident", "host", "message"]).issubset(set(cast)): + if "message" in cast.keys(): + if cast["ident"] == "znc" and cast["host"] == "znc.in": + cast["type"] = "znc" + cast["name"] = self.name + del cast["nick"] + del cast["ident"] + del cast["host"] + del cast["target"] + if not cast["type"] in ["query", "self", "highlight", "znc", "who"]: + if "target" in cast.keys(): + if cast["target"] == self.nickname: + #castDup = deepcopy(cast) + cast["mtype"] = cast["type"] + cast["type"] = "query" + cast["name"] = self.name + #self.event(**castDup) + # Don't call self.event for this one because queries are not events on a + # channel, but we still want to see them + if "user" in cast.keys(): + if cast["user"] == self.nickname: + castDup = deepcopy(cast) + castDup["mtype"] = cast["type"] + castDup["type"] = "self" + castDup["name"] = self.name + self.event(**castDup) + if "nick" in cast.keys(): + if cast["nick"] == self.nickname: + castDup = deepcopy(cast) + castDup["mtype"] = cast["type"] + castDup["type"] = "self" + castDup["name"] = self.name + self.event(**castDup) + if "message" in cast.keys() and not cast["type"] == "query": # Don't highlight queries + if self.nickname in cast["message"]: + castDup = deepcopy(cast) + castDup["mtype"] = cast["type"] + castDup["type"] = "highlight" + castDup["name"] = self.name + self.event(**castDup) + + if "name" not in cast.keys(): + cast["name"] = self.net + if cast["type"] in ["msg", "notice", "action"]: + userinfo.editUser(self.net, cast["nick"]+"!"+cast["ident"]+"@"+cast["host"]) + count.event(self.net, cast["type"]) + monitor.event(self.net, self.name, cast) + def privmsg(self, user, channel, msg): - nick, ident, host = self.parsen(user) - # ZNC adds messages when no users are in the channel, messing up tracking - #userinfo.editUser(self.net, channel, nick, user) - count.event(self.net, "msg") - #event = None - #if self.nickname.lower() in msg.lower(): - # event = "highlight" - monitor.event(self.net, self.name, {"type": "msg", "nick": nick, "ident": ident, "host": host, "target": channel, "message": msg}) + self.event(type="msg", muser=user, target=channel, message=msg) def noticed(self, user, channel, msg): - nick, ident, host = self.parsen(user) - #userinfo.editUser(self.net, channel, nick, user) - count.event(self.net, "notice") - - monitor.event(self.net, self.name, {"type": "notice", "nick": nick, "ident": ident, "host": host, "target": channel, "message": msg}) + self.event(type="notice", muser=user, target=channel, message=msg) def action(self, user, channel, msg): - nick, ident, host = self.parsen(user) - #userinfo.editUser(self.net, channel, nick, user) - count.event(self.net, "action") - - monitor.event(self.net, self.name, {"type": "action", "nick": nick, "ident": ident, "host": host, "target": channel, "message": msg}) + self.event(type="action", muser=user, target=channel, message=msg) def get(self, var): try: @@ -177,7 +217,7 @@ class IRCBot(IRCClient): oldnick, ident, host = self.parsen(olduser) userinfo.renameUser(self.net, oldnick, olduser, newnick, newnick+"!"+ident+"@"+host) self.nickname = newnick - count.event(self.net, "selfnick") + self.event(type="self", mtype="nick", nick=oldnick, ident=ident, host=host, user=newnick) def irc_ERR_NICKNAMEINUSE(self, prefix, params): self._attemptedNick = self.alterCollidedNick(self._attemptedNick) @@ -197,7 +237,7 @@ class IRCBot(IRCClient): def irc_RPL_WHOREPLY(self, prefix, params): channel = params[1] - user = params[2] + ident = params[2] host = params[3] server = params[4] nick = params[5] @@ -206,9 +246,8 @@ class IRCBot(IRCClient): if channel not in self._who: return n = self._who[channel][1] - n.append([nick, user, host, server, status, realname]) - count.event(self.net, "whoreply") - monitor.event(self.net, self.name, {"type": "who", "nick": nick, "ident": user, "host": host, "realname": realname, "target": channel, "server": server, "status": status}) + 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] @@ -260,7 +299,7 @@ class IRCBot(IRCClient): newNicklist = [] for i in nicklist[1]: for x in i: - f = self.sanit(x) + f = self.sanit(x) # need to store this as well, or potentially just do not remove it... if f: newNicklist.append(f) userinfo.initialNames(self.net, nicklist[0], newNicklist) @@ -320,11 +359,8 @@ class IRCBot(IRCClient): channel = params[0] kicked = params[1] message = params[-1] - if kicked.lower() == self.nickname.lower(): - # Yikes! - self.kickedFrom(channel, prefix, message) - else: - self.userKicked(kicked, channel, prefix, message) + # Checks on whether it was us that was kicked are done in userKicked + self.userKicked(kicked, channel, prefix, message) def irc_TOPIC(self, prefix, params): """ @@ -339,26 +375,20 @@ class IRCBot(IRCClient): def signedOn(self): self.connected = True log("signed on: %s" % self.name) - if main.config["Notifications"]["Connection"]: - sendRelayNotification(self.name, {"type": "conn", "status": "connected"}) - count.event(self.net, "signedon") + self.event(type="conn", status="connected") def joined(self, channel): if not channel in self.channels: self.channels.append(channel) self.names(channel).addCallback(self.got_names) self.who(channel).addCallback(self.got_who) - count.event(self.net, "selfjoin") - if self.name == main.config["Master"][0] and channel == main.config["Master"][1]: - for i in range(len(main.masterbuf)): - self.msg(channel, main.masterbuf.pop(0)) - main.saveConf("masterbuf") - lc = LoopingCall(self.who, channel) - self._getWho[channel] = lc - intrange = main.config["Tweaks"]["Delays"]["WhoRange"] - minint = main.config["Tweaks"]["Delays"]["WhoLoop"] - interval = randint(minint, minint+intrange) - lc.start(interval) + if main.config["Toggles"]["Who"]: + lc = LoopingCall(self.who, channel) + self._getWho[channel] = lc + intrange = main.config["Tweaks"]["Delays"]["WhoRange"] + minint = main.config["Tweaks"]["Delays"]["WhoLoop"] + interval = randint(minint, minint+intrange) + lc.start(interval) def botLeft(self, channel): if channel in self.channels: @@ -368,79 +398,58 @@ class IRCBot(IRCClient): lc.stop() del self._getWho[channel] userinfo.delChannel(self.net, channel) + log("Can no longer cover %s, removing records" % channel) def left(self, user, channel, message): - nick, ident, host = self.parsen(user) - count.event(self.net, "selfpart") - monitor.event(self.net, self.name, {"type": "part", "target": channel, "message": message}) - monitor.event(self.net, self.name, {"type": "part", "self": True, "target": channel, "message": message}) - self.botLeft(channel) - - def kickedFrom(self, channel, kicker, message): - nick, ident, host = self.parsen(kicker) - if channel in self.channels: - self.channels.remove(channel) - count.event(self.net, "selfkick") - monitor.event(self.net, self.name, {"type": "kick", "nick": nick, "ident": ident, "host": host, "target": channel, "message": message}) - monitor.event(self.net, self.name, {"type": "kick", "self": True, "nick": nick, "ident": ident, "host": host, "target": channel, "message": message}) + self.event(type="part", muser=user, target=channel, message=message) self.botLeft(channel) def userJoined(self, user, channel): nick, ident, host = self.parsen(user) userinfo.addUser(self.net, channel, nick, user) - count.event(self.net, "join") - monitor.event(self.net, self.name, {"type": "join", "nick": nick, "ident": ident, "host": host, "target": channel}) + self.event(type="join", nick=nick, ident=ident, host=host, target=channel) def userLeft(self, user, channel, message): nick, ident, host = self.parsen(user) userinfo.delUser(self.net, channel, nick, user) - count.event(self.net, "part") - monitor.event(self.net, self.name, {"type": "part", "nick": nick, "ident": ident, "host": host, "target": channel, "message": message}) + self.event(type="part", nick=nick, ident=ident, host=host, target=channel, message=message) def userQuit(self, user, quitMessage): nick, ident, host = self.parsen(user) userinfo.delUserByNetwork(self.net, nick, user) - count.event(self.net, "quit") - - monitor.event(self.net, self.name, {"type": "quit", "nick": nick, "ident": ident, "host": host, "message": quitMessage}) + self.event(type="quit", nick=nick, ident=ident, host=host, message=quitMessage) def botQuit(self, user, quitMessage): nick, ident, host = self.parsen(user) userinfo.delUserByNetwork(self.net, nick, user) - count.event(self.net, "selfquit") - - monitor.event(self.net, self.name, {"type": "quit", "nick": nick, "ident": ident, "host": host, "message": quitMessage}) - monitor.event(self.net, self.name, {"type": "quit", "self": True, "nick": nick, "ident": ident, "host": host, "message": quitMessage}) + self.event(type="quit", nick=nick, ident=ident, host=host, message=quitMessage) def userKicked(self, kickee, channel, kicker, message): nick, ident, host = self.parsen(kicker) - userinfo.editUser(self.net, channel, nick, kicker) + userinfo.editUser(self.net, kicker) userinfo.delUserByNick(self.net, channel, kickee) - count.event(self.net, "kick") - - monitor.event(self.net, self.name, {"type": "kick", "nick": nick, "ident": ident, "host": host, "target": channel, "message": message, "user": kickee}) + if kickee.lower == self.nickname.lower: + self.botLeft(channel) + self.event(type="kick", nick=nick, ident=ident, host=host, target=channel, message=message, user=kickee) def userRenamed(self, oldname, newname): nick, ident, host = self.parsen(oldname) userinfo.renameUser(self.net, nick, oldname, newname, newname+"!"+ident+"@"+host) - count.event(self.net, "nick") - monitor.event(self.net, self.name, {"type": "nick", "nick": nick, "ident": ident, "host": host, "user": newname}) + self.event(type="nick", nick=nick, ident=ident, host=host, user=newname) def topicUpdated(self, user, channel, newTopic): nick, ident, host = self.parsen(user) - userinfo.editUser(self.net, channel, nick, user) - count.event(self.net, "topic") + userinfo.editUser(self.net, user) - monitor.event(self.net, self.name, {"type": "topic", "nick": nick, "ident": ident, "host": host, "target": channel, "message": newTopic}) + self.event(type="topic", nick=nick, ident=ident, host=host, target=channel, message= newTopic) def modeChanged(self, user, channel, toset, modes, args): nick, ident, host = self.parsen(user) - userinfo.editUser(self.net, channel, nick, user) - count.event(self.net, "mode") + userinfo.editUser(self.net, user) argList = list(args) modeList = [i for i in modes] for a, m in zip(argList, modeList): - monitor.event(self.net, self.name, {"type": "mode", "nick": nick, "ident": ident, "host": host, "target": channel, "modes": m, "modeargs": a}) + self.event(type="mode", nick=nick, ident=ident, host=host, target=channel, modes=m, modeargs=a) class IRCBotFactory(ReconnectingClientFactory): def __init__(self, name, relay=None, relayCommands=None, user=None, stage2=None): @@ -469,7 +478,7 @@ class IRCBotFactory(ReconnectingClientFactory): def clientConnectionLost(self, connector, reason): if not self.relay: - userinfo.delNetwork(self.net, self.client.channels) + userinfo.delNetwork(self.name, self.client.channels) if not self.client == None: self.client.connected = False self.client.channels = [] @@ -477,9 +486,7 @@ class IRCBotFactory(ReconnectingClientFactory): log("%s: connection lost: %s" % (self.name, error)) if not self.relay: sendAll("%s: connection lost: %s" % (self.name, error)) - if main.config["Notifications"]["Connection"]: - sendRelayNotification(self.name, {"type": "conn", "status": "lost", "reason": error}) - if not self.relay: + sendRelayNotification(self.name, {"type": "conn", "status": "lost", "message": error}) self.retry(connector) #ReconnectingClientFactory.clientConnectionLost(self, connector, reason) @@ -491,9 +498,7 @@ class IRCBotFactory(ReconnectingClientFactory): log("%s: connection failed: %s" % (self.name, error)) if not self.relay: sendAll("%s: connection failed: %s" % (self.name, error)) - if main.config["Notifications"]["Connection"]: - sendRelayNotification(self.name, {"type": "conn", "status": "failed", "reason": error}) - if not self.relay: + sendRelayNotification(self.name, {"type": "conn", "status": "failed", "message": error}) self.retry(connector) #ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) diff --git a/core/relay.py b/core/relay.py index 76dbc87..765b783 100644 --- a/core/relay.py +++ b/core/relay.py @@ -6,9 +6,7 @@ from datetime import datetime import main from utils.logging.log import * -validTypes = ["msg", "notice", "action", "who", "part", "join", "kick", "quit", "nick", "topic", "mode", "conn", "znc", "query", "self", "highlight", "monitor", "err"] - -selfTypes = ["query", "self", "highlight"] +validTypes = ["msg", "notice", "action", "who", "part", "join", "kick", "quit", "nick", "topic", "mode", "conn", "znc", "query", "self", "highlight", "monitor", "err", "query", "self", "highlight"] class Relay(Protocol): def __init__(self, addr): @@ -137,7 +135,7 @@ class RelayFactory(Factory): def sendRelayNotification(name, cast): for i in main.relayConnections.keys(): if main.relayConnections[i].authed: - if cast["type"] in main.relayConnections[i].subscriptions or set(main.relayConnections[i].subscriptions).issubset(cast): + if cast["type"] in main.relayConnections[i].subscriptions: newCast = deepcopy(cast) newCast["name"] = name newCast["time"] = str(datetime.now()) diff --git a/main.py b/main.py index 34add10..1aa0cdf 100644 --- a/main.py +++ b/main.py @@ -11,7 +11,6 @@ filemap = { "pool": ["pool.json", "network, alias and relay mappings"], "help": ["help.json", "command help"], "counters": ["counters.json", "counters file"], - "masterbuf": ["masterbuf.json", "master buffer"], "monitor": ["monitor.json", "monitoring database"], "alias": ["alias.json", "alias details"], "relay": ["relay.json", "relay list"], diff --git a/modules/monitor.py b/modules/monitor.py index 45f5118..08fae94 100644 --- a/modules/monitor.py +++ b/modules/monitor.py @@ -55,11 +55,6 @@ def event(name, numberedName, cast, event=None): target = cast["target"] else: target = None - if set(["nick", "ident", "host", "message"]).issubset(set(cast)): - if main.config["Compat"]["ZNC"] and "message" in cast.keys(): - if cast["nick"][0] == main.config["Tweaks"]["ZNC"]["Prefix"] and cast["ident"] == "znc" and cast["host"] == "znc.in": - sendRelayNotification(numberedName, {"type": "znc", "message": cast["message"]}) - return sendRelayNotification(name, cast) monitorGroups = testNetTarget(name, target) diff --git a/modules/provision.py b/modules/provision.py index e1e5bc0..0eb7464 100644 --- a/modules/provision.py +++ b/modules/provision.py @@ -39,6 +39,9 @@ def provisionNetworkData(relay, alias, network, host, port, security, auth, pass if not main.config["ConnectOnCreate"]: stage3commands["status"] = [] stage3commands["status"].append("Disconnect") + if main.config["Toggles"]["CycleChans"]: + stage2commands["status"] = [] + stage2commands["status"].append("LoadMod disconkick") deliverRelayCommands(relay, commands, stage2=[[alias+"/"+network, stage2commands], [alias+"/"+network, stage3commands]]) diff --git a/modules/userinfo.py b/modules/userinfo.py index 42f2d48..d9bdddf 100644 --- a/modules/userinfo.py +++ b/modules/userinfo.py @@ -76,7 +76,7 @@ def initialNames(name, channel, names): p.sadd("live.chan."+name+"."+i, channel) p.execute() -def editUser(name, channel, nick, user): +def editUser(name, user): gnamespace = "live.who.%s" % name main.r.sadd(gnamespace, user) @@ -151,7 +151,7 @@ def delUserByNetwork(name, nick, user): p.delete(chanspace) p.execute() -def delChannel(name, channel): +def delChannel(name, channel): # This function is extremely expensive, look to replace gnamespace = "live.who.%s" % name namespace = "live.who.%s.%s" % (name, channel) p = main.r.pipeline() @@ -171,4 +171,5 @@ def delNetwork(name, channels): log("Purging channels for %s" % name) for i in channels: delChannel(name, i) + log("Finished purging channels for %s" % name) return