openclaw/src/telegram/utils.ts
Arne Moor 69608fd305 feat: add telegram provider with CLI integration
Add Telegram as a third messaging provider alongside web and twilio.

Core Features:
- Interactive login flow with phone/SMS/2FA authentication
- Send text and media messages (images, videos, audio, documents)
- Monitor incoming messages with auto-reply support
- Session management at ~/.clawdis/telegram/session/
- Full CLI integration (login, logout, status, send, relay commands)

Implementation Details:
- Uses telegram npm package for MTProto API access
- Supports both URL and local file media sending
- Cross-platform path handling (Windows/Unix)
- Optional Twilio env vars (supports Telegram-only usage)
- Minimal provider abstraction pattern
- Comprehensive test coverage (440 tests passing)

Changes:
- Add Telegram module (client, login, monitor, inbound, outbound, session)
- Add provider factory and base interfaces
- Wire Telegram functions into CLI deps
- Update env validation to make Twilio fields optional
- Add telegram to all CLI commands (login, logout, status, send, relay)
- Add null checks in Twilio code for optional env fields
- Fix send command to properly load session and connect
- Add local file support with cross-platform path handling
- Update login message to show correct ~/.clawdis path
- Add comprehensive tests and documentation

Basic Usage:
  warelay login --provider telegram
  warelay send --provider telegram --to "@user" --message "Hi"
  warelay send --provider telegram --to "@user" --media "/path/to/file.jpg"
  warelay relay --provider telegram

All tests pass (63 files, 440 tests). Zero TypeScript errors.
2025-12-05 18:59:38 +01:00

49 lines
1.3 KiB
TypeScript

import type { Api, TelegramClient } from "telegram";
// Entity type - can be User, Chat, Channel, or their empty variants
type Entity = Api.User | Api.Chat | Api.Channel;
/**
* Resolve Telegram entity (user/chat) from identifier.
* Supports @username, phone number, or user ID.
*/
export async function resolveEntity(
client: TelegramClient,
identifier: string,
): Promise<Entity> {
// Clean identifier
const clean = identifier.trim();
// Try as-is first (handles @username, phone, user ID)
try {
return (await client.getEntity(clean)) as Entity;
} catch (_firstErr) {
// If not @ prefix, try adding it
if (!clean.startsWith("@")) {
try {
return (await client.getEntity(`@${clean}`)) as Entity;
} catch {
// Fall through to error
}
}
throw new Error(
`Could not resolve Telegram entity: ${identifier}. ` +
"Use @username, phone number (+1234567890), or user ID.",
);
}
}
/**
* Extract user ID from entity as string to avoid precision loss.
* Telegram IDs are bigint and can exceed Number.MAX_SAFE_INTEGER.
*/
export function extractUserId(entity: Entity): string {
// Extract ID as unknown to bypass TypeScript's overly narrow type inference
const id = ("id" in entity ? entity.id : null) as unknown;
if (typeof id === "bigint") {
return id.toString();
}
return "0";
}