Implement a single function for all callbacks from IRC hooks and send a seperate notification if an action takes place that concerns a bot

This commit is contained in:
Mark Veidemanis 2019-08-11 20:52:10 +01:00
parent 56840e0060
commit 2757256d4f
10 changed files with 106 additions and 113 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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]
}
}

View File

@ -4,7 +4,6 @@
"del": "del <name>",
"mod": "mod <name> [<key>] [<value>]",
"get": "get <name> <variable>",
"key": "key <master|list|add|del|except|unexcept|listexcept|monitor> [<name>] [<target>] [<key...>] [<on|off>]",
"who": "who <query>",
"join": "join <name> <channel> [<key>]",
"part": "part <name> <channel>",

View File

@ -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)

View File

@ -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())

View File

@ -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"],

View File

@ -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)

View File

@ -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]])

View File

@ -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