def __init__(self, client: AsyncClient): """Setup the database Runs an initial setup or migrations depending on whether a database file has already been created Args: client: The matrix client """ # Check which type of database has been configured self.client = client self.conn = self._get_database_connection( CONFIG.database.type, CONFIG.database.connection_string) self.cursor = self.conn.cursor() self.db_type = CONFIG.database.type # Try to check the current migration version migration_level = 0 try: self._execute("SELECT version FROM migration_version") row = self.cursor.fetchone() migration_level = row[0] except Exception: self._initial_db_setup() finally: if migration_level < latest_migration_version: self._run_db_migrations(migration_level) # Load reminders from the db REMINDERS.update(self._load_reminders()) logger.info( f"Database initialization of type '{self.db_type}' complete")
async def _list_reminders(self): """Format and show known reminders for the current room Sends a message listing them in the following format: Reminders for this room: <start time>: <reminder text> [(every <recurring time>)] [(has alarm)] or if there are no reminders set: There are no reminders for this room. """ reminders = [] for reminder in REMINDERS.values(): # Filter out reminders that don't belong to this room if reminder.room_id != self.room.room_id: continue # If this is a cron-style reminder, just print the cron tab if reminder.cron_tab: line = f"`{reminder.cron_tab}`: {reminder.reminder_text}" reminders.append(line) continue # Format the start time into something readable human_readable_start_time = reminder.start_time.strftime("%b %d %Y, %H:%M") line = f"{human_readable_start_time}: {reminder.reminder_text}" # Display a recurring time if available if reminder.recurse_timedelta: # Get a nice, human-readable version to print line += f" (every {readabledelta(reminder.recurse_timedelta)})" # Note that an alarm exists if available if reminder.alarm: line += " (has alarm)" reminders.append(line) if reminders: text = "Reminders for this room:\n\n" text += "\n\n".join(reminders) else: text = "There are no reminders for this room." await send_text_to_room(self.client, self.room.room_id, text)
async def _silence(self): """Silences an ongoing alarm""" alarm_job = None # Attempt to find a reminder with an alarm currently going off reminder_text = " ".join(self.args) if reminder_text: # Find the alarm job via its reminder text alarm_job = ALARMS.get((self.room.room_id, reminder_text.upper())) if alarm_job: await self._remove_and_silence_alarm(alarm_job, reminder_text) text = f"Alarm '{reminder_text}' silenced." else: # We didn't find an alarm with that reminder text # # Be helpful and check if this is a known reminder without an alarm # currently going off reminder = REMINDERS.get((self.room.room_id, reminder_text.upper())) if reminder: text = ( f"The reminder '{reminder_text}' does not currently have an " f"alarm going off." ) else: # Nope, can't find it text = f"Unknown alarm or reminder '{reminder_text}'." else: # No reminder text provided. Check if there's a reminder currently firing # in the room instead then for alarm_info, job in ALARMS.items(): if alarm_info[0] == self.room.room_id: # Found one! reminder_text = alarm_info[ 1 ].capitalize() # normalize the text a bit await self._remove_and_silence_alarm(job, reminder_text) text = f"Alarm '{reminder_text}' silenced." # Prevent the `else` clause from being triggered break else: # If we didn't find any alarms... text = "No alarms are currently firing in this room." await send_text_to_room(self.client, self.room.room_id, text)
async def _delete_reminder(self): """Delete a reminder via its reminder text""" reminder_text = " ".join(self.args) if not reminder_text: raise CommandSyntaxError() logger.debug("Known reminders: %s", REMINDERS) logger.debug("Deleting reminder in room %s: %s", self.room.room_id, reminder_text) reminder = REMINDERS.get((self.room.room_id, reminder_text.upper())) if reminder: # Cancel the reminder and associated alarms reminder.cancel() text = "Reminder cancelled." else: text = f"Unknown reminder '{reminder_text}'." await send_text_to_room(self.client, self.room.room_id, text)
async def _list_reminders(self): """Format and show known reminders for the current room Sends a message listing them in the following format, using the alarm clock emoji ⏰ to indicate an alarm: 1️⃣ One-time Reminders * [⏰] <start time>: <reminder text> 📅 Cron Reminders * [⏰] m h d M wd (`m h d M wd`); next run in <rounded next time>; <reminder text> 🔁 Repeating Reminders * [⏰] every <recurring time>; next run in <rounded next time>; <reminder text> or if there are no reminders set: There are no reminders for this room. """ output = "" cron_reminder_lines = [] one_shot_reminder_lines = [] interval_reminder_lines = [] # Sort the reminder types for reminder in REMINDERS.values(): # Filter out reminders that don't belong to this room if reminder.room_id != self.room.room_id: continue # Organise alarms into markdown lists line = "- " if reminder.alarm: # Note that an alarm exists if available alarm_clock_emoji = "⏰" line += alarm_clock_emoji + " " # Print the duration before (next) execution next_execution = reminder.job.next_run_time next_execution = arrow.get(next_execution) # Cron-based reminders if isinstance(reminder.job.trigger, CronTrigger): # A human-readable cron tab, in addition to the actual tab line += f"{prettify_cron(reminder.cron_tab)} (`{reminder.cron_tab}`); next run {next_execution.humanize()}" # One-time reminders elif isinstance(reminder.job.trigger, DateTrigger): # Just print when the reminder will go off line += f"{next_execution.humanize()}" # Repeat reminders elif isinstance(reminder.job.trigger, IntervalTrigger): # Print the interval, and when it will next go off line += f"every {readabledelta(reminder.recurse_timedelta)}; next run {next_execution.humanize()}" # Add the reminder's text line += f'; *"{reminder.reminder_text}"*' # Output the status of each reminder. We divide up the reminders by type in order # to show them in separate sections, and display them differently if isinstance(reminder.job.trigger, CronTrigger): cron_reminder_lines.append(line) elif isinstance(reminder.job.trigger, DateTrigger): one_shot_reminder_lines.append(line) elif isinstance(reminder.job.trigger, IntervalTrigger): interval_reminder_lines.append(line) if (not one_shot_reminder_lines and not cron_reminder_lines and not interval_reminder_lines): await send_text_to_room( self.client, self.room.room_id, "*There are no reminders for this room.*", ) return if one_shot_reminder_lines: output += "\n\n" + "**1️⃣ One-time Reminders**" + "\n\n" output += "\n".join(one_shot_reminder_lines) if cron_reminder_lines: output += "\n\n" + "**📅 Cron Reminders**" + "\n\n" output += "\n".join(cron_reminder_lines) if interval_reminder_lines: output += "\n\n" + "**🔁 Repeating Reminders**" + "\n\n" output += "\n".join(interval_reminder_lines) await send_text_to_room(self.client, self.room.room_id, output)