npm package: @mks2508/telegram-message-builder-telegraf
Description: Telegraf integration for telegram-message-builder with progress tracking, log viewer, and reusable components
Author: MKS2508
Latest version: 0.1.2

# @mks2508/telegram-message-builder-telegraf

Telegraf integration for telegram-message-builder with high-level utilities for sending messages, tracking operations, and displaying logs.

## Features

- **TelegrafSender**: Send messages and media to users, groups, and group topics
- **ProgressManager**: Track long-running operations with automatic UI updates and rate limiting
- **LogViewer**: Display logs with configurable pagination styles
- **Predefined Menus**: Reusable keyboard patterns (main menu, back button)
- **Rate Limiting**: Built-in rate limiter for Telegram group chats (20 msgs/min)
- **Type Safe**: Full TypeScript support with Result pattern for error handling

## Installation

```bash
bun add telegraf @mks2508/telegram-message-builder @mks2508/telegram-message-builder-telegraf
```

## Quick Start

### Sending Messages

```typescript
import { Telegraf } from "telegraf";
import { TelegrafSender } from "@mks2508/telegram-message-builder-telegraf/sender";
import { TelegramMessageBuilder } from "@mks2508/telegram-message-builder";
import { TelegramKeyboardBuilder } from "@mks2508/telegram-message-builder";

const bot = new Telegraf(process.env.BOT_TOKEN);
const sender = new TelegrafSender(bot.telegram);

// Send to individual user
const message = TelegramMessageBuilder.text()
  .title("Welcome")
  .line("Status", "Active", { bold: true })
  .build();

const keyboard = TelegramKeyboardBuilder.inline()
  .callbackButton("OK", "confirm_ok")
  .buildMarkup();

await sender.send(123456789, message, { keyboard });

// Send to group topic (string format)
await sender.send("-100123456789/123", message);

// Send to group topic (object format)
await sender.send({
  chatId: -100123456789,
  threadId: 123
}, message, {
  silent: true,
  protect: true
});
```

### Sending Media

```typescript
import { TelegramMediaBuilder } from "@mks2508/telegram-message-builder";

// Photo with caption
const photo = TelegramMediaBuilder.photo("https://example.com/photo.jpg")
  .caption("Beautiful sunset")
  .build();

await sender.sendMedia(123456789, photo);

// Video with custom options
const video = TelegramMediaBuilder.video("/path/to/video.mp4")
  .caption("Check this out!")
  .build();

await sender.sendMedia(123456789, video, {
  keyboard,
  silent: true
});
```

### Progress Tracking

```typescript
import { ProgressManager, ProgressState } from "@mks2508/telegram-message-builder-telegraf/progress";

const progress = new ProgressManager(telegram, {
  updateInterval: 1000,
  maxUpdatesPerMinute: 15
});

// Start tracking
await progress.start(chatId, threadId, {
  total: 100,
  label: "Processing files"
});

// Update progress
await progress.update(chatId, threadId, {
  current: 50,
  state: ProgressState.InProgress,
  message: "Halfway there..."
});

// Complete
await progress.complete(chatId, threadId, {
  message: "All done!",
  showKeyboard: true
});

// Fail
await progress.fail(chatId, threadId, {
  message: "Something went wrong",
  error: new Error("Details...")
});
```

### Log Viewer

```typescript
import { LogViewer } from "@mks2508/telegram-message-builder-telegraf/logviewer";
import { RateLimiter } from "@mks2508/telegram-message-builder-telegraf/logviewer";

const rateLimiter = new RateLimiter({
  maxMessagesPerMinute: 20,
  windowMs: 60000
});

const viewer = new LogViewer(telegram, {
  type: "inline",
  maxLines: 100,
  linesPerPage: 10,
  showLineNumbers: true
}, rateLimiter);

// Add logs
viewer.addLog({
  level: "info",
  timestamp: new Date(),
  message: "Application started"
});

viewer.addLog({
  level: "success",
  timestamp: new Date(),
  message: "Operation completed"
});

viewer.addLog({
  level: "error",
  timestamp: new Date(),
  message: "Failed to connect",
  metadata: { host: "localhost", port: 5432 }
});

// Display page 1
await viewer.display(chatId, threadId, 1);
```

### Predefined Menus

```typescript
import {
  createMainMenu,
  createBackButton
} from "@mks2508/telegram-message-builder-telegraf/menus";

// Main menu with multiple options
const mainMenu = createMainMenu([
  { label: "Profile", data: "menu_profile" },
  { label: "Settings", data: "menu_settings" },
  { label: "Help", data: "menu_help" }
], {
  layout: "vertical"
});

// Simple back button
const backButton = createBackButton("main_menu");
```

## API Reference

### TelegrafSender

#### `send(target, message, options?)`

Send a text message to a chat.

**Parameters:**
- `target`: `IChatTarget` - Chat target (number, string, or object)
  - `number`: Simple chat ID (e.g., `123456789`)
  - `string`: Format `"chatId/threadId"` (e.g., `"-100123456789/123"`)
  - `object`: `{ chatId: number; threadId?: number }`
- `message`: `TelegramMessage` - Message built with TelegramMessageBuilder
- `options?`: `ISendOptionsWithKeyboard`
  - `keyboard?`: Inline keyboard markup
  - `parseMode?`: Parse mode override
  - `silent?`: Send without notification
  - `protect?`: Protect content from forwarding
  - `disableWebPagePreview?`: Disable link previews
  - `replyTo?`: Reply to specific message ID

**Returns:** `Promise<Result<ISendResult, ResultError<AppErrorCode>>>`

#### `sendMedia(target, media, options?)`

Send a media message (photo, video, document, audio, voice).

**Parameters:**
- `target`: `IChatTarget` - Chat target
- `media`: `IMediaBuildResult` - Media built with TelegramMediaBuilder
- `options?`: `ISendOptionsWithKeyboard` - Same options as `send()`

**Returns:** `Promise<Result<ISendResult, ResultError<AppErrorCode>>>`

#### `parseTarget(target)`

Parse chat target into chat ID and optional thread ID.

**Returns:** `Result<IParsedChatTarget, ResultError<AppErrorCode>>`

### ProgressManager

#### Constructor Options

```typescript
interface IProgressManagerConfig {
  updateInterval: number;      // Update interval in ms
  maxUpdatesPerMinute: number; // Rate limit for updates
}
```

#### Methods

- `start(chatId, threadId, options)`: Start tracking a new operation
- `update(chatId, threadId, options)`: Update progress
- `complete(chatId, threadId, options)`: Mark as complete
- `fail(chatId, threadId, options)`: Mark as failed
- `cancel(chatId, threadId)`: Cancel tracking

### LogViewer

#### Constructor Options

```typescript
interface IPaginationConfig {
  type: "classic" | "inline" | "virtual"; // Pagination style
  maxLines: number;                       // Maximum log entries
  linesPerPage: number;                   // Lines per page
  showLineNumbers?: boolean;              // Show line numbers
}
```

#### Pagination Styles

- **classic**: « ‹ 1/10 › »
- **inline**: Page 1/10 • Previous/Next buttons
- **virtual**: Up/Down arrows for scrolling

#### Methods

- `addLog(entry)`: Add a single log entry
- `addLogs(entries)`: Add multiple log entries
- `clear()`: Clear all logs
- `display(chatId, threadId, page)`: Display a specific page
- `update(chatId, messageId, page)`: Update existing message
- `getLogCount()`: Get total log count
- `getCurrentPage()`: Get current page number
- `getTotalPages()`: Get total page count

### RateLimiter

```typescript
const rateLimiter = new RateLimiter({
  maxMessagesPerMinute: 20,
  windowMs: 60000
});

// Check if can send
const canSend = await rateLimiter.canUpdate(chatId);

// Queue update if rate limited
await rateLimiter.queueUpdate(async () => {
  // Your update logic
});

// Record sent message
rateLimiter.recordSent(chatId);
```

## Chat ID Formats

The library supports multiple chat target formats:

```typescript
// Individual user (positive ID)
sender.send(123456789, message);

// Group chat (negative supergroup ID)
sender.send(-100123456789, message);

// Group topic - string format
sender.send("-100123456789/123", message);

// Group topic - object format
sender.send({
  chatId: -100123456789,
  threadId: 123
}, message);

// Parse utility
import { parseChatTarget, formatChatId } from "@mks2508/telegram-message-builder-telegraf/utils";

const parsed = parseChatTarget("-100123456789/123");
// { chatId: -100123456789, threadId: 123 }

const formatted = formatChatId(-100123456789, 123);
// "-100123456789/123"

// Check chat type
import { isGroupChat, isUserChat, extractGroupId } from "@mks2508/telegram-message-builder-telegraf/utils";

isGroupChat(-100123456789);  // true
isUserChat(123456789);       // true
extractGroupId(-100123456789); // 123456789
```

## Error Handling

All methods return `Result<T, E>` types for safe error handling:

```typescript
import { isErr } from "telegram-message-builder/utils";

const result = await sender.send(123456789, message);

if (isErr(result)) {
  console.error("Failed to send:", result.error);
  // Handle error
  return;
}

// Success
console.log("Sent:", result.value.messageId);
```

## License

MIT