feat: 01 schedule
This commit is contained in:
@ -98,7 +98,7 @@ def register_command_handlers(bot: telebot.TeleBot) -> None:
|
|||||||
|
|
||||||
@bot.message_handler(commands=['month'], chat_types=['group', 'supergroup'])
|
@bot.message_handler(commands=['month'], chat_types=['group', 'supergroup'])
|
||||||
def handle_month(message: telebot.types.Message) -> None:
|
def handle_month(message: telebot.types.Message) -> None:
|
||||||
"""Handle /month command - show birthdays for next 30 days."""
|
"""Handle /month command - show birthdays for next 31 days."""
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
|
|
||||||
db = get_db_session()
|
db = get_db_session()
|
||||||
@ -109,16 +109,16 @@ def register_command_handlers(bot: telebot.TeleBot) -> None:
|
|||||||
bot.reply_to(message, "Мне нужны права администратора для выполнения этой команды.")
|
bot.reply_to(message, "Мне нужны права администратора для выполнения этой команды.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get birthdays for next 30 days
|
# Get birthdays for next 31 days
|
||||||
today = datetime.now().date()
|
today = datetime.now().date()
|
||||||
birthdays = get_birthdays_in_range(db, chat_id, today, days=30)
|
birthdays = get_birthdays_in_range(db, chat_id, today, days=31)
|
||||||
|
|
||||||
if not birthdays:
|
if not birthdays:
|
||||||
bot.reply_to(message, "На ближайшие 30 дней дней рождений не запланировано.")
|
bot.reply_to(message, "На ближайшие 31 день дней рождений не запланировано.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Format message
|
# Format message
|
||||||
message_text = "🎂 Дни рождения на ближайшие 30 дней:\n\n"
|
message_text = "🎂 Дни рождения на ближайшие 31 день:\n\n"
|
||||||
for date_str, names in sorted(birthdays.items()):
|
for date_str, names in sorted(birthdays.items()):
|
||||||
names_list = ", ".join(names)
|
names_list = ", ".join(names)
|
||||||
message_text += f"• {date_str}: {names_list}\n"
|
message_text += f"• {date_str}: {names_list}\n"
|
||||||
@ -134,7 +134,7 @@ def register_command_handlers(bot: telebot.TeleBot) -> None:
|
|||||||
"📚 Команды бота:\n\n"
|
"📚 Команды бота:\n\n"
|
||||||
"/stats - Показать статистику: сколько человек поделились днем рождения\n"
|
"/stats - Показать статистику: сколько человек поделились днем рождения\n"
|
||||||
"/week - Показать дни рождения на ближайшие 7 дней\n"
|
"/week - Показать дни рождения на ближайшие 7 дней\n"
|
||||||
"/month - Показать дни рождения на ближайшие 30 дней\n"
|
"/month - Показать дни рождения на ближайшие 31 день\n"
|
||||||
"/help - Показать это сообщение\n\n"
|
"/help - Показать это сообщение\n\n"
|
||||||
"Чтобы поделиться своим днем рождения, напиши боту в личку /start\n\n"
|
"Чтобы поделиться своим днем рождения, напиши боту в личку /start\n\n"
|
||||||
"from olly & cursor with <3"
|
"from olly & cursor with <3"
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
"""Scheduler for daily birthday notifications."""
|
"""Scheduler for daily birthday notifications."""
|
||||||
import telebot
|
import telebot
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta, date
|
||||||
from typing import Optional, Dict, List, Tuple
|
from typing import Optional, Dict, List, Tuple
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from apscheduler.schedulers.blocking import BlockingScheduler
|
from apscheduler.schedulers.blocking import BlockingScheduler
|
||||||
from apscheduler.triggers.cron import CronTrigger
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
import pytz
|
import pytz
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
from database import get_db_session, User, Chat, UserChat
|
from database import get_db_session, User, Chat, UserChat
|
||||||
from messages import format_birthday_greeting, format_multiple_birthdays_greetings
|
from messages import format_birthday_greeting, format_multiple_birthdays_greetings
|
||||||
from config import Config
|
from config import Config
|
||||||
@ -119,4 +120,89 @@ def setup_scheduler(bot: telebot.TeleBot) -> BlockingScheduler:
|
|||||||
replace_existing=True
|
replace_existing=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add monthly job (1st day of each month)
|
||||||
|
scheduler.add_job(
|
||||||
|
send_monthly_birthday_overview,
|
||||||
|
trigger=CronTrigger(day=1, hour=hour, minute=minute),
|
||||||
|
args=[bot],
|
||||||
|
id='monthly_birthday_overview',
|
||||||
|
name='Send monthly birthday overview',
|
||||||
|
replace_existing=True
|
||||||
|
)
|
||||||
|
|
||||||
return scheduler
|
return scheduler
|
||||||
|
|
||||||
|
|
||||||
|
def get_birthdays_in_range_for_chat(db: Session, chat_id: int, start_date: date, days: int) -> Dict[str, List[str]]:
|
||||||
|
"""Get birthdays in the specified date range for users in the chat."""
|
||||||
|
birthdays = defaultdict(list)
|
||||||
|
|
||||||
|
# Get all users in this chat
|
||||||
|
users = db.query(User).join(UserChat).filter(
|
||||||
|
UserChat.chat_id == chat_id
|
||||||
|
).distinct().all()
|
||||||
|
|
||||||
|
end_date = start_date + timedelta(days=days)
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
# Create birthday date for current year
|
||||||
|
try:
|
||||||
|
birthday_this_year = datetime(start_date.year, user.birthday_month, user.birthday_day).date()
|
||||||
|
except ValueError:
|
||||||
|
# Invalid date (e.g., Feb 29 in non-leap year)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if birthday falls in range
|
||||||
|
if start_date <= birthday_this_year < end_date:
|
||||||
|
date_str = f"{user.birthday_day:02d}.{user.birthday_month:02d}"
|
||||||
|
birthdays[date_str].append(user.first_name)
|
||||||
|
else:
|
||||||
|
# Check next year if we're near year end
|
||||||
|
try:
|
||||||
|
birthday_next_year = datetime(start_date.year + 1, user.birthday_month, user.birthday_day).date()
|
||||||
|
if start_date <= birthday_next_year < end_date:
|
||||||
|
date_str = f"{user.birthday_day:02d}.{user.birthday_month:02d}"
|
||||||
|
birthdays[date_str].append(user.first_name)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return birthdays
|
||||||
|
|
||||||
|
|
||||||
|
def send_monthly_birthday_overview(bot: telebot.TeleBot) -> None:
|
||||||
|
"""Send monthly birthday overview (like /month command) to all chats on 1st of each month."""
|
||||||
|
db = get_db_session()
|
||||||
|
try:
|
||||||
|
today = datetime.now().date()
|
||||||
|
|
||||||
|
# Get all chats where bot is admin
|
||||||
|
admin_chats = db.query(Chat).filter(Chat.bot_is_admin == True).all()
|
||||||
|
|
||||||
|
for chat in admin_chats:
|
||||||
|
try:
|
||||||
|
# Get birthdays for next 31 days
|
||||||
|
birthdays = get_birthdays_in_range_for_chat(db, chat.chat_id, today, days=31)
|
||||||
|
|
||||||
|
if not birthdays:
|
||||||
|
# Don't send message if no birthdays
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Format message (same as /month command)
|
||||||
|
message_text = "🎂 Дни рождения на ближайшие 31 день:\n\n"
|
||||||
|
for date_str, names in sorted(birthdays.items()):
|
||||||
|
names_list = ", ".join(names)
|
||||||
|
message_text += f"• {date_str}: {names_list}\n"
|
||||||
|
|
||||||
|
bot.send_message(chat.chat_id, message_text)
|
||||||
|
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.bot_is_admin = False
|
||||||
|
db.commit()
|
||||||
|
# Silently continue for other errors
|
||||||
|
except Exception:
|
||||||
|
# Other errors - continue
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|||||||
Reference in New Issue
Block a user