ptgb
A complete Telegram Bot API client for Dart

Update Shortcuts

Part of the Examples. Update's direct-access shortcuts and the anyMessage fallback for any payload type.

Source: example/19_update_shortcuts_and_any_message.dart

// ignore_for_file: file_names
// (numbered intentionally for reading/run order -- see README.md)

// ============================================================================
// 19 — UPDATE SHORTCUTS AND THE anyMessage FALLBACK
// ============================================================================
//
// Every earlier example reaches into an `Update` via one specific getter,
// like `update.message` or `update.callbackQuery`. This example is about
// the two things that make that easier day-to-day:
//
//   1. `Update`'s direct-access shortcuts — `userId`, `messageId`,
//      `username`, `firstName`, `chatType`, `caption`, `messageThreadId`,
//      `replyToMessage`, and `entities` — which look across whichever
//      payload type the update actually carries (a plain message, an
//      edited one, a channel post, ...) so you don't have to null-check
//      five different fields yourself.
//
//   2. `anyMessage` — the raw JSON of whichever message-like payload is
//      present on the update (checked in the order: `message`,
//      `editedMessage`, `channelPost`, `editedChannelPost`,
//      `businessMessage`, `editedBusinessMessage`, `guestMessage`). Every
//      shortcut above is really just a null-safe read off `anyMessage`,
//      and you can do the same for any field ptgb doesn't have a shortcut
//      for yet — photo `file_id`s, location coordinates, a document's
//      filename, contact info, and so on all live there.
//
// HOW TO RUN:
//   dart run example/19_update_shortcuts_and_any_message.dart   (with a `.env` file)
// ============================================================================

import 'package:ptgb/ptgb.dart';

Future<void> main() async {
  final bot = Bot();

  await for (final update in bot.poll()) {
    final chatId = update.chatId;
    if (chatId == null) continue;

    // --- Part 1: the direct-access shortcuts ---------------------------
    //
    // These all read from `anyMessage` (or `from`/`chat`) under the hood,
    // so they work the same whether the update is a fresh message, an
    // edited one, or a channel post — you don't need a different code
    // path for each.
    if (update.text == '/whoami') {
      final lines = [
        'userId: ${update.userId}',
        'username: ${update.username ?? '(none set)'}',
        'firstName: ${update.firstName}',
        'chatType: ${update.chatType}',
        'messageId: ${update.messageId}',
      ];
      // `messageThreadId` is only non-null in forum supergroups / bots
      // with topic mode enabled in private chats.
      if (update.messageThreadId != null) {
        lines.add('messageThreadId: ${update.messageThreadId}');
      }
      // `replyToMessage` and `entities` are also just shortcuts — try
      // replying to another message with /whoami, or sending a message
      // containing a URL or @mention, to see them populate.
      if (update.replyToMessage != null) {
        lines
            .add('replying to message ${update.replyToMessage!['message_id']}');
      }
      if (update.entities != null) {
        final types = update.entities!.map((e) => e['type']).join(', ');
        lines.add('entities: $types');
      }
      await bot.sendMessage(chatId, lines.join('\n'));
      continue;
    }

    // `caption` is the shortcut version of `anyMessage?['caption']` — it
    // covers photos, videos, documents, etc. all at once.
    if (update.caption != null) {
      await bot.sendMessage(chatId, 'Nice caption: "${update.caption}"');
      continue;
    }

    // --- Part 2: anyMessage as a raw-JSON fallback ----------------------
    //
    // ptgb only ships typed shortcuts for the fields most bots need. For
    // everything else — the specific shape of a photo, location, contact,
    // document, etc. — read it straight off `anyMessage`, exactly like
    // you'd read any other Telegram Bot API JSON field.
    final msg = update.anyMessage;
    if (msg == null) continue;

    // Photos: `photo` is an array of `PhotoSize`s (same image at several
    // resolutions) — the last entry is the largest.
    final photoSizes = msg['photo'] as List?;
    if (photoSizes != null && photoSizes.isNotEmpty) {
      final largest = photoSizes.last as Json;
      await bot.sendMessage(
        chatId,
        'Got a photo! Largest size: '
        '${largest['width']}x${largest['height']}, file_id: ${largest['file_id']}',
      );
      continue;
    }

    // Locations: plain `latitude`/`longitude` fields.
    final location = msg['location'] as Json?;
    if (location != null) {
      await bot.sendMessage(
        chatId,
        'Location received: ${location['latitude']}, ${location['longitude']}',
      );
      continue;
    }

    // Documents: `file_name` and `mime_type` live alongside the usual `file_id`.
    final document = msg['document'] as Json?;
    if (document != null) {
      await bot.sendMessage(
        chatId,
        'Document: ${document['file_name']} (${document['mime_type']})',
      );
      continue;
    }

    // Contacts: shared straight from the user's address book.
    final contact = msg['contact'] as Json?;
    if (contact != null) {
      await bot.sendMessage(
        chatId,
        'Contact: ${contact['first_name']}${contact['phone_number']}',
      );
      continue;
    }

    if (update.text == '/start') {
      await bot.sendMessage(
        chatId,
        'Try /whoami, send a caption on a photo, or share a photo, '
        'location, document, or contact to see anyMessage in action.',
      );
    }
  }
}

Run it from the package root:

dart run example/19_update_shortcuts_and_any_message.dart