[Go] Telegram Bot 入門篇

程式語言:Go
Package:github.com/go-telegram-bot-api/telegram-bot-api
官方介紹
官方 API
telegram-bot-api GitHub

功能:Telegram Bot,可透過此介面實現資料的查詢與匯集

申請流程

  1. 私訊 @BotFather 並輸入 /newbot
  2. 輸入 bot 顯示的名稱(後續還可更改)、bot 的 username (必須為 bot 結尾)
  3. 得到 Token (例:12345:ABC_d-6)
  4. 私訊剛建立的 bot:@xxxBot

API Request

  • 格式
    • https://api.telegram.org/bot<Token>/<Method Name>
    • 例如
      • 得到機器人資訊
        • https://api.telegram.org/bot12345:ABC_d-6/getMe
      • 得到接收的訊息
        • https://api.telegram.org/bot12345:ABC_d-6/getUpdates
  • 支援 HTTPS GET 及 POST,四種方式
    • URL query string
      • https://api.telegram.org/bot12345:ABC_d-6/sendMessage?chat_id=109780439&text=Hello+World
    • application/json
    • application/x-www-form-urlencoded
      • 無法上傳檔案
    • multipart/form-data
      • 用來上傳檔案 
  • Response
    • JSON 格式
      • 成功
        • ok = True
        • result = 回覆內容
      • 失敗
        • ok = False
        • error_code
        • description = 錯誤描述
  • Method
    • 無視大小寫
      • getMe 等同 GeTmE
    • 必須使用 UTF-8 編碼
  • 輔助工具

用戶聊天訊息

  • 保存時間
    • 只有 24 小時
  • 兩種獲得方式 ,兩者無法並行使用
    • 設定 Webhook
      • 有新訊息時,Telegram 將會主動告知
      • 需要 HTTPS (TLS 1.0) 伺服器
        並且將埠開在 443, 8443, 80, 8080 其一 (就算 port 80 也要求 TLS)
      • setWebhook 設定伺服器
        • https://api.telegram.org/bot<token>/setWebhook?url=<server> 
        • 留白表示刪除
          • https://api.telegram.org/bot<token>/setWebhook?url=
      • getWebhookInfo 得知設定
        • https://api.telegram.org/bot<token>/getWebhookInfo 
      • deleteWebhook 刪除設定
        • https://api.telegram.org/bot<token>/deleteWebhook
    • 主動詢問
      • getUpdates 請求,將會回傳一個 JSON 陣列
        • https://api.telegram.org/bot<token>/getUpdates
  • 接收模式
    • 隱私模式 Privacy Mode (預設)
      • 由 / 開頭的指令
      • 對機器人 Reply 的訊息
      • 系統訊息 (e.g., 新成員)
      • 自己是管理員的頻道
    • 關閉可收到全部訊息
      1. 私訊 @BotFather
      2. 輸入 /setprivacy 指令
      3. 選擇 Bot
      4. 點擊 Disable

程式碼

不同傳送訊息的方法,需設定 token & chat_id
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
)

type APIResponse struct {
    Ok          bool            `json:"ok"`
    Result      json.RawMessage `json:"result"`
    ErrorCode   int             `json:"error_code"`
    Description string          `json:"description"`
}

type TeleBot struct {
    token string
}

const (
    apiURL = "https://api.telegram.org/bot%s/%s"
    token  = "your token"
)

var (
    bot = TeleBot{token}

    // 執行 sendMessage API
    paras = map[string]string{
        "chat_id": "your chatID",
        "text":    "",
    }
)

// get 方法
func (t *TeleBot) get(method string, paras map[string]string) ([]byte, error) {
    paras["text"] = "get"
    // 建立 request
    urlrequest := fmt.Sprintf(apiURL, t.token, method)
    req, err := http.NewRequest("GET", urlrequest, nil)
    if err != nil {
        return nil, err
    }

    // 加入參數
    q := req.URL.Query()
    for key, value := range paras {
        q.Add(key, value)
    }
    req.URL.RawQuery = q.Encode()

    fmt.Println("Get", req.URL.String())

    // 執行 request
    rsp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer rsp.Body.Close()

    // 讀取內容
    data, err := ioutil.ReadAll(rsp.Body)
    if err != nil {
        return nil, err
    }

    return data, err
}

// post x-www-form-urlencoded 方法
func (t *TeleBot) postXW(method string, paras map[string]string) ([]byte, error) {
    paras["text"] = "post x-www-form-urlencoded"
    // 建立 request
    urlrequest := fmt.Sprintf(apiURL, t.token, method)
    fmt.Println("Post", urlrequest)

    // 加入參數
    payload := url.Values{}
    for key, value := range paras {
        payload.Add(key, value)
    }

    // 執行 request
    rsp, err := http.PostForm(urlrequest, payload)
    if err != nil {
        return nil, err
    }
    defer rsp.Body.Close()

    // 讀取內容
    data, err := ioutil.ReadAll(rsp.Body)
    if err != nil {
        return nil, err
    }

    return data, err
}

// post json方法
func (t *TeleBot) postJSON(method string, paras map[string]string) ([]byte, error) {
    paras["text"] = "post json"

    // 建立 request
    urlrequest := fmt.Sprintf(apiURL, t.token, method)
    fmt.Println("Post", urlrequest)

    // 建立 json
    payload, err := json.Marshal(paras)
    if err != nil {
        return nil, err
    }

    // 執行 request
    rsp, err := http.Post(urlrequest, "application/json; charset=utf-8", bytes.NewBuffer(payload))
    if err != nil {
        return nil, err
    }
    defer rsp.Body.Close()

    // 讀取內容
    data, err := ioutil.ReadAll(rsp.Body)
    if err != nil {
        return nil, err
    }

    return data, err
}

func (t *TeleBot) decodeJSON(data []byte, container interface{}) error {
    err := json.Unmarshal(data, container)
    if err != nil {
        return err
    }

    return nil
}

func showResponse(data []byte) {
    // 解析 response
    var apiResponse APIResponse
    err := bot.decodeJSON(data, &apiResponse)
    if err != nil {
        panic(err)
    }

    fmt.Printf("OK:%v\n", apiResponse.Ok)
}

func sendMessage_byGet() {
    data, err := bot.get("sendMessage", paras)
    if err != nil {
        fmt.Println(err)
        return
    }

    showResponse(data)
}

func sendMessage_byPostXW() {
    data, err := bot.postXW("sendMessage", paras)
    if err != nil {
        fmt.Println(err)
        return
    }

    showResponse(data)
}

func sendMessage_byPostJSON() {
    data, err := bot.postJSON("sendMessage", paras)
    if err != nil {
        fmt.Println(err)
        return
    }

    showResponse(data)
}

func main() {
    sendMessage_byGet()
    sendMessage_byPostJSON()
    sendMessage_byPostXW()
}

直接使用套件 tgbotapi,需設定 token
功能:回覆同樣的訊息
package main

import (
    "log"

    "github.com/go-telegram-bot-api/telegram-bot-api"
)

func main() {
    bot, err := tgbotapi.NewBotAPI("your token")
    if err != nil {
        log.Panic(err)
    }

    bot.Debug = true

    log.Printf("Authorized on account %s", bot.Self.UserName)

    u := tgbotapi.NewUpdate(0)
    u.Timeout = 60

    updates, err := bot.GetUpdatesChan(u)

    for update := range updates {
        if update.Message == nil {
            continue
        }

        log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)

        msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text)
        msg.ReplyToMessageID = update.Message.MessageID

        bot.Send(msg)
    }
}


Webhook
使用 ngrok 測試,並設定 setWebhook
ngrok 只適用在測試,仍需架設伺服器較為穩定,例:GAE、Heroku ...等
ngrok http 5000
https://api.telegram.org/bot<token>setWebhook?url=<ngrok url>
功能:印出收到的訊息
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

type Message struct {
    MessageID int    `json:"message_id"`
    Date      int    `json:"date"`
    Text      string `json:"text"` // optional
}

type Update struct {
    UpdateID int      `json:"update_id"`
    Message  *Message `json:"message"`
}

func main() {
    http.HandleFunc("/", fooHandler)
    log.Fatal(http.ListenAndServe(":5000", nil))
}

func fooHandler(w http.ResponseWriter, r *http.Request) {
    var update Update
    data, _ := ioutil.ReadAll(r.Body)
    fmt.Println(string(data))
    json.Unmarshal(data, &update)
    fmt.Printf("%#v\n", update)
    fmt.Printf("%#v\n", update.Message)
}
直接使用套件 tgbotapi 印出收到的訊息,需設定 token 跟 接收網址
package main

import (
    "log"
    "net/http"

    "github.com/go-telegram-bot-api/telegram-bot-api"
)

func main() {
    bot, err := tgbotapi.NewBotAPI("your token")
    if err != nil {
        log.Fatal(err)
    }

    bot.Debug = true

    log.Printf("Authorized on account %s", bot.Self.UserName)

    _, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert(<ngrok url>, nil))
    if err != nil {
        log.Fatal(err)
    }
    info, err := bot.GetWebhookInfo()
    if err != nil {
        log.Fatal(err)
    }
    if info.LastErrorDate != 0 {
        log.Printf("[Telegram callback failed]%s", info.LastErrorMessage)
    }
    updates := bot.ListenForWebhook("/")
    go http.ListenAndServe(":5000", nil)

    for update := range updates {
        log.Printf("%+v\n", update)
    }
}

參考

python-telegram-bot
Telegram.Bot.申請及Channel使用方式
從零開始的 Telegram Bot
Create a Telegram EchoBot

留言