diff --git a/commands/all.py b/commands/all.py new file mode 100644 index 0000000..10ec381 --- /dev/null +++ b/commands/all.py @@ -0,0 +1,23 @@ +import main +from core.bot import deliverRelayCommands + +class All: + def __init__(self, *args): + self.all(*args) + + def all(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length): + if authed: + if length > 2: + for i in main.pool.keys(): + relay = main.pool[i]["relay"] + network = main.pool[i]["network"] + alias = main.pool[i]["alias"] + commands = {spl[1]: [" ".join(spl[2:])]} + success("Sending commands to relay %s as user %s" % (relay, alias+"/"+network)) + deliverRelayCommands(relay, commands, user=alias+"/"+network) + return + else: + incUsage("all") + return + else: + incUsage(None) diff --git a/commands/allc.py b/commands/allc.py new file mode 100644 index 0000000..9e662fe --- /dev/null +++ b/commands/allc.py @@ -0,0 +1,46 @@ +import main +from core.bot import deliverRelayCommands + +class Allc: + def __init__(self, *args): + self.allc(*args) + + def allc(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length): + if authed: + if length > 4: + targets = [] + if spl[1] == "network": + if spl[2] in main.network.keys(): + for i in main.pool.keys(): + if main.pool[i]["network"] == spl[2]: + targets.append(i) + else: + failure("No such network: %s" % spl[2]) + return + elif spl[1] == "alias": + if spl[2] in main.alias.keys(): + for i in main.pool.keys(): + if main.pool[i]["alias"] == spl[2]: + targets.append(i) + else: + failure("No such alias: %s" % spl[2]) + return + else: + incUsage("allc") + return + if len(targets) == 0: + failure("No matches found: %s" % spl[2]) + return + for i in targets: + relay = main.pool[i]["relay"] + network = main.pool[i]["network"] + alias = main.pool[i]["alias"] + commands = {spl[3]: [" ".join(spl[4:])]} + success("Sending commands to relay %s as user %s" % (relay, alias+"/"+network)) + deliverRelayCommands(relay, commands, user=alias+"/"+network) + return + else: + incUsage("allc") + return + else: + incUsage(None) diff --git a/commands/provision.py b/commands/provision.py index edc0d07..459efd8 100644 --- a/commands/provision.py +++ b/commands/provision.py @@ -19,22 +19,22 @@ class ProvisionCommand: failure("No such network: %s" % spl[3]) return - if "users" in main.relay[spl[1]]: - if not spl[2] in main.relay[spl[1]]["users"]: - failure("Relay %s not provisioned for alias %s" % (spl[1], spl[2])) - return - else: - failure("Relay %s not provisioned for alias %s" % (spl[1], spl[2])) - return + #if "users" in main.relay[spl[1]]: + # if not spl[2] in main.relay[spl[1]]["users"]: + # failure("Relay %s not provisioned for alias %s" % (spl[1], spl[2])) + # return + #else: + # failure("Relay %s not provisioned for alias %s" % (spl[1], spl[2])) + # return rtrn = provision.provisionRelayForNetwork(spl[1], spl[2], spl[3]) - if rtrn == "PROVISIONED": - failure("Relay %s already provisioned for alias %s on network %s" % (spl[1], spl[2], spl[3])) - return - elif rtrn == "DUPLICATE": - failure("Instance with relay %s and alias %s already exists for network %s" % (spl[1], spl[2], spl[3])) - return - elif rtrn: + #if rtrn == "PROVISIONED": + # failure("Relay %s already provisioned for alias %s on network %s" % (spl[1], spl[2], spl[3])) + # return + #elif rtrn == "DUPLICATE": + # failure("Instance with relay %s and alias %s already exists for network %s" % (spl[1], spl[2], spl[3])) + # return + if rtrn: success("Started provisioning network %s on relay %s for alias %s" % (spl[3], spl[1], spl[2])) info("Instance name is %s" % rtrn) return @@ -43,10 +43,10 @@ class ProvisionCommand: return if length == 3: # provision for relay and alias only rtrn = provision.provisionRelayForAlias(spl[1], spl[2]) - if rtrn == "PROVISIONED": - failure("Relay %s already provisioned for alias %s" % (spl[1], spl[2])) - return - elif rtrn: + #if rtrn == "PROVISIONED": + # failure("Relay %s already provisioned for alias %s" % (spl[1], spl[2])) + # return + if rtrn: success("Started provisioning relay %s for alias %s" % (spl[1], spl[2])) return else: diff --git a/conf/example/config.json b/conf/example/config.json index bc67bdf..a595963 100644 --- a/conf/example/config.json +++ b/conf/example/config.json @@ -15,6 +15,7 @@ "RedisSocket": "/tmp/redis.sock", "UsePassword": true, "ConnectOnCreate": false, + "Debug": false, "Dist": { "Enabled": true, "SendOutput": false, @@ -26,6 +27,8 @@ }, "Password": "s", "Tweaks": { + "MaxHash": 10, + "DedupPrecision": 9, "ZNC": { "Prefix": "*" }, diff --git a/conf/help.json b/conf/help.json index a5cb009..2f81c57 100644 --- a/conf/help.json +++ b/conf/help.json @@ -22,5 +22,7 @@ "network": "network [
]", "provision": "provision []", "cmd": "cmd ", - "token": "token [] []" + "token": "token [] []", + "all": "all ", + "allc": "allc <(network)|(alias)> " } diff --git a/core/bot.py b/core/bot.py index 7c83d9f..701714f 100644 --- a/core/bot.py +++ b/core/bot.py @@ -9,10 +9,11 @@ from random import randint from copy import deepcopy from modules import userinfo -from modules import counters as count +from modules import counters from modules import monitor from core.relay import sendRelayNotification +from utils.dedup import dedup import main from utils.logging.log import * @@ -137,7 +138,8 @@ class IRCBot(IRCClient): del cast[i] if "muser" in cast.keys(): cast["nick"], cast["ident"], cast["host"] = self.parsen(cast["muser"]) - del cast["muser"] + #if not cast["type"] in ["nick", "kick", "quit", "part", "join"]: + # 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": @@ -148,9 +150,9 @@ class IRCBot(IRCClient): del cast["host"] del cast["target"] if not cast["type"] in ["query", "self", "highlight", "znc", "who"]: - if "target" in cast.keys(): - if cast["target"].lower() == self.nickname.lower(): - #castDup = deepcopy(cast) + if "target" in cast.keys() and not cast["type"] == "mode": # don't handle modes here + if cast["target"].lower() == self.nickname.lower(): # as they are target == nickname + #castDup = deepcopy(cast) # however modes are not queries! cast["mtype"] = cast["type"] cast["type"] = "query" cast["name"] = self.name @@ -170,21 +172,21 @@ class IRCBot(IRCClient): castDup["mtype"] = cast["type"] castDup["type"] = "self" castDup["name"] = self.name - self.event(**castDup) + if not cast["target"].lower() == self.nickname.lower(): # modes has been set on us directly + self.event(**castDup) # don't tell anyone else if "message" in cast.keys() and not cast["type"] == "query": # Don't highlight queries - if self.nickname.lower() in cast["message"].lower(): - castDup = deepcopy(cast) - castDup["mtype"] = cast["type"] - castDup["type"] = "highlight" - castDup["name"] = self.name - self.event(**castDup) - - if "name" not in cast.keys(): + if not cast["message"] == None: + if self.nickname.lower() in cast["message"].lower(): + castDup = deepcopy(cast) + castDup["mtype"] = cast["type"] + castDup["type"] = "highlight" + castDup["name"] = self.name + self.event(**castDup) + + if not "name" 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) + counters.event(self.net, cast["type"]) + monitor.event(self.name, cast) def privmsg(self, user, channel, msg): self.event(type="msg", muser=user, target=channel, message=msg) @@ -212,10 +214,8 @@ class IRCBot(IRCClient): return newnick def nickChanged(self, olduser, newnick): - oldnick, ident, host = self.parsen(olduser) - userinfo.renameUser(self.net, oldnick, olduser, newnick, newnick+"!"+ident+"@"+host) self.nickname = newnick - self.event(type="self", mtype="nick", nick=oldnick, ident=ident, host=host, user=newnick) + self.event(type="self", mtype="nick", muser=olduser, user=newnick) def irc_ERR_NICKNAMEINUSE(self, prefix, params): self._attemptedNick = self.alterCollidedNick(self._attemptedNick) @@ -379,8 +379,8 @@ class IRCBot(IRCClient): if not channel in self.channels: self.channels.append(channel) self.names(channel).addCallback(self.got_names) - self.who(channel).addCallback(self.got_who) if main.config["Toggles"]["Who"]: + self.who(channel).addCallback(self.got_who) lc = LoopingCall(self.who, channel) self._getWho[channel] = lc intrange = main.config["Tweaks"]["Delays"]["WhoRange"] @@ -395,63 +395,54 @@ class IRCBot(IRCClient): lc = self._getWho[channel] 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): + userinfo.delChannel(self.net, channel) # < we do not need to deduplicate this + #log("Can no longer cover %s, removing records" % channel)# as it will only be matched once -- + # other bots have different nicknames so + def left(self, user, channel, message): # even if they saw it, they wouldn't react 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) - self.event(type="join", nick=nick, ident=ident, host=host, target=channel) + self.event(type="join", muser=user, target=channel) def userLeft(self, user, channel, message): - nick, ident, host = self.parsen(user) - userinfo.delUser(self.net, channel, nick, user) - self.event(type="part", nick=nick, ident=ident, host=host, target=channel, message=message) + self.event(type="part", muser=user, target=channel, message=message) def userQuit(self, user, quitMessage): - nick, ident, host = self.parsen(user) - self.chanlessEvent(type="quit", nick=nick, ident=ident, host=host, message=quitMessage) - userinfo.delUserByNetwork(self.net, nick, user) + self.chanlessEvent({"type": "quit", "muser": user, "message": quitMessage}) def userKicked(self, kickee, channel, kicker, message): - nick, ident, host = self.parsen(kicker) - userinfo.editUser(self.net, kicker) - userinfo.delUserByNick(self.net, channel, kickee) - if kickee.lower == self.nickname.lower: + 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) + self.event(type="kick", muser=kicker, target=channel, message=message, user=kickee) - def chanlessEvent(self, **cast): - chans = userinfo.getChansSingle(self.net, cast["nick"]) + def chanlessEvent(self, cast): + cast["nick"], cast["ident"], cast["host"] = self.parsen(cast["muser"]) + if dedup(self.name, cast): + return # stop right there sir! + chans = userinfo.getChanList(self.net, cast["nick"]) if chans == None: - self.event(**cast) + error("No channels returned for chanless event: %s" % cast) + # self.event(**cast) -- no, should NEVER happen return - for i in chans: + # getChansSingle returns all channels of the user, we only want to use + # ones we have common with them + realChans = set(chans).intersection(set(self.channels)) + for i in realChans: cast["target"] = i self.event(**cast) def userRenamed(self, oldname, newname): - nick, ident, host = self.parsen(oldname) - self.chanlessEvent(type="nick", nick=nick, ident=ident, host=host, user=newname) - userinfo.renameUser(self.net, nick, oldname, newname, newname+"!"+ident+"@"+host) + self.chanlessEvent({"type": "nick", "muser": oldname, "user": newname}) def topicUpdated(self, user, channel, newTopic): - nick, ident, host = self.parsen(user) - userinfo.editUser(self.net, user) - - self.event(type="topic", nick=nick, ident=ident, host=host, target=channel, message= newTopic) + self.event(type="topic", muser=user, target=channel, message= newTopic) def modeChanged(self, user, channel, toset, modes, args): - nick, ident, host = self.parsen(user) - userinfo.editUser(self.net, user) argList = list(args) modeList = [i for i in modes] for a, m in zip(argList, modeList): - self.event(type="mode", nick=nick, ident=ident, host=host, target=channel, modes=m, status=toset, modeargs=a) + self.event(type="mode", muser=user, target=channel, modes=m, status=toset, modeargs=a) class IRCBotFactory(ReconnectingClientFactory): def __init__(self, name, relay=None, relayCommands=None, user=None, stage2=None): @@ -488,7 +479,7 @@ class IRCBotFactory(ReconnectingClientFactory): log("%s: connection lost: %s" % (self.name, error)) if not self.relay: sendAll("%s: connection lost: %s" % (self.name, error)) - sendRelayNotification(self.name, {"type": "conn", "status": "lost", "message": error}) + sendRelayNotification({"type": "conn", "name": self.name, "status": "lost", "message": error}) self.retry(connector) #ReconnectingClientFactory.clientConnectionLost(self, connector, reason) @@ -500,7 +491,7 @@ class IRCBotFactory(ReconnectingClientFactory): log("%s: connection failed: %s" % (self.name, error)) if not self.relay: sendAll("%s: connection failed: %s" % (self.name, error)) - sendRelayNotification(self.name, {"type": "conn", "status": "failed", "message": error}) + sendRelayNotification({"type": "conn", "name": self.name, "status": "failed", "message": error}) self.retry(connector) #ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) diff --git a/core/relay.py b/core/relay.py index 765b783..4067ee0 100644 --- a/core/relay.py +++ b/core/relay.py @@ -132,11 +132,10 @@ class RelayFactory(Factory): else: return -def sendRelayNotification(name, cast): +def sendRelayNotification(cast): for i in main.relayConnections.keys(): if main.relayConnections[i].authed: if cast["type"] in main.relayConnections[i].subscriptions: newCast = deepcopy(cast) - newCast["name"] = name - newCast["time"] = str(datetime.now()) + newCast["time"] = str(datetime.now().isoformat()) main.relayConnections[i].send(dumps(newCast)) diff --git a/main.py b/main.py index 6695f73..28633ea 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,8 @@ import json import pickle from redis import StrictRedis from string import digits +from os import urandom + from utils.logging.log import * configPath = "conf/" @@ -33,6 +35,10 @@ CommandMap = {} runningSample = 0 lastMinuteSample = 0 +# Generate 16-byte hex key for message checksums +hashKey = urandom(16) +lastEvents = {} + def nets(): if not "pool" in globals(): return diff --git a/modules/monitor.py b/modules/monitor.py index 08fae94..3913c6f 100644 --- a/modules/monitor.py +++ b/modules/monitor.py @@ -1,8 +1,11 @@ from copy import deepcopy from json import dumps +from datetime import datetime import main from core.relay import sendRelayNotification +from modules import userinfo +from utils.dedup import dedup def testNetTarget(name, target): called = False @@ -50,28 +53,53 @@ def magicFunction(A, B): else: return all(A[k] in B[k] for k in set(A) & set(B)) and set(B) <= set(A) -def event(name, numberedName, cast, event=None): - if "target" in cast.keys(): - target = cast["target"] - else: - target = None +def event(numName, c): # yes I'm using a short variable because otherwise it goes off the screen + if not "target" in c.keys(): + c["target"] = None + + if dedup(numName, c): + return + # metadata scraping + # need to check if this was received from a relay + # in which case, do not do this + if c["type"] in ["msg", "notice", "action", "topic", "mode"]: + userinfo.editUser(c["name"], c["muser"]) + elif c["type"] == "nick": + userinfo.renameUser(c["name"], c["nick"], c["muser"], c["user"], c["user"]+"!"+c["ident"]+"@"+c["host"]) + elif c["type"] == "kick": + userinfo.editUser(c["name"], c["muser"]) + userinfo.delUserByNick(c["name"], c["target"], c["user"]) + elif c["type"] == "quit": + userinfo.delUserByNetwork(c["name"], c["nick"], c["muser"]) + elif c["type"] == "join": + userinfo.addUser(c["name"], c["target"], c["nick"], c["muser"]) + elif c["type"] == "part": + userinfo.delUser(c["name"], c["target"], c["nick"], c["muser"]) + + if "mtype" in c.keys(): + if c["mtype"] == "nick": + userinfo.renameUser(c["name"], c["nick"], c["muser"], c["user"], c["user"]+"!"+c["ident"]+"@"+c["host"]) + + if "muser" in c.keys(): + del c["muser"] + sendRelayNotification(c) - sendRelayNotification(name, cast) - monitorGroups = testNetTarget(name, target) + # only monitors below + monitorGroups = testNetTarget(c["name"], c["target"]) if monitorGroups == False: return for monitorGroup in monitorGroups: - matcher = magicFunction(deepcopy(cast), deepcopy(main.monitor[monitorGroup])) + matcher = magicFunction(deepcopy(c), deepcopy(main.monitor[monitorGroup])) if matcher == True: - cast["monitor"] = True + c["monitor"] = True if "send" in main.monitor[monitorGroup].keys(): for i in main.monitor[monitorGroup]["send"].keys(): if isinstance(main.monitor[monitorGroup]["send"][i], bool): - sendRelayNotification(name, {"type": "err", "name": name, "target": target, "message": cast, "reason": "errdeliv"}) + sendRelayNotification({"type": "err", "name": name, "target": target, "message": c, "reason": "errdeliv"}) continue if not i in main.pool.keys(): - sendRelayNotification(name, {"type": "err", "name": name, "target": target, "message": cast, "reason": "noname"}) + sendRelayNotification({"type": "err", "name": name, "target": target, "message": c, "reason": "noname"}) if not i in main.IRCPool.keys(): - sendRelayNotification(name, {"type": "err", "name": name, "target": target, "message": cast, "reason": "noinstance"}) + sendRelayNotification({"type": "err", "name": name, "target": target, "message": c, "reason": "noinstance"}) for x in main.monitor[monitorGroup]["send"][i]: - main.IRCPool[i].msg(x, "monitor [%s] (%s) %s" % (monitorGroup, name, cast)) + main.IRCPool[i].msg(x, "monitor [%s] (%s) %s" % (monitorGroup, c["name"], c)) diff --git a/modules/provision.py b/modules/provision.py index 0eb7464..fb3ea9c 100644 --- a/modules/provision.py +++ b/modules/provision.py @@ -34,7 +34,7 @@ def provisionNetworkData(relay, alias, network, host, port, security, auth, pass elif auth == "ns": stage2commands["status"] = [] stage2commands["nickserv"] = [] - stage2commands["status"].append("LoadMod NickServ") + stage2commands["status"].append("LoadMod nickserv") stage2commands["nickserv"].append("Set %s" % password) if not main.config["ConnectOnCreate"]: stage3commands["status"] = [] @@ -42,46 +42,47 @@ def provisionNetworkData(relay, alias, network, host, port, security, auth, pass if main.config["Toggles"]["CycleChans"]: stage2commands["status"] = [] stage2commands["status"].append("LoadMod disconkick") + stage2commands["status"].append("LoadMod chansaver") deliverRelayCommands(relay, commands, stage2=[[alias+"/"+network, stage2commands], [alias+"/"+network, stage3commands]]) return def provisionRelayForAlias(relay, alias): - if "users" in main.relay[relay].keys(): - if alias in main.relay[relay]["users"]: - return "PROVISIONED" - else: - main.relay[relay]["users"] = [] - main.relay[relay]["users"].append(alias) + #if "users" in main.relay[relay].keys(): + # if alias in main.relay[relay]["users"]: + # return "PROVISIONED" + #else: + # main.relay[relay]["users"] = [] + #main.relay[relay]["users"].append(alias) provisionUserData(relay, alias, main.alias[alias]["nick"], main.alias[alias]["altnick"], main.alias[alias]["ident"], main.alias[alias]["realname"], main.relay[relay]["password"]) - main.saveConf("relay") + #main.saveConf("relay") return True def provisionRelayForNetwork(relay, alias, network): - if set(["users", "networks"]).issubset(main.relay[relay].keys()): - if network in main.relay[relay]["networks"] and alias in main.relay[relay]["users"]: - return "PROVISIONED" - else: - main.relay[relay]["networks"] = [] - main.relay[relay]["networks"].append(network) + #if set(["users", "networks"]).issubset(main.relay[relay].keys()): + # if network in main.relay[relay]["networks"] and alias in main.relay[relay]["users"]: + # return "PROVISIONED" + #else: + # main.relay[relay]["networks"] = [] + #main.relay[relay]["networks"].append(network) provisionNetworkData(relay, alias, network, main.network[network]["host"], main.network[network]["port"], main.network[network]["security"], main.network[network]["auth"], main.alias[alias]["password"]) - main.saveConf("relay") + #main.saveConf("relay") storedNetwork = False num = 1 while not storedNetwork: i = str(num) if num == 1000: - error("Too many iterations in while trying to choose name for r: %s a: %s n: %s" % (relay, alias, network)) + error("Iteration limit exceeded while trying to choose name for r: %s a: %s n: %s" % (relay, alias, network)) return False if network+i in main.pool.keys(): diff --git a/modules/userinfo.py b/modules/userinfo.py index d9bdddf..29da9b0 100644 --- a/modules/userinfo.py +++ b/modules/userinfo.py @@ -1,6 +1,9 @@ -import main +from twisted.internet.threads import deferToThread from string import digits + +import main from utils.logging.log import * +from utils.logging.debug import debug def getWhoSingle(name, query): result = main.r.sscan("live.who."+name, 0, query, count=9999999) @@ -23,6 +26,13 @@ def getChansSingle(name, nick): return None return [i.decode() for i in result] +def getChanList(name, nick): + chanspace = "live.chan."+name+"."+nick + result = main.r.smembers(chanspace) + if len(result) == 0: + return None + return [i.decode() for i in result] + def getChans(nick): result = {} for i in main.nets(): @@ -61,14 +71,19 @@ def getNamespace(name, channel, nick): chanspace = "live.chan.%s.%s" % (name, nick) return [gnamespace, namespace, chanspace] -def initialUsers(name, channel, users): +def _initialUsers(name, channel, users): gnamespace = "live.who.%s" % name p = main.r.pipeline() for i in users: p.sadd(gnamespace, i[0]+"!"+i[1]+"@"+i[2]) p.execute() -def initialNames(name, channel, names): +def initialUsers(name, channel, users): + debug("Initialising WHO records for %s on %s" % (channel, name)) + d = deferToThread(_initialUsers, name, channel, users) + #d.addCallback(testCallback) + +def _initialNames(name, channel, names): namespace = "live.who.%s.%s" % (name, channel) p = main.r.pipeline() for i in names: @@ -76,6 +91,11 @@ def initialNames(name, channel, names): p.sadd("live.chan."+name+"."+i, channel) p.execute() +def initialNames(name, channel, names): + debug("Initialising NAMES records for %s on %s" % (channel, name)) + d = deferToThread(_initialNames, name, channel, names) + #d.addCallback(testCallback) + def editUser(name, user): gnamespace = "live.who.%s" % name main.r.sadd(gnamespace, user) @@ -151,7 +171,7 @@ def delUserByNetwork(name, nick, user): p.delete(chanspace) p.execute() -def delChannel(name, channel): # This function is extremely expensive, look to replace +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() @@ -166,10 +186,16 @@ def delChannel(name, channel): # This function is extremely expensive, look to r p.srem("live.chan."+name+"."+i.decode(), channel) p.delete(namespace) p.execute() + return [name, channel] + +def delChannel(name, channel): + debug("Purging channel %s for %s" % (channel, name)) + d = deferToThread(_delChannel, name, channel) + #d.addCallback(testCallback) def delNetwork(name, channels): - log("Purging channels for %s" % name) + debug("Purging channels for %s" % name) for i in channels: delChannel(name, i) - log("Finished purging channels for %s" % name) + #log("Finished purging channels for %s" % name) return diff --git a/threshold b/threshold index b073067..29a19c4 100755 --- a/threshold +++ b/threshold @@ -1,14 +1,19 @@ #!/usr/bin/env python from twisted.internet import reactor from twisted.internet.ssl import DefaultOpenSSLContextFactory +from sys import argv, stdout, stderr #from twisted.python import log #from sys import stdout #log.startLogging(stdout) - +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 + main.config["Debug"] = True from utils.logging.log import * from utils.loaders.command_loader import loadCommands from core.server import Server, ServerFactory diff --git a/utils/dedup.py b/utils/dedup.py new file mode 100644 index 0000000..03255e3 --- /dev/null +++ b/utils/dedup.py @@ -0,0 +1,21 @@ +from datetime import datetime +from csiphash import siphash24 +from json import dumps +import main +from utils.logging.debug import debug + +def dedup(numName, c): + # deduplication + c["approxtime"] = str(datetime.utcnow().timestamp())[:main.config["Tweaks"]["DedupPrecision"]] + castHash = siphash24(main.hashKey, dumps(c, sort_keys=True).encode("utf-8")) + del c["approxtime"] + isDuplicate= any(castHash in main.lastEvents[x] for x in main.lastEvents.keys() if not x == numName) + if isDuplicate: + debug("Duplicate: %s" % (c)) + return True + if numName in main.lastEvents.keys(): + main.lastEvents[numName].insert(0, castHash) + main.lastEvents[numName] = main.lastEvents[numName][0:main.config["Tweaks"]["MaxHash"]] + else: + main.lastEvents[numName] = [castHash] + return False diff --git a/utils/loaders/command_loader.py b/utils/loaders/command_loader.py index b22cbe1..360671d 100644 --- a/utils/loaders/command_loader.py +++ b/utils/loaders/command_loader.py @@ -1,6 +1,6 @@ from os import listdir -from utils.logging.log import * +from utils.logging.debug import debug import commands from main import CommandMap diff --git a/utils/logging/debug.py b/utils/logging/debug.py new file mode 100644 index 0000000..5a7b067 --- /dev/null +++ b/utils/logging/debug.py @@ -0,0 +1,7 @@ +import main +# we need a seperate module to log.py, as log.py is imported by main.py, and we need to access main +# to read the setting +def debug(data): + if main.config["Debug"]: + print("[DEBUG]", data) + diff --git a/utils/logging/log.py b/utils/logging/log.py index 5e1141f..6bb6a99 100644 --- a/utils/logging/log.py +++ b/utils/logging/log.py @@ -1,9 +1,6 @@ def log(data): print("[LOG]", data) -def debug(data): - print("[DEBUG]", data) - def warn(data): print("[WARNING]", data)