Graceful Shutdown

Graceful shutdown for your bot.

Basic example

It is good practice to have a graceful shutdown for bot in order to complete all running tasks.

Most common way to implement it is to use signal.Notify function. First, import required packages.

import (
    "os"
    "os/signal"
    "syscall"
)

Initialize signal handling and done chan.

sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

done := make(chan struct{}, 1)

Note: List of signals to listen for can be extended to your needs, some info about signals here.

Handle stop signal (e.g. Ctrl+C) and stop any running tasks.

go func () {
    // Wait for stop signal
    <-sigs
    
    // Stop any tasks here ...
    
    // Notify that stop is done
    done <- struct{}{}
}()

Wait for the stop process to be completed.

// Wait for done signal
<-done

// Exit program
Full Code Example
package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/mymmrac/telego"
)

func main() {
    botToken := os.Getenv("TOKEN")

    bot, err := telego.NewBot(botToken, telego.WithDefaultDebugLogger())
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    done := make(chan struct{}, 1)

    updates, _ := bot.UpdatesViaLongPolling(nil)

    for update := range updates {
        fmt.Println("Processing update:", update.UpdateID)
        time.Sleep(time.Second * 5) // Simulate long process time
        fmt.Println("Done update:", update.UpdateID)
    }

    go func() {
        <-sigs

        fmt.Println("Stopping...")

        bot.StopLongPolling()
        fmt.Println("Long polling done")

        done <- struct{}{}
    }()

    <-done
    fmt.Println("Done")
}

Code with all comments also available on GitHub here.

Long polling example

Basically the same as first example, but we need to start handler in goroutine and also call bh.Stop inside stop handler.

Full Code Example
package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/mymmrac/telego"
    th "github.com/mymmrac/telego/telegohandler"
)

func main() {
    botToken := os.Getenv("TOKEN")

    bot, err := telego.NewBot(botToken, telego.WithDefaultDebugLogger())
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    done := make(chan struct{}, 1)

    updates, _ := bot.UpdatesViaLongPolling(nil)

    bh, _ := th.NewBotHandler(bot, updates, th.WithStopTimeout(time.Second*10))

    bh.Handle(func(bot *telego.Bot, update telego.Update) {
        fmt.Println("Processing update:", update.UpdateID)
        time.Sleep(time.Second * 5) // Simulate long process time
        fmt.Println("Done update:", update.UpdateID)
    })

    go func() {
        <-sigs

        fmt.Println("Stopping...")

        bot.StopLongPolling()
        fmt.Println("Long polling done")

        bh.Stop()
        fmt.Println("Bot handler done")

        done <- struct{}{}
    }()

    go bh.Start()
    fmt.Println("Handling updates...")

    <-done
    fmt.Println("Done")
}

Code with all comments also available on GitHub here.

Webhook example

Again, pretty similar to the first and second example, but added start for webhook (no need to use goroutine as it is a non-blocking function) and also stop of webhook before stopping bot handler.

Full Code Example
package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/mymmrac/telego"
    th "github.com/mymmrac/telego/telegohandler"
)

func main() {
    botToken := os.Getenv("TOKEN")

    bot, err := telego.NewBot(botToken, telego.WithDefaultDebugLogger())
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    done := make(chan struct{}, 1)

    updates, _ := bot.UpdatesViaWebhook("/bot" + bot.Token())

    bh, _ := th.NewBotHandler(bot, updates, th.WithStopTimeout(time.Second*10))

    bh.Handle(func(bot *telego.Bot, update telego.Update) {
        fmt.Println("Processing update:", update.UpdateID)
        time.Sleep(time.Second * 5) // Simulate long process time
        fmt.Println("Done update:", update.UpdateID)
    })

    go func() {
        <-sigs

        fmt.Println("Stopping...")

        _ = bot.StopWebhook()
        fmt.Println("Webhook done")

        bh.Stop()
        fmt.Println("Bot handler done")

        done <- struct{}{}
    }()

    go bh.Start()
    fmt.Println("Handling updates...")

    _ = bot.StartListeningForWebhook("localhost:443")

    <-done
    fmt.Println("Done")
}

Code with all comments also available on GitHub here.