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 import modules.counters as count 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) 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) 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) 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") 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["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) count.event(self.name, "signedon") 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") def left(self, channel, message): if channel in self.channels: self.channels.remove(channel) keyword.actKeyword(user, channel, message, self.nickname, "SELFPART", self.name) count.event(self.name, "selfpart") 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)) count.event(self.name, "selfkick") def userJoined(self, user, channel): nick, ident, host = self.parsen(user) userinfo.setWhoSingle(self.name, nick, ident, host) count.event(self.name, "join") 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") 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) 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) 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") 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) 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") 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["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 main.config["ConnectionNotifications"]: keyword.sendMaster("CONNFAIL %s: %s" % (self.name, error)) self.retry(connector) #ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)