Your first bot
An echo bot in twenty-five real lines. No hidden setup, no framework scaffolding — just a Go program that connects to Telegram and replies to messages.
Before you start
You need three things to make it through this page: Go 1.21 or newer, a bot token from @BotFather, and an api id + hash pair from my.telegram.org. The Installation page walks through getting all three if you do not have them yet.
An echo bot in five steps
-
Create a fresh Go module
In an empty directory, initialise a module. The name is arbitrary — it does not have to be a real import path unless you plan to publish this bot.
terminal$mkdir echo-bot && cd echo-bot$go mod init echo-bot -
Pull in the library
One import path covers everything you need.
terminal$go get github.com/amarnathcjd/gogram/telegram@latest -
Drop the program in main.go
Twenty-five lines total. The first block builds the client, the second logs in as the bot, the third registers a handler that echoes any private-chat text back.
main.go package main import ( "log" "os" "github.com/amarnathcjd/gogram/telegram" ) func main() { client, err := telegram.NewClient(telegram.ClientConfig{ AppID: 12345, AppHash: "abcdef0123456789abcdef0123456789", Session: "echo-bot.session", }) if err != nil { log.Fatal(err) } if err := client.Connect(); err != nil { log.Fatal(err) } if err := client.LoginBot(os.Getenv("BOT_TOKEN")); err != nil { log.Fatal(err) } client.On("message:*", func(m *telegram.NewMessage) error { if m.IsPrivate() && m.Text() != "" { _, err := m.Reply(m.Text()) return err } return nil }) log.Println("bot ready") client.Idle() }package main import ( "log" "os" "github.com/amarnathcjd/gogram/telegram" ) func main() { client, err := telegram.NewClient(telegram.ClientConfig{ AppID: 12345, AppHash: "abcdef0123456789abcdef0123456789", Session: "echo-bot.session", }) if err != nil { log.Fatal(err) } if err := client.Connect(); err != nil { log.Fatal(err) } if err := client.LoginBot(os.Getenv("BOT_TOKEN")); err != nil { log.Fatal(err) } client.On("message:*", func(m *telegram.NewMessage) error { if m.IsPrivate() && m.Text() != "" { _, err := m.Reply(m.Text()) return err } return nil }) log.Println("bot ready") client.Idle() } -
Export the bot token and run it
The token comes from BotFather. Keep it out of your source; an env var is the simplest option and survives moving the code around.
terminal$export BOT_TOKEN=123456:your-bot-token-from-botfather$go run main.go -
Talk to your bot
Open Telegram, find your bot by username, tap Start, and send anything. It should echo back within a few hundred milliseconds. The first run also writes
echo-bot.sessionnext to your binary — restarting the program reuses the session instead of logging in again.
Anatomy of the program
Four phases, one per top-level call:
- Construct.
NewClientvalidates the config and prepares an in-memory MTProto sender. No network yet. TheSessionpath is where the auth key will be written after the first successful login. - Connect.
Connectopens the TCP (or WebSocket) connection to a data centre, performs the obfuscation handshake, and either loads the existing auth key from the session file or runs a fresh Diffie-Hellman to make one. - Log in.
LoginBotexchanges the bot token for an authenticated session with the current DC. If the bot lives on a different DC, the server tells the client to migrate; the client handles that transparently. - Listen.
Onregisters an update handler. Themessage:*pattern matches every incoming message; the filter inside the handler restricts to private-chat text.client.Idle()blocks the main goroutine so the program does not exit — the dispatcher runs on its own worker pool.
What if it does not work
Fold-outs for the most common failure modes:
The AppID or AppHash value is wrong. Double-check what you copied from my.telegram.org — the id is a plain integer, the hash is 32 hex characters. Trailing whitespace and missing quotes are the usual suspects.
The bot token is wrong, or the bot was deleted, or BotFather revoked the token since you copied it. Regenerate with /token in BotFather.
Privacy mode is on. Bots only see messages addressed to them by default. Talk to BotFather, pick your bot, choose Bot Settings → Group Privacy → Turn off, then remove and re-add the bot to the group. Private chats are unaffected.
Your network blocks outbound 443 to Telegram's IP ranges. Set up a proxy. SOCKS5 is the easiest to spin up on a jump host.
Handler panic. The dispatcher swallows panics by default so the process keeps running, but the worker that panicked is gone. Log the panic from a middleware or wrap the handler body in defer recover and log to stderr — restart after fixing.
Next steps
Now that you have a running client, the sidebar is organised roughly in the order you will reach for things. A few tent-poles:
- ClientConfig reference — every option, what it does.
- Sending messages — text, media, albums, replies.
- Dispatcher & handlers — the full handler model.
- NewMessage — every method on the message wrapper.
- Buttons & keyboards — inline UI without leaving Go.
