Implement connecting to IRC

This commit is contained in:
Mark Veidemanis 2017-11-23 20:37:00 +00:00
parent c3aa3715c6
commit 0912ac71cb
2 changed files with 230 additions and 7 deletions

View File

@ -4,6 +4,8 @@
"add": "add <name> <address> <port> <ssl|plain> <nickname>", "add": "add <name> <address> <port> <ssl|plain> <nickname>",
"del": "del <name>", "del": "del <name>",
"mod": "mod <name> [<key>] [<value>]", "mod": "mod <name> [<key>] [<value>]",
"enable": "enable <name",
"disable": "disable <name>",
"list": "list", "list": "list",
"rehash": "rehash" "rehash": "rehash"
} }

233
threshold
View File

@ -2,11 +2,15 @@
from twisted.internet import reactor from twisted.internet import reactor
from twisted.internet.ssl import DefaultOpenSSLContextFactory from twisted.internet.ssl import DefaultOpenSSLContextFactory
from twisted.internet.protocol import Protocol, Factory from twisted.internet.protocol import Protocol, Factory
from twisted.internet.endpoints import SSL4ClientEndpoint, TCP4ClientEndpoint, connectProtocol
from twisted.words.protocols.irc import IRCClient
from json import load, dump, loads from json import load, dump, loads
from sys import exit from sys import exit
listener = None listener = None
connections = {} connections = {}
IRCPool = {}
def log(data): def log(data):
print("[LOG]", data) print("[LOG]", data)
@ -33,6 +37,136 @@ def sendFailure(addr, data):
def sendInfo(addr, data): def sendInfo(addr, data):
sendData(addr, "[i] " + data) sendData(addr, "[i] " + data)
class IRCBot(IRCClient):
def __init__(self, name):
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._who = {}
self._getWho = {}
self.wholist = {}
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[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 sanitize(self, user):
user = user.replace("!", "")
user = user.replace("~", "")
user = user.replace("&", "")
user = user.replace("@", "")
user = user.replace("%", "")
user = user.replace("+", "")
return user
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):
ircLog(self.ident, "Password mismatch")
manager.handleWrongPassword(self.ident, prefix, params)
def irc_RPL_NAMREPLY(self, prefix, params):
channel = params[2]
nicklist = params[3].split(' ')
if channel not in self._names:
return
n = self._names[channel][1]
n += nicklist
def irc_RPL_ENDOFNAMES(self, prefix, params):
channel = params[1]
if channel not in self._names:
return
callbacks, namelist = self._names[channel]
for cb in callbacks:
cb.callback((channel, namelist))
del self._names[channel]
def got_names(self, nicklist):
ncklist = []
for i in nicklist[1]:
ncklist.append(self.sanitize(i))
self.nameslist[nicklist[0]] = ncklist
def who(self, channel):
channel = channel
d = defer.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):
self.wholist[whoinfo[0]] = whoinfo[1]
def signedOn(self):
if self.authtype == "ns":
self.msg(self.authentity, "IDENTIFY %s" % self.nspass)
def joined(self, channel):
self.who(channel).addCallback(self.got_who)
class Base(Protocol): class Base(Protocol):
def __init__(self, addr): def __init__(self, addr):
self.addr = addr self.addr = addr
@ -124,6 +258,37 @@ class Helper(object):
sendFailure(addr, "Usage: " + help[mode]) sendFailure(addr, "Usage: " + help[mode])
return return
def addBot(self, name):
global IRCPool
instance = pool[name]
if instance["protocol"] == "plain":
if instance["bind"] == None:
point = TCP4ClientEndpoint(reactor, instance["host"], int(instance["port"]), timeout=int(instance["timeout"]))
bot = IRCBot(name)
IRCPool[name] = bot
d = connectProtocol(point, bot)
return
else:
point = TCP4ClientEndpoint(reactor, instance["host"], int(instance["port"]), timeout=int(instance["timeout"]), bindAddress=instance["bind"])
bot = IRCBot(name)
IRCPool[name] = bot
d = connectProtocol(point, bot)
return
elif instance["protocol"] == "ssl":
contextFactory = DefaultOpenSSLContextFactory(instance["key"].encode("utf-8", "replace"), instance["certificate"].encode("utf-8", "replace"))
if instance["bind"] == None:
point = SSL4ClientEndpoint(reactor, instance["host"], int(instance["port"]), contextFactory, timeout=int(instance["timeout"]))
bot = IRCBot(name)
IRCPool[name] = bot
d = connectProtocol(point, bot)
return
else:
point = SSL4ClientEndpoint(reactor, instance["host"], int(instance["port"]), contextFactory, timeout=int(instance["timeout"]), bindAddress=instance["bind"])
bot = IRCBot(name)
IRCPool[name] = bot
d = connectProtocol(point, bot)
return
def parseCommand(self, addr, authed, data): def parseCommand(self, addr, authed, data):
global pool global pool
data = data.strip() data = data.strip()
@ -174,6 +339,33 @@ class Helper(object):
info("\n".join(poolMap)) info("\n".join(poolMap))
return return
elif cmd == "enable":
if length == 2:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
return
pool[spl[1]]["enabled"] = True
self.addBot(spl[1])
success("Successfully enabled bot %s" % spl[1])
return
else:
incUsage("enable")
return
elif cmd == "disable":
if length == 2:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
return
pool[spl[1]]["enabled"] = False
if spl[1] in IRCPool.keys():
IRCPool[spl[1]].transport.loseConnection()
success("Successfully disabled bot %s" % spl[1])
return
else:
incUsage("disable")
return
elif cmd == "add": elif cmd == "add":
if length == 6: if length == 6:
@ -193,9 +385,11 @@ class Helper(object):
failure("Port must be an integer, not %s" % spl[3]) failure("Port must be an integer, not %s" % spl[3])
return return
pool[spl[1]] = { "address": spl[2], pool[spl[1]] = { "host": spl[2],
"port": spl[3], "port": spl[3],
"protocol": protocol, "protocol": protocol,
"bind": None,
"timeout": 30,
"nickname": spl[5], "nickname": spl[5],
"username": None, "username": None,
"realname": None, "realname": None,
@ -205,10 +399,13 @@ class Helper(object):
"source": None, "source": None,
"authtype": None, "authtype": None,
"password": None, "password": None,
"authentity": None, "authentity": "NickServ",
"key": None, "key": config["ListenerKey"],
"certificate": None, "certificate": config["ListenerCertificate"],
"enabled": config["ConnectOnCreate"],
} }
if config["ConnectOnCreate"] == True:
self.addBot(spl[1])
success("Successfully created bot") success("Successfully created bot")
self.savePool() self.savePool()
return return
@ -223,6 +420,9 @@ class Helper(object):
failure("Name does not exist: %s" % spl[1]) failure("Name does not exist: %s" % spl[1])
return return
del pool[spl[1]] del pool[spl[1]]
if spl[1] in IRCPool.keys():
IRCPool[spl[1]].transport.loseConnection()
del IRCPool[spl[1]]
success("Successfully removed bot") success("Successfully removed bot")
self.savePool() self.savePool()
return return
@ -231,6 +431,7 @@ class Helper(object):
return return
elif cmd == "mod": elif cmd == "mod":
toUnset = False
if length == 2: if length == 2:
if not spl[1] in pool.keys(): if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1]) failure("Name does not exist: %s" % spl[1])
@ -258,11 +459,11 @@ class Helper(object):
if not spl[2] in pool[spl[1]].keys(): if not spl[2] in pool[spl[1]].keys():
failure("No such key: %s" % spl[2]) failure("No such key: %s" % spl[2])
return return
if spl[2] == "port": if spl[2] in ["port", "timeout"]:
try: try:
int(spl[3]) int(spl[3])
except: except:
failure("Port must be an integer, not %s" % spl[3]) failure("Value must be an integer, not %s" % spl[3])
return return
if spl[2] == "protocol": if spl[2] == "protocol":
if not spl[3] in ["ssl", "plain"]: if not spl[3] in ["ssl", "plain"]:
@ -271,7 +472,24 @@ class Helper(object):
if spl[3] == pool[spl[1]][spl[2]]: if spl[3] == pool[spl[1]][spl[2]]:
failure("Value already exists: %s" % spl[3]) failure("Value already exists: %s" % spl[3])
return return
if spl[3].lower() in ["none", "nil"]:
spl[3] = None
toUnset = True
if spl[2] == "authtype":
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
pool[spl[1]][spl[2]] = spl[3] pool[spl[1]][spl[2]] = spl[3]
self.savePool()
if toUnset:
success("Successfully unset key %s on %s" % (spl[2], spl[3], spl[1]))
else:
success("Successfully set key %s to %s on %s" % (spl[2], spl[3], spl[1])) success("Successfully set key %s to %s on %s" % (spl[2], spl[3], spl[1]))
return return
@ -300,6 +518,9 @@ if __name__ == "__main__":
config = helper.getConfig() config = helper.getConfig()
pool = helper.getPool() pool = helper.getPool()
help = helper.getHelp() help = helper.getHelp()
for i in pool.keys():
if pool[i]["enabled"] == True:
helper.addBot(i)
listener = BaseFactory() listener = BaseFactory()
if config["UseSSL"] == True: if config["UseSSL"] == True: