Update to yaboli 1.1.1
This commit is contained in:
parent
1c0097a5aa
commit
b7ccd4f59f
6 changed files with 203 additions and 195 deletions
14
.gitignore
vendored
14
.gitignore
vendored
|
|
@ -1,4 +1,14 @@
|
||||||
**/__pycache__
|
# python stuff
|
||||||
*.cookie
|
__pycache__/
|
||||||
|
|
||||||
|
# venv stuff
|
||||||
|
bin/
|
||||||
|
include/
|
||||||
|
lib/
|
||||||
|
lib64
|
||||||
|
pyvenv.cfg
|
||||||
|
|
||||||
|
# config files
|
||||||
*.conf
|
*.conf
|
||||||
|
cookie_jar
|
||||||
*.db
|
*.db
|
||||||
|
|
|
||||||
9
bot.conf.default
Normal file
9
bot.conf.default
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[general]
|
||||||
|
nick = wtf
|
||||||
|
cookie_file = cookie_jar
|
||||||
|
|
||||||
|
[rooms]
|
||||||
|
test
|
||||||
|
|
||||||
|
[wtf]
|
||||||
|
db = wtf.db
|
||||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
yaboli >=1.1.1, <1.2.0
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
[general]
|
|
||||||
nick = wtf
|
|
||||||
cookiefile = wtf.cookie
|
|
||||||
wtfdbfile = wtf.db
|
|
||||||
|
|
||||||
[rooms]
|
|
||||||
# Format:
|
|
||||||
# room
|
|
||||||
# room=password
|
|
||||||
test
|
|
||||||
273
wtf.py
273
wtf.py
|
|
@ -1,181 +1,61 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import configparser
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import yaboli
|
import yaboli
|
||||||
from yaboli.utils import *
|
from wtfdb import WtfDB
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
logger = logging.getLogger("wtf")
|
|
||||||
|
|
||||||
class WtfDB(yaboli.Database):
|
|
||||||
def initialize(self, db):
|
|
||||||
with db:
|
|
||||||
db.execute((
|
|
||||||
"CREATE TABLE IF NOT EXISTS acronyms ("
|
|
||||||
"acronym_id INTEGER PRIMARY KEY, "
|
|
||||||
"acronym TEXT NOT NULL, "
|
|
||||||
"explanation TEXT NOT NULL, "
|
|
||||||
"author TEXT NOT NULL, "
|
|
||||||
"deleted BOOLEAN NOT NULL DEFAULT 0"
|
|
||||||
")"
|
|
||||||
))
|
|
||||||
db.create_function("p_lower", 1, str.lower)
|
|
||||||
|
|
||||||
@yaboli.operation
|
|
||||||
def add(self, db, acronym, explanation, author):
|
|
||||||
with db:
|
|
||||||
db.execute((
|
|
||||||
"INSERT INTO acronyms (acronym, explanation, author) "
|
|
||||||
"VALUES (?,?,?)"
|
|
||||||
), (acronym, explanation, author))
|
|
||||||
|
|
||||||
@yaboli.operation
|
|
||||||
def find(self, db, acronym):
|
|
||||||
c = db.execute((
|
|
||||||
"SELECT acronym, explanation FROM acronyms "
|
|
||||||
"WHERE NOT deleted AND p_lower(acronym) = ? "
|
|
||||||
"ORDER BY acronym_id ASC"
|
|
||||||
), (acronym.lower(),))
|
|
||||||
return c.fetchall()
|
|
||||||
|
|
||||||
@yaboli.operation
|
|
||||||
def find_full(self, db, acronym):
|
|
||||||
c = db.execute((
|
|
||||||
"SELECT acronym_id, acronym, explanation, author FROM acronyms "
|
|
||||||
"WHERE NOT deleted AND p_lower(acronym) = ? "
|
|
||||||
"ORDER BY acronym_id ASC"
|
|
||||||
), (acronym.lower(),))
|
|
||||||
return c.fetchall()
|
|
||||||
|
|
||||||
@yaboli.operation
|
|
||||||
def get(self, db, acronym_id):
|
|
||||||
with db:
|
|
||||||
c = db.execute((
|
|
||||||
"SELECT acronym FROM acronyms "
|
|
||||||
"WHERE NOT deleted AND acronym_id=?"
|
|
||||||
), (acronym_id,))
|
|
||||||
res = c.fetchone()
|
|
||||||
return None if res is None else res[0]
|
|
||||||
|
|
||||||
@yaboli.operation
|
|
||||||
def delete(self, db, acronym_id):
|
|
||||||
with db:
|
|
||||||
db.execute("UPDATE acronyms SET deleted = 1 WHERE acronym_id = ?", (acronym_id,))
|
|
||||||
|
|
||||||
class Wtf(yaboli.Module):
|
class Wtf(yaboli.Module):
|
||||||
DESCRIPTION = (
|
DESCRIPTION = ("a database of explanations for words, acronyms and"
|
||||||
"'wtf' is a database of explanations for words, acronyms and initialisms."
|
" initialisms")
|
||||||
" It is inspired by the linux wtf program and uses its acronyms,"
|
HELP_GENERAL = DESCRIPTION
|
||||||
" in addition to ones set by users.\n"
|
HELP_SPECIFIC = [
|
||||||
)
|
"'wtf' is a database of explanations for words, acronyms and"
|
||||||
COMMANDS = (
|
" initialisms. It is inspired by the linux wtf program and uses"
|
||||||
"!wtf is <term> - look up a term\n"
|
" its acronyms, in addition to ones set by users.",
|
||||||
"!wtf add <term> <explanation> - add a new explanation\n"
|
"",
|
||||||
"!wtf detail <term> - shows more info about the term's explanations\n"
|
"!wtf is <term> - look up a term (also responds to 'wtf is')",
|
||||||
"!wtf delete <id> - delete explanation with corresponding id (look up the id using !wtf detail)\n"
|
"!wtf add <term> <explanation> - add a new explanation",
|
||||||
"!wtf replace <id> <explanation> - a shortcut for deleting and re-adding with a different explanation\n"
|
"!wtf detail <term> - shows more info about the term's explanations",
|
||||||
)
|
"!wtf delete <id> - delete explanation with corresponding id (look"
|
||||||
AUTHOR = "Created by @Garmy using github.com/Garmelon/yaboli\n"
|
" up the id using !wtf detail)",
|
||||||
|
"!wtf replace <id> <explanation> - a shortcut for deleting and"
|
||||||
|
" re-adding with a different explanation",
|
||||||
|
"",
|
||||||
|
"Uses most acronyms of arch's community/wtf package.",
|
||||||
|
"Made by @Garmy using https://github.com/Garmelon/yaboli.",
|
||||||
|
]
|
||||||
|
|
||||||
SHORT_DESCRIPTION = "a database of explanations for words, acronyms and initialisms"
|
SECTION = "wtf"
|
||||||
SHORT_HELP = SHORT_DESCRIPTION
|
|
||||||
|
|
||||||
LONG_DESCRIPTION = DESCRIPTION + COMMANDS
|
RE_IS = re.compile(r"\s*is\s+(.*)")
|
||||||
LONG_HELP = DESCRIPTION + COMMANDS + AUTHOR
|
RE_ADD = re.compile(r"\s*add\s+(\S+)\s+(.+)")
|
||||||
|
RE_DETAIL = re.compile(r"\s*detail\s+(.*)")
|
||||||
|
RE_DELETE = re.compile(r"\s*delete\s+(\d+)\s*")
|
||||||
|
RE_REPLACE = re.compile(r"\s*replace\s+(\d+)\s+(.+)")
|
||||||
|
|
||||||
RE_IS = r"\s*is\s+(.*)"
|
RE_WTF_IS = re.compile(r"\s*wtf\s+is\s+(.*)")
|
||||||
RE_ADD = r"\s*add\s+(\S+)\s+(.+)"
|
|
||||||
RE_DETAIL = r"\s*detail\s+(.*)"
|
|
||||||
RE_DELETE = r"\s*delete\s+(\d+)\s*"
|
|
||||||
RE_REPLACE = r"\s*replace\s+(\d+)\s+(.+)"
|
|
||||||
|
|
||||||
TRIGGER_WTF_IS = r"\s*wtf\s+is\s+(.*)"
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __init__(self, dbfile):
|
dbfile = self.config[self.SECTION]["db"]
|
||||||
self.db = WtfDB(dbfile)
|
self.db = WtfDB(dbfile)
|
||||||
|
|
||||||
async def on_send(self, room, message):
|
if self.standalone:
|
||||||
await self.trigger_wtf_is(room, message)
|
self.register_botrulez(kill=True, restart=True)
|
||||||
|
|
||||||
async def on_command_general(self, room, message, command, argstr):
|
self.register_general("wtf", self.cmd_wtf)
|
||||||
await self.command_wtf(room, message, command, argstr)
|
|
||||||
|
|
||||||
@yaboli.command("wtf")
|
|
||||||
async def command_wtf(self, room, message, argstr):
|
|
||||||
match_is = re.fullmatch(self.RE_IS, argstr)
|
|
||||||
match_add = re.fullmatch(self.RE_ADD, argstr)
|
|
||||||
match_detail = re.fullmatch(self.RE_DETAIL, argstr)
|
|
||||||
match_delete = re.fullmatch(self.RE_DELETE, argstr)
|
|
||||||
match_replace = re.fullmatch(self.RE_REPLACE, argstr)
|
|
||||||
|
|
||||||
if match_is:
|
|
||||||
terms = match_is.group(1)
|
|
||||||
terms = [term for term in terms.split() if term]
|
|
||||||
if not terms:
|
|
||||||
return
|
|
||||||
|
|
||||||
lines = await self._find_explanations(terms)
|
|
||||||
await room.send("\n".join(lines), message.mid)
|
|
||||||
|
|
||||||
elif match_add:
|
|
||||||
term = match_add.group(1)
|
|
||||||
explanation = match_add.group(2).strip()
|
|
||||||
await self.db.add(term, explanation, message.sender.nick)
|
|
||||||
logger.info(f"{mention(message.sender.nick)} added explanation: {term} - {explanation}")
|
|
||||||
await room.send(f"Added explanation: {term} — {explanation}", message.mid)
|
|
||||||
|
|
||||||
elif match_detail:
|
|
||||||
terms = match_detail.group(1)
|
|
||||||
terms = [term for term in terms.split() if term]
|
|
||||||
if not terms:
|
|
||||||
return
|
|
||||||
|
|
||||||
lines = await self._find_explanations(terms, detail=True)
|
|
||||||
await room.send("\n".join(lines), message.mid)
|
|
||||||
|
|
||||||
elif match_delete:
|
|
||||||
aid = match_delete.group(1)
|
|
||||||
await self.db.delete(aid)
|
|
||||||
await room.send(f"Deleted.", message.mid)
|
|
||||||
logger.info(f"{mention(message.sender.nick)} deleted explanation with id {aid}")
|
|
||||||
|
|
||||||
elif match_replace:
|
|
||||||
aid = match_replace.group(1)
|
|
||||||
explanation = match_replace.group(2).strip()
|
|
||||||
term = await self.db.get(aid)
|
|
||||||
print(term)
|
|
||||||
if term is None:
|
|
||||||
await room.send(f"No explanation with id {aid} exists.", message.mid)
|
|
||||||
else:
|
|
||||||
await self.db.delete(aid)
|
|
||||||
logger.info(f"{mention(message.sender.nick)} deleted explanation with id {aid}")
|
|
||||||
await self.db.add(term, explanation, message.sender.nick)
|
|
||||||
logger.info(f"{mention(message.sender.nick)} added explanation: {term} - {explanation}")
|
|
||||||
await room.send(f"Changed explanation: {term} — {explanation}", message.mid)
|
|
||||||
|
|
||||||
else:
|
|
||||||
text = "Usage:\n" + self.COMMANDS
|
|
||||||
await room.send(text, message.mid)
|
|
||||||
|
|
||||||
@yaboli.trigger(TRIGGER_WTF_IS, flags=re.IGNORECASE)
|
|
||||||
async def trigger_wtf_is(self, room, message, match):
|
|
||||||
terms = match.group(1)
|
|
||||||
terms = [term for term in terms.split() if term]
|
|
||||||
if not terms:
|
|
||||||
return
|
|
||||||
|
|
||||||
lines = await self._find_explanations(terms)
|
|
||||||
await room.send("\n".join(lines), message.mid)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _format_explanations(explanations, detail=False):
|
def _format_explanations(explanations, detail=False):
|
||||||
# Id, Term, Explanation, Author
|
# Id, Term, Explanation, Author
|
||||||
if detail:
|
if detail:
|
||||||
return [f"{i}: {t} — {e} (by {mention(a, ping=False)})" for i, t, e, a in explanations]
|
return [f"{i}: {t} — {e} (by {a})" for i, t, e, a in explanations]
|
||||||
else:
|
else:
|
||||||
return [ f"{t} — {e}" for _, t, e, _ in explanations]
|
return [f"{t} — {e}" for _, t, e, _ in explanations]
|
||||||
|
|
||||||
async def _find_explanations(self, terms, detail=False):
|
async def _find_explanations(self, terms, detail=False):
|
||||||
lines = []
|
lines = []
|
||||||
|
|
@ -187,25 +67,76 @@ class Wtf(yaboli.Module):
|
||||||
lines.append(f"{term!r} not found.")
|
lines.append(f"{term!r} not found.")
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def main(configfile):
|
async def on_send(self, room, message):
|
||||||
logging.basicConfig(level=logging.INFO)
|
await super().on_send(room, message)
|
||||||
|
|
||||||
config = configparser.ConfigParser(allow_no_value=True)
|
match = self.RE_WTF_IS.fullmatch(message.content)
|
||||||
config.read(configfile)
|
if match:
|
||||||
|
terms = match.group(1)
|
||||||
|
terms = [term for term in terms.split() if term]
|
||||||
|
if not terms: return
|
||||||
|
lines = await self._find_explanations(terms)
|
||||||
|
await message.reply("\n".join(lines))
|
||||||
|
|
||||||
nick = config.get("general", "nick")
|
async def cmd_wtf(self, room, message, args):
|
||||||
cookiefile = config.get("general", "cookiefile", fallback=None)
|
match_is = self.RE_IS.fullmatch(args.raw)
|
||||||
wtfdbfile = config.get("general", "wtfdbfile")
|
if match_is:
|
||||||
module = Wtf(wtfdbfile)
|
terms = match_is.group(1)
|
||||||
|
terms = [term for term in terms.split() if term]
|
||||||
|
if not terms: return
|
||||||
|
lines = await self._find_explanations(terms)
|
||||||
|
await message.reply("\n".join(lines))
|
||||||
|
return
|
||||||
|
|
||||||
bot = yaboli.ModuleBot(module, nick, cookiefile=cookiefile)
|
match_add = self.RE_ADD.fullmatch(args.raw)
|
||||||
|
if match_add:
|
||||||
|
term = match_add.group(1)
|
||||||
|
explanation = match_add.group(2).strip()
|
||||||
|
await self.db.add(term, explanation, message.sender.nick)
|
||||||
|
logger.info((f"{message.sender.atmention} added explanation:"
|
||||||
|
f" {term} - {explanation}"))
|
||||||
|
await message.reply(f"Added explanation: {term} — {explanation}")
|
||||||
|
return
|
||||||
|
|
||||||
for room, password in config.items("rooms"):
|
match_detail = self.RE_DETAIL.fullmatch(args.raw)
|
||||||
if not password:
|
if match_detail:
|
||||||
password = None
|
terms = match_detail.group(1)
|
||||||
bot.join_room(room, password=password)
|
terms = [term for term in terms.split() if term]
|
||||||
|
if not terms: return
|
||||||
|
lines = await self._find_explanations(terms, detail=True)
|
||||||
|
await message.reply("\n".join(lines))
|
||||||
|
return
|
||||||
|
|
||||||
|
match_delete = self.RE_DELETE.fullmatch(args.raw)
|
||||||
|
if match_delete:
|
||||||
|
aid = match_delete.group(1)
|
||||||
|
await self.db.delete(aid)
|
||||||
|
logger.info((f"{message.sender.atmention} deleted explanation with"
|
||||||
|
" id {aid}"))
|
||||||
|
await message.reply(f"Deleted.")
|
||||||
|
return
|
||||||
|
|
||||||
|
match_replace = self.RE_REPLACE.fullmatch(args.raw)
|
||||||
|
if match_replace:
|
||||||
|
aid = match_replace.group(1)
|
||||||
|
explanation = match_replace.group(2).strip()
|
||||||
|
term = await self.db.get(aid)
|
||||||
|
if term is None:
|
||||||
|
await message.reply(f"No explanation with id {aid} exists.")
|
||||||
|
else:
|
||||||
|
await self.db.delete(aid)
|
||||||
|
logger.info((f"{message.sender.atmention} deleted explanation"
|
||||||
|
f" with id {aid}"))
|
||||||
|
await self.db.add(term, explanation, message.sender.nick)
|
||||||
|
logger.info((f"{message.sender.atmention} added explanation:"
|
||||||
|
f" {term} - {explanation}"))
|
||||||
|
await message.reply(f"Changed explanation: {term} — {explanation}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# else...
|
||||||
|
await message.reply("Incorrect command, see the !help for details.")
|
||||||
|
|
||||||
asyncio.get_event_loop().run_forever()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main("wtf.conf")
|
yaboli.enable_logging(level=logging.DEBUG)
|
||||||
|
yaboli.run(Wtf)
|
||||||
|
|
|
||||||
67
wtfdb.py
Normal file
67
wtfdb.py
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import asyncio
|
||||||
|
import configparser
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
import yaboli
|
||||||
|
from yaboli.util import *
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class WtfDB(yaboli.Database):
|
||||||
|
def initialize(self, db):
|
||||||
|
with db:
|
||||||
|
db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS acronyms (
|
||||||
|
acronym_id INTEGER PRIMARY KEY,
|
||||||
|
acronym TEXT NOT NULL,
|
||||||
|
explanation TEXT NOT NULL,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
deleted BOOLEAN NOT NULL DEFAULT 0
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
db.create_function("p_lower", 1, str.lower)
|
||||||
|
|
||||||
|
@yaboli.operation
|
||||||
|
def add(self, db, acronym, explanation, author):
|
||||||
|
with db:
|
||||||
|
db.execute("""
|
||||||
|
INSERT INTO acronyms (acronym, explanation, author)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
""", (acronym, explanation, author))
|
||||||
|
|
||||||
|
@yaboli.operation
|
||||||
|
def find(self, db, acronym):
|
||||||
|
c = db.execute("""
|
||||||
|
SELECT acronym, explanation FROM acronyms
|
||||||
|
WHERE NOT deleted AND p_lower(acronym) = ?
|
||||||
|
ORDER BY acronym_id ASC
|
||||||
|
""", (acronym.lower(),))
|
||||||
|
return c.fetchall()
|
||||||
|
|
||||||
|
@yaboli.operation
|
||||||
|
def find_full(self, db, acronym):
|
||||||
|
c = db.execute("""
|
||||||
|
SELECT acronym_id, acronym, explanation, author FROM acronyms
|
||||||
|
WHERE NOT deleted AND p_lower(acronym) = ?
|
||||||
|
ORDER BY acronym_id ASC
|
||||||
|
""", (acronym.lower(),))
|
||||||
|
return c.fetchall()
|
||||||
|
|
||||||
|
@yaboli.operation
|
||||||
|
def get(self, db, acronym_id):
|
||||||
|
c = db.execute("""
|
||||||
|
SELECT acronym FROM acronyms
|
||||||
|
WHERE NOT deleted AND acronym_id = ?
|
||||||
|
""", (acronym_id,))
|
||||||
|
res = c.fetchone()
|
||||||
|
return None if res is None else res[0]
|
||||||
|
|
||||||
|
@yaboli.operation
|
||||||
|
def delete(self, db, acronym_id):
|
||||||
|
with db:
|
||||||
|
db.execute("""
|
||||||
|
UPDATE acronyms
|
||||||
|
SET deleted = 1
|
||||||
|
WHERE acronym_id = ?
|
||||||
|
""", (acronym_id,))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue