monolith/core/bot.py

370 lines
14 KiB
Python
Raw Normal View History

from twisted.internet.protocol import ReconnectingClientFactory
from twisted.words.protocols.irc import IRCClient
from twisted.internet.defer import Deferred
from twisted.internet.task import LoopingCall
from random import randint
import modules.keyword as keyword
import modules.userinfo as userinfo
import modules.counters as count
import modules.monitor as monitor
import main
from utils.logging.log import *
from utils.logging.send import *
class IRCBot(IRCClient):
def __init__(self, name):
self.connected = False
self.channels = []
self.buffer = ""
self.name = name
instance = main.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 = main.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)
count.event(self.name, "privmsg")
keyword.actKeyword(user, channel, msg, self.nickname, "MSG", self.name)
monitor.event(self.name, channel, {"type": "msg", "exact": user, "nick": nick, "ident": ident, "host": host, "message": msg})
def noticed(self, user, channel, msg):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
count.event(self.name, "notice")
keyword.actKeyword(user, channel, msg, self.nickname, "NOTICE", self.name)
monitor.event(self.name, channel, {"type": "notice", "exact": user, "nick": nick, "ident": ident, "host": host, "message": msg})
def action(self, user, channel, msg):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
count.event(self.name, "action")
keyword.actKeyword(user, channel, msg, self.nickname, "ACTION", self.name)
monitor.event(self.name, channel, {"type": "action", "exact": user, "nick": nick, "ident": ident, "host": host, "message": msg})
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 nickChanged(self, nick):
self.nickname = nick
count.event(self.name, "selfnick")
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]
count.event(self.name, "whoreply")
monitor.event(self.name, channel, {"type": "who", "exact": nick+"!"+user+"@"+host, "nick": nick, "ident": user, "host": host, "realname": realname, "server": server, "status": status})
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])
#twisted sucks so i have to do this to actually get the user info
def irc_JOIN(self, prefix, params):
"""
Called when a user joins a channel.
"""
nick = prefix.split('!')[0]
channel = params[-1]
if nick == self.nickname:
self.joined(channel)
else:
self.userJoined(prefix, channel)
def irc_PART(self, prefix, params):
"""
Called when a user leaves a channel.
"""
nick = prefix.split('!')[0]
channel = params[0]
if len(params) >= 2:
message = params[1]
else:
message = None
if nick == self.nickname:
self.left(channel, message)
else:
self.userLeft(prefix, channel, message)
def irc_QUIT(self, prefix, params):
"""
Called when a user has quit.
"""
#nick = prefix.split('!')[0]
self.userQuit(prefix, params[0])
def irc_NICK(self, prefix, params):
"""
Called when a user changes their nickname.
"""
nick = prefix.split('!', 1)[0]
if nick == self.nickname:
self.nickChanged(params[0])
else:
self.userRenamed(prefix, params[0])
def irc_KICK(self, prefix, params):
"""
Called when a user is kicked from a channel.
"""
#kicker = prefix.split('!')[0]
channel = params[0]
kicked = params[1]
message = params[-1]
if kicked.lower() == self.nickname.lower():
# Yikes!
self.kickedFrom(channel, prefix, message)
else:
self.userKicked(kicked, channel, prefix, message)
def irc_TOPIC(self, prefix, params):
"""
Someone in the channel set the topic.
"""
#user = prefix.split('!')[0]
channel = params[0]
newtopic = params[1]
self.topicUpdated(prefix, channel, newtopic)
#END hacks
def signedOn(self):
self.connected = True
log("signed on: %s" % self.name)
if main.config["Notifications"]["Connection"]:
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)
count.event(self.name, "signedon")
def getWho(self, channel):
self.who(channel).addCallback(self.got_who)
def joined(self, channel):
if not channel in self.channels:
self.channels.append(channel)
self.who(channel).addCallback(self.got_who)
count.event(self.name, "selfjoin")
if self.name == main.config["Master"][0] and channel == main.config["Master"][1]:
for i in range(len(main.masterbuf)):
self.msg(channel, main.masterbuf.pop(0))
main.saveConf("masterbuf")
lc = LoopingCall(self.getWho, channel)
self._getWho[channel] = lc
intrange = main.config["Tweaks"]["Delays"]["WhoRange"]
minint = main.config["Tweaks"]["Delays"]["WhoLoop"]
interval = randint(minint, minint+intrange)
lc.start(interval)
def left(self, channel, message):
if channel in self.channels:
self.channels.remove(channel)
keyword.actKeyword(self.nickname, channel, message, self.nickname, "SELFPART", self.name)
count.event(self.name, "selfpart")
monitor.event(self.name, channel, {"type": "part", "message": message})
lc = self._getWho[channel]
lc.stop()
def kickedFrom(self, channel, kicker, message):
nick, ident, host = self.parsen(kicker)
userinfo.setWhoSingle(self.name, nick, ident, host)
if channel in self.channels:
self.channels.remove(channel)
keyword.sendMaster("KICK %s: (%s/%s) %s" % (self.name, kicker, channel, message))
count.event(self.name, "selfkick")
monitor.event(self.name, channel, {"type": "kick", "exact": kicker, "nick": nick, "ident": ident, "host": host, "message": message})
lc = self._getWho[channel]
lc.stop()
def userJoined(self, user, channel):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
count.event(self.name, "join")
monitor.event(self.name, channel, {"type": "join", "exact": user, "nick": nick, "ident": ident, "host": host})
def userLeft(self, user, channel, message):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
keyword.actKeyword(user, channel, message, self.nickname, "PART", self.name)
count.event(self.name, "part")
monitor.event(self.name, channel, {"type": "part", "exact": user, "nick": nick, "ident": ident, "host": host, "message": message})
def userQuit(self, user, quitMessage):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
count.event(self.name, "quit")
keyword.actKeyword(user, None, quitMessage, self.nickname, "QUIT", self.name)
monitor.event(self.name, None, {"type": "quit", "exact": user, "nick": nick, "ident": ident, "host": host, "message": quitMessage})
def userKicked(self, kickee, channel, kicker, message):
nick, ident, host = self.parsen(kicker)
userinfo.setWhoSingle(self.name, nick, ident, host)
count.event(self.name, "kick")
keyword.actKeyword(kicker, channel, message, self.nickname, "KICK", self.name)
monitor.event(self.name, channel, {"type": "kick", "exact": kicker, "nick": nick, "ident": ident, "host": host, "message": message, "user": kickee})
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)
count.event(self.name, "nick")
monitor.event(self.name, None, {"type": "nick", "exact": oldname, "nick": nick, "ident": ident, "host": host, "user": newname})
def topicUpdated(self, user, channel, newTopic):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
count.event(self.name, "topic")
keyword.actKeyword(user, channel, newTopic, self.nickname, "TOPIC", self.name)
monitor.event(self.name, channel, {"type": "topic", "exact": user, "nick": nick, "ident": ident, "host": host, "message": newTopic})
def modeChanged(self, user, channel, toset, modes, args):
nick, ident, host = self.parsen(user)
userinfo.setWhoSingle(self.name, nick, ident, host)
count.event(self.name, "mode")
argList = list(args)
modeList = [i for i in modes]
for a, m in zip(argList, modeList):
monitor.event(self.name, channel, {"type": "mode", "exact": user, "nick": nick, "ident": ident, "host": host, "modes": m, "modeargs": a})
class IRCBotFactory(ReconnectingClientFactory):
def __init__(self, name):
self.instance = main.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)
main.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 main.config["Notifications"]["Connection"]:
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 main.config["Notifications"]["Connection"]:
keyword.sendMaster("CONNFAIL %s: %s" % (self.name, error))
self.retry(connector)
#ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)