santa/secret_santa_bot.py
2025-12-07 16:15:26 +03:00

498 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Telegram бот для тайного Санты - ИСПРАВЛЕННАЯ ВЕРСИЯ
Требует: pip install pyTelegramBotAPI
"""
import telebot
import json
import os
import random
from datetime import datetime
# ===== НАСТРОЙКИ =====
# Замените на ваш токен от BotFather
BOT_TOKEN = "8585166053:AAEsgD2ioOc-SmV2uCae7xNkrAblxYQyajg"
# ID владельца (вас) - просто укажите ТОЛЬКО ВАШ ID
OWNER_ID = 1132670685
DATA_FILE = "santa_data.json"
DEFAULT_DATA = {
"groups": {},
"distributions": {}
}
# ===== ИНИЦИАЛИЗАЦИЯ =====
bot = telebot.TeleBot(BOT_TOKEN)
# ===== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ =====
def load_data():
"""Загружает данные из JSON"""
if os.path.exists(DATA_FILE):
with open(DATA_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
return DEFAULT_DATA.copy()
def save_data(data):
"""Сохраняет данные в JSON"""
with open(DATA_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def is_owner(user_id):
"""Проверяет, владелец ли пользователь"""
return user_id == OWNER_ID
def secret_santa_algorithm(participants):
"""Алгоритм распределения"""
names = list(participants.keys())
receivers = names.copy()
random.shuffle(receivers)
for _ in range(100):
valid = True
for i, giver in enumerate(names):
if giver == receivers[i]:
valid = False
break
if valid:
break
random.shuffle(receivers)
result = {}
for giver, receiver in zip(names, receivers):
result[giver] = receiver
return result
def get_keyboard(buttons, row_width=2):
"""Создает клавиатуру с кнопками"""
markup = telebot.types.ReplyKeyboardMarkup(row_width=row_width, resize_keyboard=True)
markup.add(*[telebot.types.KeyboardButton(btn) for btn in buttons])
return markup
def show_admin_menu(chat_id):
"""Показывает админ-панель"""
markup = get_keyboard([
' Создать группу',
'📋 Список групп',
'✨ Генерировать распределение',
'📢 Отправить результаты',
'🔙 Назад'
], row_width=1)
bot.send_message(
chat_id,
"🔑 <b>Панель администратора</b>\n\nВыберите действие:",
reply_markup=markup,
parse_mode='html'
)
def show_participant_menu(chat_id):
"""Показывает меню участника"""
markup = get_keyboard([
'✏️ Присоединиться к группе',
'📋 Мои группы',
'🔙 Назад'
], row_width=1)
bot.send_message(
chat_id,
"👥 <b>Меню участника</b>\n\nВыберите действие:",
reply_markup=markup,
parse_mode='html'
)
# ===== КОМАНДЫ =====
@bot.message_handler(commands=['start'])
def start(message):
"""Команда /start"""
markup = get_keyboard(['👑 Администратор', '👥 Участник'], row_width=1)
bot.send_message(
message.chat.id,
"🎄 Добро пожаловать в бота для тайного Санты!\n\n"
"Выберите вашу роль:",
reply_markup=markup
)
@bot.message_handler(commands=['help'])
def help_cmd(message):
"""Справка"""
help_text = """
🎄 <b>Справка по боту</b>
<b>Для администраторов:</b>
• Создайте группу
• Посмотрите список групп и участников
• Генерируйте распределение (когда все добавлены)
• Отправьте результаты
<b>Для участников:</b>
• Присоединитесь к группе (вводите свое имя и название группы)
• Получите личное сообщение с получателем подарка
<b>Важно:</b> Все должны быть в приватном чате с ботом!
"""
bot.send_message(message.chat.id, help_text, parse_mode='html')
@bot.message_handler(commands=['mystatus'])
def my_status(message):
"""Показывает статус пользователя"""
user_id = message.from_user.id
username = message.from_user.username or message.from_user.first_name
if is_owner(user_id):
status = "👑 Вы владелец бота (администратор)"
else:
status = "👤 Обычный участник"
bot.send_message(
message.chat.id,
f"<b>Ваш статус:</b>\n{status}\n\n"
f"ID: {user_id}\n"
f"Username: @{username}",
parse_mode='html'
)
# ===== АДМИНИСТРАТОР =====
@bot.message_handler(func=lambda m: m.text == '👑 Администратор')
def admin_role(message):
"""Выбор роли администратора"""
if not is_owner(message.from_user.id):
bot.send_message(message.chat.id, "❌ Только владелец бота может быть администратором.")
return
show_admin_menu(message.chat.id)
@bot.message_handler(func=lambda m: m.text == ' Создать группу')
def create_group(message):
"""Создание новой группы"""
if not is_owner(message.from_user.id):
return
msg = bot.send_message(
message.chat.id,
"📝 Введите название группы (например: 'Друзья 2025'):"
)
bot.register_next_step_handler(msg, process_group_name)
def process_group_name(message):
"""Обработка названия группы"""
group_name = message.text.strip()
data = load_data()
if group_name in data["groups"]:
bot.send_message(message.chat.id, "❌ Группа с таким названием уже существует!")
show_admin_menu(message.chat.id)
return
data["groups"][group_name] = {
"created_by": message.from_user.id,
"created_at": datetime.now().isoformat(),
"participants": {},
"distributed": False
}
save_data(data)
bot.send_message(
message.chat.id,
f"✅ Группа '<b>{group_name}</b>' создана!\n\n"
f"<b>Скажите друзьям:</b>\n"
f"Напишите мне в приватный чат, выберите '👥 Участник',\n"
f"затем '✏️ Присоединиться к группе',\n"
f"введите название группы '<b>{group_name}</b>' и своё имя.",
parse_mode='html'
)
show_admin_menu(message.chat.id)
@bot.message_handler(func=lambda m: m.text == '📋 Список групп')
def list_groups(message):
"""Список всех групп"""
if not is_owner(message.from_user.id):
return
data = load_data()
groups = data["groups"]
if not groups:
bot.send_message(message.chat.id, "📭 Групп еще не создано.")
show_admin_menu(message.chat.id)
return
text = "📋 <b>Список групп:</b>\n\n"
for i, (name, info) in enumerate(groups.items(), 1):
count = len(info["participants"])
status = "✅ Распределено" if info["distributed"] else "⏳ Ожидание"
text += f"{i}. <b>{name}</b>\n Участников: {count}\n Статус: {status}\n"
if info["participants"]:
text += " Участники: "
names = list(info["participants"].keys())
text += ", ".join(names)
text += "\n"
text += "\n"
bot.send_message(message.chat.id, text, parse_mode='html')
show_admin_menu(message.chat.id)
@bot.message_handler(func=lambda m: m.text == '✨ Генерировать распределение')
def generate_distribution(message):
"""Генерирование распределения Санты"""
if not is_owner(message.from_user.id):
return
data = load_data()
groups = list(data["groups"].keys())
if not groups:
bot.send_message(message.chat.id, "❌ Групп не найдено!")
show_admin_menu(message.chat.id)
return
markup = get_keyboard(groups, row_width=1)
msg = bot.send_message(message.chat.id, "Выберите группу:", reply_markup=markup)
bot.register_next_step_handler(msg, lambda m: process_distribution(m, groups))
def process_distribution(message, groups):
"""Обработка генерирования распределения"""
if message.text not in groups:
bot.send_message(message.chat.id, "❌ Неверная группа!")
show_admin_menu(message.chat.id)
return
group_name = message.text
data = load_data()
group = data["groups"][group_name]
if len(group["participants"]) < 2:
bot.send_message(
message.chat.id,
f"В группе '<b>{group_name}</b>' должно быть минимум 2 участника!\n"
f"Сейчас в группе: {len(group['participants'])} участников.",
parse_mode='html'
)
show_admin_menu(message.chat.id)
return
# Генерируем распределение
distribution = secret_santa_algorithm(group["participants"])
# Сохраняем результат
data["distributions"][group_name] = {
"generated_at": datetime.now().isoformat(),
"distribution": distribution
}
data["groups"][group_name]["distributed"] = True
save_data(data)
bot.send_message(
message.chat.id,
f"✅ <b>Распределение для группы '{group_name}' сгенерировано!</b>\n\n"
f"Участников: {len(distribution)}\n\n"
f"Готово к отправке результатов!",
parse_mode='html'
)
show_admin_menu(message.chat.id)
@bot.message_handler(func=lambda m: m.text == '📢 Отправить результаты')
def send_results(message):
"""Отправка результатов участникам"""
if not is_owner(message.from_user.id):
return
data = load_data()
distributions = list(data["distributions"].keys())
if not distributions:
bot.send_message(message.chat.id, "❌ Нет сгенерированных распределений!")
show_admin_menu(message.chat.id)
return
markup = get_keyboard(distributions, row_width=1)
msg = bot.send_message(message.chat.id, "Выберите группу:", reply_markup=markup)
bot.register_next_step_handler(msg, lambda m: send_group_results(m, distributions, data))
def send_group_results(message, distributions, data):
"""Отправка результатов группе"""
if message.text not in distributions:
bot.send_message(message.chat.id, "❌ Неверная группа!")
show_admin_menu(message.chat.id)
return
group_name = message.text
distribution = data["distributions"][group_name]["distribution"]
sent_count = 0
failed_count = 0
for giver_name, receiver_name in distribution.items():
# Получаем user_id дарителя из сохранённых данных
participant_data = data["groups"][group_name]["participants"][giver_name]
user_id = participant_data["user_id"]
try:
bot.send_message(
user_id,
f"🎄 <b>Тайный Санта!</b>\n\n"
f"Вы дарите подарок: <b>{receiver_name}</b>\n\n"
f"Группа: <b>{group_name}</b>\n\n"
f"Сохраняйте в секрете! 🤫",
parse_mode='html'
)
sent_count += 1
except Exception as e:
failed_count += 1
print(f"Ошибка отправки {user_id} ({giver_name}): {e}")
result_msg = (
f"✅ <b>Результаты отправлены!</b>\n\n"
f"Успешно отправлено: {sent_count}\n"
)
if failed_count > 0:
result_msg += f"Ошибок: {failed_count}\n"
bot.send_message(message.chat.id, result_msg, parse_mode='html')
show_admin_menu(message.chat.id)
# ===== УЧАСТНИК =====
@bot.message_handler(func=lambda m: m.text == '👥 Участник')
def participant_role(message):
"""Выбор роли участника"""
show_participant_menu(message.chat.id)
@bot.message_handler(func=lambda m: m.text == '✏️ Присоединиться к группе')
def join_group(message):
"""Присоединение к группе"""
data = load_data()
groups = list(data["groups"].keys())
if not groups:
bot.send_message(message.chat.id, "❌ Групп еще не создано. Попросите администратора создать группу.")
show_participant_menu(message.chat.id)
return
markup = get_keyboard(groups, row_width=1)
msg = bot.send_message(
message.chat.id,
"Выберите группу, к которой хотите присоединиться:",
reply_markup=markup
)
bot.register_next_step_handler(msg, lambda m: select_group_to_join(m, groups, message.from_user.id))
def select_group_to_join(message, groups, user_id):
"""Выбор группы для присоединения"""
if message.text not in groups:
bot.send_message(message.chat.id, "❌ Неверная группа!")
show_participant_menu(message.chat.id)
return
selected_group = message.text
msg = bot.send_message(
message.chat.id,
f"📝 Введите своё имя для группы '<b>{selected_group}</b>':",
parse_mode='html'
)
bot.register_next_step_handler(msg, lambda m: add_self_to_group(m, selected_group, user_id))
def add_self_to_group(message, group_name, user_id):
"""Добавление себя в группу"""
participant_name = message.text.strip()
data = load_data()
if participant_name in data["groups"][group_name]["participants"]:
bot.send_message(
message.chat.id,
f"В группе '<b>{group_name}</b>' уже есть участник с именем '<b>{participant_name}</b>'.\n"
f"Введите другое имя или прозвище.",
parse_mode='html'
)
show_participant_menu(message.chat.id)
return
# ⭐ ГЛАВНОЕ: сохраняем user_id участника!
data["groups"][group_name]["participants"][participant_name] = {
"user_id": user_id,
"added_at": datetime.now().isoformat()
}
save_data(data)
bot.send_message(
message.chat.id,
f"✅ Отлично! Вы добавлены в группу '<b>{group_name}</b>' под именем '<b>{participant_name}</b>'.\n\n"
f"Администратор пришлёт вам результаты, когда сгенерирует распределение.",
parse_mode='html'
)
show_participant_menu(message.chat.id)
@bot.message_handler(func=lambda m: m.text == '📋 Мои группы')
def my_groups(message):
"""Показывает группы пользователя"""
user_id = message.from_user.id
data = load_data()
user_groups = []
for group_name, group_info in data["groups"].items():
for participant_name, participant_info in group_info["participants"].items():
if participant_info["user_id"] == user_id:
user_groups.append((group_name, participant_name, group_info["distributed"]))
if not user_groups:
bot.send_message(message.chat.id, "📭 Вы еще не присоединились ни к одной группе.")
show_participant_menu(message.chat.id)
return
text = "📋 <b>Ваши группы:</b>\n\n"
for group_name, participant_name, distributed in user_groups:
status = "✅ Распределено" if distributed else "⏳ Ожидание"
text += f"• <b>{group_name}</b>\n Ваше имя: {participant_name}\n Статус: {status}\n\n"
bot.send_message(message.chat.id, text, parse_mode='html')
show_participant_menu(message.chat.id)
# ===== НАВИГАЦИЯ =====
@bot.message_handler(func=lambda m: m.text == '🔙 Назад')
def back(message):
"""Возврат в главное меню"""
markup = get_keyboard(['👑 Администратор', '👥 Участник'], row_width=1)
bot.send_message(
message.chat.id,
"Выберите вашу роль:",
reply_markup=markup
)
@bot.message_handler(commands=['about'])
def about(message):
"""Информация о боте"""
bot.send_message(
message.chat.id,
"🎄 <b>Telegram бот для тайного Санты v1.2</b>\n\n"
"✨ <b>Как работает:</b>\n"
"1. Администратор создаёт группу\n"
"2. Участники сами присоединяются к группе\n"
"3. Администратор генерирует распределение\n"
"4. Каждый получает личное сообщение с получателем\n\n"
"/help - справка",
parse_mode='html'
)
@bot.message_handler(func=lambda m: True)
def unknown_message(message):
"""Обработка неизвестных сообщений"""
bot.send_message(
message.chat.id,
"❓ Команда не распознана.\n\n"
"Используйте /start для начала работы или /help для справки."
)
# ===== ЗАПУСК БОТА =====
if __name__ == '__main__':
print("🤖 Бот запущен! (Нажмите Ctrl+C для остановки)")
try:
bot.infinity_polling()
except KeyboardInterrupt:
print("⛔ Бот остановлен.")