Telegraf Documentation and Examples

repository·master·Indexed 43 days ago

https://github.com/feathers-studio/telegraf-docs

Documentation and examples for Telegraf, a modern bot API framework for building Telegram bots and Telegram Mini Apps. Includes guides on creating your first bot, implementing graceful shutdowns, managing secrets via environment variables, and deploying to AWS Lambda using the Serverless Framework. Provides detailed instructions for Telegram Mini App integration, including initData validation, local testing with testEnv, and communication between Mini Apps and bots.

Tokens
11.3K
Snippets
39
Records
43
Agent score
53%

What's inside Telegraf

  1. Introduction to Telegraf

    master

    Telegraf is a modern Telegram bot framework designed to help developers build bots using the Telegram Bot API. It provides a structured way to handle bot interactions, ranging from simple echo bots to complex applications like Mini Apps, inline dictionaries, and games.

    To use Telegraf effectively, it is recommended to have a basic understanding of JavaScript and to refer to the official Telegram Bot API Documentation alongside the Telegraf guides.

  2. Compare Mini App launch methods and capabilities

    master

    Mini Apps can be launched via several methods, each providing different data and capabilities. Use the following table to determine which method fits your needs:

    Launch MethodinitDataUnsafequery_idsendDataswitchInlineQuerychat_instance, chat_type, start_paramchat
    Keyboard button❌❌✅❌❌❌
    Inline keyboard✅✅❌❌❌❌
    Menu button✅✅❌❌❌❌
    Inline mode❌❌❌✅❌❌
    Direct link✅❌❌❌✅❌
    Attachment✅✅❌❌❌✅

    Note: Attachment menu is currently restricted to major advertisers on the Telegram Ad Platform, but can be tested on the test server.

  3. How fmt helpers and nesting work

    master

    The fmt system allows for complex, nested formatting using tagged template literals. You can nest helpers inside one another to create rich text structures. fmt also works seamlessly with message captions.

    import { fmt, bold, italics, mention, link } from "telegraf/format";
    
    // Complex nesting
    ctx.reply(fmt`
    Ground control to ${bold`${mention("Major Tom", ctx.from.id)}`}
    ${bold`Lock your ${italic`Soyuz hatch`}`}
    And ${italic`put your helmet on`}
    — ${link("David Bowie", "https://en.wikipedia.org/wiki/David_Bowie")}
    `);
    
    // Usage in captions
    ctx.replyWithPhoto(file.id, { caption: fmt`File name: ${bold(fileName)}` });
    ctx.replyWithPhoto(file.id, { caption: bold`File name: ${fileName}` });
    ctx.replyWithPhoto(file.id, { caption: bold(fileName) });
  4. Handle channel updates as an administrator

    master

    When your bot is an administrator of a channel, it receives specific updates for channel events. These updates typically reside within the channel_post or my_chat_member properties of the context.

    Joining a Channel

    When the bot first joins a channel, it receives a my_chat_member update containing old_chat_member (the state before joining) and new_chat_member (the new administrator permissions).

    New Channel Posts

    When a new message is posted in the channel, you receive a channel_post object.

    • Text Messages: Contains text and message_id.
    • Media/Multi-media: Contains media fields (e.g., photo, video, audio) and a caption.
    • Signatures: If 'Sign messages' is enabled in channel settings, the channel_post will include an author_signature (string).
    • Media Groups: For multi-media posts (like an album), each item is received as an individual request, but they all share a unique media_group_id (string) to allow you to group them.

    Note: In channel_post, the chat object remains consistent with the channel context.

    // Example: A new text message in a channel
    {
      channel_post: {
        author_signature: string, // Optional
        message_id: number,
        sender_chat: {
          "id": number,
          "title": string,
          "type": string
        },
        chat: [Object],
        date: number,
        text: string
      }
    }
    
    // Example: A multi-media post (e.g., an album)
    {
      channel_post: {
        // ... standard fields
        caption: string, // Only if media has a caption
        media_group_id: string, // Unique ID to group multi-media posts
      }
    }
  5. Use the Context object to respond to messages

    master

    The Context object (ctx) is a wrapper around the Telegram Update object. It provides several convenience methods:

    • ctx.reply(text): Sends a message back to the same chat without needing to explicitly provide a chatId.
    • ctx.update: Provides access to the raw Telegram Update object.

    For example, in a message handler, you can access user information via ctx.update.message.from.first_name.

  6. Handle user interactions in private chat

    master

    When a user interacts with your bot in a private chat, the update.message object contains specific details based on the content sent.

    • Text Messages: Include text, message_id, from (user info), chat (chat info), and date.
    • Stickers: Include the sticker object.
    • GIFs: Include both animation and document objects.
    • Files: Include the document object.
    • Forwarded Messages: Include forward_from (original sender info) and forward_date.
    // User sends a text message in private chat
    {
      message: {
        message_id: number,
        from: {
          "id": number,
          "is_bot": boolean,
          "first_name": string,
          "username": string,
          "language_code": string
        },
        chat: {
          "id": number,
          "first_name": string,
          "username": string,
          "type": "private"
        },
        date: number,
        text: string
      }
    }
    
    // User forwards a message in private chat
    {
      message: {
        message_id: number,
        from: [Object],
        chat: [Object],
        date: number,
        forward_from: {
          "id": number,
          "is_bot": boolean,
          "first_name": string,
          "last_name": string,
          "username": string
        },
        forward_date: number,
        text: string,
        entities: [
          { "offset": number, "length": number, "type": string }
        ]
      }
    }
  7. How to use session safely and avoid race conditions

    master

    Telegraf (since v4.12.0) provides safety mechanisms for sessions, but developers must follow specific patterns to avoid race conditions and data corruption:

    1. Always await async calls: Ensure all async API calls (like ctx.reply()) or next() calls are awaited or returned. Failing to do so can lead to the session being written back before operations complete.
    2. Handle errors carefully: Session data is written to the store even if a middleware throws an error. If you need to roll back changes, you must manually handle the error and revert the ctx.session state.
    3. Avoid stale data writes: Do not read a value, perform an await operation, and then write back a modified version of the original value. This can overwrite changes made by parallel updates during the await period.
    4. Webhook mode requirement: If using webhooks, you must disable webhookReply in the Telegraf configuration.
    5. Threading limitation: Session safety works with single-instance long-polling or multi-instance webhook mode, but it is not compatible with long-polling using NodeJS clusters or worker threads.
    // ✅ CORRECT: Avoid stale data by writing immediately after reading
    bot.on("message", async ctx => {
    	ctx.session.count = ctx.session.count + 1;
    });
    
    // ❌ BAD: Writing stale data after an await
    bot.on("message", async ctx => {
    	const count = ctx.session.count;
    	await fetch(API_URL, { body: count });
    	ctx.session.count = count + 1; // This might overwrite a parallel update
    });
    
    // ⚙️ REQUIRED for Webhook mode
    const bot = new Telegraf(token, { telegram: { webhookReply: false } });
  8. Handle forwarded channel messages in private chat

    master

    When a user forwards a message from a channel to your bot in a private chat, the Context object contains a message object with specific forwarding metadata.

    Key Fields:

    • forward_from_chat: Contains the channel's id, title, username, and type.
    • forward_from_message_id: The original message ID in the source channel.
    • forward_date: The original Unix/Epoch timestamp of the message.
    • forward_signature: (Optional) Present if the channel has 'Sign messages' enabled.

    Important Caveats:

    • Nested Forwards: If a user forwards a message that was already a forward from another channel, the forward_signature field will be lost.
    • Audio Messages: Forwarding audio messages does not trigger the standard forwarding metadata. Fields like forward_from_chat, forward_from_message_id, and forward_signature will not be available in the context for audio forwards.
    // Example: User forwards a channel text message
    {
      message: {
        message_id: number,
        from: [Object],
        chat: [Object],
        date: number,
        forward_from_chat: {
          "id": number,
          "title": string,
          "username": string,
          "type": string
        },
        forward_from_message_id: number,
        forward_signature: string, // Optional
        forward_date: number,
        text: string,
        entities: [
          {
            "offset": number,
            "length": number,
            "type": string
          }
        ]
      }
    }
  9. Understand the base `Context` object structure

    master

    Telegraf passes every request to your middlewares as a ctx object. Every ctx object is guaranteed to include the following four properties:

    • update: Contains the update data, including a nested update_id. This is the primary field used for processing requests.
    • telegram: Contains the Telegram API instance and configuration (e.g., token, apiRoot, options).
    • botInfo: Contains metadata about your bot (e.g., id, username, can_join_groups).
    • state: An object used for storing state within the Telegraf context.
    // The base shape of the ctx object
    {
      update: {
        update_id: number
      },
      telegram: {
        token: string,
        response: undefined /* differs based on the configuration */,
        options: {
          apiRoot: 'https://api.telegram.org',
          apiMode: 'bot',
          webhookReply: boolean,
          agent: [Agent],
          attachmentAgent: undefined,
          testEnv: boolean
        }
      },
      botInfo: {
        id: number,
        is_bot: true,
        first_name: string,
        username: string,
        can_join_groups: boolean,
        can_read_all_group_messages: boolean,
        supports_inline_queries: boolean
      },
      state: {}
    }
  10. Initialize Telegram Mini Apps in your web application

    master

    To use the Telegram Mini App API, you must first import the Telegram Web App script in your HTML before your own application logic. This exposes the window.Telegram.WebApp object.

    For TypeScript support, install the @types/telegram-web-app package to enable global types.

    Once the script is loaded, use app.ready() to signal that the page is ready for the user, and app.expand() to expand the Mini App to full screen.

    <script src="https://telegram.org/js/telegram-web-app.js"></script>
    <script src="./my-script.js"></script>
    const app = window.Telegram.WebApp;
    
    // Call as soon as your page is ready for the user to see
    app.ready();
    
    // Expand your web app to full screen
    app.expand();
  11. Inspect the `Context` object shape

    master

    Because the ctx (Context) object shape changes depending on the type of request or update received, it is recommended to log the ctx object to the console during development to understand its structure for your specific use case.

    import { Telegraf } from "telegraf";
    
    const bot = new Telegraf(process.env.BOT_TOKEN); // Your bot token 
    
    bot.use(ctx => {
      console.log(ctx);
    });
    
    bot.launch();
    
    // Enable graceful stop
    process.once('SIGINT', () => bot.stop('SIGINT'));
    process.once('SIGTERM', () => bot.stop('SIGTERM'));