Plugin Author Guide

← All docs

telebot exposes a stable extension surface in both languages. A plugin bundles bot behavior — middleware, handlers, API hooks, lifecycle — behind a single named object (or package, in Go) that installs cleanly, coexists with other plugins, and can be removed.

What a plugin can touch

Layer What you can do Node API Go API
Update pipeline Wrap every update; add handlers / error handlers app.use(fn), app.addHandler() router.Use(mw), router.Command/Text/Handle
Context & state Read/write per-user/chat state; attach per-update sessions context.user_data / chat_data c.Set(key, v) / c.Get(key)
API traffic Mutate outgoing payloads, observe/replace results, react to failures bot.transformRequest(fn, tag?), bot.transformResponse(fn, tag?), bot.onApiError(fn, tag?) b.OnResponse(h), b.OnError(h) (return unregister)
Lifecycle Async setup/teardown around start & stop app.onInit(hook), app.onShutdown(hook) Your own init before RunPolling / context cancellation
Ordering & removal Declare dependencies, control order, detach cleanly dependsOn, priority, app.removePlugin(name) router.UseNamed(name, mw) / router.RemoveMiddleware(name)

Node / TypeScript

telebot-tsA plugin is a named object with a synchronous install():

import type { Plugin, Application } from "telebot-ts";

export function protectContent(): Plugin {
  return {
    name: "telebot-plugin-protect-content",
    // Install only after these plugins; lower priority installs first
    // among simultaneously ready plugins.
    dependsOn: [],
    priority: 0,
    install(app: Application) {
      // 1. Wrap every dispatched update.
      app.use(async (context, next) => {
        const start = performance.now();
        await next();
        console.log(`update ${context.update.update_id} in ${performance.now() - start}ms`);
      });

      // 2. Decorate every outgoing API payload. The tag lets
      //    app.removePlugin() detach these hooks later.
      app.bot.transformRequest((method, payload) => {
        if (method === "sendMessage") payload.protect_content = true;
      }, "telebot-plugin-protect-content");

      // 3. Async setup runs before polling/webhook starts.
      app.onInit(async () => { /* open connections */ });
      app.onShutdown(() => { /* flush and close */ });
    },
    uninstall(app: Application) {
      // Runs before removePlugin() deregisters everything above.
    },
  };
}

Installing, ordering, removing

app.usePlugin(protectContent());      // duplicate names throw
app.flushPlugins();                   // usually automatic
app.hasPlugin("telebot-plugin-protect-content");
app.pluginState("telebot-plugin-protect-content").calls = 0; // namespaced state
app.removePlugin("telebot-plugin-protect-content");          // full detach
Plugins whose dependsOn dependencies are not installed yet are deferred and installed automatically once the dependency arrives. Dependencies still missing when runPolling/runWebhook starts make the app throw at startup.

Go

telebot-goA Go plugin is a package exporting a constructor that returns a routing.MiddlewareFunc. The built-in pkg/plugins/i18n package is the reference implementation:

import (
    "github.com/Nam088/telebot/packages/go/pkg/plugins/i18n"
    "github.com/Nam088/telebot/packages/go/pkg/routing"
)

// Register under a name so it can be removed later.
_ = router.UseNamed("i18n", i18n.New(i18n.Options{
    DefaultLocale: "en",
    Locales: map[string]map[string]string{
        "en": {"hello": "Hello, {name}!"},
        "vi": {"hello": "Xin chào, {name}!"},
    },
}))

router.Text("", func(c *routing.Context) error {
    session, _ := i18n.For(c) // per-update session via the context store
    _, err := c.Reply(session.T("hello", map[string]string{"name": "Nam"}))
    return err
})

// Detach later:
router.RemoveMiddleware("i18n")

To observe API traffic, register hooks on the *bot.Bot — both return an unregister closure:

off := b.OnResponse(func(method string, result json.RawMessage) {
    log.Printf("%s ok (%d bytes)", method, len(result))
})
offErr := b.OnError(func(method string, err error) {
    log.Printf("%s failed: %v", method, err)
})
defer off()
defer offErr()

Built-in reference plugin: i18n

Both frameworks ship an internationalization plugin demonstrating the full surface: middleware attaches a per-update translation session, locale is resolved from stored user preference → Telegram language_code → default, and {placeholder} interpolation is built in.

Node Go
Install app.usePlugin(plugins.i18n(options)) router.UseNamed("i18n", i18n.New(options))
Access in handler i18nFor(context).t("hello", { name }) i18n.For(c) → session.T("hello", params)
Persist locale session.setLocale("vi") (stored in user_data) session.SetLocale("vi") (current update)

Author checklist