🔥 Алексей. Занимаюсь frontend-разработкой и пишу SQL скрипты в одной it компании. Веду личный блог о моем опыте в IT и около IT на YouTube - https://www.youtube.com/@tehno.maniak
Представляю бесплатного Telegram-бота, разработанного для эффективной подготовки к техническому собеседованию на позицию Frontend разработчика. Бот предлагает викторины по HTML, CSS, JavaScript и React, а также рейтинговый режим для соревнования с другими пользователями. Полный код проекта можно посмотреть в моем Github-репозитории.
В боте присутствует 4 категории вопросов: HTML, CSS, JavaScript и React.
Чтобы было интереснее решать вопросы, я добавил еще 🏆Рейтинговый режим, в котором отсутствует выбор категории и вам предстоит решать все имеющиеся в базе вопросы. За каждый правильный ответ вам присуждается 1 балл. В случае неправильного ответа игра прекратится, а ее результат будет записан в базу данных и сохранен в вашем профиле.
Для добавления элемента соревнования между другими пользователями бота я добавил кнопку – Таблица лидеров, после нажатия на которую будет выведено ТОП-10 игроков Рейтингового режима.
У каждого игрока есть свой 👨🏼💻Профиль, в котором можно увидеть статистику по решенным вопросам в 4 категориях и баллы за игру в 🏆Рейтинговом режиме.
Создадим экземпляр бота и инициализируем его с API-ключом Telegram:
Определим начальное состояние сессии для каждого пользователя, которое взаимодействует с ботом:
Для скорости работы Telegram-бота добавим функцию для загрузки вопросов из JSON-файлов при запуске бота:
Для хранения данных о пользователях и их результатах в таблице лидеров инициализируем базу данных SQLite. Эта функция открывает (или создает) базу данных и создает таблицу leaderboard, если она еще не существует:
Добавим функции для работы с базой данных SQLite для управления профилями пользователей и их результатами в таблице лидеров.
Функция createProfile. Эта функция создает профиль для пользователя, если его еще нет в таблице leaderboard.
Функция updateLeaderboard. Эта функция обновляет таблицу лидеров для пользователя, добавляя или обновляя его запись в зависимости от результатов.
Функция getLeaderboard. Эта функция возвращает топ 10 пользователей из таблицы лидеров, отсортированных по убыванию очков.
Функция getTotalUsers. Эта функция возвращает общее количество пользователей в таблице лидеров.
Добавим функции обработки для команд Telegram-бота /start, /profile, /admin.
Команда /start: Эта команда инициализирует бота и приветствует пользователя, предлагая начать использование бота.
Команда /profile: Эта команда отображает профиль пользователя, включая его результаты в различных категориях.
Команда /admin: Эта команда предназначена для администрирования и доступна только пользователю с ID администратора. Она отображает общее количество пользователей.
Осталось добавить функционал бота, связанный с обработкой сообщений пользователей и викторинами.
Основная функция обработки сообщений. В зависимости от текста сообщения, бот вызывает соответствующую функцию для начала викторины по выбранной категории, запуска рейтингового режима или отображения таблицы лидеров.
Функция handleQuizAnswer. Эта функция обрабатывает ответы пользователя на вопросы викторины.
Функция startQuiz. Эта функция начинает викторину по указанной категории.
💼 Вакансии для фронтенд-разработчиков
Вакансии по фронтенду, джаваскрипт, React, Angular, Vue @jsdevjob
Заключение
В этой статье я рассмотрел основные составляющие моего Telegram-бота, необходимые для его работы. Полный код проекта можно посмотреть в моемGithub репозитории. Опробовать бота можно по ссылке. У меня есть еще пара идей для улучшения его функциональности в будущем, планирую добавить блок вопросов с задачами по JavaScript и добавить больше статистик в профилях пользователей.
Индексы. Хорошая практика их все же использовать. Тут прямо напрашивается уникальный индекс по username и не только ради ускорения поиска, см. далее.
NULL. Вместо текста "Еще не играл" в колонке last_played корректнее просто хранить NULL. Потому что однажды захочется текст поменять или сделать версию бота на разных языках. В худшем случае в БД получиться солянка значений "Еще не играл", "Не играл", "Not played" и т.д., что не позволит внятно выбрать, например, игроков кто не начинал игру.
SELECT + INSERT. Речь о функции createProfile. Для начала, такой подход не защитит от дублей при запуске более 1 процесса - с эти лучше справится уникальный индекс, см. п.1. А когда будет индекс есть прекрасная конструкция INSERT OR IGNORE, см. https://www.sqlite.org/lang_insert.html
updateLeaderboard. Разновидность п.3. Есть прекрасная конструкция INSERT ... ON CONFLICT, называется такой подход upsert, см. https://www.sqlite.org/lang_upsert.html
PS В целом в БД в качестве ИД пользователя логичнее было бы хранить именно id, а не username или first_name. Хотя бы потому что эти значения можно поменять, а id постоянен. Да и вдруг захочется в дальнейшем напоминать пользователю, что он давно не тренировался - потребуется id для отправки сообщения. Читаемое имя пользователя, конечно, тоже нужно, но главным образом для отображения, а не логики.
И в контексте текущей реализации updateLeaderboard не ясно зачем вообще createProfile. Нет в базе - значит ни разу не играл, а при первом ответе создаем профиль сразу с рейтингом.
Добавил возможность повторного прохождения вопросов в выбранной категории, даже если она уже полностью пройдена. Теперь бот спросит Вас, желаете ли вы пройти эти вопросы еще раз)
Комментарии
Привет!
Сразу бросились в глаза моменты с БД:
username
и не только ради ускорения поиска, см. далее.NULL
. Вместо текста "Еще не играл" в колонкеlast_played
корректнее просто хранитьNULL
. Потому что однажды захочется текст поменять или сделать версию бота на разных языках. В худшем случае в БД получиться солянка значений "Еще не играл", "Не играл", "Not played" и т.д., что не позволит внятно выбрать, например, игроков кто не начинал игру.SELECT + INSERT
. Речь о функцииcreateProfile
. Для начала, такой подход не защитит от дублей при запуске более 1 процесса - с эти лучше справится уникальный индекс, см. п.1. А когда будет индекс есть прекрасная конструкцияINSERT OR IGNORE
, см. https://www.sqlite.org/lang_insert.htmlupdateLeaderboard
. Разновидность п.3. Есть прекрасная конструкцияINSERT ... ON CONFLICT
, называется такой подходupsert
, см. https://www.sqlite.org/lang_upsert.htmlPS В целом в БД в качестве ИД пользователя логичнее было бы хранить именно
id
, а неusername
илиfirst_name
. Хотя бы потому что эти значения можно поменять, аid
постоянен. Да и вдруг захочется в дальнейшем напоминать пользователю, что он давно не тренировался - потребуетсяid
для отправки сообщения. Читаемое имя пользователя, конечно, тоже нужно, но главным образом для отображения, а не логики.И в контексте текущей реализации
updateLeaderboard
не ясно зачем вообщеcreateProfile
. Нет в базе - значит ни разу не играл, а при первом ответе создаем профиль сразу с рейтингом.В общем, есть куда стремиться)
Спасибо большое за такой развернутый комментарий, очень полезно) Буду реализовывать))
Обновление от 30.05.2024: