6

I use go-telegram-bot-api for building a Telegram Bot and deploying it on Heroku.

I need to set Webhooks as I used to do in Python like in this Python case.

Can't understand how to set Webhooks in go-telegram-bot-api without providing certificate files.

The main example contains such lines:

If you need to use webhooks (if you wish to run on Google App Engine), you may use a slightly different method.

package main

import (
    "gopkg.in/telegram-bot-api.v4"
    "log"
    "net/http"
)

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

    bot.Debug = true

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

    _, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem"))
    if err != nil {
        log.Fatal(err)
    }

    updates := bot.ListenForWebhook("/" + bot.Token)
    go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil)

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

But using Heroku for deploying how could I listen to Webhooks without providing pem certificate files?

franchb
  • 1,174
  • 4
  • 19
  • 42
  • I asked this question also in Github issues to `go-telegram-bot-api` repository and got a good answer: _You'd just replace it with `go http.ListenAndServe(":8080", nil)`_ and it seems to be working! – franchb Nov 10 '17 at 18:39

2 Answers2

4

Following this https://devcenter.heroku.com/articles/getting-started-with-go to get the starter code.

initTelegram() and webhookHandler() are functions to study.

Once code is deployed to Heroku, run:

heroku logs --tail -a <YOUR-APP-NAME>

send some messages to the bot, you should see messages logged.

UPDATE 1

Check your app URL is the same as baseURL variable in your code -- run heroku info -a <YOUR-APP-NAME> -- your URL should be equal to what Web URL is.

UPDATE 2

In order to check your Telegram API Webhooks response ping this address in your browser: https://api.telegram.org/bot<YOUR BOT TOKEN GOES HERE>/getWebhookInfo where you should concatenate string bot and your actual Telegram Bot Token in that address string.

Optionally if you run your code not on Heroku, running curl from terminal could be an option according to Official Telegram API Reference:

$ curl -F "url=https://your.domain.or.ip.com" -F "certificate=@/etc/ssl/certs/bot.pem" https://api.telegram.org/bot<YOUR BOT TOKEN GOES HERE>/setWebhook

THE CODE:

package main

import (
    "encoding/json"
    "io"
    "io/ioutil"
    "log"
    "os"

    "github.com/gin-gonic/gin"
    "gopkg.in/telegram-bot-api.v4"

    _ "github.com/heroku/x/hmetrics/onload"
    _ "github.com/lib/pq"
)

var (
    bot      *tgbotapi.BotAPI
    botToken = "<YOUR BOT TOKEN GOES HERE>"
    baseURL  = "https://<YOUR-APP-NAME>.herokuapp.com/"
)

func initTelegram() {
    var err error

    bot, err = tgbotapi.NewBotAPI(botToken)
    if err != nil {
        log.Println(err)
        return
    }

    // this perhaps should be conditional on GetWebhookInfo()
    // only set webhook if it is not set properly
    url := baseURL + bot.Token
    _, err = bot.SetWebhook(tgbotapi.NewWebhook(url))
    if err != nil {
        log.Println(err)
    } 
}

func webhookHandler(c *gin.Context) {
    defer c.Request.Body.Close()

    bytes, err := ioutil.ReadAll(c.Request.Body)
    if err != nil {
        log.Println(err)
        return
    }

    var update tgbotapi.Update
    err = json.Unmarshal(bytes, &update)
    if err != nil {
        log.Println(err)
        return
    }

    // to monitor changes run: heroku logs --tail
    log.Printf("From: %+v Text: %+v\n", update.Message.From, update.Message.Text)
}

func main() {
    port := os.Getenv("PORT")

    if port == "" {
        log.Fatal("$PORT must be set")
    }

    // gin router
    router := gin.New()
    router.Use(gin.Logger())

    // telegram
    initTelegram()
    router.POST("/" + bot.Token, webhookHandler)

    err := router.Run(":" + port)
    if err != nil {
        log.Println(err)
    }
}

Good luck and have fun!

franchb
  • 1,174
  • 4
  • 19
  • 42
k1m190r
  • 1,213
  • 15
  • 26
  • Thank you for your idea. I've implemented. But still the same problem. Nothing happens after a successful run on Heroku. When I try tu cURL a webhook get this json - `{"ok":true,"result":{"url":"https://sf-demo-bot.herokuapp.com/","has_custom_certificate":false,"pending_update_count":30,"last_error_date":1510981838,"last_error_message":"Wrong response from the webhook: 404 Not Found","max_connections":40}}` – franchb Nov 18 '17 at 05:12
  • Maybe Telegram API could not get OK response? I tried to add `c.String(200, "POST")` to the `webhookHandler` functions but nothing happens again. – franchb Nov 18 '17 at 06:24
  • Did you try sending message to your bot with telegram app and observe heroku logs? This is a full solution I'm sure. – k1m190r Nov 18 '17 at 07:05
  • @IlyaRusin can you please show the full curl command as you use it. – k1m190r Nov 18 '17 at 07:26
  • ... your curl json looks like your sending request to the telegram servers. I'll test some more why telegram complains about "Wrong response". Will update the answer. – k1m190r Nov 18 '17 at 07:47
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/159279/discussion-between-biosckon-and-ilya-rusin). – k1m190r Nov 18 '17 at 08:33
  • 1
    see the [chat](http://chat.stackoverflow.com/rooms/159279/discussion-between-biosckon-and-ilya-rusin) above essentially this is your URL double check you giving correct heroku app URL. – k1m190r Nov 18 '17 at 09:14
3

If you are using Heroku for your bot, You don't need certificates. Heroku provides free SSL certificates. So, You put the URL of your app with https scheme and listen with a HTTP Server on the PORT that heroku sets for your application. See the example below to understand better.

func main() {
    // Create bot instance
    u := fetchUpdates(bot)

    go http.ListenAndServe(":" + PORT, nil)
}

func fetchUpdates(bot *tgbotapi.BotAPI) tgbotapi.UpdatesChannel {
    _, err = bot.SetWebhook(tgbotapi.NewWebhook("https://dry-hamlet-60060.herokuapp.com/instagram_profile_bot/" + bot.Token))
    if err != nil {
        fmt.Fatalln("Problem in setting Webhook", err.Error())
    }

    updates := bot.ListenForWebhook("/instagram_profile_bot/" + bot.Token)

    return updates
}
franchb
  • 1,174
  • 4
  • 19
  • 42
Ishan Jain
  • 665
  • 6
  • 20
  • Сan you, please, clarify more about `/instagram_profile_bot/` at the end of your Heroku app address? Maybe my issue is I didn't add this string? The name of this string -- is this the name of Go compiled binary? Or what? – franchb Nov 17 '17 at 11:39
  • 1
    @IlyaRusin Heroku only allows the app to run one http server. My application had a couple more endpoints for other purpose. And the `/instagram_profile_bot/` was the endpoint that I wanted my bot to use for communicating with Telegram Servers. You can replace it with `/` and it'll be just fine. – Ishan Jain Nov 17 '17 at 13:58
  • And what is `tbot.BotAPI` and `tbot.UpdateChan`? Is it`tgbotapi.BotAPI?` And if yes - what is `UpdateChan`? I've got `undefined: tgbotapi.UpdateChan` – franchb Nov 17 '17 at 14:18
  • 1
    `tbot.BotAPI` is `tgbotapi.BotAPI` and `UpdateChan` is `UpdatesChannel` that [ListenForWebhook](https://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api#BotAPI.ListenForWebhook) function returns. – Ishan Jain Nov 17 '17 at 14:21
  • I've changed all this stuff but I still can't listen to my bot. I think something wrong with the library. I think I should read more carefully [https://core.telegram.org/bots/webhooks](https://core.telegram.org/bots/webhooks) – franchb Nov 17 '17 at 16:34