# TeleWire — Technical Documentation
> Full reference for developers and AI agents working with the TeleWire ProcessWire module.
---
## Table of Contents
1. [Architecture Overview](#architecture-overview)
2. [File Structure](#file-structure)
3. [Configuration Reference](#configuration-reference)
4. [Public API Reference](#public-api-reference)
5. [TelegramAPI Class Reference](#telegramapi-class-reference)
6. [Internal Methods](#internal-methods)
7. [Logging System](#logging-system)
8. [Message Formatting](#message-formatting)
9. [Usage Examples](#usage-examples)
10. [Hook Examples](#hook-examples)
11. [Error Handling](#error-handling)
12. [Troubleshooting](#troubleshooting)
---
## Architecture Overview
TeleWire consists of two classes:
```
TeleWire (ProcessWire Module) TelegramAPI (HTTP wrapper)
┌─────────────────────────┐ ┌──────────────────────────┐
│ - Module config UI │ uses ───► │ - sendMessage() │
│ - send() │ │ - sendPhoto() │
│ - sendPhoto() │ │ - sendDocument() │
│ - sendDocument() │ │ - getMe() │
│ - testConnection() │ │ - getUpdates() │
│ - getUpdates() │ │ - setWebhook() │
│ - Logging │ │ - request() (cURL) │
└─────────────────────────┘ └──────────────────────────┘
```
**TeleWire** is the ProcessWire `ConfigurableModule` that manages configuration, chat ID routing, message splitting, and logging. It delegates all HTTP communication to **TelegramAPI**, which is a standalone wrapper around the Telegram Bot API.
The module is `singular` (only one instance exists) and `autoload` (loaded on every request). The `TelegramAPI` instance is created during `init()` if a bot token is present.
---
## File Structure
```
TeleWire/
├── TeleWire.module.php # Main ProcessWire module class
├── TelegramAPI.php # Telegram Bot API HTTP wrapper
├── README.md # Marketing/overview page
├── DOCUMENTATION.md # This file
└── LICENSE # MIT License
```
---
## Configuration Reference
Configuration is stored in ProcessWire's module config system and accessible via `$this->propertyName` inside the module or `$modules->get('TeleWire')->propertyName` externally.
| Property | Type | Default | Description |
|---|---|---|---|
| `botToken` | `string` | `''` | Telegram bot token from @BotFather. Required. |
| `chatIds` | `string` | `''` | Raw string of chat IDs, comma or newline separated. |
| `parseMode` | `string` | `'HTML'` | Message parse mode: `''`, `'HTML'`, `'Markdown'`, `'MarkdownV2'` |
| `maxMessageLength` | `int` | `4096` | Max chars per message chunk. Telegram hard limit is 4096. |
| `disableLinkPreviews` | `bool` | `true` | Passes `disable_web_page_preview` to Telegram API. |
| `enableSilentMode` | `bool` | `false` | Passes `disable_notification` to Telegram API. |
| `enableLogging` | `bool` | `true` | Logs sent messages to `telewire` log. |
| `enableDebugLogging` | `bool` | `false` | Logs step-by-step debug info to `telewire-debug` log. |
| `timeout` | `int` | `5` | cURL timeout in seconds for API requests. |
### Chat ID Formats
```
# User chat ID (positive integer)
123456789
# Group chat ID (negative integer)
-1001234567890
# Multiple IDs — comma separated
123456789, -1001234567890
# Multiple IDs — newline separated
123456789
-1001234567890
```
---
## Public API Reference
### `send(string $message, array $options = []): bool`
Sends a text message to **all configured chat IDs**. Long messages are automatically split into chunks.
**Parameters:**
| Parameter | Type | Description |
|---|---|---|
| `$message` | `string` | Message text. HTML/Markdown allowed depending on `parseMode`. |
| `$options` | `array` | Optional Telegram API parameters to override module defaults. |
**Supported `$options` keys:**
| Key | Type | Description |
|---|---|---|
| `parse_mode` | `string` | Override `parseMode` for this message only. |
| `disable_web_page_preview` | `bool` | Override link preview setting. |
| `disable_notification` | `bool` | Override silent mode. |
| `reply_to_message_id` | `int` | Reply to a specific message. |
| `protect_content` | `bool` | Prevent forwarding/saving of message. |
**Returns:** `true` if all messages to all chats succeeded, `false` if any failed.
**Note:** Returns `true` even if only some chats succeeded. Check error logs for per-chat failures.
```php
$telewire = $modules->get('TeleWire');
// Basic
$telewire->send('Hello World');
// With override options
$telewire->send('Alert!', [
'disable_notification' => false, // force sound even in silent mode
'parse_mode' => 'HTML'
]);
```
---
### `sendPhoto(string $photo, string $caption = '', array $options = []): bool`
Sends a photo to all configured chat IDs.
**Parameters:**
| Parameter | Type | Description |
|---|---|---|
| `$photo` | `string` | Absolute file path or public URL. |
| `$caption` | `string` | Optional caption text (max 1024 chars). |
| `$options` | `array` | Additional Telegram API parameters. |
```php
// From local file
$telewire->sendPhoto('/var/www/site/assets/files/123/photo.jpg', 'Product image');
// From URL
$telewire->sendPhoto('https://example.com/image.png', 'New arrival');
// Without caption
$telewire->sendPhoto('/path/to/screenshot.png');
```
**Note:** Local file paths are passed directly to `TelegramAPI::sendPhoto()`, which sends them as JSON body. If your server blocks outgoing file uploads, use URLs instead.
---
### `sendDocument(string $document, string $caption = '', array $options = []): bool`
Sends a document (any file type) to all configured chat IDs.
**Parameters:**
| Parameter | Type | Description |
|---|---|---|
| `$document` | `string` | Absolute file path or public URL. |
| `$caption` | `string` | Optional caption text (max 1024 chars). |
| `$options` | `array` | Additional Telegram API parameters. |
```php
// Send a PDF report
$telewire->sendDocument('/var/www/site/assets/files/456/report.pdf', 'Monthly Report');
// Send a spreadsheet
$telewire->sendDocument('/tmp/export.xlsx', 'Data Export ' . date('Y-m-d'));
```
---
### `testConnection(): array`
Tests the bot token by calling the Telegram `getMe` endpoint. Used internally by the admin UI, but also callable from code.
**Returns:** Associative array:
```php
// On success:
[
'success' => true,
'message' => 'Connected to bot: MyBot (@my_bot)',
'data' => [ /* Telegram bot object */ ]
]
// On failure:
[
'success' => false,
'message' => 'Failed to connect: API Error (401): Unauthorized'
]
```
```php
$result = $modules->get('TeleWire')->testConnection();
if (!$result['success']) {
throw new RuntimeException('TeleWire not configured: ' . $result['message']);
}
```
---
### `getUpdates(int $offset = 0, int $limit = 10): array|false`
Retrieves pending updates from the Telegram Bot API. Useful for finding Chat IDs during initial setup.
```php
$updates = $modules->get('TeleWire')->getUpdates();
foreach ($updates as $update) {
$chatId = $update['message']['chat']['id'] ?? null;
$text = $update['message']['text'] ?? '';
echo "Chat ID: {$chatId}, Text: {$text}\n";
}
```
---
## TelegramAPI Class Reference
`TelegramAPI` extends `WireData` and can be used independently of the ProcessWire module if needed.
### Constructor
```php
$api = new TelegramAPI($token, [
'timeout' => 10,
'parseMode' => 'HTML'
]);
```
### Methods
#### `sendMessage(string|int $chatId, string $text, array $options = []): array|false`
Sends a message to a single chat. Returns Telegram's `result` object on success, `false` on failure.
```php
$result = $api->sendMessage(123456789, 'Hello!', [
'parse_mode' => 'HTML',
'disable_web_page_preview'=> true
]);
if ($result === false) {
echo $api->getLastError();
}
```
#### `sendPhoto(string|int $chatId, string $photo, string $caption = '', array $options = []): array|false`
#### `sendDocument(string|int $chatId, string $document, string $caption = '', array $options = []): array|false`
#### `getMe(): array|false`
Returns bot info from Telegram (`id`, `first_name`, `username`, `is_bot`, etc.).
#### `getUpdates(int $offset = 0, int $limit = 100, int $timeout = 0): array|false`
#### `setWebhook(string $url, array $options = []): array|false`
#### `deleteWebhook(): array|false`
#### `getLastError(): string`
Returns the last error string. Set after any failed `request()` call.
#### `getLastResponse(): array|null`
Returns the raw decoded JSON response from the last API call.
---
## Internal Methods
These are `protected` methods on `TeleWire` — not part of the public API but useful to understand behavior.
### `parseChatIds(string $chatIds): array`
Parses the raw `chatIds` config string into a clean array of validated IDs.
- Splits on whitespace, commas, or newlines
- Validates format: optionally negative, digits only
- Deduplicates
- Returns `[]` on empty/invalid input
```php
// Input: "123456, -100987\n123456"
// Output: ['123456', '-100987'] (deduped)
```
### `splitMessage(string $message): array`
Splits a long message into parts ≤ `maxMessageLength` characters.
- Uses `mb_strlen` / `mb_substr` for multibyte safety
- Prefers splitting at newline boundaries when the last newline is in the second half of the chunk
- Falls back to hard split at character limit
### `handleTestRequest()`
Called during `ready()` when `$input->post('telewire_test')` is set (AJAX from admin UI). Superuser-only. Returns JSON response and calls `exit`.
---
## Logging System
TeleWire writes to three ProcessWire log files in `/site/assets/logs/`:
| File | Trigger | Contents |
|---|---|---|
| `telewire.txt` | `enableLogging = true` | Successful send summary: count, duration |
| `telewire-errors.txt` | Always | Per-chat send failures, configuration errors |
| `telewire-debug.txt` | `enableDebugLogging = true` | Step-by-step trace of every operation |
**Reading logs in code:**
```php
// Via ProcessWire log API
$logs = $this->wire('log')->getEntries('telewire-errors', ['limit' => 20]);
foreach ($logs as $entry) {
echo $entry['text'] . "\n";
}
```
**Log format (debug):**
```
[2026-01-23 14:22:01] Sending message to 2 chat(s)
[2026-01-23 14:22:01] Sending to chat ID: 123456789
[2026-01-23 14:22:01] ✓ Sent to chat ID: 123456789
[2026-01-23 14:22:01] Total execution time: 312.5ms
```
> ⚠️ Debug logging creates large files quickly. Enable only during active troubleshooting.
---
## Message Formatting
### HTML Mode (default)
Telegram supports a limited HTML subset. Unsupported tags are stripped silently.
```php
$message = "Bold text\n";
$message .= "Italic text\n";
$message .= "Underlined\n";
$message .= "Strikethrough\n";
$message .= "inline code\n";
$message .= "
code block\nmulti-line\n"; $message .= "Link text\n"; $message .= "Nested bold italic\n"; $telewire->send($message); ``` **Special characters** in HTML mode that must be escaped if used literally: `<` → `<` `>` → `>` `&` → `&` ### Markdown Mode ```php $telewire->send('*Bold* _italic_ `code`', ['parse_mode' => 'Markdown']); ``` ### MarkdownV2 Mode Stricter than Markdown. Most punctuation must be escaped with `\`. ```php // Characters requiring escape: _ * [ ] ( ) ~ ` > # + - = | { } . ! $telewire->send('Hello\! This is *bold* and \_italic\_', ['parse_mode' => 'MarkdownV2']); ``` ### No Formatting ```php $telewire->send('Plain text