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

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

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