Skip to content

Commit 4f5a745

Browse files
author
0xMett
committed
feat: re-schedule welcome message deletion on bot restart
On startup, load persisted welcome messages from the database and re-schedule their deletion jobs so messages are cleaned up even after a bot restart.
1 parent 780e92e commit 4f5a745

5 files changed

Lines changed: 90 additions & 1 deletion

File tree

src/python_italy_bot/db/base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Abstract repository interface for persistence."""
22

33
from abc import ABC, abstractmethod
4+
from datetime import datetime
45

56
from .models import Chat, KnownUser
67

@@ -421,6 +422,13 @@ async def get_welcome_message_user(
421422
"""Get the user_id associated with a welcome message, or None."""
422423
...
423424

425+
@abstractmethod
426+
async def get_all_welcome_messages(
427+
self,
428+
) -> list[tuple[int, int, int, datetime]]:
429+
"""Return all tracked welcome messages as (chat_id, message_id, user_id, created_at)."""
430+
...
431+
424432
@abstractmethod
425433
async def delete_welcome_message(self, chat_id: int, message_id: int) -> None:
426434
"""Remove a welcome message mapping."""

src/python_italy_bot/db/in_memory.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,5 +239,14 @@ async def get_welcome_message_user(
239239
) -> int | None:
240240
return self._welcome_message_map.get((chat_id, message_id))
241241

242+
async def get_all_welcome_messages(
243+
self,
244+
) -> list[tuple[int, int, int, datetime]]:
245+
now = datetime.now(timezone.utc)
246+
return [
247+
(chat_id, message_id, user_id, now)
248+
for (chat_id, message_id), user_id in self._welcome_message_map.items()
249+
]
250+
242251
async def delete_welcome_message(self, chat_id: int, message_id: int) -> None:
243252
self._welcome_message_map.pop((chat_id, message_id), None)

src/python_italy_bot/db/postgres.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""PostgreSQL implementation of the repository using psycopg async pool."""
22

3+
from datetime import datetime
4+
35
from psycopg_pool import AsyncConnectionPool
46

57
from .base import AsyncRepository
@@ -427,6 +429,18 @@ async def get_welcome_message_user(
427429
row = await cur.fetchone()
428430
return row[0] if row else None
429431

432+
async def get_all_welcome_messages(
433+
self,
434+
) -> list[tuple[int, int, int, datetime]]:
435+
async with self._pool.connection() as conn:
436+
async with conn.cursor() as cur:
437+
await cur.execute(
438+
"SELECT chat_id, message_id, user_id, created_at "
439+
"FROM welcome_messages ORDER BY created_at"
440+
)
441+
rows = await cur.fetchall()
442+
return [(r[0], r[1], r[2], r[3]) for r in rows]
443+
430444
async def delete_welcome_message(self, chat_id: int, message_id: int) -> None:
431445
async with self._pool.connection() as conn:
432446
await conn.execute(

src/python_italy_bot/main.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Entry point for the Python Italy Telegram Bot."""
22

33
import logging
4+
from datetime import datetime, timezone
45

56
from telegram.ext import ApplicationBuilder
67

@@ -11,9 +12,14 @@
1112
from .handlers.moderation import create_moderation_handlers
1213
from .handlers.ping import create_ping_handlers
1314
from .handlers.settings import create_settings_handlers
15+
1416
# from .handlers.spam import create_spam_handler
1517
from .handlers.utils import create_user_tracking_handler
16-
from .handlers.welcome import create_welcome_handlers
18+
from .handlers.welcome import (
19+
DEFAULT_WELCOME_DELAY_MINUTES,
20+
_delete_welcome_message,
21+
create_welcome_handlers,
22+
)
1723
from .services.captcha import CaptchaService
1824
from .services.moderation import ModerationService
1925
# from .services.spam_detector import SpamDetector
@@ -61,6 +67,51 @@ async def _post_init(application) -> None:
6167
application.add_handler(handler)
6268
# application.add_handler(create_spam_handler(spam_detector))
6369

70+
# Re-schedule deletion for welcome messages persisted before a restart
71+
await _reschedule_welcome_deletions(application, captcha_service)
72+
73+
74+
async def _reschedule_welcome_deletions(
75+
application, captcha_service: CaptchaService
76+
) -> None:
77+
"""Re-schedule deletion jobs for welcome messages that survived a bot restart."""
78+
job_queue = application.job_queue
79+
if job_queue is None:
80+
return
81+
82+
messages = await captcha_service.get_all_welcome_messages()
83+
if not messages:
84+
return
85+
86+
now = datetime.now(timezone.utc)
87+
welcome_map = application.bot_data.setdefault("welcome_message_map", {})
88+
scheduled = 0
89+
90+
for chat_id, message_id, user_id, created_at in messages:
91+
# Restore in-memory mapping for ban-by-reply
92+
welcome_map[(chat_id, message_id)] = user_id
93+
94+
delay_minutes = await captcha_service.get_welcome_delay(chat_id)
95+
if delay_minutes is None:
96+
delay_minutes = DEFAULT_WELCOME_DELAY_MINUTES
97+
if delay_minutes <= 0:
98+
continue
99+
100+
# Calculate remaining time; delete immediately if overdue
101+
elapsed = (now - created_at).total_seconds()
102+
remaining = max(delay_minutes * 60 - elapsed, 0)
103+
104+
job_queue.run_once(
105+
_delete_welcome_message,
106+
when=remaining,
107+
data=(chat_id, message_id),
108+
name=f"del_welcome_{chat_id}_{message_id}",
109+
)
110+
scheduled += 1
111+
112+
if scheduled:
113+
logger.info("Re-scheduled deletion for %d welcome message(s)", scheduled)
114+
64115

65116
async def _post_shutdown(application) -> None:
66117
"""Clean up resources on shutdown."""

src/python_italy_bot/services/captcha.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Captcha verification logic (file + secret command flow)."""
22

33
import re
4+
from datetime import datetime
45
from pathlib import Path
56

67
from telegram import (
@@ -204,3 +205,9 @@ async def get_welcome_message_user(
204205
async def delete_welcome_message(self, chat_id: int, message_id: int) -> None:
205206
"""Remove a welcome message mapping."""
206207
await self._repo.delete_welcome_message(chat_id, message_id)
208+
209+
async def get_all_welcome_messages(
210+
self,
211+
) -> list[tuple[int, int, int, datetime]]:
212+
"""Return all tracked welcome messages as (chat_id, message_id, user_id, created_at)."""
213+
return await self._repo.get_all_welcome_messages()

0 commit comments

Comments
 (0)