Помоги пожалуйста исправить код для работы телегра...
تم الإنشاء في: ١٦ فبراير ٢٠٢٥
تم الإنشاء في: ١٦ فبراير ٢٠٢٥
Помоги пожалуйста исправить код для работы телеграм бота "Quiz IT". Я попыталась создавать код для его работы, но так как я абсолютно не знаю как это делать, я зашла в тупик
Распишу как представлю своего бота.
Ниже приведён пример относительно компактного, но при этом рабочего кода на python-telegram-bot (v20+), в котором учтены все основные моменты, о которых вы упоминали:
/start
– приветствие;/help
– список доступных команд;/points
– показать текущие очки пользователя;/quiz
– начать викторину (сразу выводит выбор уровня);/level
– отдельная команда для выбора уровня;/rules
– правила викторины;/stop
– досрочное завершение викторины.Обратите внимание:
SELECT … ORDER BY RANDOM() LIMIT 1
, если нужно брать по одному вопросу за раз; но тогда понадобится следить, чтобы вопрос не повторялся. Ниже реализован вариант, когда мы за один раз берём все 15 вопросов для выбранного уровня, перемешиваем, а потом выдаём по одному.pythonimport logging import sqlite3 import base64 import io from telegram import ( Update, InlineKeyboardButton, InlineKeyboardMarkup, InputMediaPhoto ) from telegram.ext import ( ApplicationBuilder, CommandHandler, CallbackQueryHandler, ContextTypes, ConversationHandler, MessageHandler, filters ) from telegram.error import BadRequest # В python-telegram-bot v20+ логгер лучше настраивать вот так: logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) # --- Настройка глобального доступа к БД --- DB_PATH = "quiz_db.sqlite" # Путь к вашей базе данных SQLite # Предположим, что у вас есть 3 таблицы: # Light (id от 1 до 15), Normal (id от 16 до 30), Hard (id от 31 до 45). # Столбцы: id, image(base64), question, v1, v2, v3, answer, hint def get_connection(): """Возвращает новое соединение с БД.""" conn = sqlite3.connect(DB_PATH) # Чтобы обращаться к столбцам по имени (dict-стиль), укажем row_factory conn.row_factory = sqlite3.Row return conn # Сопоставляем уровень сложности названию таблицы TABLE_NAMES = { 'easy': 'Light', 'medium': 'Normal', 'hard': 'Hard' } # --- Константы для ConversationHandler (если используем) или хранения состояний --- CHOOSING_LEVEL = 1 ANSWER_QUESTION = 2 # Этот ключ будем использовать в user_data для хранения нужной информации QUIZ_DATA_KEY = "quiz_data" TOKEN = "<ВАШ_TELEGRAM_БОТ_TOKEN>" # ======================= ФУНКЦИИ РАБОТЫ С БД ======================= def load_questions(level: str): """ Загружает все 15 вопросов для выбранного уровня (easy/medium/hard) из соответствующей таблицы (Light/Normal/Hard). Возвращает список словарей, где каждый словарь — это одна строка из БД. """ table_name = TABLE_NAMES[level] conn = get_connection() cursor = conn.cursor() # Берём все 15 вопросов (если их ровно 15) или можно ограничить явно LIMIT 15 cursor.execute(f"SELECT * FROM {table_name}") rows = cursor.fetchall() conn.close() # Преобразуем rows в обычный список словарей для удобства questions = [dict(row) for row in rows] return questions # ======================= ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ======================= async def show_question(update: Update, context: ContextTypes.DEFAULT_TYPE): """ Показать текущий вопрос пользователю в виде сообщения с кнопками. Кроме того, запустить таймер на 50 секунд, чтобы если пользователь не ответит, засчитать автоматический пропуск. """ user_data = context.user_data.setdefault(QUIZ_DATA_KEY, {}) current_index = user_data.get("current_index", 0) questions = user_data.get("questions", []) # Если вопросов больше нет или достигли конца списка if current_index >= len(questions): await finish_quiz(update, context) return current_question = questions[current_index] question_text = current_question["question"] # Составляем список вариантов ответа (4 варианта: v1, v2, v3, answer) variants = [ current_question["v1"], current_question["v2"], current_question["v3"], current_question["answer"] ] # Чтобы варианты шли в случайном порядке import random random.shuffle(variants) # Формируем кнопки с callback_data = тексту варианта, # чтобы потом проще было сравнивать keyboard = [] for variant in variants: keyboard.append([InlineKeyboardButton(variant, callback_data=variant)]) reply_markup = InlineKeyboardMarkup(keyboard) # Попробуем вывести изображение, если оно есть (base64). # Если оно не пустое, декодируем и отправляем как фото. base64_image = current_question.get("image", "") if base64_image: try: decoded_image = base64.b64decode(base64_image) # Отправляем вопрос с фото await update.message.reply_photo( photo=decoded_image, caption=f"Вопрос {current_index + 1}:\n{question_text}", reply_markup=reply_markup ) except (base64.binascii.Error, BadRequest) as e: # Если вдруг ошибка декодирования/отправки, просто отправим текст logger.warning(f"Не удалось отправить изображение: {e}") await update.message.reply_text( text=f"Вопрос {current_index + 1}:\n{question_text}", reply_markup=reply_markup ) else: # Если картинки нет, просто отправим вопрос текстом await update.message.reply_text( text=f"Вопрос {current_index + 1}:\n{question_text}", reply_markup=reply_markup ) # --- Запускаем таймер на 50 секунд --- # Удаляем предыдущую задачу (если была), чтобы не наложились job_removed = remove_job_if_exists(f"question_timer_{update.effective_user.id}", context) # Создаём новую задачу context.job_queue.run_once( callback=question_time_is_up, when=50, # 50 секунд name=f"question_timer_{update.effective_user.id}", data={ "chat_id": update.effective_chat.id, "user_id": update.effective_user.id } ) def remove_job_if_exists(name: str, context: ContextTypes.DEFAULT_TYPE): """ Удаляем Job (таймер), если он существует, чтобы не наслаивалось несколько таймеров. Возвращает True, если удалили, иначе False. """ current_jobs = context.job_queue.get_jobs_by_name(name) if not current_jobs: return False for job in current_jobs: job.schedule_removal() return True async def question_time_is_up(context: ContextTypes.DEFAULT_TYPE): """ Вызывается по таймеру через 50 секунд, если пользователь не успел ответить. """ job_data = context.job.data chat_id = job_data["chat_id"] user_id = job_data["user_id"] user_data = context.application.user_data[user_id].get(QUIZ_DATA_KEY, {}) questions = user_data.get("questions", []) current_index = user_data.get("current_index", 0) if current_index >= len(questions): # Возможно, пользователь уже закончил return current_question = questions[current_index] # Отправляем сообщение, что время вышло и правильный ответ correct_answer = current_question["answer"] hint = current_question.get("hint", "Нет подсказки.") text_time_is_up = ( f"Время вышло! Правильный ответ: *{correct_answer}*\n\n" f"Подсказка (hint): {hint}" ) await context.bot.send_message( chat_id=chat_id, text=text_time_is_up, parse_mode="Markdown" ) # Переходим к следующему вопросу user_data["current_index"] = current_index + 1 # Сохраняем обратно context.application.user_data[user_id][QUIZ_DATA_KEY] = user_data # Показываем следующий вопрос # Чтобы показать следующий вопрос, нам надо "подделать" объект update: # но проще вызвать специализированную функцию повторно. class FakeUpdate: message = None fake_update = FakeUpdate() fake_update.message = type("Msg", (), {"reply_text": lambda x, **kw: None}) fake_update.effective_user = type("User", (), {"id": user_id}) fake_update.effective_chat = type("Chat", (), {"id": chat_id}) await show_question(fake_update, context) async def finish_quiz(update: Update, context: ContextTypes.DEFAULT_TYPE): """ Завершение викторины – вывод итогового результата и обнуление данных в user_data (если надо). """ user_data = context.user_data.get(QUIZ_DATA_KEY, {}) score = user_data.get("score", 0) await update.message.reply_text( f"Викторина окончена!\nВаш результат: {score} баллов.\nСпасибо за игру!" ) # Сброс данных, если хотите context.user_data[QUIZ_DATA_KEY] = {} # ======================= ОБРАБОТЧИКИ КОМАНД ======================= async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Приветственное сообщение на /start.""" await update.message.reply_text( "Привет! Я — QuizTGIT.\n" "Приготовьтесь проверить свои знания и прокачать навыки с помощью увлекательных викторин!\n\n" "Список доступных команд смотрите в /help." ) async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Вывод списка команд на /help.""" commands_text = ( "/start - Начало работы, приветственное сообщение\n" "/help - Список команд\n" "/points - Показать, сколько у вас сейчас баллов\n" "/quiz - Начать викторину (с выбором уровня)\n" "/level - Выбрать уровень сложности\n" "/rules - Правила игры\n" "/stop - Досрочно завершить викторину\n" ) await update.message.reply_text(commands_text) async def points_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Показать текущие очки пользователя.""" user_data = context.user_data.get(QUIZ_DATA_KEY, {}) score = user_data.get("score", 0) await update.message.reply_text(f"Ваши текущие баллы: {score}") async def quiz_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """ Запуск викторины. Отправляем кнопки с выбором уровня. Можно сразу сделать переход к тому же, что и /level. """ await choose_level_command(update, context) async def choose_level_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Выбор уровня сложности /level — выводит кнопки easy/medium/hard.""" keyboard = [ [InlineKeyboardButton("Easy", callback_data="easy")], [InlineKeyboardButton("Medium", callback_data="medium")], [InlineKeyboardButton("Hard", callback_data="hard")], ] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text( "Выбери уровень сложности:", reply_markup=reply_markup ) async def rules_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Объяснение правил викторины.""" rules_text = ( "Правила викторины:\n" "1) Выбираете уровень сложности.\n" "2) Вам приходит вопрос и 4 варианта ответа.\n" "3) На ответ у вас есть 50 секунд.\n" "4) За правильный ответ +1 балл, за неправильный 0.\n" "5) В конце покажем ваш итоговый счёт.\n" "Удачи!" ) await update.message.reply_text(rules_text) async def stop_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Принудительно завершить викторину (если она идёт).""" user_data = context.user_data.get(QUIZ_DATA_KEY, {}) score = user_data.get("score", 0) await update.message.reply_text( f"Викторина остановлена. Ваши баллы: {score}\n" "Спасибо за участие! Продолжайте развиваться!" ) context.user_data[QUIZ_DATA_KEY] = {} # ======================= ОБРАБОТЧИКИ CALLBACK (кнопок) ======================= async def level_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): """ Вызывается, когда пользователь нажимает на одну из кнопок выбора уровня. В callback_data будет 'easy', 'medium' или 'hard'. Загружаем вопросы, перемешиваем и начинаем задавать. """ query = update.callback_query level = query.data # 'easy', 'medium', 'hard' # Подтверждаем нажатие (обязательно для Inline кнопок) await query.answer() # Загружаем вопросы для нужного уровня questions = load_questions(level) if not questions: await query.message.reply_text( "Не удалось загрузить вопросы для выбранного уровня." ) return import random random.shuffle(questions) # Перемешиваем список вопросов # Сохраняем в user_data user_data = context.user_data.setdefault(QUIZ_DATA_KEY, {}) user_data["questions"] = questions user_data["current_index"] = 0 user_data["score"] = 0 # обнуляем счёт # Удаляем кнопки выбора уровня, заменяем сообщение await query.edit_message_text( text=f"Отлично! Начинаем уровень: {level.capitalize()}" ) # Показываем первый вопрос # Но у нас текущий CallbackQuery не содержит update.message, # поэтому нужно «искусственно» вызвать show_question. # Либо воспользуемся query.message.chat_id fake_update = Update( update.update_id, message=query.message ) await show_question(fake_update, context) async def answer_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): """ Обработка нажатия на варианты ответа (4 кнопки). Сравниваем с правильным ответом, прибавляем очки, выводим hint и идём дальше. """ query = update.callback_query chosen_answer = query.data # что нажал пользователь # Подтверждаем клик await query.answer() user_data = context.user_data.get(QUIZ_DATA_KEY, {}) current_index = user_data.get("current_index", 0) questions = user_data.get("questions", []) if current_index >= len(questions): # Викторина уже закончена await query.message.reply_text("Вопросов больше нет!") return current_question = questions[current_index] correct_answer = current_question["answer"] hint = current_question.get("hint", "Нет подсказки.") # Снимаем таймер, чтобы не сработал через 50 сек remove_job_if_exists(f"question_timer_{query.from_user.id}", context) if chosen_answer == correct_answer: user_data["score"] = user_data.get("score", 0) + 1 result_text = ( f"Верно!\n" f"Правильный ответ: *{correct_answer}*\n" f"Подсказка: {hint}\n\n" f"Вы заработали +1 балл!" ) else: result_text = ( f"Неверно.\n" f"Правильный ответ: *{correct_answer}*\n" f"Подсказка: {hint}" ) await query.message.reply_text(result_text, parse_mode="Markdown") # Переходим к следующему вопросу user_data["current_index"] = current_index + 1 context.user_data[QUIZ_DATA_KEY] = user_data # Вызываем show_question для следующего fake_update = Update( update.update_id, message=query.message ) await show_question(fake_update, context) # ======================= ГЛАВНАЯ ФУНКЦИЯ MAIN ======================= def main(): app = ApplicationBuilder().token(TOKEN).build() # Регистрируем хендлеры команд app.add_handler(CommandHandler("start", start_command)) app.add_handler(CommandHandler("help", help_command)) app.add_handler(CommandHandler("points", points_command)) app.add_handler(CommandHandler("quiz", quiz_command)) app.add_handler(CommandHandler("level", choose_level_command)) app.add_handler(CommandHandler("rules", rules_command)) app.add_handler(CommandHandler("stop", stop_command)) # Регистрируем хендлеры нажатия кнопок # 1) Выбор уровня app.add_handler(CallbackQueryHandler(level_callback, pattern="^(easy|medium|hard)$")) # 2) Ответ на вопрос (проверка) # Здесь нет чёткого паттерна, так как callback_data – это текст варианта. # Но мы можем отфильтровать, чтобы это не совпало со "easy|medium|hard" # или прописать другой метод. Для упрощения – другой CallbackQueryHandler: app.add_handler(CallbackQueryHandler(answer_callback)) # Запускаем бота print("Бот запущен...") app.run_polling() if __name__ == "__main__": main()
Подключение к базе:
get_connection()
возвращает соединение с SQLite, где DB_PATH
– ваш путь к файлу БД.TABLE_NAMES
в зависимости от уровня: Light
, Normal
, Hard
.Выбор уровня:
/quiz
(или /level
) бот выдаёт кнопки Easy, Medium, Hard.level_callback
, где мы загружаем 15 вопросов из таблицы, перемешиваем и сохраняем в user_data
.Показ вопроса:
show_question
берёт из user_data
текущий индекс и вопрос.base64
, отправляет её (через reply_photo
); если нет – текстом.question_time_is_up
.Таймер (JobQueue
):
question_time_is_up
, бот сообщает «Время вышло!», показывает правильный ответ и hint, затем переходит к следующему вопросу.answer_callback
срабатывает, и в нём снимается этот таймер (remove_job_if_exists
).Проверка ответа (answer_callback
):
chosen_answer
(то, что нажал пользователь) с correct_answer
.Завершение:
finish_quiz
: «Викторина окончена», вывод текущего счёта./stop
можно прервать: выводится «Викторина остановлена».Просмотр очков:
/points
показывает, сколько баллов у пользователя на данный момент (user_data["score"]
).Таким образом, вы имеете пример полноценного рабочего кода, который можно адаптировать под ваши конкретные нужды. При желании можно усложнять логику: добавлять сохранение прогресса в БД, разные режимы повторного прохождения, детальные логи и т. д.
Важное замечание: Следите за тем, чтобы названия столбцов в SQL-таблицах и то, что вы извлекаете в коде (
row["question"]
,row["v1"]
и т. п. ) совпадали. Если в базе другие названия, нужно подкорректировать запрос.
Надеюсь, этот пример поможет вам успешно запустить и настроить вашего Telegram-бота-викторину!
По координатам двух точек на плоскости выведи координаты точки середины отрезка, соединяющего их.
Координаты точек A и B — целые или дробные числа. Есть 4 числа по одному в строке: абсцисса точки A=1ордината точки A=1, абсцисса точки B=2 и ордината точки B=2.
Надо вывести координаты середины отрезка: абсциссу и ординату через пробел в одной строке.
Прочитать дробные числа можно так value = float(input())
Напиши программу (10 класс информатика)
Вот простой пример решения на Python:
python# Считываем координаты точки A и точки B x1 = float(input()) y1 = float(input()) x2 = float(input()) y2 = float(input()) # Координаты середины отрезка mx = (x1 + x2) / 2 my = (y1 + y2) / 2 # Выводим абсциссу и ординату середины через пробел print(mx, my)
Как это работает:
Если ввести, например:
text1 1 2 2
Вы получите:
1.5 1.5