Как я Telegram бота на Go писал. Часть первая. Начало.
В этой серии заметок хотел бы немного коснуться данной темы. Серия будет включаться в себя три раздела:
- Разработка бота на Go для поиска заклинаний для Dungeon And Dragons
- Публикация бота на платформе heroku
- Добавление аналитики использования бота
Итак, начнём!
Во-первых, нам потребуется сам список заклинаний DnD. Найти его можете в моём
bitbucket-репозитории этого
бота.
Во-вторых, создать новую папку. Например, dndspellsbot. В-третьих, получить
ключ для вашего нового бота у служебного аккаунта
@BotFatherBot, отправив ему команду /newbot
и сохранить полученный в ответ токен в безопасное место. В-четвертых, чтобы не
изобретать велосипед, добавить библиотеку telegram-bot-
api:
go get gopkg.in/telegram-bot-api.v4
И, наконец-то, в-пятых, написать код самого бота.
config.json
Создадим пустой Go-файл dndspellsbot.go
в папке dndspellsbot. Туда же
добавим конфигурационный файл config.json
со следующим содержимым:
{
"TelegramBotToken": "токен-полученный-от-BotFather"
}
Конечно же, можно обойтись без этих усложнений и зашить токен прямо в коде бота, но в случае, если вы случайно зальетё код вашего бота в какой-то публичный репозиторий на github/bitbucket/whatever, то утечёт и токен, а это, согласитесь, неприятно. И это только сейчас у нас такой простой конфиг - в дальнейшем будут добавляться новые свойства и всё будет храниться в одном месте. Удобно, согласитесь? Получить этот токен из конфигурационного файла довольно просто стандартными средствами Go:
package main
import (
"fmt"
"gopkg.in/telegram-bot-api.v4"
"log"
"os"
"encoding/json"
)
type Config struct {
TelegramBotToken string
}
func main() {
file, _ := os.Open("config.json")
decoder := json.NewDecoder(file)
configuration := Config{}
err := decoder.Decode(&configuration)
if err != nil {
log.Panic(err)
}
fmt.Println(configuration.TelegramBotToken)
}
Теперь при добавлении в config.json
новых свойств и модификации структуры
Config
можно получать необходимые параметры конфигурации.
ping?
В репозитории telegram-bot-api
есть пример echo-бота - бота, который
отвечает вам те же сообщением. Возьмём его за основу:
bot, err := tgbotapi.NewBotAPI(configuration.TelegramBotToken)
if err != nil {
log.Panic(err)
}
bot.Debug = false
log.Printf("Authorized on account %s", bot.Self.UserName)
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates, err := bot.GetUpdatesChan(u)
if err != nil {
log.Panic(err)
}
// В канал updates будут приходить все новые сообщения.
for update := range updates {
// Создав структуру - можно её отправить обратно боту
msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text)
msg.ReplyToMessageID = update.Message.MessageID
bot.Send(msg)
}
Вы уже добавила вашего бота в свой список контактов? Если нет - сейчас самое
время. Теперь самое интересное - запускаем наше приложение командой go run dndspellsbot.go
и пишем что-нибудь нашему боту. Если всё сделано правильно,
то бот ответит вам вашим же сообщением:
Однако, мы хотели не этого. Опустим подробности парсинга XML - пост не об этом
(полный код можно будет найти по ссылке в конце статьи). Будем считать, что у
нас есть слайс всех заклинаний и любой запрос пользователя к боту - это запрос
на поиск подходящего заклинания по его имени (усложнять можно бесконечно, но
на данный момент этого достаточно):
...
query := update.Message.Text
// Получим те заклинания, в имени которых есть искомое слово или фраза
filteredSpells := Filter(spells.Spells, func(spell Spell) bool {
return strings.Index(strings.ToLower(spell.Name), strings.ToLower(query)) >= 0
})
// Если не нашлось ни одного заклинания - скажем об этом пользователю
if len(filteredSpells) == 0 {
msg := tgbotapi.NewMessage(update.Message.Chat.ID, "No one spells matches")
bot.Send(msg)
}
// Каждое заклинание отправляем отдельным сообщением
for _, spell := range(filteredSpells) {
text := ""
for _, t := range(spell.Texts) {
text = text + t + "\n"
}
msg := tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("%s\n%s", spell.Name, text))
bot.Send(msg)
}
...
// Функция фильтрации слайсов
func Filter(spells []Spell, fn func(spell Spell) bool) []Spell {
var filtered []Spell
for _, spell := range(spells) {
if fn(spell) {
filtered = append(filtered, spell)
}
}
return filtered
}
Теперь, при любом запросе от пользователя, бот будет возвращать все подходящие
заклинания из phb.xml
. В следующей статье немного улучшим нашего бота:
добавим форматирование сообщений, inline-режим и добавим пару команд. Код бота
можно найти на bitbucket.