kittenbark/tg

A zero-dependency Go library for Telegram bots. The API is a declarative pipeline — updates flow top-to-bottom through filters and handlers, and you wire them together in a single chain.

Every Telegram API method is code-generated from the official schema, so SendMessage, SendPhoto, EditMessageText and ~200 others are always available, typed, and consistent.


Getting started

go get github.com/kittenbark/tg@latest

Set KBTG_TOKEN=<your token> (or KITTENBARK_TG_TOKEN), then:

package main

import (
	"context"
	"github.com/kittenbark/tg"
)

func main() {
	tg.NewFromEnv().
		OnError(tg.OnErrorLog).
		Filter(tg.OnPrivate).
		Command("/start", tg.CommonTextReply("hello!")).
		Branch(tg.OnMessage, func(ctx context.Context, upd *tg.Update) error {
			msg := upd.Message
			_, err := tg.CopyMessage(ctx, msg.Chat.Id, msg.Chat.Id, msg.MessageId)
			return err
		}).
		Start()
}

Pipeline

The bot is a chain. Each .Branch checks its filter — if it matches, the handler runs and the update is consumed; if not, it falls through to the next step.

tg.NewFromEnv().
Scheduler(). // auto rate-limit (prevents 429s)
OnError(tg.OnErrorLog).
Filter(tg.OnPrivate). // drop everything not from a DM
Command("/start", greetHandler).
Command("/help", helpHandler).
Branch(tg.OnPhoto, handlePhoto).
Branch(tg.OnDocument, handleDocument).
Branch(tg.OnText, tg.CommonTextReply("unrecognized command")).
Start()
Method What it does
Filter(pred) Drop updates that don’t match — nothing below sees them
Branch(pred, handler) Handle matching updates, let others fall through
Command("/cmd", handler) Shorthand for Branch(tg.OnCommand("/cmd"), handler)
Handle(handler) Catch-all — sees every update that reaches it
Default(handler) Same as Handle but conventionally placed last
Scheduler() Attach rate limiter (respects Telegram’s per-chat/global limits)
OnError(fn) Set error handler (tg.OnErrorLog, tg.OnErrorExit, or custom)

Filters

Filters are plain functions func(ctx, upd) bool, composable with All, Either, Not:

tg.All(tg.OnUrl, myDomainCheck) // AND
tg.Either(tg.OnPhoto, tg.OnVideo) // OR
tg.Not(tg.OnForwarded) // NOT

Content

Filter Matches
OnMessage Any message
OnText Text message
OnPhoto Photo
OnVideo Video
OnDocument Document
OnAudio Audio
OnVoice Voice message
OnAnimation GIF/animation
OnSticker Sticker
OnMedia Any media type
OnUrl Message contains a URL
OnTextRegexp(re) Text matches regex
OnCommand("/cmd") Specific command
OnCallback Inline button press
OnCallbackWithData[T](pred) Typed button callback

Scope

Filter Matches
OnPrivate Private chat / DM
OnPrivateMessage Private message specifically
OnPublicMessage Group or channel
OnChat(ids...) Whitelist specific chats
OnSender(ids...) Whitelist specific users
OnForwarded Forwarded message
OnReply Reply to another message
OnEdited Edited message
OnChance(0.5) Random — true with given probability

Handler combinators

Handlers are plain functions func(ctx, upd) error, composable too:

// Run all in sequence, stop on first error
tg.Chain(logHandler, processHandler, replyHandler)

// Try each, stop on first success (no error)
tg.Fallback(premiumHandler, freeHandler)

// Serialize concurrent access
tg.Synced(handler)

Common helpers

Ready-made handlers for the most frequent patterns:

tg.CommonTextReply("text") // reply with plain text
tg.CommonTextReplyExpiring(5*time.Minute, "text") // reply, auto-delete after duration
tg.CommonReactionReply("👌") // set reaction on message
tg.CommonDeleteMessage      // delete the triggering message
tg.CommonRestrictSender() // mute sender

Media

// Upload from disk
tg.SendDocument(ctx, chatId, tg.FromDisk("report.pdf"))

// Reference by file_id or URL
tg.SendPhoto(ctx, chatId, tg.FromCloud(fileId))

// Download to temp file
path, err := tg.GenericDownloadTemp(ctx, fileId)
defer os.Remove(path)

// Albums (grouped media) arrive as separate updates — HandleAlbum batches them:
tg.HandleAlbum(func (ctx context.Context, updates []*tg.Update) error {
// all photos in the album are here
return nil
})

Configuration

NewFromEnv() reads from env vars prefixed KBTG_ or KITTENBARK_TG_:

Variable Description
TOKEN Bot token from @BotFather
TEST_TOKEN Fallback token for testing
API_URL Custom Bot API server URL
TIMEOUT_HANDLE Handler timeout in seconds (0 = unlimited)
ON_ERROR log, exit, or ignore
SYNCED_HANDLE true to process updates sequentially
DOWNLOAD_TYPE classic, local_move, or local_copy

Or construct directly:

tg.New(&tg.Config{
Token:         os.Getenv("TOKEN"),
TimeoutHandle: 30 * time.Second,
OnError:       tg.OnErrorLog,
})

Full example walkthrough: tg / examples