diff --git a/feature_requests.txt b/feature_requests.txt deleted file mode 100644 index 2fe3ecd..0000000 --- a/feature_requests.txt +++ /dev/null @@ -1,2 +0,0 @@ -- mention (without @) or otherwise make clear who which point goes to -- add command to display people with most points (top 10 or so) diff --git a/import_points.py b/import_points.py new file mode 100644 index 0000000..3c2b8c3 --- /dev/null +++ b/import_points.py @@ -0,0 +1,30 @@ +import asyncio +import sqlite3 +import sys + +import yaboli +from yaboli.utils import * + +from plusone import PointsDB + + +async def add_points(db, room, points): + for (nick, amount) in points: + print(f"&{room}: {mention(nick, ping=False)} + {amount}") + await db.add_points(room, nick, amount) + +def main(to_dbfile, from_dbfile, room): + from_db = sqlite3.connect(from_dbfile) + res = from_db.execute("SELECT nick, points FROM Points") + points = res.fetchall() + + to_db = PointsDB(to_dbfile) + asyncio.get_event_loop().run_until_complete(add_points(to_db, room, points)) + +if __name__ == "__main__": + if len(sys.argv) == 4: + main(sys.argv[1], sys.argv[2], sys.argv[3]) + else: + print(" USAGE:") + print(f"{sys.argv[0]} ") + exit(1) diff --git a/plusone.py b/plusone.py index 273e553..acda07d 100644 --- a/plusone.py +++ b/plusone.py @@ -7,132 +7,144 @@ import yaboli from yaboli.utils import * -# Turn all debugging on -asyncio.get_event_loop().set_debug(True) -#logging.getLogger("asyncio").setLevel(logging.INFO) -#logging.getLogger("yaboli").setLevel(logging.DEBUG) -logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger("plusone") - -class PointDB(yaboli.Database): - def initialize(self, db): # called automatically - db.execute(( - "CREATE TABLE IF NOT EXISTS Points ( " - "nick TEXT UNIQUE NOT NULL, " - "points INTEGER " - ")" - )) - db.commit() +class PointsDB(yaboli.Database): + def initialize(self, db): + with db: + db.execute(( + "CREATE TABLE IF NOT EXISTS points (" + "normalized_nick TEXT PRIMARY KEY, " + "nick TEXT NOT NULL, " + "room TEXT NOT NULL, " + "points INTEGER NOT NULL" + ")" + )) @yaboli.operation - def add_point(self, db, nick): - nick = normalize(nick) - - cur = db.cursor() - cur.execute("INSERT OR IGNORE INTO Points (nick, points) VALUES (?, 0)", (nick,)) - cur.execute("UPDATE Points SET points=points+1 WHERE nick=?", (nick,)) - db.commit() + def add_points(self, db, room, nick, points): + normalized_nick = normalize(nick) + with db: + db.execute( + "INSERT OR IGNORE INTO points VALUES (?,?,?,0)", + (normalized_nick, nick, room) + ) + db.execute(( + "UPDATE points " + "SET points=points+?, nick=? " + "WHERE normalized_nick=? AND room=?" + ), (points, nick, normalized_nick, room)) @yaboli.operation - def points_of(self, db, nick): - nick = normalize(nick) + def points_of(self, db, room, nick): + normalized_nick = normalize(nick) + print(nick, normalized_nick, room) + res = db.execute(( + "SELECT points FROM points " + "WHERE normalized_nick=? AND room=?" + ), (normalized_nick, room)) + points = res.fetchone() + return points[0] if points else 0 - cur = db.execute("SELECT points FROM Points WHERE nick=?", (nick,)) - res = cur.fetchone() - return res[0] if res is not None else 0 - - -PLUSONE_RE = r"(\+1|:\+1:|:bronze(!\?|\?!)?:)\s*(.*)" -MENTION_RE = r"((to)\s+@?|@)(\S+)" - -class PlusOne(yaboli.Bot): - """ - Count +1s awarded to users by other users. - """ - - PING_TEXT = ":bronze!?:" - SHORT_HELP = "/me counts :+1:s" - LONG_HELP = ( - "Counts +1/:+1:/:bronze:s: Simply reply \"+1\" to someone's message to award them a point.\n" - "Alternatively, specify a person with: \"+1 [to] @person\"\n" - "\n" - "!points - show your own points\n" - "!points [ ...] - list other people's points\n" - "\n" - "Created by @Garmy using https://github.com/Garmelon/yaboli.\n" +class PlusOne: + SHORT_DESCRIPTION = "counts :+1:s" + DESCRIPTION = ( + "'plusone' counts +1/:+1:/:bronze:s:" + " Simply reply '+1' to someone's message to give them a point.\n" + " Alternatively, specify a person with: '+1 [to] @person'.\n" ) + COMMANDS = ( + "!points - show your own points\n" + "!points [ ...] - list other people's points\n" + ) + AUTHOR = "Created by @Garmy using github.com/Garmelon/yaboli\n" - async def on_created(self, room): - room.pointdb = PointDB(f"points-{room.roomname}.db") + PLUSONE_RE = r"\s*(\+1|:\+1:|:bronze(!\?|\?!)?:)\s*(.*)" + MENTION_RE = r"(to\s+@?|@)(\S+)" + + def __init__(self, dbfile): + self.db = PointsDB(dbfile) + + @yaboli.command("points") + async def command_points(self, room, message, argstr): + args = yaboli.Bot.parse_args(argstr) + if args: + lines = [] + for nick in args: + if nick[0] == "@": # a bit hacky, requires you to mention nicks starting with '@' + nick = nick[1:] + + points = await self.db.points_of(room.roomname, nick) + line = f"{mention(nick, ping=False)} has {points} point{'' if points == 1 else 's'}." + lines.append(line) + + text = "\n".join(lines) + await room.send(text, message.mid) + + else: # your own points + points = await self.db.points_of(room.roomname, message.sender.nick) + text = f"You have {points} point{'' if points == 1 else 's'}." + await room.send(text, message.mid) + + @yaboli.trigger(PLUSONE_RE, flags=re.IGNORECASE) + async def trigger_plusone(self, room, message, match): + specific = re.match(self.MENTION_RE, match.group(3)) + + nick = None + if specific: + nick = specific.group(2) + elif message.parent: + parent_message = await room.get_message(message.parent) + nick = parent_message.sender.nick + + if nick is None: + text = "You can't +1 nothing..." + elif similar(nick, message.sender.nick): + text = "There's no such thing as free points on the internet." + else: + await self.db.add_points(room.roomname, nick, 1) + text = f"Point for user {mention(nick, ping=False)} registered." + await room.send(text, message.mid) + +class PlusOneBot(yaboli.Bot): + PING_TEXT = ":bronze?!:" + SHORT_HELP = PlusOne.SHORT_DESCRIPTION + LONG_HELP = PlusOne.DESCRIPTION + PlusOne.COMMANDS + PlusOne.AUTHOR + + def __init__(self, nick, dbfile, cookiefile=None): + super().__init__(nick, cookiefile=cookiefile) + self.plusone = PlusOne(dbfile) + + async def on_send(self, room, message): + await super().on_send(room, message) + + await self.plusone.trigger_plusone(room, message) async def on_command_specific(self, room, message, command, nick, argstr): if similar(nick, room.session.nick) and not argstr: await self.botrulez_ping(room, message, command, text=self.PING_TEXT) await self.botrulez_help(room, message, command, text=self.LONG_HELP) await self.botrulez_uptime(room, message, command) - await self.botrulez_kill(room, message, command) - await self.botrulez_restart(room, message, command) - + await self.botrulez_kill(room, message, command, text="-1") + await self.botrulez_restart(room, message, command, text="∓1") async def on_command_general(self, room, message, command, argstr): if not argstr: await self.botrulez_ping(room, message, command, text=self.PING_TEXT) await self.botrulez_help(room, message, command, text=self.SHORT_HELP) - await self.command_points(room, message, command, argstr) - - async def on_send(self, room, message): - await super().on_send(room, message) - await self.trigger_plusone(room, message) - - @yaboli.command("points") - async def command_points(self, room, message, argstr): - args = self.parse_args(argstr) - if not args: - points = await room.pointdb.points_of(message.sender.nick) - await room.send( - f"You have {points} point{'s' if points != 1 else ''}.", - message.mid - ) - else: - response = [] - for arg in args: - if arg[:1] == "@": - nick = arg[1:] - else: - nick = arg - points = await room.pointdb.points_of(nick) - response.append(f"{mention(nick)} has {points} point{'' if points == 1 else 's'}.") - await room.send("\n".join(response), message.mid) - - @yaboli.trigger(PLUSONE_RE) - async def trigger_plusone(self, room, message, match): - nick = None - specific = re.match(MENTION_RE, match.group(3)) - - if specific: - nick = specific.group(3) - if nick[0] == "@": - nick = nick[1:] - elif message.parent: - parent_message = await room.get_message(message.parent) - nick = parent_message.sender.nick - - if nick is None: - await room.send("You can't +1 nothing...", message.mid) - elif similar(nick, message.sender.nick): - await room.send("Don't +1 yourself, that's not how things work.", message.mid) - else: - await room.pointdb.add_point(nick) - await room.send(f"Point for user {mention(nick, ping=False)} registered.", message.mid) + await self.plusone.command_points(room, message, command, argstr) def main(configfile): + logging.basicConfig(level=logging.INFO) + config = configparser.ConfigParser(allow_no_value=True) config.read(configfile) nick = config.get("general", "nick") cookiefile = config.get("general", "cookiefile", fallback=None) - bot = PlusOne(nick, cookiefile=cookiefile) + dbfile = config.get("general", "dbfile", fallback=None) + bot = PlusOneBot(nick, dbfile, cookiefile=cookiefile) for room, password in config.items("rooms"): if not password: