fix (tasklist): Various bugfixes.

Fix issue where empty tasklist would raise ZeroDivisionError.
Fix issue where empty tasklist UI would error.
Fix issue where >4000 char tasklists would error on bulk edit.
Fix `/tasks edit` not responding to interaction.
Fix task acmpl going over 100 chars.
Fix empty tasklist text and UI layout.
Add `/tasks upload` for editing long tasklist.
Small UI tweaks.
Make `LionContext.error_reply` always ephemeral if possible.
This commit is contained in:
2023-07-07 22:53:34 +03:00
parent 6d6a3346e5
commit c6f462d131
4 changed files with 263 additions and 104 deletions

View File

@@ -34,6 +34,7 @@ class Tasklist:
label_range_re = re.compile(
r"^(?P<start>(\d+\.)*\d+)\.?((\s*(?P<range>-)\s*)(?P<end>(\d+\.)*\d*\.?))?$"
)
line_regex = re.compile(r"(?P<depth>\s*)-?\s*(\[\s*(?P<check>[^]]?)\s*\]\s*)?(?P<content>.*)")
def __init__(self, bot: LionBot, data: TasklistData, userid: int):
self.bot = bot
@@ -273,3 +274,89 @@ class Tasklist:
)).format(range=split)
)
return list(taskids)
def flatten(self):
"""
Flatten the tasklist to a map of readable strings parseable by `parse_tasklist`.
"""
labelled = self.labelled
lines = {}
total_len = 0
for label, task in labelled.items():
prefix = ' ' * (len(label) - 1)
box = '- [ ]' if task.completed_at is None else '- [x]'
line = f"{prefix}{box} {task.content}"
if total_len + len(line) > 4000:
break
lines[task.taskid] = line
total_len += len(line)
return lines
def parse_tasklist(self, task_lines):
t = self.bot.translator.t
taskinfo = [] # (parent, truedepth, ticked, content)
depthtree = [] # (depth, index)
for line in task_lines:
match = self.line_regex.match(line)
if not match:
raise UserInputError(
t(_p(
'modal:tasklist_bulk_editor|error:parse_task',
"Malformed taskline!\n`{input}`"
)).format(input=line)
)
depth = len(match['depth'])
check = bool(match['check'])
content = match['content']
if not content:
continue
if len(content) > 100:
raise UserInputError(
t(_p(
'modal:tasklist_bulk_editor|error:task_too_long',
"Please keep your tasks under 100 characters!"
))
)
for i in range(len(depthtree)):
lastdepth = depthtree[-1][0]
if lastdepth >= depth:
depthtree.pop()
if lastdepth <= depth:
break
parent = depthtree[-1][1] if depthtree else None
depthtree.append((depth, len(taskinfo)))
taskinfo.append((parent, len(depthtree) - 1, check, content))
print(taskinfo)
return taskinfo
async def write_taskinfo(self, taskinfo):
"""
Create tasks from `taskinfo` (matching the output of `parse_tasklist`).
"""
now = utc_now()
created = {}
target_depth = 0
while True:
to_insert = {}
for i, (parent, truedepth, ticked, content) in enumerate(taskinfo):
if truedepth == target_depth:
to_insert[i] = (
self.userid,
content,
created[parent] if parent is not None else None,
now if ticked else None
)
if to_insert:
# Batch insert
tasks = await self.data.Task.table.insert_many(
('userid', 'content', 'parentid', 'completed_at'),
*to_insert.values()
)
for i, task in zip(to_insert.keys(), tasks):
created[i] = task['taskid']
target_depth += 1
else:
# Reached maximum depth
break