Make bot less vulnerable
The bot still stores any number of explanations for a given word, and prints all of them when using !wtf detail <word>. This means that it is still vulnerable to some types of attacks. Maybe I'll fix that sometime in the future.
This commit is contained in:
parent
f80a8e0b7c
commit
7d7c5a0c5f
2 changed files with 57 additions and 18 deletions
65
wtf.py
65
wtf.py
|
|
@ -12,6 +12,11 @@ else:
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Wtf(yaboli.Module):
|
class Wtf(yaboli.Module):
|
||||||
|
MAX_TERMS = 5
|
||||||
|
MAX_TERM_LENGTH = 1024
|
||||||
|
MAX_EXPLANATIONS = 15
|
||||||
|
MAX_EXPLANATION_LENGTH = 1024
|
||||||
|
|
||||||
DESCRIPTION = ("a database of explanations for words, acronyms and"
|
DESCRIPTION = ("a database of explanations for words, acronyms and"
|
||||||
" initialisms")
|
" initialisms")
|
||||||
HELP_GENERAL = DESCRIPTION
|
HELP_GENERAL = DESCRIPTION
|
||||||
|
|
@ -53,49 +58,75 @@ class Wtf(yaboli.Module):
|
||||||
|
|
||||||
self.register_general("wtf", self.cmd_wtf)
|
self.register_general("wtf", self.cmd_wtf)
|
||||||
|
|
||||||
@staticmethod
|
def _format_explanations(self, 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 {a})" for i, t, e, a in explanations]
|
explanations = [f"{i}: {t} — {e} (by {a})" for i, t, e, a in explanations]
|
||||||
else:
|
else:
|
||||||
return [f"{t} — {e}" for _, t, e, _ in explanations]
|
explanations = [f"{t} — {e}" for _, t, e, _ in explanations]
|
||||||
|
|
||||||
|
if len(explanations) > self.MAX_EXPLANATIONS:
|
||||||
|
message = ("Some explanations were omitted because this bot only"
|
||||||
|
f" displays {self.MAX_EXPLANATIONS} explanations per"
|
||||||
|
" term.")
|
||||||
|
explanations = explanations[:-1] + [message]
|
||||||
|
|
||||||
|
return explanations
|
||||||
|
|
||||||
async def _find_explanations(self, terms, detail=False):
|
async def _find_explanations(self, terms, detail=False):
|
||||||
lines = []
|
lines = []
|
||||||
for term in terms:
|
for term in terms:
|
||||||
explanations = await self.db.find_full(term)
|
explanations = await self.db.find_full(term, self.MAX_EXPLANATIONS + 1)
|
||||||
if explanations:
|
if explanations:
|
||||||
lines.extend(self._format_explanations(explanations, detail=detail))
|
lines.extend(self._format_explanations(explanations, detail=detail))
|
||||||
else:
|
else:
|
||||||
lines.append(f"{term!r} not found.")
|
lines.append(f"{term!r} not found.")
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
async def send_explanations(self, message, termstr):
|
||||||
|
terms = [term for term in termstr.split() if term]
|
||||||
|
terms = terms[:self.MAX_TERMS]
|
||||||
|
|
||||||
|
if not terms: return
|
||||||
|
|
||||||
|
if max(map(len, terms)) > self.MAX_TERM_LENGTH:
|
||||||
|
await message.reply(("A term can be at most"
|
||||||
|
f" {self.MAX_TERM_LENGTH} characters long."))
|
||||||
|
return
|
||||||
|
|
||||||
|
lines = await self._find_explanations(terms)
|
||||||
|
await message.reply("\n".join(lines))
|
||||||
|
return
|
||||||
|
|
||||||
async def on_send(self, room, message):
|
async def on_send(self, room, message):
|
||||||
await super().on_send(room, message)
|
await super().on_send(room, message)
|
||||||
|
|
||||||
match = self.RE_WTF_IS.fullmatch(message.content)
|
match = self.RE_WTF_IS.fullmatch(message.content)
|
||||||
if match:
|
if match:
|
||||||
terms = match.group(1)
|
terms = match.group(1)
|
||||||
terms = [term for term in terms.split() if term]
|
await self.send_explanations(message, terms)
|
||||||
if not terms: return
|
|
||||||
lines = await self._find_explanations(terms)
|
|
||||||
await message.reply("\n".join(lines))
|
|
||||||
|
|
||||||
async def cmd_wtf(self, room, message, args):
|
async def cmd_wtf(self, room, message, args):
|
||||||
match_is = self.RE_IS.fullmatch(args.raw)
|
match_is = self.RE_IS.fullmatch(args.raw)
|
||||||
if match_is:
|
if match_is:
|
||||||
terms = match_is.group(1)
|
terms = match_is.group(1)
|
||||||
terms = [term for term in terms.split() if term]
|
await self.send_explanations(message, terms)
|
||||||
if not terms: return
|
|
||||||
lines = await self._find_explanations(terms)
|
|
||||||
await message.reply("\n".join(lines))
|
|
||||||
return
|
|
||||||
|
|
||||||
match_add = self.RE_ADD.fullmatch(args.raw)
|
match_add = self.RE_ADD.fullmatch(args.raw)
|
||||||
if match_add:
|
if match_add:
|
||||||
term = match_add.group(1)
|
term = match_add.group(1)
|
||||||
explanation = match_add.group(2).strip()
|
explanation = match_add.group(2).strip()
|
||||||
|
|
||||||
|
if len(term) > self.MAX_TERM_LENGTH:
|
||||||
|
await message.reply(("A term can be at most"
|
||||||
|
f" {self.MAX_TERM_LENGTH} characters long."))
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(explanation) > self.MAX_EXPLANATION_LENGTH:
|
||||||
|
await message.reply(("An explanation can be at most"
|
||||||
|
f" {self.MAX_EXPLANATION_LENGTH} characters long."))
|
||||||
|
return
|
||||||
|
|
||||||
await self.db.add(term, explanation, message.sender.nick)
|
await self.db.add(term, explanation, message.sender.nick)
|
||||||
logger.info((f"{message.sender.atmention} added explanation:"
|
logger.info((f"{message.sender.atmention} added explanation:"
|
||||||
f" {term} - {explanation}"))
|
f" {term} - {explanation}"))
|
||||||
|
|
@ -124,6 +155,12 @@ class Wtf(yaboli.Module):
|
||||||
if match_replace:
|
if match_replace:
|
||||||
aid = match_replace.group(1)
|
aid = match_replace.group(1)
|
||||||
explanation = match_replace.group(2).strip()
|
explanation = match_replace.group(2).strip()
|
||||||
|
|
||||||
|
if len(explanation) > self.MAX_EXPLANATION_LENGTH:
|
||||||
|
await message.reply(("An explanation can be at most"
|
||||||
|
f" {self.MAX_EXPLANATION_LENGTH} characters long."))
|
||||||
|
return
|
||||||
|
|
||||||
term = await self.db.get(aid)
|
term = await self.db.get(aid)
|
||||||
if term is None:
|
if term is None:
|
||||||
await message.reply(f"No explanation with id {aid} exists.")
|
await message.reply(f"No explanation with id {aid} exists.")
|
||||||
|
|
|
||||||
10
wtfdb.py
10
wtfdb.py
|
|
@ -31,21 +31,23 @@ class WtfDB(yaboli.Database):
|
||||||
""", (acronym, explanation, author))
|
""", (acronym, explanation, author))
|
||||||
|
|
||||||
@yaboli.operation
|
@yaboli.operation
|
||||||
def find(self, db, acronym):
|
def find(self, db, acronym, limit):
|
||||||
c = db.execute("""
|
c = db.execute("""
|
||||||
SELECT acronym, explanation FROM acronyms
|
SELECT acronym, explanation FROM acronyms
|
||||||
WHERE NOT deleted AND p_lower(acronym) = ?
|
WHERE NOT deleted AND p_lower(acronym) = ?
|
||||||
ORDER BY acronym_id ASC
|
ORDER BY acronym_id ASC
|
||||||
""", (acronym.lower(),))
|
LIMIT ?
|
||||||
|
""", (acronym.lower(), limit))
|
||||||
return c.fetchall()
|
return c.fetchall()
|
||||||
|
|
||||||
@yaboli.operation
|
@yaboli.operation
|
||||||
def find_full(self, db, acronym):
|
def find_full(self, db, acronym, limit):
|
||||||
c = db.execute("""
|
c = db.execute("""
|
||||||
SELECT acronym_id, acronym, explanation, author FROM acronyms
|
SELECT acronym_id, acronym, explanation, author FROM acronyms
|
||||||
WHERE NOT deleted AND p_lower(acronym) = ?
|
WHERE NOT deleted AND p_lower(acronym) = ?
|
||||||
ORDER BY acronym_id ASC
|
ORDER BY acronym_id ASC
|
||||||
""", (acronym.lower(),))
|
LIMIT ?
|
||||||
|
""", (acronym.lower(), limit))
|
||||||
return c.fetchall()
|
return c.fetchall()
|
||||||
|
|
||||||
@yaboli.operation
|
@yaboli.operation
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue