Skip to content

Announcements

Current version: v0.0.5

telegram-bot-essentials/announcements (namespace TelegramBotEssentials\Announcements) lets admins compose a message once and broadcast it to every bot user, or a filtered subset. Broadcasts run with live progress, per-target delivery tracking, and can be retracted (deleted) afterwards — all without blocking the bot's normal interactive traffic.

Installation

bash
composer require telegram-bot-essentials/announcements
php artisan migrate

Publish config to change the queue name or batch size:

bash
php artisan vendor:publish --tag=tbe-announcements-config
php
// config/tbe-announcements.php
return [
    'queue' => env('TBE_ANNOUNCEMENTS_QUEUE', 'announcements'),
    'batch_size' => (int) env('TBE_ANNOUNCEMENTS_BATCH_SIZE', 100),
];

Make sure a worker actually processes that queue, alongside your default one:

bash
php artisan queue:work --queue=default,announcements

Dedicated queue by design

Announcements deliberately dispatch onto their own (low-priority) queue rather than default, so a broadcast to thousands of users can't starve interactive bot responses (button presses, state answers) that share the same worker pool.

Data model

ModelPurpose
AnnouncementThe composed message: method (html, copy, or forward), message_text/message_id/from_chat_id depending on method, sent_at, plus action_status_chat_id/action_status_message_id for the live progress message
AnnouncementTargetOne row per recipient BotUser, with status: null (pending) → sent / forbidden (blocked/blocked-the-bot) → deleted (after retraction)

Announcement::sendTo(int $chatId) dispatches by method:

php
'copy'    => wHook()->api()->copyMessage([...]);      // re-sends as a new message, no "Forwarded from" tag
'forward' => wHook()->api()->forwardMessage([...]);   // keeps the "Forwarded from" tag
'html'    => wHook()->api()->sendMessage([..., 'parse_mode' => 'HTML']); // freeform composed text

Sending flow

Admin composes & previews the announcement (sent to the admin's own chat first, to catch mistakes)

"Start Sending" → targets are chunked into config('tbe-announcements.batch_size') groups

SendAnnouncementJob (queued, one per chunk, exports/imports WebhookContext across the queue boundary)
      │  for each target: Announcement::sendTo($peerId)
      │    success → AnnouncementTarget.status = 'sent', message_id stored
      │    failure (blocked/kicked/etc.) → status = 'forbidden'

Progress message (action_status_chat_id/message_id) is edited live with an ASCII progress bar,
throttled via a 3-second Cache::add() lock per announcement so concurrent job completions
don't hammer editMessageText() into a 429 rate limit

Retraction flow

Symmetric to sending: DeleteAnnouncementJob walks the same sent targets, calls deleteMessage() per recipient, and flips status to deleted (or forbidden if the delete call fails), updating the same throttled progress message.

Why WebhookContext matters here

Both jobs are constructed with WebhookContext $context and call wHook()->importContext($context) as the very first line of handle() — because a batch job runs on a queue worker with no incoming Telegram webhook of its own, wHook() would otherwise have no bot/API/tenant to operate with. This is the same export/import pattern documented in Reference → Webhook Context Export; Announcements is the primary real-world consumer of it in this ecosystem.

Admin UI

AnnouncementsQuery / AnnouncementsAnswer / AnnouncementsFeature / AnnouncementsKey provide the full admin flow: list, detail (with live sent/pending/deleted/forbidden counts), compose, preview, send, and retract.

Localization

Ships English (en) and Persian (fa) translations out of the box:

bash
php artisan vendor:publish --tag=tbe-announcements-translations