import logging import random import requests import os from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultArticle, InputTextMessageContent from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters, InlineQueryHandler, ChatMemberHandler from database_sqlite import Database from config import BOT_TOKEN, OPENWEATHER_API_KEY, NEWS_API_KEY # Настройка логирования logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) # Инициализация базы данных db = Database() # Предопределенные ответы PREDEFINED_RESPONSES = { 'привет': 'Привет! Как дела?', 'как дела': 'Отлично! А у тебя?', 'что делаешь': 'Отвечаю на сообщения в чате 😊', 'спасибо': 'Пожалуйста! Рад помочь!', 'пока': 'До свидания! Заходи еще!', } # Текст справки HELP_TEXT = """ 🤖 Команды бота: Основные: /start - Начать работу с ботом /help - Показать это сообщение /info - Информация о вас Рейтинг: /rank - Ваш текущий рейтинг /ranks_info - Информация о системе рангов /leaderboard - Топ-10 участников Информация: /weather [город] - Погода в городе /news - Последние новости /translate [текст] [язык] - Перевод текста Прочее: Введите "реквизиты" - Получить реквизиты для оплаты Игры: /play_game - Запустить мини-игру Модерация (только для админов): /warn [пользователь] [причина] - Выдать предупреждение /mute [пользователь] [время] - Заглушить пользователя /unmute [пользователь] - Снять заглушку /ban [пользователь] [причина] - Забанить пользователя /unban [пользователь] - Разбанить пользователя /kick [пользователь] [причина] - Кикнуть пользователя /promote [пользователь] - Повысить до модератора /demote [пользователь] - Понизить с модератора """ # Текст реквизитов BANK_DETAILS_TEXT = "Денежные средства можно перечислить по номерам: \r\n💳 89066935474 Елена, \r\n💳89207144698 Людмила\r\nБольшая просьба не писать сообщения при переводе, карту могут заблокировать!!!" class TelegramBot: def __init__(self): self.application = ApplicationBuilder().token(BOT_TOKEN).build() self.setup_handlers() def setup_handlers(self): """Настройка обработчиков команд и сообщений""" # Команды self.application.add_handler(CommandHandler("start", self.start)) self.application.add_handler(CommandHandler("help", self.help)) self.application.add_handler(CommandHandler("rank", self.rank)) self.application.add_handler(CommandHandler("ranks_info", self.ranks_info)) self.application.add_handler(CommandHandler("leaderboard", self.leaderboard)) self.application.add_handler(CommandHandler("info", self.user_info)) self.application.add_handler(CommandHandler("weather", self.weather)) self.application.add_handler(CommandHandler("news", self.news)) self.application.add_handler(CommandHandler("translate", self.translate)) self.application.add_handler(CommandHandler("play_game", self.play_game)) self.application.add_handler(CommandHandler("ban", self.ban_user)) self.application.add_handler(CommandHandler("mute", self.mute_user)) self.application.add_handler(CommandHandler("warn", self.warn_user)) self.application.add_handler(CommandHandler("kick", self.kick_user)) self.application.add_handler(CommandHandler("unmute", self.unmute_user)) self.application.add_handler(CommandHandler("unban", self.unban_user)) self.application.add_handler(CommandHandler("promote", self.promote_user)) self.application.add_handler(CommandHandler("demote", self.demote_user)) self.application.add_handler(CommandHandler("import_csv", self.import_csv)) self.application.add_handler(CommandHandler("schedule_post", self.schedule_post)) self.application.add_handler(CommandHandler("list_posts", self.list_posts)) self.application.add_handler(CommandHandler("delete_post", self.delete_post)) self.application.add_handler(CommandHandler("publish_now", self.publish_now)) # Обработчики сообщений self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message)) self.application.add_handler(ChatMemberHandler(self.handle_new_chat_members, ChatMemberHandler.CHAT_MEMBER)) # Инлайновые запросы self.application.add_handler(InlineQueryHandler(self.handle_inline_query)) # Callback queries для игр from telegram.ext import CallbackQueryHandler self.application.add_handler(CallbackQueryHandler(self.handle_callback)) async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработчик команды /start""" user = update.effective_user db.add_user(user.id, user.username, user.first_name, user.last_name) keyboard = [ [InlineKeyboardButton("📋 Помощь", callback_data='cmd_help')], [InlineKeyboardButton("🎮 Мини игры", callback_data='cmd_play_game')] ] reply_markup = InlineKeyboardMarkup(keyboard) welcome_text = f""" Привет, {user.first_name}! 👋 Я ваш помощник в чате. Вот что я умею: • Отвечать на сообщения • Вести рейтинг участников • Предоставлять погоду и новости • Играть в мини-игры • Помогать с модерацией Используйте /help для подробной информации. """ await update.message.reply_text(welcome_text, reply_markup=reply_markup) async def help(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработчик команды /help""" keyboard = [ [InlineKeyboardButton("🚀 Старт", callback_data='cmd_start')], [InlineKeyboardButton("🔄 Начать заново", callback_data='cmd_restart')] ] reply_markup = InlineKeyboardMarkup(keyboard) help_text = """ 🤖 Команды бота: Основные: /start - Начать работу с ботом /help - Показать это сообщение /info - Информация о вас Рейтинг: /rank - Ваш текущий рейтинг /ranks_info - Информация о системе рангов /leaderboard - Топ-10 участников Информация: /weather [город] - Погода в городе /news - Последние новости /translate [текст] [язык] - Перевод текста Прочее: Введите "реквизиты" - Получить реквизиты для оплаты Игры: /play_game - Запустить мини-игру Посты по расписанию (только для админов): /schedule_post [время] [текст] - Запланировать пост /list_posts - Показать запланированные посты /delete_post [ID] - Удалить пост по расписанию /publish_now [ID] - Опубликовать пост немедленно Посты по расписанию (только для админов): /schedule_post [время] [текст] - Запланировать пост /list_posts - Показать запланированные посты /delete_post [ID] - Удалить пост по расписанию /publish_now [ID] - Опубликовать пост немедленно Модерация (только для админов): /warn [пользователь] [причина] - Выдать предупреждение /mute [пользователь] [время] - Заглушить пользователя /unmute [пользователь] - Снять заглушку /ban [пользователь] [причина] - Забанить пользователя /unban [пользователь] - Разбанить пользователя /kick [пользователь] [причина] - Кикнуть пользователя /promote [пользователь] - Повысить до модератора /demote [пользователь] - Понизить с модератора /import_csv [файл] - Импорт пользователей из CSV файла """ await update.message.reply_text(help_text, parse_mode='HTML', reply_markup=reply_markup) async def rank(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Показать рейтинг пользователя""" user = update.effective_user db.add_user(user.id, user.username, user.first_name, user.last_name) user_info = db.get_user_info(user.id) if user_info: rank_text = f""" 🏆 Ваш рейтинг: Очки: {user_info['Очки']} Предупреждений: {user_info['Предупреждений']} Роль: {user_info['Роль']} """ else: rank_text = "Информация о вас не найдена." await update.message.reply_text(rank_text, parse_mode='HTML') async def leaderboard(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Показать топ пользователей""" top_users = db.get_top_users(10) if top_users: leaderboard_text = "🏆 Топ-10 участников:\n\n" for i, (user_id, username, first_name, score) in enumerate(top_users, 1): name = username if username else first_name leaderboard_text += f"{i}. {name} - {score} очков\n" else: leaderboard_text = "Рейтинг пока пуст." await update.message.reply_text(leaderboard_text, parse_mode='HTML') async def user_info(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Показать информацию о пользователе""" user = update.effective_user # Проверяем, является ли пользователь администратором is_admin = await self.is_admin(update.effective_chat, user.id) if len(context.args) > 0 and is_admin: # Администратор запрашивает информацию о другом пользователе target_user = context.args[0] try: target_user_id = int(target_user) user_info = db.get_user_info(target_user_id) if user_info: info_text = f""" 👤 Информация о пользователе: ID: {user_info['ID']} Имя: {user_info['Имя']} Имя пользователя: @{user_info['Имя пользователя']} Репутация: {user_info['Репутация']} Ранг: {user_info['Ранг']} Количество сообщений: {user_info['Количество сообщений']} Активные дни: {user_info['Активные дни']} Дней с присоединения: {user_info['Дней с присоединения']} Последнее сообщение: {user_info['Последнее сообщение'] or 'никогда'} Присоединился: {user_info['Присоединился']} Покинул: {user_info['Покинул'] or 'ещё здесь'} Язык: {user_info['Язык']} Действия: {user_info['Действия']} Очки: {user_info['Очки']} Предупреждений: {user_info['Предупреждений']} Роль: {user_info['Роль']} """ else: info_text = f"Пользователь с ID {target_user_id} не найден." except ValueError: info_text = f"Неверный формат ID пользователя: {target_user}" else: # Обычный пользователь запрашивает свою информацию db.add_user(user.id, user.username, user.first_name, user.last_name) user_info = db.get_user_info(user.id) if user_info: info_text = f""" 👤 Информация о вас: ID: {user_info['ID']} Имя: {user_info['Имя']} Имя пользователя: @{user_info['Имя пользователя']} Репутация: {user_info['Репутация']} Ранг: {user_info['Ранг']} Количество сообщений: {user_info['Количество сообщений']} Активные дни: {user_info['Активные дни']} Дней с присоединения: {user_info['Дней с присоединения']} Последнее сообщение: {user_info['Последнее сообщение'] or 'никогда'} Присоединился: {user_info['Присоединился']} Покинул: {user_info['Покинул'] or 'ещё здесь'} Язык: {user_info['Язык']} Действия: {user_info['Действия']} Очки: {user_info['Очки']} Предупреждений: {user_info['Предупреждений']} Роль: {user_info['Роль']} """ else: info_text = "Информация не найдена." await update.message.reply_text(info_text, parse_mode='HTML') async def weather(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Получить погоду""" if not OPENWEATHER_API_KEY: await update.message.reply_text("API ключ для погоды не настроен.") return city = ' '.join(context.args) if context.args else 'Moscow' try: url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={OPENWEATHER_API_KEY}&units=metric&lang=ru" response = requests.get(url) data = response.json() if data['cod'] == 200: weather_text = f""" 🌤️ Погода в {data['name']}: Температура: {data['main']['temp']}°C Ощущается как: {data['main']['feels_like']}°C Влажность: {data['main']['humidity']}% Описание: {data['weather'][0]['description']} """ else: weather_text = "Город не найден." await update.message.reply_text(weather_text, parse_mode='HTML') except Exception as e: await update.message.reply_text(f"Ошибка при получении погоды: {e}") async def news(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Получить новости""" if not NEWS_API_KEY: await update.message.reply_text("API ключ для новостей не настроен.") return try: url = f"https://newsapi.org/v2/top-headlines?country=ru&apiKey={NEWS_API_KEY}" response = requests.get(url) data = response.json() if data['status'] == 'ok' and data['articles']: news_text = "📰 Последние новости:\n\n" for i, article in enumerate(data['articles'][:5], 1): news_text += f"{i}. {article['title']}\n{article['url']}\n\n" else: news_text = "Новости не найдены." await update.message.reply_text(news_text, parse_mode='HTML') except Exception as e: await update.message.reply_text(f"Ошибка при получении новостей: {e}") async def translate(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Перевод текста""" if len(context.args) < 2: await update.message.reply_text("Использование: /translate [текст] [язык] (например: /translate hello en)") return text = ' '.join(context.args[:-1]) target_lang = context.args[-1] # Простой перевод с помощью Google Translate API (нужен API ключ) try: # В реальном проекте используйте Google Translate API или другой сервис await update.message.reply_text(f"Перевод '{text}' на {target_lang}: [здесь будет перевод]") except Exception as e: await update.message.reply_text(f"Ошибка при переводе: {e}") async def play_game(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Запустить мини-игру""" keyboard = [ [InlineKeyboardButton("Камень-ножницы-бумага", callback_data='game_rps')], [InlineKeyboardButton("Крестики-нолики", callback_data='game_tic_tac_toe')], [InlineKeyboardButton("Викторина", callback_data='game_quiz')], [InlineKeyboardButton("⬅️ Назад", callback_data='cmd_start')] ] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text("🎮 Выберите игру:", reply_markup=reply_markup) async def handle_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработка callback запросов от инлайновых кнопок""" query = update.callback_query await query.answer() if query.data == 'cmd_start' or query.data == 'cmd_restart': await self.show_start_menu(query) elif query.data == 'cmd_help': await self.show_help_menu(query) elif query.data == 'cmd_play_game': await self.show_games_menu(query) elif query.data == 'game_rps': await self.start_rps_game(query, context) elif query.data == 'game_tic_tac_toe': await self.start_tic_tac_toe_game(query, context) elif query.data == 'game_quiz': await self.start_quiz_game(query, context) elif query.data.startswith('rps_'): await self.handle_rps(query, context) elif query.data.startswith('tictactoe_'): await self.handle_tic_tac_toe_move(query, context) elif query.data.startswith('quiz_'): await self.handle_quiz_answer(query, context) async def start_rps_game(self, query, context): """Запуск игры 'Камень-ножницы-бумага'""" keyboard = [ [InlineKeyboardButton("🪨 Камень", callback_data='rps_rock')], [InlineKeyboardButton("📄 Бумага", callback_data='rps_paper')], [InlineKeyboardButton("✂️ Ножницы", callback_data='rps_scissors')] ] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("🤖 Камень, ножницы, бумага!\n\nВыберите ваш ход:", reply_markup=reply_markup) async def start_tic_tac_toe_game(self, query, context): """Запуск игры 'Крестики-нолики'""" board = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']] context.user_data['tictactoe_board'] = board context.user_data['tictactoe_turn'] = 'user' keyboard = self.create_tic_tac_toe_keyboard(board) reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("❌⭕ Крестики-нолики!\n\nВы ходите первым. Выберите клетку:", reply_markup=reply_markup) async def handle_tic_tac_toe_move(self, query, context): """Обработка ходов в игре Крестики-нолики""" move_data = query.data.split('_')[1] if move_data == 'restart': await self.start_tic_tac_toe_game(query, context) return if len(move_data) != 2: await query.answer("Неверный формат хода!") return row, col = map(int, move_data) board = context.user_data.get('tictactoe_board') turn = context.user_data.get('tictactoe_turn') if not board or not (0 <= row < 3 and 0 <= col < 3) or board[row][col] != ' ' or turn != 'user': await query.answer("Неверный ход!") return # Ход пользователя board[row][col] = 'X' if self.check_winner(board, 'X'): await query.edit_message_text("🎉 Вы выиграли! 🏆") db.update_score(query.from_user.id, 15) db.update_reputation(query.from_user.id, 15) rank_update = db.update_rank(query.from_user.id, query.message.chat.id, query.from_user.first_name) if rank_update and rank_update.get("promoted"): await query.message.chat.send_message( f"🌟 За активное участие в группе {rank_update['name']} получил(-а) новое звание {rank_update['new_rank']}!" ) # Показать финальное поле с кнопками keyboard = self.create_tic_tac_toe_keyboard(board, game_over=True) reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("🎉 Вы выиграли! 🏆", reply_markup=reply_markup) context.user_data.pop('tictactoe_board', None) context.user_data.pop('tictactoe_turn', None) return if self.is_board_full(board): keyboard = self.create_tic_tac_toe_keyboard(board, game_over=True) reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("🤝 Ничья!", reply_markup=reply_markup) return # Ход бота bot_row, bot_col = self.get_bot_move(board) board[bot_row][bot_col] = 'O' if self.check_winner(board, 'O'): keyboard = self.create_tic_tac_toe_keyboard(board, game_over=True) reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("😞 Вы проиграли. Бот победил!", reply_markup=reply_markup) return if self.is_board_full(board): await query.edit_message_text("🤝 Ничья!") return keyboard = self.create_tic_tac_toe_keyboard(board) reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("❌⭕ Крестики-нолики!\n\nВаш ход. Выберите клетку:", reply_markup=reply_markup) def create_tic_tac_toe_keyboard(self, board, game_over=False): """Создание клавиатуры для игры""" keyboard = [] for i in range(3): row = [] for j in range(3): symbol = board[i][j] if symbol == 'X': text = '❌' elif symbol == 'O': text = '⭕' else: text = ' ' if game_over: # Пустая callback_data для завершенной игры row.append(InlineKeyboardButton(text, callback_data='game_over')) else: row.append(InlineKeyboardButton(text, callback_data=f'tictactoe_{i}{j}')) keyboard.append(row) # Кнопки управления keyboard.append([ InlineKeyboardButton("🔄 Новая игра", callback_data='tictactoe_restart'), InlineKeyboardButton("⬅️ Назад к играм", callback_data='cmd_play_game') ]) return keyboard def check_winner(self, board, player): """Проверка победителя""" for row in board: if all(cell == player for cell in row): return True for col in range(3): if all(row[col] == player for row in board): return True if all(board[i][i] == player for i in range(3)) or all(board[i][2-i] == player for i in range(3)): return True return False def is_board_full(self, board): """Проверка, заполнена ли доска""" return all(cell != ' ' for row in board for cell in row) def get_bot_move(self, board): """Получение хода бота (простая логика)""" # Сначала проверяем, можем ли выиграть for i in range(3): for j in range(3): if board[i][j] == ' ': board[i][j] = 'O' if self.check_winner(board, 'O'): board[i][j] = ' ' return i, j board[i][j] = ' ' # Блокируем ход пользователя for i in range(3): for j in range(3): if board[i][j] == ' ': board[i][j] = 'X' if self.check_winner(board, 'X'): board[i][j] = ' ' return i, j board[i][j] = ' ' # Случайный ход import random while True: i, j = random.randint(0, 2), random.randint(0, 2) if board[i][j] == ' ': return i, j async def handle_rps(self, query, context): """Обработка ходов в игре КНБ""" user_choice = query.data.split('_')[1] bot_choice = random.choice(['rock', 'paper', 'scissors']) choices = {'rock': '🪨 Камень', 'paper': '📄 Бумага', 'scissors': '✂️ Ножницы'} result = self.determine_rps_winner(user_choice, bot_choice) if result == 'win': message = f"🎉 Вы выиграли!\n\nВаш выбор: {choices[user_choice]}\nМой выбор: {choices[bot_choice]}" db.update_score(query.from_user.id, 5) db.update_reputation(query.from_user.id, 5) db.update_reputation(query.from_user.id, 5) elif result == 'lose': message = f"😞 Вы проиграли!\n\nВаш выбор: {choices[user_choice]}\nМой выбор: {choices[bot_choice]}" else: message = f"🤝 Ничья!\n\nВаш выбор: {choices[user_choice]}\nМой выбор: {choices[bot_choice]}" keyboard = [ [InlineKeyboardButton("🔄 Новая игра", callback_data='game_rps')], [InlineKeyboardButton("⬅️ Назад к играм", callback_data='cmd_play_game')] ] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text(message, reply_markup=reply_markup) def determine_rps_winner(self, user, bot): """Определение победителя в КНБ""" if user == bot: return 'draw' elif (user == 'rock' and bot == 'scissors') or \ (user == 'paper' and bot == 'rock') or \ (user == 'scissors' and bot == 'paper'): return 'win' else: return 'lose' async def start_quiz_game(self, query, context): """Запуск викторины""" questions = [ {"question": "Столица Франции?", "answers": ["Париж", "Лондон", "Берлин"], "correct": 0}, {"question": "2 + 2 = ?", "answers": ["3", "4", "5"], "correct": 1}, {"question": "Цвет неба?", "answers": ["Зеленый", "Синий", "Красный"], "correct": 1}, {"question": "Сколько планет в Солнечной системе?", "answers": ["8", "9", "10"], "correct": 0}, {"question": "Какой язык программирования мы используем?", "answers": ["JavaScript", "Python", "Java"], "correct": 1} ] question = random.choice(questions) context.user_data['quiz_correct'] = question['correct'] context.user_data['quiz_question'] = question keyboard = [] for i, answer in enumerate(question['answers']): keyboard.append([InlineKeyboardButton(answer, callback_data=f'quiz_{i}')]) reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text(f"🧠 Викторина:\n\n{question['question']}", reply_markup=reply_markup) async def handle_quiz_answer(self, query, context): """Обработка ответа на вопрос викторины""" answer_index = int(query.data.split('_')[1]) correct_index = context.user_data.get('quiz_correct') if correct_index is None: await query.edit_message_text("Вопрос викторины не найден.") return question = context.user_data.get('quiz_question', {}) if answer_index == correct_index: keyboard = [ [InlineKeyboardButton("🔄 Новая игра", callback_data='game_quiz')], [InlineKeyboardButton("⬅️ Назад к играм", callback_data='cmd_play_game')] ] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text(f"🎉 Правильно! Вы заработали 5 очков!\n\nВопрос: {question.get('question', '')}\nОтвет: {question.get('answers', [])[correct_index]}", reply_markup=reply_markup) db.update_score(query.from_user.id, 5) else: keyboard = [ [InlineKeyboardButton("🔄 Новая игра", callback_data='game_quiz')], [InlineKeyboardButton("⬅️ Назад к играм", callback_data='cmd_play_game')] ] reply_markup = InlineKeyboardMarkup(keyboard) correct_answer = question.get('answers', [])[correct_index] if correct_index < len(question.get('answers', [])) else "неизвестен" await query.edit_message_text(f"❌ Неправильно!\n\nВопрос: {question.get('question', '')}\nПравильный ответ: {correct_answer}", reply_markup=reply_markup) # Очистка данных викторины context.user_data.pop('quiz_correct', None) context.user_data.pop('quiz_question', None) async def ban_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Забанить пользователя (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") return if len(context.args) < 2: await update.message.reply_text("Использование: /ban [пользователь] [причина]") return user_id = context.args[0] reason = ' '.join(context.args[1:]) try: await update.effective_chat.ban_member(int(user_id)) await update.message.reply_text(f"🚫 Пользователь {user_id} забанен.\nПричина: {reason}") except Exception as e: await update.message.reply_text(f"❌ Ошибка при бане пользователя: {e}") async def unban_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Разбанить пользователя (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") return if len(context.args) < 1: await update.message.reply_text("Использование: /unban [пользователь]") return user_id = context.args[0] try: await update.effective_chat.unban_member(int(user_id)) await update.message.reply_text(f"✅ Пользователь {user_id} разбанен.") except Exception as e: await update.message.reply_text(f"❌ Ошибка при разбане пользователя: {e}") async def mute_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Заглушить пользователя (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") return if len(context.args) < 2: await update.message.reply_text("Использование: /mute [пользователь] [время_в_секундах]") return user_id = context.args[0] try: mute_time = int(context.args[1]) except ValueError: await update.message.reply_text("❌ Время должно быть числом в секундах.") return from datetime import datetime, timedelta until_date = datetime.now() + timedelta(seconds=mute_time) try: await update.effective_chat.restrict_member( int(user_id), until_date=until_date, can_send_messages=False ) await update.message.reply_text(f"🔇 Пользователь {user_id} заглушен на {mute_time} секунд.") except Exception as e: await update.message.reply_text(f"❌ Ошибка при mute пользователя: {e}") async def unmute_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Снять заглушку с пользователя (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") return if len(context.args) < 1: await update.message.reply_text("Использование: /unmute [пользователь]") return user_id = context.args[0] try: await update.effective_chat.restrict_member( int(user_id), can_send_messages=True, can_send_media_messages=True, can_send_other_messages=True, can_add_web_page_previews=True ) await update.message.reply_text(f"🔊 Заглушка с пользователя {user_id} снята.") except Exception as e: await update.message.reply_text(f"❌ Ошибка при unmute пользователя: {e}") async def kick_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Кикнуть пользователя (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") return if len(context.args) < 2: await update.message.reply_text("Использование: /kick [пользователь] [причина]") return user_id = context.args[0] reason = ' '.join(context.args[1:]) try: await update.effective_chat.ban_member(int(user_id)) await update.effective_chat.unban_member(int(user_id)) # Разбан сразу после бана = кик await update.message.reply_text(f"👢 Пользователь {user_id} кикнут.\nПричина: {reason}") except Exception as e: await update.message.reply_text(f"❌ Ошибка при кике пользователя: {e}") async def promote_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Повысить пользователя до модератора (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") #return if len(context.args) < 1: await update.message.reply_text("Использование: /promote [пользователь]") return user_id = context.args[0] try: await update.effective_chat.promote_member( int(user_id), can_delete_messages=True, can_restrict_members=True, can_invite_users=True ) await update.message.reply_text(f"⬆️ Пользователь {user_id} повышен до модератора.") except Exception as e: await update.message.reply_text(f"❌ Ошибка при повышении пользователя: {e}") async def demote_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Понизить пользователя с модератора (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") return if len(context.args) < 1: await update.message.reply_text("Использование: /demote [пользователь]") return user_id = context.args[0] try: await update.effective_chat.promote_member( int(user_id), can_delete_messages=False, can_restrict_members=False, can_invite_users=False, can_pin_messages=False ) await update.message.reply_text(f"⬇️ Пользователь {user_id} понижен с модератора.") except Exception as e: await update.message.reply_text(f"❌ Ошибка при понижении пользователя: {e}") async def warn_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Выдать предупреждение пользователю (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") return if len(context.args) < 2: await update.message.reply_text("Использование: /warn [пользователь] [причина]") return user_id = context.args[0] reason = ' '.join(context.args[1:]) db.add_warning(user_id, reason, update.effective_user.id) await update.message.reply_text(f"⚠️ Предупреждение выдано пользователю {user_id} по причине: {reason}") async def ranks_info(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Показать информацию о системе рангов""" ranks_text = """ 🏆 Военная система рангов Как работает система: • Каждый участник начинает с звания "Рядовой" • Звание повышается автоматически при достижении определенного количества очков • Очки начисляются за активность в чате и победы в играх Иерархия званий: 🪖 Рядовой (0 очков) 🪖 Ефрейтор (100 очков) 🪖 Младший сержант (235 очков) 🪖 Сержант (505 очков) 🪖 Старший сержант (810 очков) 🪖 Старшина (1,250 очков) 🪖 Прапорщик (1,725 очков) 🪖 Старший прапорщик (2,335 очков) 🪖 Младший лейтенант (2,980 очков) 🪖 Лейтенант (3,760 очков) 🪖 Старший лейтенант (4,575 очков) 🪖 Капитан (5,525 очков) 🪖 Майор (6,510 очков) 🪖 Подполковник (7,630 очков) 🪖 Полковник (8,785 очков) 🪖 Генерал майор (16,075 очков) 🪖 Генерал лейтенант (32,150 очков) 🪖 Генерал полковник (64,300 очков) 🪖 Генерал армии (128,600 очков) 🪖 Маршал (256,000 очков) Как получить очки: • +1 очко за каждое сообщение в чате • +5 очков за победу в мини-игре • +15 очков за победу в крестиках-ноликах • Бонусные очки за активность (случайно 1-5 очков) Команды для работы с рангом: /rank - Показать ваш текущий ранг и очки /leaderboard - Топ-10 участников по очкам /info - Детальная информация о вашем профиле Служите чату верой и правдой, чтобы достичь высших воинских званий! 🎖️ """ await update.message.reply_text(ranks_text, parse_mode='HTML') async def import_csv(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Импорт пользователей из CSV файла (только админы)""" if not await self.is_admin(update.effective_chat, update.effective_user.id): await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") return if len(context.args) < 1: await update.message.reply_text("Использование: /import_csv [путь_к_файлу]\n\nПо умолчанию: /import_csv chat_-1001519866478_users_full_20251014.csv") return csv_file = context.args[0] if context.args else 'chat_-1001519866478_users_full_20251014.csv' # Если путь относительный, добавляем путь к директории telegram_bot if not os.path.isabs(csv_file): csv_file = os.path.join('telegram_bot', csv_file) await update.message.reply_text(f"🔄 Начинаю импорт пользователей из файла: {csv_file}") try: success = db.import_users_from_csv(csv_file) if success: await update.message.reply_text("✅ Импорт пользователей успешно завершен!") else: await update.message.reply_text("❌ Произошла ошибка при импорте пользователей.") except Exception as e: await update.message.reply_text(f"❌ Ошибка при импорте: {str(e)}") async def schedule_post(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Запланировать пост для публикации""" user = update.effective_user # Проверка прав администратора if not await self.is_admin(update.effective_chat, user.id): await update.message.reply_text("❌ У вас нет прав для планирования постов.") return if len(context.args) < 2: await update.message.reply_text( "Использование: /schedule_post [время] [текст поста]\n\n" "Формат времени:\n" "• Абсолютное: 2024-01-15 14:30:00\n" "• Относительное: +30m (через 30 минут), +2h (через 2 часа), +1d (через день)\n\n" "Пример: /schedule_post +2h Привет всем!" ) return # Парсинг времени и текста time_str = context.args[0] text = ' '.join(context.args[1:]) try: schedule_time = self.parse_schedule_time(time_str) except ValueError as e: await update.message.reply_text(f"❌ Ошибка формата времени: {str(e)}") return if schedule_time <= datetime.now(): await update.message.reply_text("❌ Время публикации должно быть в будущем!") return # Добавление поста в базу данных post_id = db.add_scheduled_post( chat_id=update.effective_chat.id, text=text, schedule_time=schedule_time.strftime('%Y-%m-%d %H:%M:%S'), created_by=user.id ) if post_id: await update.message.reply_text( f"✅ Пост запланирован!\n\n" f"📅 Время публикации: {schedule_time.strftime('%Y-%m-%d %H:%M:%S')}\n" f"📝 Текст: {text[:50]}{'...' if len(text) > 50 else ''}\n" f"🆔 ID поста: {post_id}" ) else: await update.message.reply_text("❌ Ошибка при сохранении поста в базу данных.") async def list_posts(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Показать список запланированных постов""" user = update.effective_user # Проверка прав администратора if not await self.is_admin(update.effective_chat, user.id): await update.message.reply_text("❌ У вас нет прав для просмотра постов.") return posts = db.get_scheduled_posts(chat_id=update.effective_chat.id) if not posts: await update.message.reply_text("📭 Нет запланированных постов.") return response = "📋 Запланированные посты:\n\n" for post in posts: post_id, chat_id, text, image_path, schedule_time, created_by, status, published_at, created_at, creator_name = post response += f"🆔 {post_id}\n" response += f"📅 {schedule_time}\n" response += f"👤 Создал: {creator_name or 'Неизвестен'}\n" response += f"📝 {text[:100]}{'...' if len(text) > 100 else ''}\n" if image_path: response += f"🖼 Изображение: {image_path}\n" response += "\n" + "─" * 30 + "\n" await update.message.reply_text(response, parse_mode='HTML') async def delete_post(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Удалить запланированный пост""" user = update.effective_user # Проверка прав администратора if not await self.is_admin(update.effective_chat, user.id): await update.message.reply_text("❌ У вас нет прав для удаления постов.") return if len(context.args) < 1: await update.message.reply_text("Использование: /delete_post [ID поста]") return try: post_id = int(context.args[0]) except ValueError: await update.message.reply_text("❌ ID поста должен быть числом.") return success = db.delete_scheduled_post(post_id, user.id) if success: await update.message.reply_text(f"✅ Пост {post_id} удален.") else: await update.message.reply_text(f"❌ Пост {post_id} не найден или у вас нет прав для его удаления.") async def publish_now(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Опубликовать пост немедленно""" user = update.effective_user # Проверка прав администратора if not await self.is_admin(update.effective_chat, user.id): await update.message.reply_text("❌ У вас нет прав для публикации постов.") return if len(context.args) < 1: await update.message.reply_text("Использование: /publish_now [ID поста]") return try: post_id = int(context.args[0]) except ValueError: await update.message.reply_text("❌ ID поста должен быть числом.") return # Получаем пост из базы данных posts = db.get_scheduled_posts() post = None for p in posts: if p[0] == post_id: # p[0] is post_id post = p break if not post: await update.message.reply_text(f"❌ Пост {post_id} не найден.") return post_id, chat_id, text, image_path, schedule_time, created_by, status, published_at, created_at, creator_name = post try: if image_path and os.path.exists(image_path): with open(image_path, 'rb') as photo: await update.effective_chat.send_photo(photo, caption=text) else: await update.effective_chat.send_message(text) # Отмечаем пост как опубликованный db.mark_post_published(post_id) await update.message.reply_text(f"✅ Пост {post_id} опубликован немедленно!") except Exception as e: await update.message.reply_text(f"❌ Ошибка при публикации поста: {str(e)}") def parse_schedule_time(self, time_str): """Парсинг времени публикации из строки""" from datetime import datetime, timedelta import re current_time = datetime.now() # Абсолютное время: 2024-01-15 14:30:00 или 2024-01-15 14:30 absolute_pattern = r'^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?$' match = re.match(absolute_pattern, time_str) if match: year, month, day, hour, minute = map(int, match.groups()[:5]) second = int(match.group(6)) if match.group(6) else 0 return datetime(year, month, day, hour, minute, second) # Относительное время: +30m, +2h, +1d relative_pattern = r'^(\+|\-)(\d+)([mhd])$' match = re.match(relative_pattern, time_str) if match: sign, amount, unit = match.groups() amount = int(amount) if unit == 'm': # минуты delta = timedelta(minutes=amount) elif unit == 'h': # часы delta = timedelta(hours=amount) elif unit == 'd': # дни delta = timedelta(days=amount) if sign == '-': delta = -delta return current_time + delta raise ValueError("Неверный формат времени. Используйте абсолютное время (2024-01-15 14:30) или относительное (+30m, +2h, +1d)") async def is_admin(self, chat, user_id): """Проверка, является ли пользователь администратором""" try: member = await chat.get_member(user_id) return member.status in ['administrator', 'creator'] except: return False async def handle_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработка текстовых сообщений""" user = update.effective_user message_text = update.message.text.lower() # Добавление пользователя в базу данных db.add_user(user.id, user.username, user.first_name, user.last_name) # Начисление очков за сообщение и обновление статистики db.update_score(user.id) # Обновление репутации за активность db.update_reputation(user.id, 1) # Проверка на повышение ранга rank_update = db.update_rank(user.id, update.effective_chat.id, user.first_name) if rank_update and rank_update.get("promoted"): await update.effective_chat.send_message( f"🌟 За активное участие в группе {rank_update['name']} получил(-а) новое звание {rank_update['new_rank']}!" ) # Проверка на слова "реквизиты" if "реквизиты" in message_text or "реквизит" in message_text: await update.message.reply_text(BANK_DETAILS_TEXT, reply_to_message_id=update.message.message_id) return # Предопределенные ответы response_found = False for key, response in PREDEFINED_RESPONSES.items(): if key in message_text: await update.message.reply_text(response) response_found = True break if not response_found: # Если нет предопределенного ответа, показываем сообщение и help keyboard = [ [InlineKeyboardButton("📋 Помощь", callback_data='cmd_help')], [InlineKeyboardButton("🎮 Мини игры", callback_data='cmd_play_game')] ] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text( "🤖 Извините, я не понял ваше сообщение. Вот что я умею:\n\n" "• Отвечать на простые вопросы\n" "• Вести рейтинг участников\n" "• Предоставлять погоду и новости\n" "• Играть в мини-игры\n" "• Помогать с модерацией\n\n" "Используйте /help для подробной информации.", reply_markup=reply_markup ) return # Случайное начисление очков от 1 до 5 вместо случайных ответов if random.random() < 0.1: # 10% шанс bonus_points = random.randint(1, 5) db.update_score(user.id, bonus_points) await update.message.reply_text(f"🎁 Бонус! Вам начислено {bonus_points} очков!") async def handle_new_chat_members(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработка новых участников чата""" for member in update.chat_member.new_chat_members: db.add_user(member.id, member.username, member.first_name, member.last_name) welcome_text = f""" 🎉 Добро пожаловать в чат, {member.first_name}! Мы рады видеть вас здесь! Используйте /help чтобы узнать о возможностях бота. """ await update.effective_chat.send_message(welcome_text) async def handle_inline_query(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработка инлайновых запросов""" query = update.inline_query.query results = [] try: if query.startswith('weather'): city = query.split(' ', 1)[1] if len(query.split(' ', 1)) > 1 else 'Moscow' if OPENWEATHER_API_KEY: url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={OPENWEATHER_API_KEY}&units=metric&lang=ru" response = requests.get(url) data = response.json() if data['cod'] == 200: weather_text = f"🌤️ Погода в {data['name']}:\nТемпература: {data['main']['temp']}°C\nОщущается как: {data['main']['feels_like']}°C\nВлажность: {data['main']['humidity']}%\nОписание: {data['weather'][0]['description']}" results.append(InlineQueryResultArticle( id='1', title=f"Погода в {city}", input_message_content=InputTextMessageContent(weather_text) )) else: results.append(InlineQueryResultArticle( id='1', title="Город не найден", input_message_content=InputTextMessageContent("Город не найден") )) elif query.startswith('translate'): # Базовая заглушка для перевода text_parts = query.split(' ', 2) if len(text_parts) >= 3: text = text_parts[1] lang = text_parts[2] result_text = f"Перевод '{text}' на {lang}: [здесь будет перевод]" results.append(InlineQueryResultArticle( id='1', title=f"Перевод на {lang}", input_message_content=InputTextMessageContent(result_text) )) await update.inline_query.answer(results) except Exception as e: print(f"Ошибка при обработке инлайнового запроса: {e}") async def show_start_menu(self, query): """Показать стартовое меню""" keyboard = [ [InlineKeyboardButton("📋 Помощь", callback_data='cmd_help')], [InlineKeyboardButton("🎮 Мини игры", callback_data='cmd_play_game')] ] reply_markup = InlineKeyboardMarkup(keyboard) welcome_text = f""" Привет, {query.from_user.first_name}! 👋 Я ваш помощник в чате. Вот что я умею: • Отвечать на сообщения • Вести рейтинг участников • Предоставлять погоду и новости • Играть в мини-игры • Помогать с модерацией Используйте /help для подробной информации. """ await query.edit_message_text(welcome_text, reply_markup=reply_markup) async def show_help_menu(self, query): """Показать меню помощи""" keyboard = [ [InlineKeyboardButton("🚀 Старт", callback_data='cmd_start')], [InlineKeyboardButton("🔄 Начать заново", callback_data='cmd_restart')] ] reply_markup = InlineKeyboardMarkup(keyboard) help_text = HELP_TEXT await query.edit_message_text(help_text, parse_mode='HTML', reply_markup=reply_markup) async def show_games_menu(self, query): """Показать меню выбора игр""" keyboard = [ [InlineKeyboardButton("Камень-ножницы-бумага", callback_data='game_rps')], [InlineKeyboardButton("Крестики-нолики", callback_data='game_tic_tac_toe')], [InlineKeyboardButton("Викторина", callback_data='game_quiz')], [InlineKeyboardButton("⬅️ Назад", callback_data='cmd_start')] ] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("🎮 Выберите игру:", reply_markup=reply_markup) def run(self): """Запуск бота""" print("Bot started!") self.application.run_polling() if __name__ == '__main__': bot = TelegramBot() bot.run()