From 86a5dd19b547f2f8a78e56299d40af49d81b142a Mon Sep 17 00:00:00 2001 From: Finn Christiansen Date: Sun, 16 Jun 2024 21:44:14 +0200 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=90=9B=20Fix=20database=20connection?= =?UTF-8?q?=20for=20container=20by=20exposing=20environment=20variable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/compose.yml b/compose.yml index e2e4fbb..c4e98b5 100644 --- a/compose.yml +++ b/compose.yml @@ -8,5 +8,6 @@ services: - BOT_HOMESERVER - BOT_USERNAME - BOT_PASSWORD + - DB_URI volumes: - ./data/:/bot/data From 1f1ae5077edff44c184aed59bf34b2d193ca2eaa Mon Sep 17 00:00:00 2001 From: Finn Christiansen Date: Sun, 16 Jun 2024 22:45:24 +0200 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20Added=20periodic=20reminders=20?= =?UTF-8?q?and=20included=20alembic=20migration=20in=20container?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 1 + Dockerfile | 2 +- alembic/env.py | 2 ++ .../c2bcd940c9e8_add_current_reminder_date.py | 30 ++++++++++++++++++ entrypoint.sh | 3 ++ matrix_bot_praying_times/__main__.py | 31 +++++++++++++------ matrix_bot_praying_times/models/user.py | 3 +- 7 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 alembic/versions/c2bcd940c9e8_add_current_reminder_date.py create mode 100755 entrypoint.sh diff --git a/.dockerignore b/.dockerignore index a7ff473..1a3a766 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ .env bin lib +include diff --git a/Dockerfile b/Dockerfile index ce0950f..f812f0d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,4 +10,4 @@ RUN pip3 install -r requirements.txt COPY . . -CMD ["python", "-m", "matrix_bot_praying_times"] +ENTRYPOINT ["/bot/entrypoint.sh"] diff --git a/alembic/env.py b/alembic/env.py index 0d79cb1..f2ba3a7 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -6,6 +6,8 @@ from sqlalchemy import pool from alembic import context +from dotenv import load_dotenv +load_dotenv() # this is the Alembic Config object, which provides # access to the values within the .ini file in use. diff --git a/alembic/versions/c2bcd940c9e8_add_current_reminder_date.py b/alembic/versions/c2bcd940c9e8_add_current_reminder_date.py new file mode 100644 index 0000000..a8db828 --- /dev/null +++ b/alembic/versions/c2bcd940c9e8_add_current_reminder_date.py @@ -0,0 +1,30 @@ +"""Add current reminder date + +Revision ID: c2bcd940c9e8 +Revises: 12669d9a145b +Create Date: 2024-06-16 22:21:27.188128 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'c2bcd940c9e8' +down_revision: Union[str, None] = '12669d9a145b' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('user', sa.Column('current_reminder_date', sa.Date(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('user', 'current_reminder_date') + # ### end Alembic commands ### diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..d9e2035 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/sh +alembic upgrade head +python -m matrix_bot_praying_times diff --git a/matrix_bot_praying_times/__main__.py b/matrix_bot_praying_times/__main__.py index 361253d..52b37a9 100644 --- a/matrix_bot_praying_times/__main__.py +++ b/matrix_bot_praying_times/__main__.py @@ -30,6 +30,13 @@ Session = sessionmaker(bind=engine) session = Session() +@bot.listener.on_reaction_event +def initial_schedule() -> None: + users = session.query(User).filter(User.location != "").all() + for user in users: + schedule_reminder(user.username) + + @bot.listener.on_message_event async def echo(room, message) -> None: @@ -134,20 +141,26 @@ def get_praying_times(date: datetime.date, username) -> Dict[str, str]: def schedule_reminder(username) -> None: now = datetime.datetime.now(datetime.UTC) user = session.query(User).filter_by(username=username).one_or_none() - # as a workaround until it's finished schedule the next 7 days - for i in range(7): - times = get_praying_times(datetime.date.today() + datetime.timedelta(days=i), username) - for prayer, time in times.items(): - praying_time = datetime.datetime.fromisoformat(time) - if praying_time > now: - seconds = int((praying_time - now).total_seconds()) - message = "{} is at {}".format(prayer, praying_time.strftime("%H:%M")) - asyncio.ensure_future(remind(username, message, seconds - user.reminder_time_in_minues * 60)) + user.current_reminder_date = datetime.date.today() + session.add(user) + session.commit() + times = get_praying_times(datetime.date.today(), username) + for prayer, time in times.items(): + praying_time = datetime.datetime.fromisoformat(time) + if praying_time > now: + # schedule praying times but skip the ones which already have been passed today + seconds = int((praying_time - now).total_seconds()) + message = "{} is at {}".format(prayer, praying_time.strftime("%H:%M")) + asyncio.ensure_future(remind(username, message, seconds - user.reminder_time_in_minues * 60)) + # save date of last schedule for user async def remind(username, message, seconds) -> None: await asyncio.sleep(seconds) user = session.query(User).filter_by(username=username).one_or_none() + # check if a new reminder has to be scheduled + if user.current_reminder_date < datetime.date.today(): + schedule_reminder(user.username) await bot.api.send_markdown_message(user.room_id, message) diff --git a/matrix_bot_praying_times/models/user.py b/matrix_bot_praying_times/models/user.py index a7acfdd..fc1fdcc 100644 --- a/matrix_bot_praying_times/models/user.py +++ b/matrix_bot_praying_times/models/user.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, String, Integer +from sqlalchemy import Column, String, Integer, Date from ..db import Base @@ -11,3 +11,4 @@ class User(Base): location = Column(String) room_id = Column(String) reminder_time_in_minutes = Column(Integer) + current_reminder_date = Column(Date) From f25cd833f8aa399f179f797d9ece3c198c6a45d1 Mon Sep 17 00:00:00 2001 From: Finn Christiansen Date: Mon, 17 Jun 2024 07:37:14 +0200 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9B=20Fix=20typo=20which=20causes?= =?UTF-8?q?=20crash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- matrix_bot_praying_times/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix_bot_praying_times/__main__.py b/matrix_bot_praying_times/__main__.py index 52b37a9..6b3e87c 100644 --- a/matrix_bot_praying_times/__main__.py +++ b/matrix_bot_praying_times/__main__.py @@ -120,7 +120,7 @@ async def set_reminder(room, message) -> None: response = "You did not set your location yet." await bot.api.send_markdown_message(room.room_id, response) else: - user.reminder_time_in_minues = minutes + user.reminder_time_in_minutes = minutes user.room_id = room.room_id response = "Your reminder was set to {} minutes before praying.".format(minutes) schedule_reminder(username) @@ -151,7 +151,7 @@ def schedule_reminder(username) -> None: # schedule praying times but skip the ones which already have been passed today seconds = int((praying_time - now).total_seconds()) message = "{} is at {}".format(prayer, praying_time.strftime("%H:%M")) - asyncio.ensure_future(remind(username, message, seconds - user.reminder_time_in_minues * 60)) + asyncio.ensure_future(remind(username, message, seconds - user.reminder_time_in_minutes * 60)) # save date of last schedule for user From f5f44852d3bc081fc87f5d61054435d598e3d678 Mon Sep 17 00:00:00 2001 From: Finn Christiansen Date: Mon, 17 Jun 2024 21:32:59 +0200 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=90=9B=20Fixing=20initial=20schedulin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- matrix_bot_praying_times/__main__.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/matrix_bot_praying_times/__main__.py b/matrix_bot_praying_times/__main__.py index 6b3e87c..017e00d 100644 --- a/matrix_bot_praying_times/__main__.py +++ b/matrix_bot_praying_times/__main__.py @@ -5,6 +5,7 @@ import asyncio import pytz from typing import Dict import os +import logging from dotenv import load_dotenv from .models.user import User @@ -29,15 +30,22 @@ engine = create_engine(os.getenv("DB_URI")) Session = sessionmaker(bind=engine) session = Session() +logging.basicConfig(level=logging.INFO) -@bot.listener.on_reaction_event -def initial_schedule() -> None: - users = session.query(User).filter(User.location != "").all() + +@bot.listener.on_startup +async def initial_schedule(room_id): + logging.info("starting praying times bot") + users = session.query(User).filter(User.location != "", User.reminder_time_in_minutes is not None).all() for user in users: + logging.info("scheduling initial reminder for {}".format(user.username)) + # reset last reminder schedule time + user.current_reminder_date = None + session.add(user) + session.commit() schedule_reminder(user.username) - @bot.listener.on_message_event async def echo(room, message) -> None: match = botlib.MessageMatch(room, message, bot, PREFIX) @@ -79,7 +87,7 @@ async def usage(room, message) -> None: - **!set-reminder** where X is the number of minutes you want to receive the reminder before praying """ - if match.is_not_from_this_bot() and not match.prefix(): + if match.is_not_from_this_bot() and not match.prefix() and room.room_id: await bot.api.send_markdown_message(room.room_id, response) @@ -122,6 +130,8 @@ async def set_reminder(room, message) -> None: else: user.reminder_time_in_minutes = minutes user.room_id = room.room_id + session.add(user) + session.commit() response = "Your reminder was set to {} minutes before praying.".format(minutes) schedule_reminder(username) await bot.api.send_markdown_message(room.room_id, response) @@ -139,6 +149,7 @@ def get_praying_times(date: datetime.date, username) -> Dict[str, str]: def schedule_reminder(username) -> None: + logging.info("scheduling reminder for {}".format(username)) now = datetime.datetime.now(datetime.UTC) user = session.query(User).filter_by(username=username).one_or_none() user.current_reminder_date = datetime.date.today() @@ -151,6 +162,7 @@ def schedule_reminder(username) -> None: # schedule praying times but skip the ones which already have been passed today seconds = int((praying_time - now).total_seconds()) message = "{} is at {}".format(prayer, praying_time.strftime("%H:%M")) + logging.info("scheduling reminder for {} with message: {}".format(username, message)) asyncio.ensure_future(remind(username, message, seconds - user.reminder_time_in_minutes * 60)) # save date of last schedule for user