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>",
"del": "del <name>",
"mod": "mod <name> [<key>] [<value>]",
"enable": "enable <name",
"disable": "disable <name>",
"list": "list",
"rehash": "rehash"
}

233
threshold
View File

@ -2,11 +2,15 @@
from twisted.internet import reactor
from twisted.internet.ssl import DefaultOpenSSLContextFactory
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 sys import exit
listener = None
connections = {}
IRCPool = {}
def log(data):
print("[LOG]", data)
@ -33,6 +37,136 @@ def sendFailure(addr, data):
def sendInfo(addr, 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):
def __init__(self, addr):
self.addr = addr
@ -124,6 +258,37 @@ class Helper(object):
sendFailure(addr, "Usage: " + help[mode])
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):
global pool
data = data.strip()
@ -174,6 +339,33 @@ class Helper(object):
info("\n".join(poolMap))
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":
if length == 6:
@ -193,9 +385,11 @@ class Helper(object):
failure("Port must be an integer, not %s" % spl[3])
return
pool[spl[1]] = { "address": spl[2],
pool[spl[1]] = { "host": spl[2],
"port": spl[3],
"protocol": protocol,
"bind": None,
"timeout": 30,
"nickname": spl[5],
"username": None,
"realname": None,
@ -205,10 +399,13 @@ class Helper(object):
"source": None,
"authtype": None,
"password": None,
"authentity": None,
"key": None,
"certificate": None,
"authentity": "NickServ",
"key": config["ListenerKey"],
"certificate": config["ListenerCertificate"],
"enabled": config["ConnectOnCreate"],
}
if config["ConnectOnCreate"] == True:
self.addBot(spl[1])
success("Successfully created bot")
self.savePool()
return
@ -223,6 +420,9 @@ class Helper(object):
failure("Name does not exist: %s" % spl[1])
return
del pool[spl[1]]
if spl[1] in IRCPool.keys():
IRCPool[spl[1]].transport.loseConnection()
del IRCPool[spl[1]]
success("Successfully removed bot")
self.savePool()
return
@ -231,6 +431,7 @@ class Helper(object):
return
elif cmd == "mod":
toUnset = False
if length == 2:
if not spl[1] in pool.keys():
failure("Name does not exist: %s" % spl[1])
@ -258,11 +459,11 @@ class Helper(object):
if not spl[2] in pool[spl[1]].keys():
failure("No such key: %s" % spl[2])
return
if spl[2] == "port":
if spl[2] in ["port", "timeout"]:
try:
int(spl[3])
except:
failure("Port must be an integer, not %s" % spl[3])
failure("Value must be an integer, not %s" % spl[3])
return
if spl[2] == "protocol":
if not spl[3] in ["ssl", "plain"]:
@ -271,7 +472,24 @@ class Helper(object):
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] == "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]
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]))
return
@ -300,6 +518,9 @@ if __name__ == "__main__":
config = helper.getConfig()
pool = helper.getPool()
help = helper.getHelp()
for i in pool.keys():
if pool[i]["enabled"] == True:
helper.addBot(i)
listener = BaseFactory()
if config["UseSSL"] == True: