Add notification settings

This commit is contained in:
2026-01-04 02:47:38 +07:00
parent 7a3576aec0
commit 475e2cf4cd
14 changed files with 517 additions and 26 deletions

View File

@@ -83,9 +83,15 @@ class TelegramNotifier:
db: AsyncSession,
marathon_id: int,
message: str,
exclude_user_id: int | None = None
exclude_user_id: int | None = None,
check_setting: str | None = None
) -> int:
"""Send notification to all marathon participants with linked Telegram."""
"""Send notification to all marathon participants with linked Telegram.
Args:
check_setting: If provided, only send to users with this setting enabled.
Options: 'notify_events', 'notify_disputes', 'notify_moderation'
"""
result = await db.execute(
select(User)
.join(Participant, Participant.user_id == User.id)
@@ -100,6 +106,10 @@ class TelegramNotifier:
for user in users:
if exclude_user_id and user.id == exclude_user_id:
continue
# Check notification setting if specified
if check_setting and not getattr(user, check_setting, True):
logger.info(f"[Notify] Skipping user {user.nickname} - {check_setting} is disabled")
continue
if await self.send_message(user.telegram_id, message):
sent_count += 1
@@ -113,7 +123,7 @@ class TelegramNotifier:
event_type: str,
marathon_title: str
) -> int:
"""Notify participants about event start."""
"""Notify participants about event start (respects notify_events setting)."""
event_messages = {
"golden_hour": f"🌟 <b>Начался Golden Hour</b> в «{marathon_title}»!\n\nВсе очки x1.5 в течение часа!",
"jackpot": f"🎰 <b>JACKPOT</b> в «{marathon_title}»!\n\nОчки x3 за следующий сложный челлендж!",
@@ -128,7 +138,9 @@ class TelegramNotifier:
f"📌 Новое событие в «{marathon_title}»!"
)
return await self.notify_marathon_participants(db, marathon_id, message)
return await self.notify_marathon_participants(
db, marathon_id, message, check_setting='notify_events'
)
async def notify_event_end(
self,
@@ -137,7 +149,7 @@ class TelegramNotifier:
event_type: str,
marathon_title: str
) -> int:
"""Notify participants about event end."""
"""Notify participants about event end (respects notify_events setting)."""
event_names = {
"golden_hour": "Golden Hour",
"jackpot": "Jackpot",
@@ -150,7 +162,9 @@ class TelegramNotifier:
event_name = event_names.get(event_type, "Событие")
message = f"⏰ <b>{event_name}</b> в «{marathon_title}» завершён"
return await self.notify_marathon_participants(db, marathon_id, message)
return await self.notify_marathon_participants(
db, marathon_id, message, check_setting='notify_events'
)
async def notify_marathon_start(
self,
@@ -186,7 +200,14 @@ class TelegramNotifier:
challenge_title: str,
assignment_id: int
) -> bool:
"""Notify user about dispute raised on their assignment."""
"""Notify user about dispute raised on their assignment (respects notify_disputes setting)."""
# Check user's notification settings
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if user and not user.notify_disputes:
logger.info(f"[Dispute] Skipping user {user.nickname} - notify_disputes is disabled")
return False
logger.info(f"[Dispute] Sending notification to user_id={user_id} for assignment_id={assignment_id}")
dispute_url = f"{settings.FRONTEND_URL}/assignments/{assignment_id}"
@@ -227,7 +248,14 @@ class TelegramNotifier:
challenge_title: str,
is_valid: bool
) -> bool:
"""Notify user about dispute resolution."""
"""Notify user about dispute resolution (respects notify_disputes setting)."""
# Check user's notification settings
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if user and not user.notify_disputes:
logger.info(f"[Dispute] Skipping user {user.nickname} - notify_disputes is disabled")
return False
if is_valid:
message = (
f"❌ <b>Спор признан обоснованным</b>\n\n"
@@ -251,7 +279,14 @@ class TelegramNotifier:
marathon_title: str,
game_title: str
) -> bool:
"""Notify user that their proposed game was approved."""
"""Notify user that their proposed game was approved (respects notify_moderation setting)."""
# Check user's notification settings
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if user and not user.notify_moderation:
logger.info(f"[Moderation] Skipping user {user.nickname} - notify_moderation is disabled")
return False
message = (
f"✅ <b>Твоя игра одобрена!</b>\n\n"
f"Марафон: {marathon_title}\n"
@@ -267,7 +302,14 @@ class TelegramNotifier:
marathon_title: str,
game_title: str
) -> bool:
"""Notify user that their proposed game was rejected."""
"""Notify user that their proposed game was rejected (respects notify_moderation setting)."""
# Check user's notification settings
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if user and not user.notify_moderation:
logger.info(f"[Moderation] Skipping user {user.nickname} - notify_moderation is disabled")
return False
message = (
f"❌ <b>Твоя игра отклонена</b>\n\n"
f"Марафон: {marathon_title}\n"
@@ -284,7 +326,14 @@ class TelegramNotifier:
game_title: str,
challenge_title: str
) -> bool:
"""Notify user that their proposed challenge was approved."""
"""Notify user that their proposed challenge was approved (respects notify_moderation setting)."""
# Check user's notification settings
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if user and not user.notify_moderation:
logger.info(f"[Moderation] Skipping user {user.nickname} - notify_moderation is disabled")
return False
message = (
f"✅ <b>Твой челлендж одобрен!</b>\n\n"
f"Марафон: {marathon_title}\n"
@@ -302,7 +351,14 @@ class TelegramNotifier:
game_title: str,
challenge_title: str
) -> bool:
"""Notify user that their proposed challenge was rejected."""
"""Notify user that their proposed challenge was rejected (respects notify_moderation setting)."""
# Check user's notification settings
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if user and not user.notify_moderation:
logger.info(f"[Moderation] Skipping user {user.nickname} - notify_moderation is disabled")
return False
message = (
f"❌ <b>Твой челлендж отклонён</b>\n\n"
f"Марафон: {marathon_title}\n"