Compare commits

..

2 commits

Author SHA1 Message Date
Adrien Abraham 1ee34987ac reminder: save reminders in storage 2023-10-10 15:14:48 +02:00
Adrien Abraham 4b2916fb50 config: fix JSON error 2023-10-10 15:14:33 +02:00
2 changed files with 95 additions and 9 deletions

View file

@ -69,7 +69,7 @@
"commands": ["doupsland"] "commands": ["doupsland"]
}, },
"gpt3": { "gpt3": {
"openai_key": "" "openai_key": "",
"join_lines": "; ", "join_lines": "; ",
"computing_replies": ["Hmm…"] "computing_replies": ["Hmm…"]
}, },

View file

@ -1,6 +1,7 @@
import datetime import datetime
import re import re
import string import string
import uuid
from typing import Optional from typing import Optional
from apscheduler.triggers.date import DateTrigger from apscheduler.triggers.date import DateTrigger
@ -9,13 +10,52 @@ from edmond.plugin import Plugin
class ReminderPlugin(Plugin): class ReminderPlugin(Plugin):
"""Reminders using an async scheduler.""" """Reminders using an async scheduler.
Reminders can be set in a given amount of time or to a given hour. They are
kept between restarts.
"""
REQUIRED_CONFIGS = [ REQUIRED_CONFIGS = [
"commands", "at_word", "in_word", "day_letter", "hour_letter", "commands", "at_word", "in_word", "day_letter", "hour_letter",
"minute_letter", "second_letter", "reminder_format", "done" "minute_letter", "second_letter", "reminder_format", "done"
] ]
def on_join(self, event):
"""Reschedule undelivered reminders."""
nick = event.source.nick
if nick != self.bot.nick:
return
joined_channel = event.target
reminder_format = self.config["reminder_format"]
saved_reminders = self.get_storage_value("reminders", [])
self.set_storage_value("reminders", [])
for reminder in saved_reminders:
# Ignore reminders for other targets than this channel.
if reminder["target"] != joined_channel:
continue
when = datetime.datetime.fromisoformat(reminder["when"])
# If the timer passed, deliver it now and forget it.
if when <= datetime.datetime.now():
message = reminder_format.format(
username=reminder["sender"],
reminder=reminder["message"]
)
self.bot.say(reminder["target"], message)
continue
# Else schedule it properly (but skip storage backup, we're on it).
self.schedule(
when,
reminder["message"],
reminder["sender"],
reminder["target"],
)
def on_pubmsg(self, event): def on_pubmsg(self, event):
if not self.should_handle_command(event.arguments[0]): if not self.should_handle_command(event.arguments[0]):
return False return False
@ -43,7 +83,6 @@ class ReminderPlugin(Plugin):
reminder = " ".join(words[2:]) reminder = " ".join(words[2:])
self.setup_reminder(mode, time, reminder, sender, target) self.setup_reminder(mode, time, reminder, sender, target)
return True return True
# self.bot.scheduler.add_job(, 'cron', hour='1', minute='52')
def parse_time(self, time: str) -> Optional[dict[str, int]]: def parse_time(self, time: str) -> Optional[dict[str, int]]:
"""Parse a time request string. """Parse a time request string.
@ -72,11 +111,20 @@ class ReminderPlugin(Plugin):
self, self,
mode: str, mode: str,
time: dict[str, int], time: dict[str, int],
reminder: str, message: str,
sender: str, sender: str,
target: str target: str
) -> None: ) -> None:
"""Remind something at a given time.""" """Remind something at a given time.
There are two possible modes, "at" for a given time and "in" for a
given delay. In both cases, it should result to a datetime in the
future, or it will be discarded.
Reminders are saved in the bot's storage to prevent losing reminders
between restarts. Only when a reminder is delivered it is removed from
the storage.
"""
now = datetime.datetime.now() now = datetime.datetime.now()
if mode == self.config["at_word"]: if mode == self.config["at_word"]:
if "day" in time: # "day" is not supported in at mode. if "day" in time: # "day" is not supported in at mode.
@ -100,15 +148,53 @@ class ReminderPlugin(Plugin):
self.signal_failure(target) self.signal_failure(target)
return return
self.schedule(when, message, sender, target)
self.bot.say(target, self.config["done"])
def schedule(
self,
when: datetime.datetime,
message: str,
sender: str,
target: str,
) -> None:
"""Schedule the reminder to be delivered at the given datetime."""
identifier = str(uuid.uuid4())
# Store the reminder in case the bot shuts down before delivery.
reminder = {
"id": identifier,
"when": when.isoformat(),
"message": message,
"sender": sender,
"target": target,
}
self.append_storage_list_value("reminders", reminder)
self.bot.scheduler.add_job( self.bot.scheduler.add_job(
self.remind, self.remind,
trigger=DateTrigger(when), trigger=DateTrigger(when),
args=(reminder, sender, target) args=(identifier, message, sender, target)
) )
self.bot.log_d(f"Scheduled for {when}, time was {time}.") self.bot.log_d(f"Scheduled {identifier} for {when}.")
self.bot.say(target, self.config["done"])
async def remind(self, reminder: str, username: str, target: str) -> None: async def remind(
self,
identifier: str,
reminder: str,
username: str,
target: str
) -> None:
self.bot.log_d(
f"Delivering reminder {identifier} in {target} for {username}"
)
reminder_format = self.config["reminder_format"] reminder_format = self.config["reminder_format"]
message = reminder_format.format(username=username, reminder=reminder) message = reminder_format.format(username=username, reminder=reminder)
self.bot.say(target, message) self.bot.say(target, message)
# Remove the reminder from the saved reminder list.
saved_reminders = self.get_storage_value("reminders", [])
for reminder in saved_reminders.copy():
if reminder["id"] == identifier:
saved_reminders.remove(reminder)
break
self.set_storage_value("reminders", saved_reminders)