golang telegram

golang telegram

В этой статье вы узнаете, как легко установить связь между вашим ботом и программой, написанной на языке программирования Golang.

Цели

Ваш бот будет обрабатывать два возможных варианта:

  • Словарь: вы отправляете слово, а бот присылает вам различные значения, основанные на классификации слова по частям речи
  • Информация об авторе: отвечает на запрос информации об авторе

Создание Telegram-бота

Первое, что вам следует сделать, это создать бота в своём аккаунте Telegram. Для этого вам нужно будет найти в Телеграмме BotFather и отправить ему команду /newbot, предварительно выбрав имя для вашего бота. После этого вы получите токен. Этот токен очень важен, он как пароль для управления вашим ботом.

Первые шаги

В Telegram существуют серверы, которые работают как промежуточное программное обеспечение между чатами и приложением. Они очень похожи на почтовое отделение, но вместо почтового индекса здесь у вас есть chat_id.

Вы должны создать структуру для своего бота, чтобы он мог конкретно отвечать на запросы пользователей. Итак, мы создадим структуру для обработки запросов от Telegram.

Здесь вы можете посмотреть другие поля, включённые в запрос: https://core.telegram.org/bots/api#update

import requests, uvicorn, json from fastapi import FastAPI, Form, Response from twilio.twiml.messaging_response import MessagingResponse bot_app = FastAPI() def formatOutput(Body, data): if 'error' not in data: data_output = json.dumps(data['nutritions']) data_output = data_output.replace(" ", "") #remove whitespaces else: data_output = json.dumps(data) data_output = data_output.replace(":", "\n") #create paragraphs data_output = data_output.replace('"', '') #get only the nutrition information data_output = data_output.replace(",", "\n") #create paragraphs data_output = data_output.replace("{","").replace("}", "") #remove brackets data_output = Body +"\n \n" + data_output #add the header return data_output def getInfoFruit(fruit_name): fruit_name = fruit_name.lower() url = 'https://fruityvice.com/api/fruit/{}'.format(fruit_name) resp = requests.get(url) if resp.status_code != 200: return {"error": "not a fruit"} data = resp.json() return data @bot_app.post("/bot") async def chat(Body: str = Form(...)): data = getInfoFruit(Body) output = formatOutput(Body, data) response = MessagingResponse() msg = response.message(output) return Response(content=str(response), media_type="application/xml") if __name__ == "__main__": uvicorn.run(bot_app, host="0.0.0.0", port=5000)

Вы должны изменить “YOUR_TOKEN” на токен, который предоставит вам BotFather, поэтому “bot” + YOUR_TOKEN.

Но... что он будет делать?

Легко понять, что каждый раз, когда запускается обработчик, это означает, что если вы отправляете сообщение внутри чата со своим ботом (да, вы должны открыть чат с созданным вами ботом) — он ответит вам: Hello World.

Внедрение вашего бота “Hello World”

Я должен выразить огромную благодарность автору https://www.sohamkamani.com/golang/telegram-bot / для этого урока, потому что он очень помог мне понять, как мы можем создать бота и развернуть его.

  • Установите ngrok отсюда https://ngrok.com/download
  • После установки вы запускаете на своем терминале следующую команду:
ngrok http 3000

3. Теперь вы должны иметь возможность видеть свой общедоступный IP:

golang telegram

В данном случае это: https://d860-85-245-152-103.eu.ngrok.io

4. Откройте терминал заново и запустите:

curl -F "url=https://d860-85-245-152-103.eu.ngrok.io" https://api.telegram.org/bot+YOUR_TOKEN/setWebhook

(следует добавить после “bot” ваш личный токен)

После запуска этой команды вы должны увидеть:

{"ok":true,"result":true,"description":"Webhook was set"}

Достаточно быстро и легко создать общедоступный IP-адрес и подключиться к вашему локальному IP 3000. Это не производственное решение, потому что ngrok позволяет вам иметь один и тот же общедоступный IP-адрес только в течение 2 часов, используя бесплатный доступ. Но в любом случае, это действительно полезно знать для вашего развития.

Протестируйте вашего бота

Зайдите в чат своего бота и напишите что-нибудь, и бот ответит вам ”Hello Word".

Пример использования словаря

Мы будем использовать FREE DICTIONARY API, https://dictionaryapi.dev /. Это действительно полный API, который предоставляет нам информацию о: значениях, синонимах, антонимах, частях речи и т.д. Чтобы использовать это, мы должны создать запрос к API, подобный приведённому ниже примеру:

В качестве примера, чтобы получить определение английского слова hello, вы можете отправить запрос наhttps://api.dictionaryapi.dev/api/v2/entries/en/hello

Для этого урока я решил использовать только значения слова, но идея будет заключаться в том, чтобы получить одно определение для каждой части речи. Например, если мы выберем слово TIME, API найдет 3 части речи, и первое определение для каждой будет:

  • noun: Неизбежное продвижение в будущее с прохождением настоящих и прошлых событий.
  • verb: Измерять или записывать время, продолжительность или скорость.
  • interjection: напоминание судьи игрокам продолжить игру после паузы.

Итак, в этом случае сообщение должно отображаться следующим образом:

noun : The inevitable progression into the future with the passing of present and past events. verb : To measure or record the time, duration, or rate of. interjection : Reminder by the umpire for the players to continue playing after their pause.

Код для этого:

// Store data from the Api type Response []struct { Word string `json:"word"` Phonetic string `json:"phonetic,omitempty"` Phonetics []struct { Text string `json:"text"` Audio string `json:"audio"` SourceURL string `json:"sourceUrl,omitempty"` License struct { Name string `json:"name"` URL string `json:"url"` } `json:"license,omitempty"` } `json:"phonetics"` Meanings []struct { PartOfSpeech string `json:"partOfSpeech"` Definitions []struct { Definition string `json:"definition"` Synonyms []string `json:"synonyms"` Antonyms []interface{} `json:"antonyms"` Example string `json:"example,omitempty"` } `json:"definitions"` Synonyms []string `json:"synonyms"` Antonyms []interface{} `json:"antonyms"` } `json:"meanings"` License struct { Name string `json:"name"` URL string `json:"url"` } `json:"license"` SourceUrls []string `json:"sourceUrls"` } func wordsPlay(chatID int64, string_input string) error { requestURL := "https://api.dictionaryapi.dev/api/v2/entries/en/" + string_input response, err := getWordData(requestURL) if err != nil { sendMessage(chatID, "Meaning not found for the word: "+string_input) return err } // Return the 1st definition that comes for each part of speech err = sendMessage(chatID, formatResponse(response)) return err } func getWordData(url string) (Response, error) { var result Response req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { fmt.Printf("client: could not create request: %s\n", err) os.Exit(1) } res, err := http.DefaultClient.Do(req) if err != nil { fmt.Printf("client: error making http request: %s\n", err) os.Exit(1) } fmt.Printf("client: got response!\n") fmt.Printf("client: status code: %d\n", res.StatusCode) body, err := ioutil.ReadAll(res.Body) // response body is []byte if err != nil { return result, err } // read json data into a Result struct err = json.Unmarshal(body, &result) if err != nil { return result, err } return result, nil } func formatResponse(response Response) string { var str_response string var elem_str string for _, element := range response[0].Meanings { elem_str = element.PartOfSpeech + " : " + element.Definitions[0].Definition + "\n" str_response += elem_str } return str_response }

В этом фрагменте кода у нас есть 3 функции и 1 структура.

Я объясню, как работает этот фрагмент кода:

  • wordsPlay(chatId int64, string_input string) - это основная функция данного варианта использования. Создаёт URL-адрес для запроса.
  • getWordData(url string) получает URL и запрашивает API, после получения результата создаёт структуру из ранее определённой структуры ответа.
  • Основная функция (wordsPlay) проверяет, является ли результат из Dictionary API действительным, если нет, отправляет сообщение с надписью:
“Meaning not found for the word: “+string_input

Если результат действителен, то вызывает функцию для форматирования выходных данных.

4. formatResponse(response Response) получает объект Response, а затем создаёт строку, используя значения для каждой из найденных частей речи:

for _, element := range response[0].Meanings { elem_str = element.PartOfSpeech + " : " + element.Definitions[0].Definition + "\n" str_response += elem_str }

5. Отправляет ответ боту, используя chat_id

Чтобы этот код заработал, нам нужно обновить код обработчика, но давайте реализуем авторский вариант использования. В этом простом случае мы хотим ответить, указав только имя автора бота и местоположение:

func author(chatID int64) error { err := sendMessage(chatID, "Alberto Vilas ; Lisbon - Portugal") if err != nil { log.Fatal(err) } return nil }

Чтобы всё сработало, нам нужно создать записи для функций. Это означает, что нам нужно определить, когда мы должны вызывать “wordsPlay”, а когда “author”. После скриншотов вы сможете увидеть полный код.

golang telegram
golang telegram

Полный код

package main import ( "bytes" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "os" "strings" "github.com/sirupsen/logrus" ) // Create a new instance of the logger. You can have any number of instances. var log = logrus.New() // Create a struct that mimics the webhook response body // https://core.telegram.org/bots/api#update type webhookReqBody struct { Message struct { Text string `json:"text"` Chat struct { ID int64 `json:"id"` } `json:"chat"` } `json:"message"` } // Store data from the Api type Response []struct { Word string `json:"word"` Phonetic string `json:"phonetic,omitempty"` Phonetics []struct { Text string `json:"text"` Audio string `json:"audio"` SourceURL string `json:"sourceUrl,omitempty"` License struct { Name string `json:"name"` URL string `json:"url"` } `json:"license,omitempty"` } `json:"phonetics"` Meanings []struct { PartOfSpeech string `json:"partOfSpeech"` Definitions []struct { Definition string `json:"definition"` Synonyms []string `json:"synonyms"` Antonyms []interface{} `json:"antonyms"` Example string `json:"example,omitempty"` } `json:"definitions"` Synonyms []string `json:"synonyms"` Antonyms []interface{} `json:"antonyms"` } `json:"meanings"` License struct { Name string `json:"name"` URL string `json:"url"` } `json:"license"` SourceUrls []string `json:"sourceUrls"` } func sendMessage(chatID int64, text string) error { // Create the request body struct reqBody := &sendMessageReqBody{ ChatID: chatID, Text: text, } // Create the JSON body from the struct reqBytes, err := json.Marshal(reqBody) if err != nil { return err } // Send a post request with your token res, err := http.Post("https://api.telegram.org/bot+YOUR_TOKEN/sendMessage", "application/json", bytes.NewBuffer(reqBytes)) if err != nil { return err } if res.StatusCode != http.StatusOK { return errors.New("unexpected status" + res.Status) } return nil } // This handler is called everytime telegram sends us a webhook event func Handler(res http.ResponseWriter, req *http.Request) { // First, decode the JSON response body body := &webhookReqBody{} if err := json.NewDecoder(req.Body).Decode(body); err != nil { fmt.Println("could not decode request body", err) return } chat_id := body.Message.Chat.ID text := body.Message.Text log.Info("body handle: \n") log.Info(body) log.Info("TEXT: " + text) if strings.Contains(strings.ToLower(text), "author") { err := author(chat_id) if err != nil { fmt.Println("error in sending reply:", err) } } else if text != "" { wordsPlay(chat_id, text) } // log a confirmation message if the message is sent successfully fmt.Println("reply sent") } //The below code deals with the process of sending a response message // to the user type sendMessageReqBody struct { ChatID int64 `json:"chat_id"` Text string `json:"text"` } func author(chatID int64) error { err := sendMessage(chatID, "Alberto Vilas ; Lisbon - Portugal") if err != nil { log.Fatal(err) } return nil } func getWordData(url string) (Response, error) { var result Response req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { fmt.Printf("client: could not create request: %s\n", err) os.Exit(1) } res, err := http.DefaultClient.Do(req) if err != nil { fmt.Printf("client: error making http request: %s\n", err) os.Exit(1) } fmt.Printf("client: got response!\n") fmt.Printf("client: status code: %d\n", res.StatusCode) body, err := ioutil.ReadAll(res.Body) // response body is []byte if err != nil { return result, err } // read json data into a Result struct err = json.Unmarshal(body, &result) if err != nil { return result, err } return result, nil } func formatResponse(response Response) string { var str_response string var elem_str string for _, element := range response[0].Meanings { elem_str = element.PartOfSpeech + " : " + element.Definitions[0].Definition + "\n" str_response += elem_str } log.Info("RESPONSE: ") log.Info(str_response) return str_response } func wordsPlay(chatID int64, string_input string) error { requestURL := "https://api.dictionaryapi.dev/api/v2/entries/en/" + string_input response, err := getWordData(requestURL) if err != nil { sendMessage(chatID, "Meaning not found for the word: "+string_input) return err } // Return the 1st definition that comes for each part of speech err = sendMessage(chatID, formatResponse(response)) return err } func log_init() error { file, err := os.OpenFile("log_file.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err == nil { log.Out = file } else { log.Info("Failed to log to file, using default stderr") } return nil } func main() { //init the log file err := log_init() if err != nil { os.Exit(1) } http.ListenAndServe(":3000", http.HandlerFunc(Handler)) }

Ваши последующие шаги

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

Статья была взята из этого источника:

11
1 комментарий

Для чего создавать дополнительный слой в виде API? Это все можно было написать в одном софте, если было сложно интегрировать go-telegram-bot-api или написать собственную либу взаимодействия, то для чего писать статью?

По сути статья не про бот на go, статья про мидлварь на go ¯\_(ツ)_/¯

Ответить