Найти в Дзене
Дети Свифта

"Танцы с бубном" вокруг телеграмм-бота викторины на java

Предыстория. Я не профессиональный it-разработчик. Пройдя два года назад онлайн курсы по программированию на java я, в свободное от основной профессиональной деятельности время, создаю так называемые pet-projects - небольшие телеграмм-боты, которыми, в том числе, активно пользуюсь сама. Написав в 2023 году своего второго бота – картинную галерею (функционал которого был достаточно прост – выдавать пользователю по запросу примеры картин художников, экспонирующиеся в крупных российских музеях), во мне зародилась мысль трансформировать его в викторину, где пользователь должен угадать художника той или иной картины. Полтора года глаза боялись, но в конце 2024 руки взялись-таки за его написание. Техническое задание. Функционал викторины классический: - телеграмм-бот в случайном порядке должен присылать пользователю картины известных художников, которые сохранены в его базе, - под картиной предлагаются кнопки с вариантами ответа, - при выборе правильного ответа – присылается сообщение с резу
https://www.drive2.ru/l/656322131193242336/
https://www.drive2.ru/l/656322131193242336/

Предыстория.

Я не профессиональный it-разработчик. Пройдя два года назад онлайн курсы по программированию на java я, в свободное от основной профессиональной деятельности время, создаю так называемые pet-projects - небольшие телеграмм-боты, которыми, в том числе, активно пользуюсь сама.

Написав в 2023 году своего второго бота – картинную галерею (функционал которого был достаточно прост – выдавать пользователю по запросу примеры картин художников, экспонирующиеся в крупных российских музеях), во мне зародилась мысль трансформировать его в викторину, где пользователь должен угадать художника той или иной картины.

Полтора года глаза боялись, но в конце 2024 руки взялись-таки за его написание.

Техническое задание.

Функционал викторины классический:

- телеграмм-бот в случайном порядке должен присылать пользователю картины известных художников, которые сохранены в его базе,

- под картиной предлагаются кнопки с вариантами ответа,

- при выборе правильного ответа – присылается сообщение с результатом, названием картины и новая картина с вариантами ответов,

- если ответ неверный, пользователя об этом оповещают и предлагают либо выбрать другой вариант, либо нажать на соответствующую кнопку для получения нового вопроса-картины,

- если какая-то картина высылается повторно – варианты ответов должны выдаваться в иной последовательности, чем предлагались ранее,

- ведется статистика правильных ответов с возможностью ее обнуления,

- в перспективе – расширение тематики викторин.

Начало реализации.

Хранение вопросов и вариантов ответов к ним, работу с базой этих вопросов я реализовала через json:

1. Создала json-файл с массивом вопросов, где сам вопрос – это наименование файла с картиной (в формате .jpg, сами файлы хранятся в отдельной ресурсной папке), а варианты ответов на вопросы – отдельный массив.

Пример выдержки из json-файла:

-2

2. Создала java-объект с полями, соответствующими полям объекта json-файла:

-3

3. Используя библиотеку jackson прописала парсинг значений из json-файла для получения объектов:

-4

Поскольку картин-вопросов у меня было 400, реализация выбора вопроса из коллекции вопросов рандомным способом мне представлялась достаточной. Для этого я написала метод:

-5

Предложение вариантов ответа реализовано через InlineKeyboardMarkup:

-6

Для получения значений полей объекта я использовала рефлексию. Чтобы реализовать вариативный порядок формирования вариантов ответов (в случае, если какая-то картина высылается повторно, чтобы варианты ответов выдавались в иной последовательности, чем предлагались ранее), использовала перемешивание элементов внутри массива:

-7

Это то основное, что было написано довольно быстро.

После этого я прописала код класса-наследника TelegramLongPollingBot, где происходила обработка запросов пользователя, являющихся командами, текстовыми сообщениями или ответами Inline-клавиатуры. В этом же классе я проинициализировала получаемый объект и все его поля. Запустила бот и, на стадии моно-тестирования, все работало без нареканий: бот выдавал картинку, под ней - Inline-кнопки с фамилиями художников, при нажатии на которые происходила корректная обратная реакция – определялись верные и неверные ответы.

Возникшие трудности.

Первая трудность, которая возникла еще на стадии самотестирования бота, было то, что рандомный метод выбора вопроса из коллекции вопросов работал странно: из 400 объектов его словно замыкало на 10-20 и он навязчиво высылал картины в этом диапазоне, а порой выдавал одну картину 2 и более раз подряд.

Вторая трудность уже проявилась, когда я развернула бот на хостинге и предоставила его для использования широкой аудитории – бот работал со всеми пользователями как с одним. То есть, значения полей (правильный ответ, описание и пр.) были актуальными всегда для вопроса, который бот выслал последим независимо от того, что это за пользователь. Иными словами, получаю, например, я очередной вопрос от бота. Если до того, как я на него отвечу, какому-то другому пользователю бот тоже вышлет вопрос, то что бы я не отвечала – любой мой ответ уже будет неверным, т.к. в памяти бота правильный ответ привязан к тому вопросу, который он в принципе отправил последним, а не последним именно мне.

Изобретение собственного «велосипеда» с квадратными колесами.

Перебрав массу различных вариантов в пределах имеющихся знаний и информации в Интернет, я нашла следующее решение вышеуказанным трудностям.

Чтобы привязать бота к работе с запросами конкретного пользователя, при первом запуске программы каждым пользователем в ресурсной папке создается файл, в имени которого содержится id-пользователя и маркер «q» (определяющий, что это файл для вопросов). Этот же файл используется в дальнейшем для обеспечения уникальности присылаемых вопросов (без повторов, пока не будут отправлены все вопросы, имеющиеся в памяти бота).

-8

Когда пользователь начинает викторину, в указанный файл записывается id-вопроса, отправленного ботом. В дальнейшем перед тем, как отправить пользователю новый вопрос, бот формирует список вопросов json-файла, сравнивает его со списком вопросов из файла пользователя и формирует список вопросов, id которых нет в файле пользователя. И вот уже из этого списка-разницы рандомно высылает пользователю новый вопрос, id которого тут же записывается в файл пользователя последней строкой.

Когда все вопросы из json-файла заданы, файл пользователя обнуляется и начинается все сначала.

-9

Указанный «велосипед» с квадратными колесами обеспечил отправку вопросов без повторов в пределах имеющегося перечня и решил первую трудность.

Для решения второй трудности используется тот же файл с id пользователя и маркером «q» в названии. Для того, чтобы корректно обработать вызов Inline-кнопки, нажатой конкретным пользователем, необходимо верно идентифицировать, какой последний вопрос был ему задан. Для этого мы получаем последнюю строку с id-вопроса из файла пользователя и по извлеченному значению получаем вопрос из json-файла.

-10

И уже у этого вопроса впоследствии определяем и получаем значение полей.

-11

Прописав все эти методы для идентификации вопроса и его полей по каждому отдельно пользователю, в классе-наследнике TelegramLongPollingBot я создала метод отправки вопроса пользователю

-12

и прописала обработку обновлений в методе onUpdateReceived () с вызовом метода отправки вопросов:

-13

Ведение статистики правильных ответов пользователя я также реализовала через создание файла .txt, где в названии используется id пользователя и маркер («а», указывающий, что это файл с ответами). Если ответ правильный – в файл записывается цифра 1, если неправильный – срочная буква «w». При запросе статистики подсчитывается количество строк с цифрами и делится на общее количество строк в этом файле.

-14

При необходимости, можно обнулить статистику, для чего прописала метод:

-15

Итоги.

Вот такой получился «велосипед». Этот «велосипед» едет и пользователь даже не догадывается о его квадратных колесах.

Часто меня спрашивают – зачем я так подробно в своих статьях описываю те или иные процессы? Держала бы все при себе, пусть другие новички и дилетанты самостоятельно набивают шишки и мастерят свои поделки, авось бросят эти тщетные занятия и не будут покушаться на святая-святых - it-сферу. Но ведь я при написании своих pet-projects использую разъяснения альтруистов с различных ресурсов, так почему же не поделиться в ответ? – может быть кому-то мои варианты решений и пригодятся. А в "святая-святых", уверена, для каждого найдется место и своя ниша.

Поэтому надеюсь, этот разбор проекта, полный код которого выложен на GitHub, кому-нибудь поможет при разработке своих программ.

Если интересно, как фактически все отрабатывает, то милости прошу: игра "Викторина".

О.Блас

-16