Separate out everything into files and implement a modules system to segment commands

This commit is contained in:
Mark Veidemanis 2018-02-23 22:05:40 +00:00
parent 4b3541625a
commit cb7142ef88
30 changed files with 1346 additions and 1123 deletions

0
commands/__init__.py Normal file
View File

110
commands/add.py Normal file
View File

@ -0,0 +1,110 @@
from core.main import *
import core.helper as helper
class Add:
def __init__(self, register):
register("add", self.add)
def add(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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:
helper.addBot(name)
success("Successfully created bot")
saveConf("pool")
return
else:
incUsage(None)

80
commands/default.py Normal file
View File

@ -0,0 +1,80 @@
from core.main import *
class Default:
def __init__(self, register):
register("default", self.default)
def default(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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]
saveConf("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
else:
incUsage(None)

29
commands/delete.py Normal file
View File

@ -0,0 +1,29 @@
from core.main import *
class Delete:
def __init__(self, register):
register("del", self.delete)
def delete(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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")
saveConf("pool")
return
else:
incUsage("del")
return
else:
incUsage(None)

29
commands/disable.py Normal file
View File

@ -0,0 +1,29 @@
from core.main import *
class Disable:
def __init__(self, register):
register("disable", self.disable)
def disable(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
if length == 2:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
return
pool[spl[1]]["enabled"] = False
saveConf("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
else:
incUsage(None)

20
commands/dist.py Normal file
View File

@ -0,0 +1,20 @@
from core.main import *
from subprocess import run, PIPE
class Dist:
def __init__(self, register):
register("dist", self.dist)
def dist(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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
else:
incUsage(None)

26
commands/enable.py Normal file
View File

@ -0,0 +1,26 @@
from core.main import *
import core.helper as helper
class Enable:
def __init__(self, register):
register("enable", self.enable)
def enable(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
if length == 2:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
return
pool[spl[1]]["enabled"] = True
saveConf("pool")
if not spl[1] in IRCPool.keys():
helper.addBot(spl[1])
else:
pass
success("Successfully enabled bot %s" % spl[1])
return
else:
incUsage("enable")
return
else:
incUsage(None)

22
commands/get.py Normal file
View File

@ -0,0 +1,22 @@
from core.main import *
class Get:
def __init__(self, register):
register("get", self.get)
def get(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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
else:
incUsage(None)

15
commands/help.py Normal file
View File

@ -0,0 +1,15 @@
from core.main import *
class Help:
def __init__(self, register):
register("help", self.help)
def help(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
helpMap = []
for i in help.keys():
helpMap.append("%s: %s" % (i, help[i]))
info("\n".join(helpMap))
return
else:
incUsage(None)

33
commands/join.py Normal file
View File

@ -0,0 +1,33 @@
from core.main import *
class Join:
def __init__(self, register):
register("join", self.join)
def join(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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
else:
incUsage(None)

149
commands/key.py Normal file
View File

@ -0,0 +1,149 @@
from core.main import *
import modules.keyword as keyword
class Key:
def __init__(self, register):
register("key", self.key)
def key(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
if data.startswith("key add"):
if not data in ["key add ", "key add"] and data[3] == " ":
keywordsToAdd = data[8:]
keywords = keywordsToAdd.split(",")
for i in keywords:
rtrn = keyword.addKeyword(i)
if rtrn == "EXISTS":
failure("Keyword already exists: %s" % i)
elif rtrn == "ISIN":
failure("Keyword already matched: %s" % i)
elif rtrn == True:
success("Keyword added: %s" % i)
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 i in keywords:
rtrn = keyword.delKeyword(i)
if rtrn == "NOKEY":
failure("Keyword does not exist: %s" % i)
elif rtrn == True:
success("Keyword deleted: %s" % i)
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])
saveConf("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]]
saveConf("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]]
saveConf("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]]
saveConf("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
else:
incUsage(None)

18
commands/list.py Normal file
View File

@ -0,0 +1,18 @@
from core.main import *
class List:
def __init__(self, register):
register("list", self.list)
def list(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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
else:
incUsage(None)

26
commands/load.py Normal file
View File

@ -0,0 +1,26 @@
from core.main import *
class Load:
def __init__(self, register):
register("load", self.load)
def load(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
if length == 2:
if spl[1] in filemap.keys():
loadConf(spl[1])
success("Loaded %s from %s" % (spl[1], filemap[spl[1]][0]))
return
elif spl[1] == "all":
for i in filemap.keys():
loadConf(i)
success("Loaded %s from %s" % (i, filemap[i][0]))
return
else:
incUsage("load")
return
else:
incUsage("load")
return
else:
incUsage(None)

15
commands/logout.py Normal file
View File

@ -0,0 +1,15 @@
from core.main import *
class Logout:
def __init__(self, register):
register("logout", self.logout)
def logout(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
obj.authed = False
if obj.addr in MonitorPool:
MonitorPool.remove(obj.addr)
success("Logged out")
return
else:
incUsage(None)

89
commands/mod.py Normal file
View File

@ -0,0 +1,89 @@
from core.main import *
class Mod:
def __init__(self, register):
register("mod", self.mod)
def mod(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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()
saveConf("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)

23
commands/part.py Normal file
View File

@ -0,0 +1,23 @@
from core.main import *
class Part:
def __init__(self, register):
register("part", self.part)
def part(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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
else:
incUsage(None)

22
commands/password.py Normal file
View File

@ -0,0 +1,22 @@
from core.main import *
class Password:
def __init__(self, register):
register("pass", self.password)
def password(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
info("You are already authenticated")
return
else:
if length == 2:
if spl[1] == config["Password"]:
success("Authenticated successfully")
obj.authed = True
return
else:
failure("Password incorrect")
obj.transport.loseConnection()
return
else:
incUsage("pass")

26
commands/save.py Normal file
View File

@ -0,0 +1,26 @@
from core.main import *
class Save:
def __init__(self, register):
register("save", self.save)
def save(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
if length == 2:
if spl[1] in filemap.keys():
saveConf(spl[1])
success("Saved %s to %s" % (spl[1], filemap[spl[1]][0]))
return
elif spl[1] == "all":
for i in filemap.keys():
saveConf(i)
success("Saved %s from %s" % (i, filemap[i][0]))
return
else:
incUsage("save")
return
else:
incUsage("save")
return
else:
incUsage(None)

49
commands/stats.py Normal file
View File

@ -0,0 +1,49 @@
from core.main import *
class Stats:
def __init__(self, register):
register("stats", self.stats)
def stats(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
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")
else:
incUsage(None)

26
commands/who.py Normal file
View File

@ -0,0 +1,26 @@
from core.main import *
import modules.userinfo as userinfo
class Who:
def __init__(self, register):
register("who", self.who)
def who(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length):
if authed:
if length == 2:
result = userinfo.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
else:
incUsage(None)

240
core/bot.py Normal file
View File

@ -0,0 +1,240 @@
from twisted.internet.protocol import ReconnectingClientFactory
from twisted.words.protocols.irc import IRCClient
from twisted.internet.defer import Deferred
import modules.keyword as keyword
import modules.userinfo as userinfo
from core.main import *
from utils.logging.log import *
from utils.logging.send import *
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)
userinfo.setWhoSingle(self.name, nick, ident, host)
keyword.actKeyword(user, channel, msg, self.nickname, "PRV", self.name)
def noticed(self, user, channel, msg):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
keyword.actKeyword(user, channel, msg, self.nickname, "NOT", self.name)
def action(self, user, channel, msg):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
keyword.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)
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):
userinfo.setWho(self.name, whoinfo[1])
def signedOn(self):
self.connected = True
log("signed on: %s" % self.name)
if config["ConnectionNotifications"]:
keyword.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)
keyword.sendMaster("KICK %s: (%s/%s) %s" % (self.name, kicker, channel, message))
def userJoined(self, user, channel):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
def userLeft(self, user, channel):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
def userQuit(self, user, quitMessage):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
keyword.actKeyword(user, None, quitMessage, self.nickname, "QUT", self.name)
def userKicked(self, kickee, channel, kicker, message):
nick, ident, host = self.parsen(kicker)
userinfo.setWhoSingle(self.name, nick, ident, host)
keyword.actKeyword(kicker, channel, message, self.nickname, "KCK", self.name)
def userRenamed(self, oldname, newname):
nick, ident, host = self.parsen(oldname)
userinfo.setWhoSingle(self.name, nick, ident, host)
userinfo.setWhoSingle(self.name, newname, ident, host)
def topicUpdated(self, user, channel, newTopic):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
keyword.actKeyword(user, channel, newTopic, self.nickname, "TOP", self.name)
def modeChanged(self, user, channel, toset, modes, args):
nick, ident, host = self.parsen(user)
userinfo.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):
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))
sendAll("%s: connection lost: %s" % (self.name, error))
if config["ConnectionNotifications"]:
keyword.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))
sendAll("%s: connection failed: %s" % (self.name, error))
if config["ConnectionNotifications"]:
keyword.sendMaster("CONNFAIL %s: %s" % (self.name, error))
self.retry(connector)
#ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)

44
core/helper.py Normal file
View File

@ -0,0 +1,44 @@
from twisted.internet import reactor
from twisted.internet.ssl import DefaultOpenSSLContextFactory
from core.bot import IRCBot, IRCBotFactory
from core.main import *
from utils.logging.log import *
def addBot(name):
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

50
core/main.py Normal file
View File

@ -0,0 +1,50 @@
from json import load, dump, loads
from utils.loaders.command_loader import loadCommands
from utils.logging.log import *
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"],
}
numbers = "0123456789"
listener = None
connections = {}
IRCPool = {}
ReactorPool = {}
FactoryPool = {}
MonitorPool = []
CommandMap = {}
def register(command, function):
if not command in CommandMap:
CommandMap[command] = function
log("Registering command %s" % command)
else:
error("Duplicate command: %s" % (command))
def saveConf(var):
with open(configPath+filemap[var][0], "w") as f:
dump(globals()[var], f, indent=4)
return
def loadConf(var):
with open(configPath+filemap[var][0], "r") as f:
globals()[var] = load(f)
def initConf():
for i in filemap.keys():
loadConf(i)
def initMain():
initConf()
loadCommands(register)

30
core/parser.py Normal file
View File

@ -0,0 +1,30 @@
from core.main import *
from utils.logging.log import *
from utils.logging.send import *
def parseCommand(addr, authed, data):
#call command modules with: (addr, authed, data, spl, success, failure, info, incUsage, length)
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: incorrectUsage(addr, mode)
length = len(spl)
if len(spl) > 0:
cmd = spl[0]
else:
failure("No text was sent")
return
for i in CommandMap.keys():
if data.startswith(i):
CommandMap[i](addr, authed, data, obj, spl, success, failure, info, incUsage, length)
return
incUsage(None)
return

74
modules/keyword.py Normal file
View File

@ -0,0 +1,74 @@
from core.main import *
from utils.logging.log import *
def sendMaster(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(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(user, channel, message, nickname, actType, name):
toSend = 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+"}")
sendMaster("NICK %s %s (T:%s): (%s/%s) %s" % (actType, name, msgLower.count(nickLower), user, channel, msgLower))
if toSend:
sendMaster("MATCH %s %s (U:%s T:%s): (%s/%s) %s" % (actType, name, toSend[1], toSend[2], user, channel, toSend[0]))
def addKeyword(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)
saveConf("keyconf")
return True
def delKeyword(keyword):
if not keyword in keyconf["Keywords"]:
return "NOKEY"
keyconf["Keywords"].remove(keyword)
saveConf("keyconf")
return True

31
modules/userinfo.py Normal file
View File

@ -0,0 +1,31 @@
from core.main import *
#from utils.logging.log import *
def setWho(network, newObjects):
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(network, nick, ident, host):
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(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

1151
threshold

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
from os import listdir
from core.main import *
from utils.logging.log import *
import commands
def loadCommands(func):
for filename in listdir('commands'):
if filename.endswith('.py') and filename != "__init__.py":
commandName = filename[0:-3]
className = commandName.capitalize()
#try:
__import__('commands.%s' % commandName)
eval('commands.%s.%s(func)' % (commandName, className))
#except Exception as err:
# error("Exception while loading command %s:\n%s" % (commandName, err))

View File

@ -9,4 +9,3 @@ def warn(data):
def error(data):
print("[ERROR]", data)
exit(1)

26
utils/logging/send.py Normal file
View File

@ -0,0 +1,26 @@
from core.main import connections, help
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)
def sendAll(data):
for i in connections:
connections[i].send(data)
return
def incorrectUsage(addr, mode):
if mode == None:
sendFailure(addr, "Incorrect usage")
return
if mode in help.keys():
sendFailure(addr, "Usage: " + help[mode])
return