=== Makarski Bot Connector for Telegram ===
Contributors: makaravich
Tags: telegram, bot, messenger, chatbot, notifications
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 8.0
Stable tag: 0.3.5
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Connect WordPress to a Telegram bot. Handle messages, commands, payments and build custom bot logic using WordPress action hooks.

== Description ==

**Makarski Bot Connector for Telegram** provides a clean foundation for building Telegram bots powered by WordPress. It handles all the low-level communication with the Telegram Bot API so you can focus on your bot's logic.

= What it does =

* Registers a Telegram bot webhook or runs in polling mode (via WP-Cron)
* Automatically creates WordPress users for incoming chat IDs
* Dispatches incoming messages and commands through WordPress action hooks
* Provides a full-featured BotApi class with 30+ methods
* Ships a tabbed Analytics page, mass Broadcast, and in-admin Documentation

= Key features =

* **Webhook & Polling** — works on any hosting; polling mode requires no public HTTPS URL
* **Command routing** — register bot commands with `register_bot_command()`
* **Normalized message hook** — `tgbot_message` fires for all message types (text, photo, voice, video, document, callback_query) with a consistent object structure
* **Broadcast** — send mass messages to all bot users from the WP admin (Telegram Bot → Broadcast); filter by language, compose per-locale messages, cron-batched delivery with real-time progress and history
* **Analytics** — a tabbed page (Telegram Bot → Analytics): overview counters, command usage, blocked chats and failed sends, first-touch acquisition sources from `?start=` deep links, arrival languages, referrals; consumer plugins can add their own tabs via the `tgbot_analytics_tabs` filter
* **Auto-split long messages** — text over Telegram's 4096-character limit is split at paragraph/line/word boundaries with HTML tags kept valid per chunk
* **Privacy by default** — auto-created bot users are hidden from author archives, the users sitemap, and the public REST users collection (opt out with the `tgbot_protect_bot_users` filter)
* **Send gating** — the Enable-bot toggle silences outgoing traffic too; check `tgbot_can_send()` in your own sending paths
* **Referral links** — built-in `?start=ref_<code>` tracking with a reward hook for your plugin
* **Stars payments** — built-in support for Telegram Stars invoices, pre-checkout, and refunds
* **Multipart uploads** — send photos, audio, voice messages, videos, and documents
* **Documentation in wp-admin** — the full developer manual on its own admin page
* **Internationalization** — translatable, ships with a .pot file

= Available BotApi methods =

`send_message`, `send_plain_message`, `send_markdown_message`, `send_photo`, `send_document`, `send_audio`, `send_voice`, `send_video`, `send_animation`, `send_chat_action`, `send_location`, `send_stars_invoice`, `forward_message`, `copy_message`, `delete_message`, `delete_messages`, `edit_message`, `edit_message_markup`, `answer_callback_query`, `answer_pre_checkout_query`, `set_my_commands`, `set_webhook`, `delete_webhook`, `get_webhook_info`, `get_updates`, `get_document_url`, `get_photo_url`, `refund_star_payment`

= Available action hooks =

**`tgbot_message`** *(primary hook)*
Fires for every non-command incoming message (text, media, callback_query).

    add_action( 'tgbot_message', function( $bot, $user_id, $message ) {
        // $message->type   — 'text' | 'image' | 'voice' | 'video' | 'audio'
        //                     | 'document' | 'sticker' | 'video_note' | 'callback_query'
        // $message->text   — message text, caption, or callback data
        // $message->files  — array of WP attachment IDs (downloaded files)
        // $message->has_media_group — true if part of a multi-file album
        // $message->media_group_id  — Telegram media_group_id string
        if ( $message->type === 'voice' ) {
            $bot->send_message( 'Got a voice message!' );
        }
    }, 10, 3 );

**`tgbot_handle_custom_bot_commands`**
Fires when a slash command is received, before the built-in dispatcher.

    add_action( 'tgbot_handle_custom_bot_commands', function( $bot, $user_id, $command ) {
        // $command — e.g. '/mycommand'
    }, 10, 3 );

**`tgbot_bot_call`**
Fires for every incoming update, including commands. Useful for cross-cutting concerns.

**`tgbot_pre_checkout_query`**
Fires when a Telegram Stars pre-checkout query arrives.

    add_action( 'tgbot_pre_checkout_query', function( $bot, $query, $user_id ) {
        $bot->answer_pre_checkout_query( $query->id, true );
    }, 10, 3 );

**`tgbot_successful_payment`**
Fires after a successful Telegram Stars payment.

    add_action( 'tgbot_successful_payment', function( $bot, $payment, $user_id ) {
        // $payment->telegram_payment_charge_id — use for refunds
        // $payment->total_amount — amount in Stars
    }, 10, 3 );

**`tgbot_raw_message`**
Fires with the raw Telegram update object for advanced use cases.

**`tgbot_my_chat_member`**
Fires when the bot is added to or removed from a group.

**`tgbot_user_registered`**
Fires when a WordPress user is created for a new Telegram chat.

    add_action( 'tgbot_user_registered', function( $wp_user_id, $chat_id, $from ) {
        // $from — Telegram 'from' object (may be null)
    }, 10, 3 );

**`tgbot_referral_completed`**
Fires when a new chat registers via a `?start=ref_<code>` referral link — grant your reward here.

= Registering bot commands =

Use `TGBot\register_bot_command()` inside an `init` hook:

    add_action( 'init', function() {
        TGBot\register_bot_command( 'hello', function( $bot ) {
            $bot->send_message( 'Hello, ' . $bot->chat_id . '!' );
        } );

        TGBot\register_bot_command( 'ping', function( $bot ) {
            $bot->send_message( 'Pong 🏓' );
        } );
    } );

= Minimal plugin example =

    add_action( 'init', function() {
        TGBot\register_bot_command( 'start', function( $bot ) {
            $bot->send_message( 'Welcome!' );
        } );
    } );

    add_action( 'tgbot_message', function( $bot, $user_id, $msg ) {
        if ( $msg->type === 'text' ) {
            $bot->send_message( 'You said: ' . esc_html( $msg->text ) );
        }
    }, 10, 3 );

== Installation ==

1. Upload the `makarski-bot-connector-for-telegram` folder to `/wp-content/plugins/`
2. Activate the plugin via **Plugins** in WordPress admin
3. Go to **Telegram Bot → Settings**
4. Enter your bot token (get one from [@BotFather](https://t.me/BotFather))
5. Choose connection mode:
   * **Webhook** — paste your site's HTTPS URL, click *Set Webhook*. Requires a public HTTPS URL.
   * **Polling** — no public URL needed. The bot polls Telegram via WP-Cron. Works on localhost.

== Frequently Asked Questions ==

= Does this work on localhost? =

Yes — use **Polling** mode. Telegram cannot reach localhost for webhooks, but polling works anywhere.

= What PHP version is required? =

PHP 8.0 or higher. PHP 8.1+ recommended.

= Can I use this with shared hosting? =

Yes. Tested on Orangehost and similar shared environments. Use Polling mode if outgoing HTTPS connections are restricted, or configure `ALTERNATE_WP_CRON` if WP-Cron has issues.

= How do I handle multiple files sent at once (media groups)? =

Each file in a Telegram album arrives as a separate `tgbot_message` action with `has_media_group = true` and the same `media_group_id`. Handle each file individually or buffer them yourself using `media_group_id` as the grouping key.

= Is `tgbot_process_multimedia_message` still supported? =

Yes, as a deprecated alias for `tgbot_message`. Migrate to `tgbot_message` — the signature is identical.

== Screenshots ==

1. Settings page — enable/disable toggle, token, connection mode (webhook/polling)
2. Users list — Telegram Nickname column added automatically for linked users
3. Broadcast page — recipient list with language filter, real-time progress bar, history table

== Changelog ==

= 0.3.5 =
* New Analytics admin page (Telegram Bot → Analytics) with tabs: Overview, Commands, Delivery (blocked chats + failed sends), Sources (first-touch acquisition from `?start=` deep links), Languages (arrival language_code), Referrals
* New analytics tables (`tgbot_users`, `tgbot_commands`, `tgbot_deliveries`, `tgbot_referrals`) filled automatically by the connector; existing bot users are back-filled on upgrade
* Blocked-chat detection: a permanent Telegram refusal ("blocked by the user", "user is deactivated", "chat not found") stamps `blocked_at`; the next incoming update clears it
* Built-in referral tracking via `?start=ref_<code>` deep links — `TGBot\Analytics::referral_code()` builds codes, the `tgbot_referral_completed` action lets your plugin grant rewards
* New `tgbot_user_registered` action — fires when a WordPress user is created for a new Telegram chat
* Tab API for consumer plugins: the `tgbot_analytics_tabs` filter plus public rendering helpers `AdminAnalytics::card()` / `cards()` / `table()`
* New Documentation admin page (Telegram Bot → Documentation) — the full developer manual rendered in wp-admin, with a GitHub link

= 0.3.4 =
* Compatibility: tested up to WordPress 7.1
* **Privacy fix:** WordPress users auto-created by the connector for Telegram chats are no longer exposed on public site surfaces. Before this fix the author archive `/author/<chat_id>/` (and `?author=<ID>`) returned 200 with the chat_id in the page title, letting anyone confirm from outside whether a given Telegram account had contacted the bot. Bot-user author archives now return a genuine 404, and bot users are excluded from the core users sitemap and the public REST users collection. Real (human) author pages are not affected. Existing bot users are marked automatically on upgrade. Opt out with `add_filter( 'tgbot_protect_bot_users', '__return_false' )`.
* BotApi: `send_message()`, `send_plain_message()`, `send_markdown_message()` auto-split text longer than the Telegram limit (4096 UTF-16 code units) into several messages — split prefers paragraph, then line, then word boundaries; HTML tags left open at a boundary are closed and re-opened so every chunk stays valid; `reply_markup` is attached to the last chunk. Previously such messages were rejected by Telegram ("message is too long") and silently lost.
* Broadcast admin UI: the "HTML" format works now — message text was run through `sanitize_textarea_field()`, which strips all tags, so HTML broadcasts arrived unformatted; HTML messages are now sanitized with `wp_kses()` against the Telegram-supported tag whitelist
* Fix: empty "Date" column in the user-profile Broadcast History for pending/failed rows (`job_created` SQL alias was missing)
* The "Enable bot" toggle now also gates OUTGOING messages: while the bot is disabled, all user-facing API calls (send*/edit*/delete*/forward*/copy*/answer*/pin*) are suppressed and return `ok = false` with a distinguishable `tgbot_disabled = true` property; administrative calls (getMe, webhook management, getUpdates, setMyCommands, refunds) keep working. New helper `tgbot_can_send()` (filterable via `tgbot_can_send`) is the single source of truth for consumer plugins with their own sending paths.

= 0.3.3 =
* BotApi: new `get_chat_member()` method — returns ChatMember object (status: creator, administrator, member, restricted, left, kicked); returns null on failure
* BotApi: `update_chat_id()` now also reads `my_chat_member->chat->id` — enables processing of bot-added-to-group updates
* ProcessMessages: new `tgbot_my_chat_member` action hook — fires when the bot is added to or removed from a group, passes (Bot $bot, int $group_wp_user_id, object $mch)
* BotApi: `send_photo()` accepts new optional `$reply_markup` parameter — send an image with inline buttons in one message
* Settings: webhook endpoint is normalized on save (slashes trimmed, invalid characters stripped) — a trailing slash used to silently break the rewrite rule so the webhook returned 404
* Fix: an empty endpoint no longer registers a rewrite rule that hijacks the homepage

= 0.3.2 =
* BotApi: fixed `update_chat_id()` — replaced `absint()` with `intval()` so negative group chat IDs are preserved correctly (group support)
* BotApi: `send_message()` accepts new optional `$reply_to_message_id` parameter — sends the reply using Bot API 7.0+ `reply_parameters`
* BotApi: new `get_me()` method — fetches bot info via `getMe` and caches per-token with a 24-hour transient
* BotApi / Bot: `run_command()` parses the command parameter — `/start payload` sets `$bot->command_param = 'payload'` for use in command callbacks; max command+param length increased from 100 to 200

= 0.3.1 =
* Broadcast API: new `campaign_key` column for deduplication; new `tgbot_broadcast()` helper for programmatic broadcasts from child plugins
* Audience registry: `tgbot_audiences` filter lets child plugins register named recipient segments; Broadcast UI shows a segment selector
* Locale mapping: added `uk` → `uk_UA` locale code

= 0.3.0 =
* New Broadcast feature: send messages to WordPress users with Telegram usernames from a dedicated admin page (Telegram Bot → Broadcast)
* Per-locale message composition — write separate texts for each language used by your users
* Message format selector: Plain text, HTML, or MarkdownV2
* Cron-batched delivery (200 messages/batch, ~20 msg/sec) — safe for large user bases without blocking the site
* Real-time progress bar with sent/failed counts and estimated time remaining
* Broadcast history table on the Broadcast page; per-user broadcast history on the user profile page
* New top-level "Telegram Bot" admin menu with Settings and Broadcast subpages
* New BotApi method: `send_plain_message()` — send a message without parse_mode
* Refactored broadcast admin code into TGBot\AdminBroadcast class for consistency

= 0.2.43 =
* Auto-generate webhook secret on plugin activation so the mandatory secret check always has a value
* Fixed incoming message type detection: reuse already-parsed request object instead of re-calling get_request()

= 0.2.42 =
* Webhook secret is now required: all requests without a valid X-Telegram-Bot-Api-Secret-Token header are rejected
* Sanitize incoming text fields (message text, caption, callback data) before dispatching to action hooks
* Validate update_id presence after JSON decode
* Replaced direct curl in send_multipart_request() with wp_remote_post() + http_api_curl hook

= 0.2.41 =
* Renamed plugin folder and main file to match the approved WordPress.org slug `makarski-bot-connector-for-telegram`

= 0.2.40 =
* Webhook secret token support (`X-Telegram-Bot-Api-Secret-Token` header validation)
* Input sanitization improvements: `absint()` for chat_id, `sanitize_textarea_field()` for text in BotApi
* `Core::set_current_user()` hardened with `get_userdata()` check and `absint()`
* Added `== External Services ==` section to readme.txt

= 0.2.30 =
* Renamed `tgbot_process_multimedia_message` to `tgbot_message`; added deprecated alias for backward compatibility
* Added `tgbot_raw_message` hook for advanced use cases
* Added `media_group_id` field to normalized message object

= 0.2.28 =
* Fixed PHP 8.5 deprecation: removed `curl_close()` (no-op since PHP 8.0)
* Fixed implicit nullable parameter in `send_document()`

= 0.2.27 =
* Added 11 new BotApi methods: `send_chat_action`, `send_audio`, `send_voice`, `send_video`, `send_animation`, `forward_message`, `copy_message`, `send_location`, `delete_messages`, `set_my_commands`, `refund_star_payment`

= 0.2.26 =
* Fixed callback_query commands (inline buttons) not dispatching in polling mode

= 0.2.25 =
* Auto-created user email now uses `tg-{chat_id}@{site_domain}` instead of `{id}@example.com`

= 0.2.22 – 0.2.24 =
* Fixed polling reschedule logic: moved to `sanitize_callback` so Save always applies mode even without settings change
* Removed debug logging from production code

= 0.2.18 – 0.2.21 =
* Fixed textdomain loading order (PHP notice in WP 6.7+)
* Fixed `finish_request()` header order for correct webhook response
* Fixed polling cron scheduling bugs (namespace, `add_option` hook, missing cron detection)

= 0.2.17 =
* Fixed polling namespace bug (`\BotApi` → `BotApi`)
* Added error logging to `wp_schedule_event` and polling tick

= 0.1.0 – 0.2.16 =
* Initial implementation: webhook management, polling mode, user auto-creation, Stars payments, command routing, internationalization

== External Services ==

This plugin communicates with the **Telegram Bot API** (api.telegram.org) to operate a Telegram bot connected to your WordPress site.

**What the service is:** Telegram Bot API is a third-party service by Telegram FZ-LLC that allows applications to send and receive messages through Telegram bots.

**What data is sent and when:**
* Bot token — sent with every API request to authenticate the bot.
* Chat ID — sent when delivering messages to a specific user.
* Message text and media files — sent when the bot responds to users.
* Incoming updates (messages, commands, files) — received from Telegram via webhook (HTTP POST from Telegram to your site) or polling (your site requests updates from Telegram periodically via WP-Cron).

No data is sent to Telegram unless the bot is enabled and a valid token is configured.

**Telegram Privacy Policy:** https://telegram.org/privacy
**Telegram Terms of Service:** https://telegram.org/tos

== Upgrade Notice ==

= 0.3.0 =
New Broadcast feature requires no action — database tables are created automatically on first load after update.

= 0.2.30 =
The `tgbot_process_multimedia_message` hook is deprecated. Please migrate your code to use `tgbot_message` — the signature is identical.
