Skip to content

Commit

Permalink
Fix habit tracker messages that reach the next day.
Browse files Browse the repository at this point in the history
  • Loading branch information
Nurdok committed Dec 21, 2024
1 parent 6ca4bf6 commit 5d05744
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 18 deletions.
6 changes: 5 additions & 1 deletion src/spanreed/apis/obsidian.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
from typing import Any


class ObsidianApiTimeoutError(TimeoutError):
pass


class ObsidianPlugin(Plugin):
@classmethod
def name(cls) -> str:
Expand Down Expand Up @@ -74,7 +78,7 @@ async def _send_request(
except TimeoutError:
# Delete the request from the queue
await redis_api.lrem(request_queue_name, 0, json.dumps(request))
raise TimeoutError(
raise ObsidianApiTimeoutError(
f"Obsidian API request timed out ({request_id=})."
)
self._logger.info(f"Got response: {response=}")
Expand Down
47 changes: 30 additions & 17 deletions src/spanreed/plugins/habit_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
UserInteractionPriority,
UserInteractionPreempted,
)
from spanreed.apis.obsidian import ObsidianApi
from spanreed.apis.obsidian import ObsidianApi, ObsidianApiTimeoutError
from spanreed.plugins.spanreed_monitor import suppress_and_log_exception


Expand All @@ -35,6 +35,16 @@ def __post_init__(self) -> None:
self.habits[index] = Habit(**habit)


def time_until_end_of_day() -> datetime.timedelta:
"""
Get timedelta until end of day on the datetime passed, or current time.
"""
now = datetime.datetime.now()
tomorrow = now + datetime.timedelta(days=1)
return datetime.datetime.combine(tomorrow, datetime.time.min) - now



class HabitTrackerPlugin(Plugin):

async def run(self) -> None:
Expand Down Expand Up @@ -143,10 +153,11 @@ async def get_done_habits(self, user: User) -> list[str]:

async def get_habits_to_poll(self, user: User) -> list[Habit]:
config: UserConfig = await self.get_config(user)
done_habits: list[str] = await self.get_done_habits(user)
return [
habit
for habit in config.habits
if habit.name not in await self.get_done_habits(user)
if habit.name not in done_habits
]

async def mark_habit_as_done(self, user: User, habit_name: str) -> None:
Expand All @@ -162,7 +173,7 @@ async def poll_user_for_all_habits(self, user: User) -> None:
bot: TelegramBotApi = await TelegramBotApi.for_user(user)

self._logger.info(f"Running periodic check for user {user}")
async with suppress_and_log_exception(TimeoutError):
async with suppress_and_log_exception(ObsidianApiTimeoutError):
habits: list[Habit] = await self.get_habits_to_poll(user)
if not habits:
return
Expand All @@ -187,17 +198,19 @@ async def run_for_user(self, user: User) -> None:

while True:
self._logger.info(f"Polling user {user}")
try:
async with bot.user_interaction(
priority=UserInteractionPriority.LOW,
propagate_preemption=True,
):
self._logger.info("Got user interaction lock")
await self.poll_user_for_all_habits(user)
except UserInteractionPreempted:
self._logger.info("User interaction preempted, trying again")
else:
self._logger.info("Sleeping for 4 hours")
await asyncio.sleep(
datetime.timedelta(hours=4).total_seconds()
)
with suppress(TimeoutError):
async with asyncio.timeout(time_until_end_of_day().total_seconds()):
try:
async with bot.user_interaction(
priority=UserInteractionPriority.LOW,
propagate_preemption=True,
):
self._logger.info("Got user interaction lock")
await self.poll_user_for_all_habits(user)
except UserInteractionPreempted:
self._logger.info("User interaction preempted, trying again")
else:
self._logger.info("Sleeping for 4 hours")
await asyncio.sleep(
datetime.timedelta(hours=4).total_seconds()
)

0 comments on commit 5d05744

Please sign in to comment.