ptgb

pub package license: MIT docs

A complete client for the Telegram Bot API. Methods for every endpoint + keyboards, media, webhooks, payments, stickers, business accounts, and etc.

import 'package:ptgb/ptgb.dart';

Future<void> main() async {
  final bot = Bot(); // loads your token from a .env file — see Quick Start
  await for (final update in bot.poll()) {
    if (update.text == '/start') {
      await bot.sendMessage(chatId: update.chatId!, text: 'Hello from ptgb!');
    }
  }
}

Contents

Features

  • Full API coverage — messaging, media, chat & forum administration, inline mode, payments & Telegram Stars, stickers, games, Telegram Business accounts, Stories, and Web Apps.
  • Every parameter is named. bot.sendMessage(chatId: id, text: 'hi'), never bot.sendMessage(id, 'hi') — so calls are self-explanatory and safe to reorder, and it's the same style everywhere in the library, down to every keyboard button and media constructor.
  • Two update sources — long-polling out of the box (Bot.poll) or your own webhook server (Bot.serveWebhook).
  • Typed helpers, not raw JSON, everywhere — keyboards (InlineKeyboardMarkup, ReplyKeyboardMarkup), media (InputMedia*), permissions (ChatPermissions, ChatAdministratorRights), inline query results (InlineQueryResult*/InputMessageContent*), and every incoming Update/Message payload and outgoing method's response (User/Chat/Message, ChatFullInfo, StickerSet, ...) — no more something['somethingelse'].
  • Built-in user & chat storage. BotStorage (powered by pdata) remembers every user and chat your bot has seen, plus any custom data you attach to them, in a plain JSON/YAML/TOML file — no database required. See Saving users and chats.
  • Optional rate limiting — pass Bot(rateLimiter: RateLimiter()) to automatically pace outgoing requests instead of handling every 429 yourself.
  • Telegram Mini App support — verify a Web App's signed initData with Bot.verifyWebAppInitData.
  • A low-level escape hatch (Bot.call) for any Bot API method that doesn't have a typed wrapper yet.

Installation

dart pub add ptgb

or add it to pubspec.yaml directly:

dependencies:
  ptgb: ^3.0.0

Getting a bot token

  1. Message @BotFather on Telegram and send /newbot.
  2. Copy the token it gives you (looks like 123456:ABC-your-token-here).
  3. Keep it somewhere safe — never commit it to source control. See Quick Start below for the recommended way to load it.

Quick start

Recommended: just run your bot with Bot() and no arguments. The very first time, since there's no .env file yet, ptgb creates one for you at .env with step-by-step instructions on where to get a token, and throws an EnvFileNotFoundException so you know to open the file, paste your token in, and run again:

import 'package:ptgb/ptgb.dart';

Future<void> main() async {
  final bot = Bot(); // reads TOKEN from .env, creating a starter file for you

  await for (final update in bot.poll()) {
    if (update.text == '/start') {
      await bot.sendMessage(chatId: update.chatId!, text: 'Hello from ptgb!');
    }
  }
}

The generated .env looks like this — fill in the blank after TOKEN=:

# This file holds your bot's secret token. Never share it or commit this
# file to git (add ".env" to your .gitignore).
#
# How to get a token:
#   1. Open Telegram and start a chat with @BotFather.
#   2. Send /newbot and follow the prompts (or /token to reuse an
#      existing bot).
#   3. Copy the token BotFather gives you and paste it below, after the
#      "=" sign, with no spaces and no quotes.
#
# TOKEN=123456789:AAExampleTokenTextGoesRightHere
TOKEN=

Using a different filename, key, or starter template? Pass dotFileName, envKey, and/or dotEnvTemplate:

final bot = Bot(dotFileName: 'secrets.env', envKey: 'BOT_TOKEN');

Add .env to your .gitignore so it never gets committed.

Alternative: pass the token directly if you're managing it yourself, e.g. from a secrets manager at deploy time:

final bot = Bot(token: myTokenFromSomewhereElse);

Either way works — just never hard-code a real token as a literal string in code that ends up in version control.

Saving users and chats

Most bots eventually need to know who has talked to them before — for a /users admin command, a per-user setting, or just to avoid re-onboarding someone. BotStorage does this for you, backed by a plain file on disk (no database setup):

import 'package:ptgb/ptgb.dart';

Future<void> main() async {
  final bot = Bot();
  final storage = BotStorage(path: 'bot_data.json');
  await storage.load();

  await for (final update in bot.poll()) {
    if (update.from != null) await storage.saveUser(user: update.from!);

    if (update.text == '/users') {
      await bot.sendMessage(
        chatId: update.chatId!,
        text: 'I know ${storage.allUsers().length} user(s) so far!',
      );
    }
  }
}

You can also attach your own data to a saved user or chat — a language preference, an onboarding step, anything — with setUserData/setChatData and read it back with getUserData/getChatData. See the BotStorage class docs and example/ for the full API (getUser, getChat, allChats, removeUser, removeChat, ...).

Examples

The example/ folder has a full, numbered set of runnable programs, from a minimal echo bot up to a "god mode" bot exercising keyboards, media, payments, stickers, invite links, and webhooks. Start with example/README.md for the full list and reading order.

Things to keep in mind

  • Every parameter is named. All Bot methods and every constructor in the library (keyboards, media, inline query results, etc.) take named parameters only — bot.sendPhoto(chatId: id, photo: file), not bot.sendPhoto(id, file). This makes call sites self-documenting and keeps them working if a method ever gains new parameters in the middle.
  • Treat your token like a password. Anyone who has it can control your bot. Keep it out of version control.
  • poll() and serveWebhook() are mutually exclusive. Telegram only delivers updates through one channel at a time — call setWebhook before using webhooks, and deleteWebhook before switching back to polling.
  • ptgb does not retry or throttle requests for you by default. Every failed call throws a TelegramApiException; wrap your update handling in try/catch so one bad call (blocked user, rate limit, invalid chat_id) doesn't crash your whole process. See example/15_error_handling_and_retries.dart. If you'd rather pace requests proactively, pass Bot(rateLimiter: RateLimiter()) — see example/17_rate_limiting.dart.
  • Inline query results are typed classes, not raw JSON Maps. Build a list of InlineQueryResult* subtypes (InlineQueryResultArticle, InlineQueryResultPhoto, InlineQueryResultCachedPhoto, ...) and pass it to Bot.answerInlineQuery — see example/08_inline_queries.dart and example/41_inline_query_result_gallery.dart.
  • Incoming User/Chat/Message payloads (and every method's response) are typed classes. Update's own getters (.message, .chat, .from, ...) return typed wrappers directly — update.message?.text, update.from?.username, etc. — with .raw always available underneath for anything not covered by a getter. See example/18_typed_message_helpers.dart. These are common class names, so if another package you're using also exports a User, Chat, or Message, import one of them with a prefix to disambiguate.
  • Want to remember your bot's users/chats between runs? See Saving users and chatsBotStorage handles it without a database.
  • Requires Dart SDK ^3.5.0.

Documentation

Full docs / wiki: doc.psdkjoon.ir/ptgb (mirrors: doc.psdk.space/ptgb, doc.psdk.fun/ptgb).

Contributing

Bug reports, feature requests, and pull requests are welcome on GitHub. If you're filing a bug, a minimal reproduction and the relevant Bot API method name help a lot.

License

MIT — see the LICENSE file for details.

Libraries

ptgb
ptgb — a full, pure-Dart Telegram Bot API client.