Port PlusOne to new yaboli version
This commit is contained in:
parent
4715ab14e2
commit
11b4ade690
3 changed files with 165 additions and 74 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,3 +1,6 @@
|
||||||
|
**/__pycache__
|
||||||
yaboli
|
yaboli
|
||||||
__pycache__
|
websockets
|
||||||
|
join_rooms.py
|
||||||
|
*.cookie
|
||||||
*.db
|
*.db
|
||||||
|
|
|
||||||
86
database.py
Normal file
86
database.py
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import asyncio
|
||||||
|
from functools import wraps
|
||||||
|
import sqlite3
|
||||||
|
import threading
|
||||||
|
|
||||||
|
__all__ = ["Database"]
|
||||||
|
|
||||||
|
|
||||||
|
def shielded(afunc):
|
||||||
|
#@wraps(afunc)
|
||||||
|
async def wrapper(*args, **kwargs):
|
||||||
|
return await asyncio.shield(afunc(*args, **kwargs))
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
class PooledConnection:
|
||||||
|
def __init__(self, pool):
|
||||||
|
self._pool = pool
|
||||||
|
|
||||||
|
self.connection = None
|
||||||
|
|
||||||
|
async def open(self):
|
||||||
|
self.connection = await self._pool._request()
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
conn = self.connection
|
||||||
|
self.connection = None
|
||||||
|
await self._pool._return(conn)
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
await self.open()
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc, tb):
|
||||||
|
await self.close()
|
||||||
|
|
||||||
|
class Pool:
|
||||||
|
def __init__(self, filename, size=10):
|
||||||
|
self.filename = filename
|
||||||
|
self.size = size
|
||||||
|
|
||||||
|
self._available_connections = asyncio.Queue()
|
||||||
|
|
||||||
|
for i in range(size):
|
||||||
|
conn = sqlite3.connect(self.filename, check_same_thread=False)
|
||||||
|
self._available_connections.put_nowait(conn)
|
||||||
|
|
||||||
|
def connection(self):
|
||||||
|
return PooledConnection(self)
|
||||||
|
|
||||||
|
async def _request(self):
|
||||||
|
return await self._available_connections.get()
|
||||||
|
|
||||||
|
async def _return(self, conn):
|
||||||
|
await self._available_connections.put(conn)
|
||||||
|
|
||||||
|
class Database:
|
||||||
|
def __init__(self, filename, pool_size=10, event_loop=None):
|
||||||
|
self._filename = filename
|
||||||
|
self._pool = Pool(filename, size=pool_size)
|
||||||
|
self._loop = event_loop or asyncio.get_event_loop()
|
||||||
|
|
||||||
|
def operation(func):
|
||||||
|
@wraps(func)
|
||||||
|
@shielded
|
||||||
|
async def wrapper(self, *args, **kwargs):
|
||||||
|
async with self._pool.connection() as conn:
|
||||||
|
return await self._run_in_thread(func, conn.connection, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _target_function(loop, future, func, *args, **kwargs):
|
||||||
|
result = None
|
||||||
|
try:
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
finally:
|
||||||
|
loop.call_soon_threadsafe(future.set_result, result)
|
||||||
|
|
||||||
|
async def _run_in_thread(self, func, *args, **kwargs):
|
||||||
|
finished = asyncio.Future()
|
||||||
|
target_args = (self._loop, finished, func, *args)
|
||||||
|
|
||||||
|
thread = threading.Thread(target=self._target_function, args=target_args, kwargs=kwargs)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
await finished
|
||||||
|
return finished.result()
|
||||||
148
plusone.py
148
plusone.py
|
|
@ -1,14 +1,16 @@
|
||||||
|
import asyncio
|
||||||
|
import re
|
||||||
|
|
||||||
import yaboli
|
import yaboli
|
||||||
from yaboli.utils import *
|
from yaboli.utils import *
|
||||||
import asyncio
|
import database
|
||||||
import random
|
|
||||||
import re
|
# List of rooms kept in separate file, which is .gitignore'd
|
||||||
import sys
|
import join_rooms
|
||||||
|
|
||||||
|
|
||||||
|
class PointDB(database.Database):
|
||||||
class PointDB(yaboli.Database):
|
@database.Database.operation
|
||||||
@yaboli.Database.operation
|
|
||||||
def initialize(conn):
|
def initialize(conn):
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
cur.execute((
|
cur.execute((
|
||||||
|
|
@ -18,21 +20,21 @@ class PointDB(yaboli.Database):
|
||||||
")"
|
")"
|
||||||
))
|
))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
@yaboli.Database.operation
|
@database.Database.operation
|
||||||
def add_point(conn, nick):
|
def add_point(conn, nick):
|
||||||
nick = mention_reduced(nick)
|
nick = mention_reduced(nick)
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
|
|
||||||
cur.execute("INSERT OR IGNORE INTO Points (nick, points) VALUES (?, 0)", (nick,))
|
cur.execute("INSERT OR IGNORE INTO Points (nick, points) VALUES (?, 0)", (nick,))
|
||||||
cur.execute("UPDATE Points SET points=points+1 WHERE nick=?", (nick,))
|
cur.execute("UPDATE Points SET points=points+1 WHERE nick=?", (nick,))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
@yaboli.Database.operation
|
@database.Database.operation
|
||||||
def points_of(conn, nick):
|
def points_of(conn, nick):
|
||||||
nick = mention_reduced(nick)
|
nick = mention_reduced(nick)
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
|
|
||||||
cur.execute("SELECT points FROM Points WHERE nick=?", (nick,))
|
cur.execute("SELECT points FROM Points WHERE nick=?", (nick,))
|
||||||
res = cur.fetchone()
|
res = cur.fetchone()
|
||||||
if res is not None:
|
if res is not None:
|
||||||
|
|
@ -40,65 +42,51 @@ class PointDB(yaboli.Database):
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
PLUSONE_RE = r"(\+1|:\+1:|:bronze(!\?|\?!)?:)\s*(.*)"
|
||||||
|
MENTION_RE = r"((for|to)\s+)?@(\S+)"
|
||||||
|
|
||||||
class PlusOne(yaboli.Bot):
|
class PlusOne(yaboli.Bot):
|
||||||
"""
|
"""
|
||||||
Count +1s awarded to users by other users.
|
Count +1s awarded to users by other users.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PLUSONE_RE = r"(\+1|:\+1:|:bronze(!\?|\?!)?:)\s*(.*)"
|
async def created(self, room):
|
||||||
MENTION_RE = r"((for|to)\s+)?@(\S+)"
|
room.pointsdb = PointDB(f"points-{room.roomname}.db")
|
||||||
|
await room.pointsdb.initialize()
|
||||||
def __init__(self, db):
|
|
||||||
super().__init__("PlusOne")
|
async def send(self, room, message):
|
||||||
|
ping_text = ":bronze!?:"
|
||||||
self.db = db
|
short_help = "/me counts :+1:s"
|
||||||
|
long_help = (
|
||||||
self.help_general = "/me counts :+1:s."
|
|
||||||
self.help_specific = (
|
|
||||||
"Counts +1/:+1:/:bronze:s: Simply reply \"+1\" to someone's message to award them a point.\n"
|
"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|for] @person\"\n"
|
"Alternatively, specify a person with: \"+1 [to|for] @person\"\n"
|
||||||
|
"\n"
|
||||||
"!points - show your own points\n"
|
"!points - show your own points\n"
|
||||||
"!points <person1> [<person2> ...] - list other people's points\n\n"
|
"!points <person1> [<person2> ...] - list other people's points\n"
|
||||||
"Created by @Garmy using yaboli.\n"
|
"\n"
|
||||||
"For additional info, try \"!help @{nick} <topic>\". Topics:\n"
|
"Created by @Garmy using https://github.com/Garmelon/yaboli.\n"
|
||||||
)
|
)
|
||||||
self.help_specific += self.list_help_topics()
|
await self.botrulez_ping_general(room, message, ping_text=ping_text)
|
||||||
self.ping_message = ":bronze!?:"
|
await self.botrulez_ping_specific(room, message, ping_text=ping_text)
|
||||||
|
await self.botrulez_help_general(room, message, help_text=short_help)
|
||||||
self.register_command("points", self.command_points, specific=False)
|
await self.botrulez_help_specific(room, message, help_text=long_help)
|
||||||
self.register_trigger(self.PLUSONE_RE, self.trigger_plusone)
|
await self.botrulez_uptime(room, message)
|
||||||
|
await self.botrulez_kill(room, message)
|
||||||
async def trigger_plusone(self, message, match):
|
await self.botrulez_restart(room, message)
|
||||||
nick = None
|
|
||||||
specific = re.match(self.MENTION_RE, match.group(3))
|
|
||||||
|
|
||||||
if specific:
|
await self.command_points(room, message)
|
||||||
nick = specific.group(3)
|
|
||||||
elif message.parent:
|
await self.trigger_plusone(room, message)
|
||||||
parent_message = await self.room.get_message(message.parent)
|
|
||||||
nick = parent_message.sender.nick
|
forward = send
|
||||||
|
|
||||||
if nick is None:
|
@yaboli.command("points", specific=False, args=True)
|
||||||
await self.room.send("You can't +1 nothing...", message.mid)
|
async def command_points(self, room, message, argstr):
|
||||||
elif similar(nick, message.sender.nick):
|
|
||||||
newtext = random.choice([
|
|
||||||
"Don't +1 yourself, that's... nasty.",
|
|
||||||
"Don't +1 yourself, that's... It just doesn't work that way, alright?",
|
|
||||||
"No points for " + nick + "!",
|
|
||||||
"No", # Attempt to trigger @Elements
|
|
||||||
"Would you kindly stop that?",
|
|
||||||
"Where'd you get that idea from? It would never work."
|
|
||||||
])
|
|
||||||
await self.room.send(newtext, message.mid)
|
|
||||||
else:
|
|
||||||
await self.db.add_point(nick)
|
|
||||||
await self.room.send(f"Point for user {mention(nick)} registered.", message.mid)
|
|
||||||
|
|
||||||
async def command_points(self, message, argstr):
|
|
||||||
args = self.parse_args(argstr)
|
args = self.parse_args(argstr)
|
||||||
if not args:
|
if not args:
|
||||||
points = await self.db.points_of(message.sender.nick)
|
points = await room.pointsdb.points_of(message.sender.nick)
|
||||||
await self.room.send(
|
await room.send(
|
||||||
f"You have {points} point{'s' if points != 1 else ''}.",
|
f"You have {points} point{'s' if points != 1 else ''}.",
|
||||||
message.mid
|
message.mid
|
||||||
)
|
)
|
||||||
|
|
@ -107,21 +95,35 @@ class PlusOne(yaboli.Bot):
|
||||||
for arg in args:
|
for arg in args:
|
||||||
if arg[:1] == "@":
|
if arg[:1] == "@":
|
||||||
nick = arg[1:]
|
nick = arg[1:]
|
||||||
points = await self.db.points_of(nick)
|
|
||||||
response.append(f"@{mention(nick)} has {points} point{'' if points == 1 else 's'}.")
|
|
||||||
else:
|
else:
|
||||||
response.append(f"{arg!r} is not a mention.")
|
nick = arg
|
||||||
await self.room.send("\n".join(response), message.mid)
|
points = await room.pointsdb.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)
|
||||||
|
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.pointsdb.add_point(nick)
|
||||||
|
await room.send(f"Point for user {mention(nick)} registered.", message.mid)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) == 3:
|
bot = PlusOne("PlusOne")
|
||||||
db = PointDB(sys.argv[2])
|
join_rooms.join_rooms(bot)
|
||||||
asyncio.get_event_loop().run_until_complete(db.initialize())
|
asyncio.get_event_loop().run_forever()
|
||||||
run_bot(PlusOne, sys.argv[1], db)
|
|
||||||
else:
|
|
||||||
print("USAGE:")
|
|
||||||
print(f" {sys.argv[0]} <room> <pointsdb>")
|
|
||||||
return
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue