From 1af7998666814c588a2d726d7681326c788aa914 Mon Sep 17 00:00:00 2001 From: neersighted Date: Tue, 28 Feb 2012 08:33:58 +0800 Subject: [PATCH] Added mute plugin, and mode plugin --- plugins/botmodes.py | 140 +++++++++++++++++++++++++ plugins/mute.py | 41 ++++++++ plugins/usertracking.py | 221 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 402 insertions(+) create mode 100644 plugins/botmodes.py create mode 100644 plugins/mute.py create mode 100644 plugins/usertracking.py diff --git a/plugins/botmodes.py b/plugins/botmodes.py new file mode 100644 index 0000000..b25d96d --- /dev/null +++ b/plugins/botmodes.py @@ -0,0 +1,140 @@ +from util import hook + + +def db_init(db): + db.execute("create table if not exists botmodes(modename, nick, user, host, authed, admin, channel, chanmodes, usermodes)") + db.commit() + + +class Checker(object): + def __init__(self, bot, user, channel): + + self._bot = bot + + self._user = user + self._channel = channel + + def check(self, mode, db, bot=None, user=None, channel=None): + db_init(db) + bot = bot or self._bot + user = user or self._user + channel = channel or self._channel + checks = [mode] + if user: + checks.append(user.nick) + checks.append(user.user) + checks.append(user.host) + checks.append(user.authed or "") + checks.append(str(user.nick in bot.config["admins"])) + else: + checks.extend([""] * 4) + checks.append(str(False)) + + if channel: + checks.append(channel.name) + checks.append("".join(channel.modes.keys())) + else: + checks.extend([""] * 2) + + if channel and user and user in channel.usermodes: + checks.append("".join(channel.usermodes[user])) + else: + checks.append("") + + return bool(query(db, checks).fetchone()) + + +def query(db, checks): + return db.execute("select * from botmodes where " + "? glob modename and " + "lower(?) glob lower(nick) and " + "lower(?) glob lower(user) and " + "lower(?) glob lower(host) and " + "lower(?) glob lower(authed) and " + "? glob admin and " + "lower(?) glob lower(channel) and " + "? glob chanmodes and " + "? glob usermodes order by modename", checks) + + +def posquery(db, checks): + return db.execute("select * from botmodes where " + "modename glob ? and " + "lower(nick) glob lower(?) and " + "lower(user) glob lower(?) and " + "lower(host) glob ? and " + "authed glob ? and " + "admin glob ? and " + "lower(channel) glob lower(?) and " + "chanmodes glob ? and " + "usermodes glob ? order by modename", checks) + + +#called from usertracking, not as it's own sieve +def valueadd(bot, input, func, kind, args): + channel = None + if input.chan in input.users.channels: + channel = input.users.channels[input.chan] + user = None + if input.nick in input.users.users: + user = input.users.users[input.nick] + input["modes"] = Checker(bot, user, channel) + + +@hook.command +def mode(inp, input=None, db=None): + ".mode - Set modes on various things" + if input.nick not in input.bot.config["admins"]: + input.notice("Only bot admins can use this command!") + return + db_init(db) + split = inp.split(" ") + print repr(split) + if split[0] in ["set", "delete"]: + names = dict(mode=None, nick="*", user="*", host="*", authed="*", admin="*", channel="*", chanmodes="*", usermodes="*") + elif split[0] == "search": + names = dict(mode="*", nick="*", user="*", host="*", authed="*", admin="*", channel="*", chanmodes="*", usermodes="*", limit="5") + elif split[0] == "query": + names = dict(mode="", nick="", user="", host="", authed="", admin="", channel="", chanmodes="", usermodes="", limit="5") + dictized = dict([y for y in [x.split("=") for x in split[1:]] if len(y) == 2]) + names.update(dictized) + if names["mode"] == None: + input.notice("mode name is required!") + return + + namemap = "mode nick user host authed admin channel chanmodes usermodes".split(" ") + sqlargs = [names[i] for i in namemap] + if split[0] in ["query", "search"]: + if split[0] == "query": + result = query(db, sqlargs).fetchall() + else: + result = posquery(db, sqlargs).fetchall() + names["limit"] = int(names["limit"]) + if not len(result): + input.notice("no results") + return + elif len(result) > names["limit"]: + input.notice("exceeded your provided limit (limit=%d), cutting off" % names["limit"]) + + result = result[:names["limit"]] + result = [namemap] + [[repr(j)[1:] for j in i] for i in result] + + #hack to justify into a table + lengths = [[len(result[x][y]) for y in range(len(result[x]))] for x in range(len(result))] + lengths = [max([lengths[x][i] for x in range(len(result))]) for i in range(len(result[0]))] + for i in result: + out = "" + for j in range(len(result[0])): + out += i[j].ljust(lengths[j] + 1) + input.notice(out) + elif split[0] == "set": + if "".join(sqlargs[1:]) == "*******" and ("iamsure" not in names or names["iamsure"] != "yes"): + input.notice("you're trying to set a mode on everything. please repeat with 'iamsure=yes' on the query to confirm.") + return + db.execute("insert into botmodes(modename, nick, user, host, authed, admin, channel, chanmodes, usermodes) values(?, ?, ?, ?, ?, ?, ?, ?, ?)", sqlargs) + db.commit() + input.notice("done.") + elif split[0] == "delete": + db.execute("delete from botmodes where modename=? and nick=? and user=? and host=? and authed=? and admin=? and channel=? and chanmodes=? and usermodes=?", sqlargs) + db.commit() + input.notice("done.") diff --git a/plugins/mute.py b/plugins/mute.py new file mode 100644 index 0000000..cd72342 --- /dev/null +++ b/plugins/mute.py @@ -0,0 +1,41 @@ +from util import hook +import usertracking +import time + + +@hook.sieve +def mutesieve(bot, input, func, kind, args): + if kind == "event": + return input + if "chan" in input.keys() and input.chan in input.conn.users.channels and hasattr(input.conn.users[input.chan], "mute"): + if input.command == "PRIVMSG" and input.lastparam[1:] == "unmute": + return input + else: + return None + return input + + +@hook.command +def mute(inp, input=None, db=None, bot=None, users=None): + if inp and inp in input.conn.users.channels.keys(): + input.chan = inp + ".mute - Mutes the bot" + if usertracking.query(db, bot.config, input.nick, input.chan, "mute") or "o" in users[input.chan].usermodes[input.nick]: + users[input.chan].mute = "%s %d" % (input.nick, time.time()) + input.notice("Muted") + else: + input.notice("Only bot admins can use this command!") + +@hook.command +def unmute(inp, input=None, db=None, bot=None, users=None): + if inp and inp in users.channels.keys(): + input.chan = inp + ".unmute - Unmutes the bot" + if usertracking.query(db, bot.config, input.nick, input.chan, "mute") or "o" in users[input.chan].usermodes[input.nick]: + if hasattr(users[input.chan], "mute"): + input.notice("Unmuted") + del users[input.chan].mute + else: + input.notice("Not Muted") + else: + input.notice("Only bot admins can use this command!") \ No newline at end of file diff --git a/plugins/usertracking.py b/plugins/usertracking.py new file mode 100644 index 0000000..812663a --- /dev/null +++ b/plugins/usertracking.py @@ -0,0 +1,221 @@ +from util import hook +import sqlite3 +import re +import time +import sys +import botmodes +import thread + +loaded = False +userlock = thread.allocate_lock() +flag_re = re.compile(r"^([@+]*)(.*)$") + + +#controls access to user database +def query(db, config, user, channel, permission): + if user in config["admins"]: + return True + + return False + + +class Users(object): + def __init__(self, users={}, channels={}): + self.users = dict(users) + self.channels = dict(channels) + + def __getitem__(self, item): + try: + return self.users[item] + except KeyError: + return self.channels[item] + + def _join(self, nick, user, host, channel, modes=""): + userobj = self._user(nick, user, host) + chanobj = self._chan(channel) + chanobj.users[nick] = userobj + chanobj.usermodes[nick] = set(modes.replace("@", "o").replace("+", "v")) + + def _exit(self, nick, channel): + "all types of channel-=user events" + chanobj = self.channels[channel] + del chanobj.users[nick] + del chanobj.usermodes[nick] + + def _chnick(self, old, new): + print "changing nick '%s' to '%s'" % (old, new) + user = self.users[old] + del self.users[old] + self.users[new] = user + user.nick = new + + def _mode(self, chan, mode, argument=None): + if chan not in self.channels: + return + changetype = mode[0] + modeid = mode[1] + if modeid in "ov": + if changetype == "+": + self.channels[chan].usermodes[argument].add(modeid) + else: + self.channels[chan].usermodes[argument].remove(modeid) + else: + if changetype == "+": + self.channels[chan].modes[modeid] = argument + else: + del self.channels[chan].modes[modeid] + + def _trydelete(self, nick): + for i in self.channels.values(): + if nick in i.users: + return + del self.users[nick] + + def _user(self, nick, user, host): + if nick in self.users.keys(): + userobj = self.users[nick] + else: + userobj = User(nick, user, host) + self.users[nick] = userobj + return userobj + + def _chan(self, name): + if name in self.channels.keys(): + chanobj = self.channels[name] + else: + chanobj = Channel(name, self.users) + self.channels[name] = chanobj + return chanobj + + +class User(object): + def __init__(self, nick, user, host, lastmsg=0): + self.nick = nick + self.user = user + self.host = host + self.realname = None + self.channels = None + self.server = None + self.authed = None + self.lastmsg = lastmsg or time.time() + + def isadmin(self, bot): + return self.nick in bot.config["admins"] + + +class Channel(object): + def __init__(self, name, users, topic=None): + self.name = name + self.topic = topic + self.users = Userdict(users) + self.usermodes = Userdict(users) + self.modes = dict() + + def isop(self, nick): + return "o" in self.usermodes[nick] + + def isvoice(self, nick): + return "v" in self.usermodes[nick] + + +class Userdict(dict): + def __init__(self, users, *args, **named): + self.users = users + dict.__init__(self, *args, **named) + + def __getitem__(self, item): + try: + return dict.__getitem__(self, self.users[item]) + except KeyError: + return dict.__getitem__(self, item) + + def __setitem__(self, item, value): + try: + return dict.__setitem__(self, self.users[item], value) + except KeyError: + return dict.__setitem__(self, item, value) + + +@hook.sieve +def valueadd(bot, input, func, kind, args): + global loaded + if not userlock.acquire(): raise Exception("Problem acquiring userlock, probable thread crash. Abort.") + try: + if not hasattr(input.conn, "users") or not loaded: + loaded = True + input.conn.users = Users() + input.conn.users.users[input.nick] = User(input.nick, input.nick, "127.0.0.1") + input["users"] = input.conn.users + input["userdata"] = input.conn.users._user(input.nick, input.user, input.host) + if input.chan in input.conn.users.channels: + input["chandata"] = input.conn.users[input.chan] + else: + input["chandata"] = None + botmodes.valueadd(bot, input, func, kind, args) + return input + except: + raise + finally: + userlock.release() + + +@hook.event("332 353 311 319 312 330 318 JOIN PART KICK QUIT PRIVMSG MODE NICK") +@hook.singlethread +def tracking(inp, command=None, input=None, users=None): + if not userlock.acquire(): raise Exception("Problem acquiring userlock, probable thread crash. Abort.") + try: + if command in ["JOIN", "PART", "KICK", "QUIT", "PRIVMSG", "MODE", "NICK"]: + if input.nick != input.conn.nick and input.chan.startswith("#") and input.chan not in users.channels: + input.conn.send("NAMES " + input.chan) + users._chan(input.chan) + if command == "353": # when the names list comes in + chan = inp[2] + names = inp[3] + for name in names.split(" "): + match = flag_re.match(name) + flags = match.group(1) + nick = match.group(2) + users._join(nick, None, None, chan, flags) + elif command == "311": # whois: nick, user, host, realname" + nick = inp[1] + user = inp[2] + host = inp[3] + if nick not in input.conn.users.users.keys(): + users._user(nick, user, host) + users[nick].realname = inp[5] + elif command == "319": # whois: channel list + users[inp[1]].channels = inp[2].split(" ") + elif command == "312": # whois: server + users[inp[1]].server = inp[2] + elif command == "330": # whois: user logged in + print inp + users[inp[1]].authed = inp[2] + elif command == "318": # whois: end of whois + user = users[inp[1]] + user.authed = user.authed or "" + elif command == "JOIN": + users._join(input.nick, input.user, input.host, input.chan) + elif command in ["PART", "KICK", "QUIT"]: + for channel in users.channels.values(): + if input.nick in channel.users: + users._exit(input.nick, channel.name) + users._trydelete(input.nick) + elif command == "PRIVMSG": # updates last seen time - different from seen plugin + users[input.nick].lastmsg = time.time() + elif command == "MODE": # mode changes - getting op and suchh + users._mode(*inp) + elif command == "NICK": + users._chnick(input.nick, inp[0]) + except: + raise + finally: + userlock.release() + + +@hook.command +def mymodes(inp, input=None, users=None): + modes = users[input.chan].usermodes[input.nick] + if len(modes): + return "+" + "".join(modes) + else: + return "but you have no modes ..."