Telegram bot in Go: speak robot

Last time I taught my bot to speak human. This time I’m gonna teach it to speak robot. I’m going to add a few bot commands. In Telegram the bots receive text exactly as you send it. By convention though, when the first word starts with a backslash (/) it’s interpreted as a command. Commands are used to tell the bot what to do. It’s a bit like shell, where you spend, I assume, most of your time.

The go-telegram-bot-api/telegram-bot-api package provides some functions to check whether there’s a command in the received message and lets you extract it and its arguments.

for update := range updates {
    if update.Message.IsCommand() {
        command := update.Message.Command()
        arguments := update.Message.CommandArguments()

After some light refactoring my bot is ready to accept commands as well as regular text. Actually, the plain text is simply mapped to the /add command.

if message.IsCommand() {
    switch command := message.Command(); command {
    case "a", "add":
    case "e", "export":
    case "h", "help":
    case "s", "since":
    case "t", "top":
    case "test":
        c.sendText(fmt.Sprintf("Eh? /%s?", command))
} else {


The simplest command would be /test, it just sends some hardcoded text back. It’s pretty trivial to implement:

func (c context) test() {, "It works"))



Export is a very important command. I consider it so important that I implemented it first. It allows the user to download all of its data stored in the bot’s database. Say no to vendor lock-in.

This is really annoying these days. Take any app, like a fitness tracker app for example. It keeps track of your runs and stores your data somewhere on the phone or in the cloud. And one day it stops working or you decide to start using another app. And what do you do? How do you migrate your data? Usually, there’s no easy way or no way at all. You’d have to abandon all of your history and your progress and start over in the new app.

I don’t want that for my yet non-existent users. Let them take their data home whenever they want. That’s why I have the /export command. Just say /export to the bot and it’s happy to comply:


To generate the CSV file I use the built-in package encoding/csv. It’s very easy to export all the rows into a CSV file :

buffer := &bytes.Buffer{}
csv := csv.NewWriter(buffer)

// For each row
for ... {
    csv.Write([]string{name, date})

// Flush when done (important!)

// Send
        Name:  "data.csv",
        Bytes: buffer.Bytes()}))

/add and /since

The /add and the /since commands are bread and butter of this bot. As the names imply, one is for adding the events and the other is for checking when the event was last added. /add is a 2-in-1 command, like shampoo & conditioner in one bottle, it displays the time before adding.



To practice SQL, I added a relatively useless command to display 10 most logged events.

SELECT name, COUNT(name) freq FROM events
    WHERE user = <user-id>
    GROUP BY name
    ORDER BY freq DESC
    LIMIT 10

On my really dumb dataset it looks like this:


I have to say that I’m pretty impressed with SQL so far. I knew theoretically what it could do. But now I get to try it and it’s really cool how I can just write a simple query instead of writing a bunch of code with loops and variables. Probably more efficient too.

What’s next

The bot is getting smarter. Now it could respond in different ways to a few basic commands. More is coming. I’d like the bot to be able to draw charts and show some statistics, like how often something happened or distribution throughout the day/week/month. Do you have any ideas?


If you’re curious, the code is available on GitHub. This version is tagged day-5.

Also published on DEV and Medium