@daemux/telegram-adapter 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +8 -0
- package/dist/api-send.d.ts +40 -0
- package/dist/api-send.d.ts.map +1 -0
- package/dist/api-send.js +77 -0
- package/dist/api-send.js.map +1 -0
- package/dist/api.d.ts +39 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +123 -0
- package/dist/api.js.map +1 -0
- package/dist/channel-convert.d.ts +9 -0
- package/dist/channel-convert.d.ts.map +1 -0
- package/dist/channel-convert.js +67 -0
- package/dist/channel-convert.js.map +1 -0
- package/dist/channel.d.ts +60 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +234 -0
- package/dist/channel.js.map +1 -0
- package/dist/format.d.ts +13 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +129 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/message-resolver.d.ts +26 -0
- package/dist/message-resolver.d.ts.map +1 -0
- package/dist/message-resolver.js +155 -0
- package/dist/message-resolver.js.map +1 -0
- package/dist/poller.d.ts +40 -0
- package/dist/poller.d.ts.map +1 -0
- package/dist/poller.js +95 -0
- package/dist/poller.js.map +1 -0
- package/dist/types.d.ts +158 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
package/dist/channel.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TelegramChannel - Main channel implementation
|
|
3
|
+
* Implements the EnhancedChannel interface for Telegram Bot API.
|
|
4
|
+
* Uses long polling, HTML formatting, and user allowlist enforcement.
|
|
5
|
+
*/
|
|
6
|
+
import { TelegramApi, TelegramApiError } from './api';
|
|
7
|
+
import { sendMessage, sendPhoto, sendDocument, sendAudio, sendVideo, sendVoice, sendVideoNote, sendAnimation, } from './api-send';
|
|
8
|
+
import { TelegramPoller } from './poller';
|
|
9
|
+
import { markdownToTelegramHtml, chunkText, escapeHtml } from './format';
|
|
10
|
+
import { convertTelegramMessage, isUserAllowed } from './channel-convert';
|
|
11
|
+
const DEFAULT_MAX_FILE_SIZE = 20 * 1024 * 1024; // 20 MB
|
|
12
|
+
const HTML_PARSE_ERROR_RE = /can't parse entities|parse entities|find end of the entity/i;
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// TelegramChannel
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export class TelegramChannel {
|
|
17
|
+
id = 'telegram';
|
|
18
|
+
type = 'telegram';
|
|
19
|
+
connected = false;
|
|
20
|
+
api = null;
|
|
21
|
+
poller = null;
|
|
22
|
+
config = null;
|
|
23
|
+
logger;
|
|
24
|
+
handlers = {
|
|
25
|
+
connected: [],
|
|
26
|
+
disconnected: [],
|
|
27
|
+
error: [],
|
|
28
|
+
message: [],
|
|
29
|
+
};
|
|
30
|
+
constructor(logger) {
|
|
31
|
+
this.logger = logger ?? {
|
|
32
|
+
info: (msg) => console.log(`[telegram] ${msg}`),
|
|
33
|
+
warn: (msg) => console.warn(`[telegram] ${msg}`),
|
|
34
|
+
error: (msg, err) => console.error(`[telegram] ${msg}`, err ?? ''),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Connect to Telegram by verifying the bot token and starting long polling. */
|
|
38
|
+
async connect(config) {
|
|
39
|
+
const channelConfig = validateConfig(config);
|
|
40
|
+
this.config = channelConfig;
|
|
41
|
+
this.api = new TelegramApi(channelConfig.botToken);
|
|
42
|
+
const me = await this.api.getMe();
|
|
43
|
+
this.logger.info(`Telegram bot connected: @${me.username} (id: ${me.id})`);
|
|
44
|
+
this.poller = new TelegramPoller({
|
|
45
|
+
api: this.api,
|
|
46
|
+
handler: (update) => this.handleUpdate(update),
|
|
47
|
+
pollTimeoutSec: channelConfig.pollTimeoutSec,
|
|
48
|
+
});
|
|
49
|
+
this.connected = true;
|
|
50
|
+
await this.emit('connected');
|
|
51
|
+
// Start polling in the background (non-blocking).
|
|
52
|
+
this.poller.start().catch((err) => {
|
|
53
|
+
this.emit('error', err instanceof Error ? err : new Error(String(err)));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Disconnect: stop polling and release resources. */
|
|
57
|
+
async disconnect() {
|
|
58
|
+
this.poller?.stop();
|
|
59
|
+
this.poller = null;
|
|
60
|
+
this.api = null;
|
|
61
|
+
this.connected = false;
|
|
62
|
+
await this.emit('disconnected', 'manual');
|
|
63
|
+
}
|
|
64
|
+
/** Send a text message, converting markdown to HTML if requested. */
|
|
65
|
+
async sendText(chatId, text, options) {
|
|
66
|
+
this.requireConnected();
|
|
67
|
+
const parseMode = options?.parseMode ?? 'text';
|
|
68
|
+
const formatted = this.formatText(text, parseMode);
|
|
69
|
+
const telegramParseMode = parseMode !== 'text' ? 'HTML' : undefined;
|
|
70
|
+
const chunks = chunkText(formatted);
|
|
71
|
+
let lastMessageId = '';
|
|
72
|
+
for (const chunk of chunks) {
|
|
73
|
+
lastMessageId = await this.sendSingleText(chatId, chunk, telegramParseMode, options);
|
|
74
|
+
}
|
|
75
|
+
return lastMessageId;
|
|
76
|
+
}
|
|
77
|
+
/** Send a media attachment (photo, video, audio, document, etc.). */
|
|
78
|
+
async sendMedia(chatId, attachment, options) {
|
|
79
|
+
this.requireConnected();
|
|
80
|
+
const replyTo = parseIntOrUndefined(options?.replyToId);
|
|
81
|
+
const msg = await this.routeMediaSend(chatId, attachment, replyTo);
|
|
82
|
+
return String(msg.message_id);
|
|
83
|
+
}
|
|
84
|
+
/** Download a file by its Telegram file_id. */
|
|
85
|
+
async downloadAttachment(fileId) {
|
|
86
|
+
this.requireConnected();
|
|
87
|
+
const fileInfo = await this.api.getFile(fileId);
|
|
88
|
+
if (!fileInfo.file_path) {
|
|
89
|
+
throw new Error('File path not available from Telegram');
|
|
90
|
+
}
|
|
91
|
+
const maxSize = this.config?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
92
|
+
const data = await this.api.downloadFile(fileInfo.file_path, maxSize);
|
|
93
|
+
const fileName = fileInfo.file_path.split('/').pop();
|
|
94
|
+
return { data, fileName };
|
|
95
|
+
}
|
|
96
|
+
/** Register an event handler. Returns an unsubscribe function. */
|
|
97
|
+
on(event, handler) {
|
|
98
|
+
const list = this.handlers[event];
|
|
99
|
+
list.push(handler);
|
|
100
|
+
return () => {
|
|
101
|
+
const idx = list.indexOf(handler);
|
|
102
|
+
if (idx !== -1)
|
|
103
|
+
list.splice(idx, 1);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** Register a handler compatible with the basic Channel.onMessage interface. */
|
|
107
|
+
onMessage(handler) {
|
|
108
|
+
this.handlers.message.push(handler);
|
|
109
|
+
}
|
|
110
|
+
/** Send a message via the basic Channel.send interface. */
|
|
111
|
+
async send(target, message, options) {
|
|
112
|
+
return this.sendText(target.channelId, message, {
|
|
113
|
+
parseMode: 'markdown',
|
|
114
|
+
replyToId: options?.replyToId,
|
|
115
|
+
threadId: target.threadId,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// -----------------------------------------------------------------------
|
|
119
|
+
// Internal: Update Processing
|
|
120
|
+
// -----------------------------------------------------------------------
|
|
121
|
+
async handleUpdate(update) {
|
|
122
|
+
const msg = update.message ?? update.edited_message;
|
|
123
|
+
if (!msg)
|
|
124
|
+
return;
|
|
125
|
+
if (!isUserAllowed(msg, this.config))
|
|
126
|
+
return;
|
|
127
|
+
const richMessage = convertTelegramMessage(msg);
|
|
128
|
+
await this.emit('message', richMessage);
|
|
129
|
+
}
|
|
130
|
+
// -----------------------------------------------------------------------
|
|
131
|
+
// Internal: Sending
|
|
132
|
+
// -----------------------------------------------------------------------
|
|
133
|
+
formatText(text, parseMode) {
|
|
134
|
+
if (parseMode === 'markdown')
|
|
135
|
+
return markdownToTelegramHtml(text);
|
|
136
|
+
return text;
|
|
137
|
+
}
|
|
138
|
+
async sendSingleText(chatId, text, parseMode, options) {
|
|
139
|
+
const replyTo = parseIntOrUndefined(options?.replyToId);
|
|
140
|
+
const threadId = parseIntOrUndefined(options?.threadId);
|
|
141
|
+
try {
|
|
142
|
+
const result = await sendMessage(this.api, chatId, text, {
|
|
143
|
+
parseMode,
|
|
144
|
+
replyToMessageId: replyTo,
|
|
145
|
+
messageThreadId: threadId,
|
|
146
|
+
});
|
|
147
|
+
return String(result.message_id);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
// If HTML parsing fails, fall back to plain text (matches openclaw pattern).
|
|
151
|
+
if (parseMode === 'HTML' && isHtmlParseError(err)) {
|
|
152
|
+
const result = await sendMessage(this.api, chatId, escapeHtml(text), {
|
|
153
|
+
replyToMessageId: replyTo,
|
|
154
|
+
messageThreadId: threadId,
|
|
155
|
+
});
|
|
156
|
+
return String(result.message_id);
|
|
157
|
+
}
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async routeMediaSend(chatId, attachment, replyTo) {
|
|
162
|
+
const opts = {
|
|
163
|
+
caption: attachment.caption,
|
|
164
|
+
parseMode: 'HTML',
|
|
165
|
+
fileName: attachment.fileName,
|
|
166
|
+
replyToMessageId: replyTo,
|
|
167
|
+
};
|
|
168
|
+
switch (attachment.type) {
|
|
169
|
+
case 'photo':
|
|
170
|
+
return sendPhoto(this.api, chatId, attachment.data, opts);
|
|
171
|
+
case 'voice':
|
|
172
|
+
return sendVoice(this.api, chatId, attachment.data, opts);
|
|
173
|
+
case 'audio':
|
|
174
|
+
return sendAudio(this.api, chatId, attachment.data, opts);
|
|
175
|
+
case 'video':
|
|
176
|
+
return sendVideo(this.api, chatId, attachment.data, opts);
|
|
177
|
+
case 'animation':
|
|
178
|
+
return sendAnimation(this.api, chatId, attachment.data, opts);
|
|
179
|
+
case 'video_note':
|
|
180
|
+
return sendVideoNote(this.api, chatId, attachment.data, { replyToMessageId: replyTo });
|
|
181
|
+
default:
|
|
182
|
+
return sendDocument(this.api, chatId, attachment.data, opts);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// -----------------------------------------------------------------------
|
|
186
|
+
// Internal: Event Emission
|
|
187
|
+
// -----------------------------------------------------------------------
|
|
188
|
+
requireConnected() {
|
|
189
|
+
if (!this.api)
|
|
190
|
+
throw new Error('Telegram channel is not connected');
|
|
191
|
+
}
|
|
192
|
+
async emit(event, ...args) {
|
|
193
|
+
for (const handler of this.handlers[event]) {
|
|
194
|
+
try {
|
|
195
|
+
await handler(...args);
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
this.logger.error(`Event handler error [${event}]`, err);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Helpers
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
function isHtmlParseError(err) {
|
|
207
|
+
if (err instanceof TelegramApiError) {
|
|
208
|
+
return HTML_PARSE_ERROR_RE.test(err.message);
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
function parseIntOrUndefined(value) {
|
|
213
|
+
if (value === undefined)
|
|
214
|
+
return undefined;
|
|
215
|
+
const n = parseInt(value, 10);
|
|
216
|
+
return Number.isFinite(n) ? n : undefined;
|
|
217
|
+
}
|
|
218
|
+
function validateConfig(config) {
|
|
219
|
+
if (!config.botToken || typeof config.botToken !== 'string') {
|
|
220
|
+
throw new Error('Telegram botToken is required');
|
|
221
|
+
}
|
|
222
|
+
if (config.allowedUserIds !== undefined) {
|
|
223
|
+
if (!Array.isArray(config.allowedUserIds)) {
|
|
224
|
+
throw new Error('allowedUserIds must be an array of numbers');
|
|
225
|
+
}
|
|
226
|
+
for (const id of config.allowedUserIds) {
|
|
227
|
+
if (typeof id !== 'number' || !Number.isFinite(id)) {
|
|
228
|
+
throw new Error('Each element in allowedUserIds must be a finite number');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return config;
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"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"}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram HTML Formatter
|
|
3
|
+
* Converts markdown to Telegram-compatible HTML and handles text chunking.
|
|
4
|
+
* Uses HTML mode (not MarkdownV2) for simpler escaping and reliable rendering.
|
|
5
|
+
*/
|
|
6
|
+
import type { ChannelFormatter } from '@daemux/plugin-sdk';
|
|
7
|
+
export declare function escapeHtml(text: string): string;
|
|
8
|
+
export declare function markdownToTelegramHtml(markdown: string): string;
|
|
9
|
+
export declare function chunkText(text: string, maxLength?: number): string[];
|
|
10
|
+
/** Truncate text suitable for a media caption (max 1024 chars). */
|
|
11
|
+
export declare function truncateCaption(text: string): string;
|
|
12
|
+
export declare const telegramHtmlFormatter: ChannelFormatter;
|
|
13
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAM3D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/C;AAUD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAqD/D;AASD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAA8B,GAAG,MAAM,EAAE,CAmBzF;AAmBD,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASpD;AAMD,eAAO,MAAM,qBAAqB,EAAE,gBAanC,CAAC"}
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram HTML Formatter
|
|
3
|
+
* Converts markdown to Telegram-compatible HTML and handles text chunking.
|
|
4
|
+
* Uses HTML mode (not MarkdownV2) for simpler escaping and reliable rendering.
|
|
5
|
+
*/
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// HTML Escaping
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
export function escapeHtml(text) {
|
|
10
|
+
return text
|
|
11
|
+
.replace(/&/g, '&')
|
|
12
|
+
.replace(/</g, '<')
|
|
13
|
+
.replace(/>/g, '>');
|
|
14
|
+
}
|
|
15
|
+
function escapeHtmlAttr(text) {
|
|
16
|
+
return escapeHtml(text).replace(/"/g, '"');
|
|
17
|
+
}
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Markdown to Telegram HTML conversion
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
export function markdownToTelegramHtml(markdown) {
|
|
22
|
+
if (!markdown)
|
|
23
|
+
return '';
|
|
24
|
+
let html = markdown;
|
|
25
|
+
// Extract and preserve code blocks before any other processing.
|
|
26
|
+
// This prevents formatting rules from being applied inside code.
|
|
27
|
+
const codeBlocks = [];
|
|
28
|
+
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
29
|
+
const escaped = escapeHtml(code);
|
|
30
|
+
const langAttr = lang ? ` class="language-${escapeHtmlAttr(lang)}"` : '';
|
|
31
|
+
const placeholder = `\x00CB${codeBlocks.length}\x00`;
|
|
32
|
+
codeBlocks.push(`<pre><code${langAttr}>${escaped}</code></pre>`);
|
|
33
|
+
return placeholder;
|
|
34
|
+
});
|
|
35
|
+
// Extract and preserve inline code.
|
|
36
|
+
const inlineCodes = [];
|
|
37
|
+
html = html.replace(/`([^`]+)`/g, (_match, code) => {
|
|
38
|
+
const placeholder = `\x00IC${inlineCodes.length}\x00`;
|
|
39
|
+
inlineCodes.push(`<code>${escapeHtml(code)}</code>`);
|
|
40
|
+
return placeholder;
|
|
41
|
+
});
|
|
42
|
+
// Escape remaining HTML entities (not inside code).
|
|
43
|
+
html = escapeHtml(html);
|
|
44
|
+
// Bold: **text**
|
|
45
|
+
html = html.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
|
|
46
|
+
// Italic: *text* (not preceded/followed by another *)
|
|
47
|
+
html = html.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, '<i>$1</i>');
|
|
48
|
+
// Strikethrough: ~~text~~
|
|
49
|
+
html = html.replace(/~~(.+?)~~/g, '<s>$1</s>');
|
|
50
|
+
// Links: [text](url)
|
|
51
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, text, url) => `<a href="${escapeHtmlAttr(url)}">${text}</a>`);
|
|
52
|
+
// Restore inline code placeholders.
|
|
53
|
+
for (let i = 0; i < inlineCodes.length; i++) {
|
|
54
|
+
html = html.replace(`\x00IC${i}\x00`, inlineCodes[i]);
|
|
55
|
+
}
|
|
56
|
+
// Restore code block placeholders.
|
|
57
|
+
for (let i = 0; i < codeBlocks.length; i++) {
|
|
58
|
+
html = html.replace(`\x00CB${i}\x00`, codeBlocks[i]);
|
|
59
|
+
}
|
|
60
|
+
return html;
|
|
61
|
+
}
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Text Chunking (Telegram has a 4096 char message limit)
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
const TELEGRAM_MAX_MESSAGE_LENGTH = 4096;
|
|
66
|
+
const TELEGRAM_MAX_CAPTION_LENGTH = 1024;
|
|
67
|
+
export function chunkText(text, maxLength = TELEGRAM_MAX_MESSAGE_LENGTH) {
|
|
68
|
+
if (!text)
|
|
69
|
+
return [''];
|
|
70
|
+
if (text.length <= maxLength)
|
|
71
|
+
return [text];
|
|
72
|
+
const chunks = [];
|
|
73
|
+
let remaining = text;
|
|
74
|
+
while (remaining.length > 0) {
|
|
75
|
+
if (remaining.length <= maxLength) {
|
|
76
|
+
chunks.push(remaining);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
let breakPoint = findBreakPoint(remaining, maxLength);
|
|
80
|
+
chunks.push(remaining.slice(0, breakPoint));
|
|
81
|
+
remaining = remaining.slice(breakPoint).trimStart();
|
|
82
|
+
}
|
|
83
|
+
return chunks;
|
|
84
|
+
}
|
|
85
|
+
function findBreakPoint(text, maxLength) {
|
|
86
|
+
// Prefer breaking at a blank line (paragraph boundary).
|
|
87
|
+
const doubleNewline = text.lastIndexOf('\n\n', maxLength);
|
|
88
|
+
if (doubleNewline > maxLength * 0.3)
|
|
89
|
+
return doubleNewline;
|
|
90
|
+
// Fall back to single newline.
|
|
91
|
+
const newline = text.lastIndexOf('\n', maxLength);
|
|
92
|
+
if (newline > maxLength * 0.3)
|
|
93
|
+
return newline;
|
|
94
|
+
// Fall back to space.
|
|
95
|
+
const space = text.lastIndexOf(' ', maxLength);
|
|
96
|
+
if (space > maxLength * 0.3)
|
|
97
|
+
return space;
|
|
98
|
+
// Hard break as last resort.
|
|
99
|
+
return maxLength;
|
|
100
|
+
}
|
|
101
|
+
/** Truncate text suitable for a media caption (max 1024 chars). */
|
|
102
|
+
export function truncateCaption(text) {
|
|
103
|
+
if (text.length <= TELEGRAM_MAX_CAPTION_LENGTH)
|
|
104
|
+
return text;
|
|
105
|
+
const truncated = text.slice(0, TELEGRAM_MAX_CAPTION_LENGTH - 3);
|
|
106
|
+
const lastSpace = truncated.lastIndexOf(' ');
|
|
107
|
+
if (lastSpace > TELEGRAM_MAX_CAPTION_LENGTH * 0.5) {
|
|
108
|
+
return truncated.slice(0, lastSpace) + '...';
|
|
109
|
+
}
|
|
110
|
+
return truncated + '...';
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Telegram HTML Formatter (implements ChannelFormatter)
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
export const telegramHtmlFormatter = {
|
|
116
|
+
bold: (text) => `<b>${escapeHtml(text)}</b>`,
|
|
117
|
+
italic: (text) => `<i>${escapeHtml(text)}</i>`,
|
|
118
|
+
strikethrough: (text) => `<s>${escapeHtml(text)}</s>`,
|
|
119
|
+
code: (text) => `<code>${escapeHtml(text)}</code>`,
|
|
120
|
+
codeBlock: (text, language) => {
|
|
121
|
+
const langAttr = language ? ` class="language-${escapeHtmlAttr(language)}"` : '';
|
|
122
|
+
return `<pre><code${langAttr}>${escapeHtml(text)}</code></pre>`;
|
|
123
|
+
},
|
|
124
|
+
link: (text, url) => `<a href="${escapeHtmlAttr(url)}">${escapeHtml(text)}</a>`,
|
|
125
|
+
escape: escapeHtml,
|
|
126
|
+
fromMarkdown: markdownToTelegramHtml,
|
|
127
|
+
chunk: chunkText,
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Adapter Plugin Entry Point
|
|
3
|
+
* Registers the TelegramChannel with the daemux plugin system.
|
|
4
|
+
*/
|
|
5
|
+
import { TelegramChannel } from './channel';
|
|
6
|
+
export { TelegramChannel } from './channel';
|
|
7
|
+
export { TelegramApi, TelegramApiError } from './api';
|
|
8
|
+
export { sendMessage, sendPhoto, sendDocument, sendAudio, sendVideo, sendVoice, sendVideoNote, sendSticker, sendAnimation, } from './api-send';
|
|
9
|
+
export type { MediaSendOptions } from './api-send';
|
|
10
|
+
export { TelegramPoller } from './poller';
|
|
11
|
+
export { telegramHtmlFormatter, markdownToTelegramHtml, chunkText, escapeHtml } from './format';
|
|
12
|
+
export { resolveMessageType, resolveFileId, resolveAttachment } from './message-resolver';
|
|
13
|
+
export type { ResolvedAttachment } from './message-resolver';
|
|
14
|
+
export type { ChannelFormatter, RichChannelMessage, ChannelAttachment, ChannelSendOptions } from '@daemux/plugin-sdk';
|
|
15
|
+
export type { TelegramUser, TelegramChat, TelegramMessage, TelegramUpdate, TelegramChannelConfig, TelegramFileInfo, TelegramBotInfo, ChannelMessageType, } from './types';
|
|
16
|
+
export declare const manifest: {
|
|
17
|
+
name: string;
|
|
18
|
+
version: string;
|
|
19
|
+
description: string;
|
|
20
|
+
author: string;
|
|
21
|
+
};
|
|
22
|
+
interface PluginAPI {
|
|
23
|
+
registerChannel(channel: {
|
|
24
|
+
id: string;
|
|
25
|
+
type: string;
|
|
26
|
+
connect(config: Record<string, unknown>): Promise<void>;
|
|
27
|
+
disconnect(): Promise<void>;
|
|
28
|
+
send(target: {
|
|
29
|
+
channelId: string;
|
|
30
|
+
userId?: string;
|
|
31
|
+
threadId?: string;
|
|
32
|
+
}, message: string, options?: {
|
|
33
|
+
attachments?: Array<{
|
|
34
|
+
type: string;
|
|
35
|
+
data: Buffer;
|
|
36
|
+
filename: string;
|
|
37
|
+
}>;
|
|
38
|
+
replyToId?: string;
|
|
39
|
+
}): Promise<string>;
|
|
40
|
+
onMessage(handler: (message: {
|
|
41
|
+
id: string;
|
|
42
|
+
channelId: string;
|
|
43
|
+
senderId: string;
|
|
44
|
+
content: string;
|
|
45
|
+
timestamp: number;
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
}) => Promise<void>): void;
|
|
48
|
+
}): void;
|
|
49
|
+
log(level: string, message: string, data?: Record<string, unknown>): void;
|
|
50
|
+
}
|
|
51
|
+
/** Activate the plugin. Called by the daemux plugin loader. */
|
|
52
|
+
export declare function activate(api: PluginAPI): Promise<void>;
|
|
53
|
+
/** Deactivate the plugin. Called when the plugin is unloaded. */
|
|
54
|
+
export declare function deactivate(): Promise<void>;
|
|
55
|
+
/** Get the current channel instance (for programmatic access). */
|
|
56
|
+
export declare function getChannel(): TelegramChannel | null;
|
|
57
|
+
/** Default export for plugin loading (matches anthropic-provider pattern). */
|
|
58
|
+
declare const _default: {
|
|
59
|
+
manifest: {
|
|
60
|
+
name: string;
|
|
61
|
+
version: string;
|
|
62
|
+
description: string;
|
|
63
|
+
author: string;
|
|
64
|
+
};
|
|
65
|
+
activate: typeof activate;
|
|
66
|
+
deactivate: typeof deactivate;
|
|
67
|
+
};
|
|
68
|
+
export default _default;
|
|
69
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,EACL,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAC1D,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,GACrD,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1F,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACtH,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAMjB,eAAO,MAAM,QAAQ;;;;;CAKpB,CAAC;AAMF,UAAU,SAAS;IACjB,eAAe,CAAC,OAAO,EAAE;QACvB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACxD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CACF,MAAM,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,EACjE,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;YAAE,WAAW,CAAC,EAAE,KAAK,CAAC;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,QAAQ,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GACtG,OAAO,CAAC,MAAM,CAAC,CAAC;QACnB,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE;YAC3B,EAAE,EAAE,MAAM,CAAC;YACX,SAAS,EAAE,MAAM,CAAC;YAClB,QAAQ,EAAE,MAAM,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;YAChB,SAAS,EAAE,MAAM,CAAC;YAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAC5B,GAAG,IAAI,CAAC;IACT,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3E;AAQD,+DAA+D;AAC/D,wBAAsB,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAO5D;AAED,iEAAiE;AACjE,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAOhD;AAED,kEAAkE;AAClE,wBAAgB,UAAU,IAAI,eAAe,GAAG,IAAI,CAEnD;AAED,8EAA8E;;;;;;;;;;;AAC9E,wBAAkD"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Adapter Plugin Entry Point
|
|
3
|
+
* Registers the TelegramChannel with the daemux plugin system.
|
|
4
|
+
*/
|
|
5
|
+
import { TelegramChannel } from './channel';
|
|
6
|
+
// Re-export public API
|
|
7
|
+
export { TelegramChannel } from './channel';
|
|
8
|
+
export { TelegramApi, TelegramApiError } from './api';
|
|
9
|
+
export { sendMessage, sendPhoto, sendDocument, sendAudio, sendVideo, sendVoice, sendVideoNote, sendSticker, sendAnimation, } from './api-send';
|
|
10
|
+
export { TelegramPoller } from './poller';
|
|
11
|
+
export { telegramHtmlFormatter, markdownToTelegramHtml, chunkText, escapeHtml } from './format';
|
|
12
|
+
export { resolveMessageType, resolveFileId, resolveAttachment } from './message-resolver';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Plugin Manifest
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export const manifest = {
|
|
17
|
+
name: '@daemux/telegram-adapter',
|
|
18
|
+
version: '1.0.0',
|
|
19
|
+
description: 'Telegram Bot API channel adapter for daemux',
|
|
20
|
+
author: 'daemux',
|
|
21
|
+
};
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Plugin Lifecycle
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
let channelInstance = null;
|
|
26
|
+
/** Activate the plugin. Called by the daemux plugin loader. */
|
|
27
|
+
export async function activate(api) {
|
|
28
|
+
api.log('info', 'Activating Telegram adapter plugin');
|
|
29
|
+
channelInstance = new TelegramChannel();
|
|
30
|
+
api.registerChannel(channelInstance);
|
|
31
|
+
api.log('info', 'Telegram channel registered successfully');
|
|
32
|
+
}
|
|
33
|
+
/** Deactivate the plugin. Called when the plugin is unloaded. */
|
|
34
|
+
export async function deactivate() {
|
|
35
|
+
if (channelInstance) {
|
|
36
|
+
if (channelInstance.connected) {
|
|
37
|
+
await channelInstance.disconnect();
|
|
38
|
+
}
|
|
39
|
+
channelInstance = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Get the current channel instance (for programmatic access). */
|
|
43
|
+
export function getChannel() {
|
|
44
|
+
return channelInstance;
|
|
45
|
+
}
|
|
46
|
+
/** Default export for plugin loading (matches anthropic-provider pattern). */
|
|
47
|
+
export default { manifest, activate, deactivate };
|
|
48
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,uBAAuB;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,EACL,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAC1D,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,GACrD,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAc1F,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,0BAA0B;IAChC,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,6CAA6C;IAC1D,MAAM,EAAE,QAAQ;CACjB,CAAC;AA6BF,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,IAAI,eAAe,GAA2B,IAAI,CAAC;AAEnD,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAc;IAC3C,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,oCAAoC,CAAC,CAAC;IAEtD,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;IACxC,GAAG,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;IAErC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,0CAA0C,CAAC,CAAC;AAC9D,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,eAAe,EAAE,CAAC;QACpB,IAAI,eAAe,CAAC,SAAS,EAAE,CAAC;YAC9B,MAAM,eAAe,CAAC,UAAU,EAAE,CAAC;QACrC,CAAC;QACD,eAAe,GAAG,IAAI,CAAC;IACzB,CAAC;AACH,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,UAAU;IACxB,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,8EAA8E;AAC9E,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Type & File ID Resolver
|
|
3
|
+
* Extracts message type and primary file ID from a Telegram message.
|
|
4
|
+
*/
|
|
5
|
+
import type { TelegramMessage, ChannelMessageType } from './types';
|
|
6
|
+
export interface ResolvedAttachment {
|
|
7
|
+
type: ChannelMessageType;
|
|
8
|
+
fileId: string;
|
|
9
|
+
mimeType?: string;
|
|
10
|
+
fileName?: string;
|
|
11
|
+
fileSize?: number;
|
|
12
|
+
duration?: number;
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Determine the message type from a Telegram message.
|
|
18
|
+
* Priority order matches Telegram's conventions: animation before video
|
|
19
|
+
* (since animations have both animation and document fields).
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveMessageType(msg: TelegramMessage): ChannelMessageType;
|
|
22
|
+
/** Extract the primary file_id from a Telegram message, if it has a file attachment. */
|
|
23
|
+
export declare function resolveFileId(msg: TelegramMessage): string | undefined;
|
|
24
|
+
/** Extract all attachment metadata from a Telegram message. */
|
|
25
|
+
export declare function resolveAttachment(msg: TelegramMessage): ResolvedAttachment | undefined;
|
|
26
|
+
//# sourceMappingURL=message-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-resolver.d.ts","sourceRoot":"","sources":["../src/message-resolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAMnE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,GAAG,kBAAkB,CAY3E;AAMD,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAYtE;AAMD,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,kBAAkB,GAAG,SAAS,CAyFtF"}
|