Бот дразнилка для telegram

Ильдар Сарибжанов | 28.11.2016

Предыстория

Однажды, в одной группе мессенджера телеграм появился занятный бот, который умел передразнивать фразы «да» и «нет». Если сообщение любого из участников заканчивалось на что-то похожее на «да/нет», он отвечал: «Пакет го*на» или «Го*на пакет» (не будем нецензурщиной осквернять блог). В общем. прикольно.

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

Как все создавалось

На тот момент у Ильи (еще один наш разработчик) уже был опыт написания бота для promurom.ru, он его писал на php. «Php — это не интересно», — подумал я, к тому же давно было желание в изучении nodejs, а тут нескольких зайцев: и прокачка скилов, и развлечение, и новые технологии =) Про написание ботов есть стопицот постов и мануалов, я не буду подробно рассказывать все этапы регистрации и разработки (чего там разрабатывать? 3 строчки кода и 2 библиотеки для nodejs). Самая главная библиотека Telegram Bot API for NodeJS — это обертка над стандартным АПИ телеграма. Для меня очень сложным был процесс понимания как же боты работают, не саму обработку запросов, а именно процесс общения между сервером телеграм и моим сервером. Я себе представлял это так: пользователь шлет команду боту, сообщение уходит на сервер теоеграм, оттуда идет на мой сервер, забирает ответ и отправляет этот ответ в чат (забегая вперед скажу, что так он тоже может, но процесс настройки чуть сложнее). В действительности все оказалось проще, собственно сам бот крутить на сервере и с некоторой периодичностью присасывается к серверу телеграми и слушает не пришло ли ему чего (Long polling) Никаких сложных настроек сервера и протоколов.

За пару часов я накидал частичный аналог старого бота, который реагировал на да/не и еще пару фраз, запулил его на сервере и добавил в офисную группу.

Расширяемый лексикон

Теперь переходим к следующему этапу, наделим бота способностью учиться. Как-то не сразу в моей голове сложилось понимание как реализовать весь процесс и в каком формате хранить данные. Сначала родилось решение «в лоб» завести базу из одной таблицы, состоящей из 2х полей, где будут храниться фразы и ответы а них. Но этот вариант с моими навыками на тот момент показался сложноватым. К тому же идея при каждом запросе дергать базу тоже не внушала оптимизма. В дело вступил коллективный разум, я закинул проблему в общий чат и обсуждение началось. Конечно, буквально за пару десятков фраз мы ушли то основной темы и стали рассуждать как можно еще «улучшить» бота, наделить его ИИ и т.д. Но одна хорошая мысль прозвучала: «Не бойся часто дергать файловую систему, это не так долго (тем более в век ssd), а самое главное — просто». В общем, принял решение, что хранить словарь буду в файле на диске. А потом мне пришло понимание, что я работаю с процессом, который не останавливается, nodejs запускается аки демон и висит в процессах, пока его не убьешь явно. Т.е. при запуске все переменные попадают в память и никуда оттуда не деваются, если, конечно, ты сам их не прибьешь, следовательно, словарь будет все время лежать в ОЗУ, а это самое быстрое что может быть. И так решение: словарь храним на диске, при запуске забираем его в память и работаем из ОЗУ, при расширении словаря пишем на диск обновленный словарь — ПРОФИТ!

С форматом хранения не стал долго ломать голову, есть стандартный для js — json (в честь Джейсона Стетема, наверное), вот его и взял за основу, фраза в качестве ключа, а ответ значение:

{
	"фраза 1": "ответ 1",
	"Фраза 2": "ответ 2"
}

Потом пришла мысль: «А если будет несколько ответов на одну фразу?». Несколько одинаковых ключей быть не может, поэтому, не мудрствуя лукаво — фраза в качестве ключа, а значения хранить в массиве. И в итоге получаем такую структуру:

{
	"фраза 1":[
		"ответ 1.1",
		"ответ 1.2"
	],
	"фраза 2":[
		"ответ 2.1",
		"ответ 2.2"
	]
}

При выборе ответа берем какую-нибудь функцию генерации случайного числа и на его основе выбираем элемент массива. С форматом хранения ок, а как же обучать?

Что я понял про команды бота для телеграм? Все фразы, которые начинаются со слеша имеют атрибут команда, для чего их регистрировать через папу-бота, мне так и не понятно, может, я что-то не так читал или это проблема обертки для nodejs, но факт остается фактом. Обработку команды /help также пишет сам разработчик, следовательно что он хочет, то и выводит, я думал, что хэлп на уровне сервера работает, и отдает все зарегистрированные команды. Т.е. если бот не приватный, то можно вообще любую фразу интерпретировать как команду. Я создал команду обучения:

/learn_new фраза=ответ

Если формат будет не правильный, бот об этом сообщит. Из недостатков: в качестве фразы может быть только одно слово, бот реагирует только на последнее слово.

Вот как-то так =) Ищите во всех телеграмах страны, бот с именем Draznilka.

1480259650397