В этой статье я хочу рассказать о том, как происходило написание Telegram-бота, который мог бы отправлять статьи с Habrahabr, сначала на Python, а потом и на Go, и что из этого вышло.
Небольшая предыстория
Началось всё летом, когда Telegram всё сильнее начал проникать в мою жизнь. После написания пары тестовых ботов на Python возникла идея написать что-нибудь и для Habrahabr. Первая задумка была простая: бот должен был просто присылать уведомления о новых статьях.
Первая версия (на Python 3.5)
После недолгого гугления обнаружилось, что у сайта есть RSS-лента. Процесс рассылки статей сразу упростился: теперь не надо было парсить весь сайт, достаточно смотреть только RSS-ленту раз в несколько минут (в моём случае – 10).
Довольно быстро была написана первая версия бота. Для взаимодействия с API Telegram использовалась библиотека pyTelegramBotAPI, для парсинга RSS-ленты – feedparser.
В процессе работы структура сильно усложнилась: теперь бот не просто присылал новые статьи, но и мог отсеивать те статьи, которые не содержали теги, на которые пользователь был подписан. Реализация была очень простой: использовалась база данных SQLite, в которой было всего 2 столбца (id и теги), благо, Python3 имел встроенную библиотеку для взаимодействия с ней. Также добавились команды для редактирования тегов: пользователь мог удалять, добавлять и копировать теги. Последнее происходило очень просто: пользователь присылал боту ссылку на профиль, после чего происходил поиск тегов с помощью (Каюсь! Не знал о том, что это плохо) регулярного выражения.
Проблемы
Конечная программа получилось довольно нестабильной: довольно часто падала (для того, чтобы это избежать пришлось обернуть часть кода в while True:
). Не стоит забывать и про то регулярное выражение. В итоге, бот, хоть и криво, но работал, поэтому было решено оставить всё как есть и позволить ему жить (как оказалось, до конца февраля). Исходники можно (но нужно ли?) найти GitHub.
Вторая версия (на Go)
Довольно давно в моей голове засела идея попробовать Go, но руки никак не доходили. И вот, наконец, было найдено свободное время. После чтения документации так и хотелось что-нибудь написать, а тут как раз бот на Python упал (даже while True
не помог). Понимая, что это знак свыше, я начал переписывать бота на Go.
К сожалению, реального опыта разработки на Go у меня нет, а спросить не у кого. Поэтому решения, которые я использую, опытным людям могут показаться не самыми лучшими. Если что-то действительно плохо, то, пожалуйста, напишите, что не так. А теперь перейдём к описанию архитектуры приложения.
Начну с библиотек. Для взаимодействия с API Telegram используется telegram-bot-api.v4, для взаимодействия с SQLite3 – go-sqlite3. RSS-лента парсится с помощью gofeed. Для копирования тегов пользователя используется аналог Beautiful Soup на Go – soup.
Теперь перейдем к коду. Существует основной цикл, который получает новые сообщения от бота. В нём происходит определение типа команд:
for update := range updateChannel {
if update.Message == nil {
continue
} else if update.Message.Command() == "start" {
startChan <- update.Message
} else if update.Message.Command() == "help" {
helpChan <- update.Message
...
} else {
message := tgbotapi.NewMessage(update.Message.Chat.ID, "...")
message.ReplyToMessageID = update.Message.MessageID
bot.send(message)
}
}
Как можно было понять из кода, каждая команда обрабатывается в отдельной goroutine, передача сообщений осуществляется с помощью каналов:
startChan := make(chan *tgbotapi.Message, 50)
helpChan := make(chan *tgbotapi.Message, 50)
getTagsChan := make(chan *tgbotapi.Message, 50)
...
// Goroutines
go bot.start(startChan)
go bot.help(helpChan)
go bot.getTags(getTagsChan)
...
Также имеется возможность рассылать оповещения пользователям. Это происходит с помощью специальной веб-страницы.
Проект решено было разбить на несколько пакетов:
- main – происходит загрузка конфиг-файла, запуск бота и сайта
- bot – отвечает за работу бота
- website – отвечает за работу сайта
- logging – логгирует ошибки
Вот как выглядит процесс запуска программы:
- Открываются файлы для логгирования
- Происходит чтение конфиг-файла
- Происходит инициализация бота и открытие базы данных
- Запускается бот в отдельной goroutine (при запуске бота идёт запуск goroutine с функциями, которые обрабатывают команды)
- Запускается сайт в отдельной goroutine
Если во время запуска или работы бота происходит ошибка, которая не позволит программе нормально работать, то программа завершается с кодом 1.
Проблемы
К сожалению, проблем не удалось избежать и в этот раз. Но они связаны не с работой бота, а с разработкой программы и собиранием исполнительного файла под Linux (разработка происходит на Windows 10, сервер работает на Ubuntu 16.04): для работы библиотеки go-sqlite3
нужен CGO, а при выполнение команды go build
с флагом CGO_ENABLED=1
появляются ошибки. Из-за этого приходится компилировать проект на виртуальной машине с Ubuntu.
Зачем?
Вы можете спросить: "Ну и зачем использовать этого бота, если есть сайт?". Постараюсь ответить. Но перед этим хочу сказать, что бот ни в коем случае не является заменой сайта, лишь дополнением.
- Возможность собрать "всё в одну кучу". Лично для меня Telegram – это источник всех новостей и интересных материалов, поэтому мне хотелось иметь возможность получать статьи с Хабра и там
- Возможность просматривать статьи с помощью Instant View. Для этого был написан свой шаблон. Благодаря этому, можно читать почти все статьи в удобном формате прямо с телефона (некоторые статьи содержат теги, которые IV не может правильно интерпретировать, поэтому часть статей недоступна для просмотра) Пример сообщения от бота
- Также была реализована возможность отправить боту ссылку на статью с Habrahabr, после чего он вернёт её в таком же формате, в каком отправляет сам (с Instant View, ссылкой на статью и на комментарии) Пример (с клиента для Windows)
Заключение
Подводя итог, хочется сказать, что опыт написания бота на Go был довольно интересным и полезным (надеюсь). Если вы заинтересовались ботом, то его самого можно найти здесь, а его исходники – опять же на GitHub.