498 lines
18 KiB
Python
498 lines
18 KiB
Python
"""
|
||
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("⛔ Бот остановлен.") |