diff --git a/handlers/scheduler.py b/handlers/scheduler.py index d66d90a..cdf568b 100644 --- a/handlers/scheduler.py +++ b/handlers/scheduler.py @@ -1,12 +1,13 @@ """Scheduler for daily birthday notifications.""" import telebot from datetime import datetime -from typing import Optional +from typing import Optional, Dict, List, Tuple +from collections import defaultdict from apscheduler.schedulers.blocking import BlockingScheduler from apscheduler.triggers.cron import CronTrigger import pytz from database import get_db_session, User, Chat, UserChat -from messages import format_birthday_greeting +from messages import format_birthday_greeting, format_multiple_birthdays_greetings from config import Config @@ -27,7 +28,10 @@ def send_birthday_notifications(bot: telebot.TeleBot) -> None: if not users_with_birthday: return - # For each user, send greetings to all their chats + # Group users by chat: chat_id -> list of (first_name, theme, user_id) + chat_users: Dict[int, List[Tuple[str, str, int]]] = defaultdict(list) + + # For each user, find all their chats for user in users_with_birthday: # Get all chats where user is a member user_chats = db.query(Chat).join(UserChat).filter( @@ -35,14 +39,14 @@ def send_birthday_notifications(bot: telebot.TeleBot) -> None: Chat.bot_is_admin == True ).all() - greeting = format_birthday_greeting(user.first_name, user.preference_theme) - for chat in user_chats: try: # Check if user is still in chat member = bot.get_chat_member(chat.chat_id, user.user_id) if member.status not in ['left', 'kicked']: - bot.send_message(chat.chat_id, greeting) + chat_users[chat.chat_id].append( + (user.first_name, user.preference_theme, user.user_id) + ) except telebot.apihelper.ApiTelegramException as e: # Handle errors: user blocked bot, bot removed from chat, etc. if e.error_code == 403: @@ -57,6 +61,31 @@ def send_birthday_notifications(bot: telebot.TeleBot) -> None: except Exception: # Other errors - continue pass + + # Send greetings grouped by chat + for chat_id, users_data in chat_users.items(): + try: + if len(users_data) == 1: + # Single user - use simple format + first_name, theme, user_id = users_data[0] + greeting = format_birthday_greeting(first_name, theme, user_id) + bot.send_message(chat_id, greeting, parse_mode='HTML') + else: + # Multiple users - use special format + greeting = format_multiple_birthdays_greetings(users_data) + bot.send_message(chat_id, greeting, parse_mode='HTML') + except telebot.apihelper.ApiTelegramException as e: + # Handle errors: bot removed from chat, etc. + if e.error_code == 403: + # Bot was blocked or removed from chat + chat = db.query(Chat).filter(Chat.chat_id == chat_id).first() + if chat: + chat.bot_is_admin = False + db.commit() + # Silently continue for other errors + except Exception: + # Other errors - continue + pass finally: db.close() diff --git a/messages.py b/messages.py index 2ff3a5a..4c15a01 100644 --- a/messages.py +++ b/messages.py @@ -38,6 +38,20 @@ def get_theme_emoji(theme: str) -> str: """Get emoji for the given theme.""" return THEME_EMOJIS.get(theme, "🎉") # Default emoji +# Birthday greeting opening phrases (random variations) +BIRTHDAY_GREETINGS = [ + "С днем рождения", + "Поздравляю с днем рождения", + "С твоим днем рождения", + "Поздравляю тебя с днем рождения", + "Поздравляю с днем рождения", +] + + +def get_birthday_greeting_opening() -> str: + """Get a random birthday greeting opening phrase.""" + return random.choice(BIRTHDAY_GREETINGS) + # Birthday messages for each theme BIRTHDAY_MESSAGES = { "Автомобили": [ @@ -132,8 +146,43 @@ def get_birthday_message(theme: str) -> str: return random.choice(messages) -def format_birthday_greeting(first_name: str, theme: str) -> str: - """Format a complete birthday greeting with emoji.""" +def format_birthday_greeting(first_name: str, theme: str, user_id: int) -> str: + """Format a complete birthday greeting with emoji and user link.""" emoji = get_theme_emoji(theme) + greeting_opening = get_birthday_greeting_opening() message = get_birthday_message(theme) - return f"{emoji} С днем рождения {first_name}, {message}" + # Format: party popper + theme emoji + greeting + bold name with link + user_link = f"tg://user?id={user_id}" + return f"🎉 {greeting_opening}, {emoji} {first_name}, {message}" + + +def format_multiple_birthdays_greetings(users_data: list[tuple[str, str, int]]) -> str: + """Format greetings for multiple users celebrating birthday today. + + Args: + users_data: List of tuples (first_name, theme, user_id) + + Returns: + Formatted message with all greetings + """ + count = len(users_data) + # Determine correct form of "человек" + if count == 1: + person_word = "человек" + elif count in [2, 3, 4]: + person_word = "человека" + else: + person_word = "человек" + + header = f"🎉 Сегодня день рождения отмечают {count} {person_word}:\n\n" + + greetings = [] + for first_name, theme, user_id in users_data: + emoji = get_theme_emoji(theme) + greeting_opening = get_birthday_greeting_opening() + message = get_birthday_message(theme) + user_link = f"tg://user?id={user_id}" + greeting = f"{emoji} {first_name} — {greeting_opening.lower()}, {message}" + greetings.append(greeting) + + return header + "\n\n".join(greetings)