@zhin.js/adapter-telegram 1.0.36 → 1.0.37
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/CHANGELOG.md +7 -0
- package/lib/adapter.d.ts +22 -0
- package/lib/adapter.d.ts.map +1 -0
- package/lib/adapter.js +344 -0
- package/lib/adapter.js.map +1 -0
- package/lib/bot.d.ts +140 -0
- package/lib/bot.d.ts.map +1 -0
- package/lib/bot.js +865 -0
- package/lib/bot.js.map +1 -0
- package/lib/index.d.ts +4 -176
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +9 -1198
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +29 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/package.json +4 -4
package/lib/bot.js
ADDED
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Bot 实现
|
|
3
|
+
*/
|
|
4
|
+
import { Telegraf } from "telegraf";
|
|
5
|
+
import { Message, segment, } from "zhin.js";
|
|
6
|
+
export class TelegramBot extends Telegraf {
|
|
7
|
+
adapter;
|
|
8
|
+
$config;
|
|
9
|
+
$connected = false;
|
|
10
|
+
get pluginLogger() {
|
|
11
|
+
return this.adapter.plugin.logger;
|
|
12
|
+
}
|
|
13
|
+
get $id() {
|
|
14
|
+
return this.$config.name;
|
|
15
|
+
}
|
|
16
|
+
constructor(adapter, $config) {
|
|
17
|
+
super($config.token);
|
|
18
|
+
this.adapter = adapter;
|
|
19
|
+
this.$config = $config;
|
|
20
|
+
}
|
|
21
|
+
async $connect() {
|
|
22
|
+
try {
|
|
23
|
+
// Set up message handler
|
|
24
|
+
this.on("message", async (ctx) => {
|
|
25
|
+
await this.handleTelegramMessage(ctx);
|
|
26
|
+
});
|
|
27
|
+
// Set up callback query handler (for inline buttons)
|
|
28
|
+
this.on("callback_query", async (ctx) => {
|
|
29
|
+
// Handle callback queries as special messages
|
|
30
|
+
if (ctx.callbackQuery && "data" in ctx.callbackQuery) {
|
|
31
|
+
const message = this.$formatCallbackQuery(ctx);
|
|
32
|
+
this.adapter.emit("message.receive", message);
|
|
33
|
+
this.pluginLogger.debug(`${this.$config.name} recv callback ${message.$channel.type}(${message.$channel.id}): ${segment.raw(message.$content)}`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Start bot with polling or webhook
|
|
37
|
+
if (this.$config.polling !== false) {
|
|
38
|
+
// Use polling by default
|
|
39
|
+
await this.launch({
|
|
40
|
+
allowedUpdates: this.$config.allowedUpdates,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else if (this.$config.webhook) {
|
|
44
|
+
// Use webhook
|
|
45
|
+
const { domain, path = "/telegram-webhook", port } = this.$config.webhook;
|
|
46
|
+
await this.launch({
|
|
47
|
+
webhook: {
|
|
48
|
+
domain,
|
|
49
|
+
port,
|
|
50
|
+
hookPath: path,
|
|
51
|
+
},
|
|
52
|
+
allowedUpdates: this.$config.allowedUpdates,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
throw new Error("Either polling must be enabled or webhook configuration must be provided");
|
|
57
|
+
}
|
|
58
|
+
this.$connected = true;
|
|
59
|
+
const me = await this.telegram.getMe();
|
|
60
|
+
this.pluginLogger.info(`Telegram bot ${this.$config.name} connected successfully as @${me.username}`);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
this.pluginLogger.error("Failed to connect Telegram bot:", error);
|
|
64
|
+
this.$connected = false;
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async $disconnect() {
|
|
69
|
+
try {
|
|
70
|
+
await this.stop();
|
|
71
|
+
this.$connected = false;
|
|
72
|
+
this.pluginLogger.info(`Telegram bot ${this.$config.name} disconnected`);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
this.pluginLogger.error("Error disconnecting Telegram bot:", error);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async handleTelegramMessage(ctx) {
|
|
80
|
+
if (!ctx.message)
|
|
81
|
+
return;
|
|
82
|
+
const message = this.$formatMessage(ctx.message);
|
|
83
|
+
this.adapter.emit("message.receive", message);
|
|
84
|
+
this.pluginLogger.debug(`${this.$config.name} recv ${message.$channel.type}(${message.$channel.id}): ${segment.raw(message.$content)}`);
|
|
85
|
+
}
|
|
86
|
+
$formatMessage(msg) {
|
|
87
|
+
// Determine channel type
|
|
88
|
+
const channelType = msg.chat.type === "private" ? "private" : "group";
|
|
89
|
+
const channelId = msg.chat.id.toString();
|
|
90
|
+
// Parse message content
|
|
91
|
+
const content = this.parseMessageContent(msg);
|
|
92
|
+
const result = Message.from(msg, {
|
|
93
|
+
$id: msg.message_id.toString(),
|
|
94
|
+
$adapter: "telegram",
|
|
95
|
+
$bot: this.$config.name,
|
|
96
|
+
$sender: {
|
|
97
|
+
id: msg.from?.id.toString() || "",
|
|
98
|
+
name: msg.from?.username || msg.from?.first_name || "Unknown",
|
|
99
|
+
},
|
|
100
|
+
$channel: {
|
|
101
|
+
id: channelId,
|
|
102
|
+
type: channelType,
|
|
103
|
+
},
|
|
104
|
+
$content: content,
|
|
105
|
+
$raw: "text" in msg ? msg.text || "" : "",
|
|
106
|
+
$timestamp: msg.date * 1000,
|
|
107
|
+
$recall: async () => {
|
|
108
|
+
try {
|
|
109
|
+
await this.telegram.deleteMessage(parseInt(channelId), parseInt(result.$id));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
this.pluginLogger.error("Error recalling Telegram message:", error);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
$reply: async (content, quote) => {
|
|
117
|
+
if (!Array.isArray(content))
|
|
118
|
+
content = [content];
|
|
119
|
+
// Handle reply
|
|
120
|
+
if (quote) {
|
|
121
|
+
const replyToMessageId = typeof quote === "boolean" ? result.$id : quote;
|
|
122
|
+
content.unshift({ type: "reply", data: { id: replyToMessageId } });
|
|
123
|
+
}
|
|
124
|
+
return await this.adapter.sendMessage({
|
|
125
|
+
context: "telegram",
|
|
126
|
+
bot: this.$config.name,
|
|
127
|
+
id: channelId,
|
|
128
|
+
type: channelType,
|
|
129
|
+
content: content,
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
$formatCallbackQuery(ctx) {
|
|
136
|
+
if (!ctx.callbackQuery || !("data" in ctx.callbackQuery)) {
|
|
137
|
+
throw new Error("Invalid callback query");
|
|
138
|
+
}
|
|
139
|
+
const query = ctx.callbackQuery;
|
|
140
|
+
const msg = query.message;
|
|
141
|
+
const channelType = msg && "chat" in msg && msg.chat.type === "private" ? "private" : "group";
|
|
142
|
+
const channelId = msg && "chat" in msg ? msg.chat.id.toString() : query.from.id.toString();
|
|
143
|
+
const result = Message.from(query, {
|
|
144
|
+
$id: query.id,
|
|
145
|
+
$adapter: "telegram",
|
|
146
|
+
$bot: this.$config.name,
|
|
147
|
+
$sender: {
|
|
148
|
+
id: query.from.id.toString(),
|
|
149
|
+
name: query.from.username || query.from.first_name,
|
|
150
|
+
},
|
|
151
|
+
$channel: {
|
|
152
|
+
id: channelId,
|
|
153
|
+
type: channelType,
|
|
154
|
+
},
|
|
155
|
+
$content: [{ type: "text", data: { text: query.data } }],
|
|
156
|
+
$raw: query.data,
|
|
157
|
+
$timestamp: Date.now(),
|
|
158
|
+
$recall: async () => {
|
|
159
|
+
// Callback queries cannot be recalled
|
|
160
|
+
},
|
|
161
|
+
$reply: async (content) => {
|
|
162
|
+
if (!Array.isArray(content))
|
|
163
|
+
content = [content];
|
|
164
|
+
const sentMsg = await this.sendContentToChat(parseInt(channelId), content);
|
|
165
|
+
return sentMsg.message_id.toString();
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
parseMessageContent(msg) {
|
|
171
|
+
const segments = [];
|
|
172
|
+
// Handle text messages
|
|
173
|
+
if ("text" in msg && msg.text) {
|
|
174
|
+
// Check for reply
|
|
175
|
+
if (msg.reply_to_message) {
|
|
176
|
+
segments.push({
|
|
177
|
+
type: "reply",
|
|
178
|
+
data: {
|
|
179
|
+
id: msg.reply_to_message.message_id.toString(),
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
// Parse text with entities
|
|
184
|
+
if (msg.entities && msg.entities.length > 0) {
|
|
185
|
+
segments.push(...this.parseTextWithEntities(msg.text, msg.entities));
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
segments.push({ type: "text", data: { text: msg.text } });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Handle photo
|
|
192
|
+
if ("photo" in msg && msg.photo) {
|
|
193
|
+
const largestPhoto = msg.photo[msg.photo.length - 1];
|
|
194
|
+
segments.push({
|
|
195
|
+
type: "image",
|
|
196
|
+
data: {
|
|
197
|
+
file_id: largestPhoto.file_id,
|
|
198
|
+
file_unique_id: largestPhoto.file_unique_id,
|
|
199
|
+
width: largestPhoto.width,
|
|
200
|
+
height: largestPhoto.height,
|
|
201
|
+
file_size: largestPhoto.file_size,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
if (msg.caption) {
|
|
205
|
+
segments.push({ type: "text", data: { text: msg.caption } });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Handle video
|
|
209
|
+
if ("video" in msg && msg.video) {
|
|
210
|
+
segments.push({
|
|
211
|
+
type: "video",
|
|
212
|
+
data: {
|
|
213
|
+
file_id: msg.video.file_id,
|
|
214
|
+
file_unique_id: msg.video.file_unique_id,
|
|
215
|
+
width: msg.video.width,
|
|
216
|
+
height: msg.video.height,
|
|
217
|
+
duration: msg.video.duration,
|
|
218
|
+
file_size: msg.video.file_size,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
if (msg.caption) {
|
|
222
|
+
segments.push({ type: "text", data: { text: msg.caption } });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Handle audio
|
|
226
|
+
if ("audio" in msg && msg.audio) {
|
|
227
|
+
segments.push({
|
|
228
|
+
type: "audio",
|
|
229
|
+
data: {
|
|
230
|
+
file_id: msg.audio.file_id,
|
|
231
|
+
file_unique_id: msg.audio.file_unique_id,
|
|
232
|
+
duration: msg.audio.duration,
|
|
233
|
+
performer: msg.audio.performer,
|
|
234
|
+
title: msg.audio.title,
|
|
235
|
+
file_size: msg.audio.file_size,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
// Handle voice
|
|
240
|
+
if ("voice" in msg && msg.voice) {
|
|
241
|
+
segments.push({
|
|
242
|
+
type: "voice",
|
|
243
|
+
data: {
|
|
244
|
+
file_id: msg.voice.file_id,
|
|
245
|
+
file_unique_id: msg.voice.file_unique_id,
|
|
246
|
+
duration: msg.voice.duration,
|
|
247
|
+
file_size: msg.voice.file_size,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
// Handle document
|
|
252
|
+
if ("document" in msg && msg.document) {
|
|
253
|
+
segments.push({
|
|
254
|
+
type: "file",
|
|
255
|
+
data: {
|
|
256
|
+
file_id: msg.document.file_id,
|
|
257
|
+
file_unique_id: msg.document.file_unique_id,
|
|
258
|
+
file_name: msg.document.file_name,
|
|
259
|
+
mime_type: msg.document.mime_type,
|
|
260
|
+
file_size: msg.document.file_size,
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
// Handle sticker
|
|
265
|
+
if ("sticker" in msg && msg.sticker) {
|
|
266
|
+
segments.push({
|
|
267
|
+
type: "sticker",
|
|
268
|
+
data: {
|
|
269
|
+
file_id: msg.sticker.file_id,
|
|
270
|
+
file_unique_id: msg.sticker.file_unique_id,
|
|
271
|
+
width: msg.sticker.width,
|
|
272
|
+
height: msg.sticker.height,
|
|
273
|
+
is_animated: msg.sticker.is_animated,
|
|
274
|
+
is_video: msg.sticker.is_video,
|
|
275
|
+
emoji: msg.sticker.emoji,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
// Handle location
|
|
280
|
+
if ("location" in msg && msg.location) {
|
|
281
|
+
segments.push({
|
|
282
|
+
type: "location",
|
|
283
|
+
data: {
|
|
284
|
+
longitude: msg.location.longitude,
|
|
285
|
+
latitude: msg.location.latitude,
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
return segments.length > 0
|
|
290
|
+
? segments
|
|
291
|
+
: [{ type: "text", data: { text: "" } }];
|
|
292
|
+
}
|
|
293
|
+
parseTextWithEntities(text, entities) {
|
|
294
|
+
const segments = [];
|
|
295
|
+
let lastOffset = 0;
|
|
296
|
+
for (const entity of entities) {
|
|
297
|
+
// Add text before entity
|
|
298
|
+
if (entity.offset > lastOffset) {
|
|
299
|
+
const beforeText = text.slice(lastOffset, entity.offset);
|
|
300
|
+
if (beforeText) {
|
|
301
|
+
segments.push({ type: "text", data: { text: beforeText } });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const entityText = text.slice(entity.offset, entity.offset + entity.length);
|
|
305
|
+
switch (entity.type) {
|
|
306
|
+
case "mention":
|
|
307
|
+
case "text_mention":
|
|
308
|
+
segments.push({
|
|
309
|
+
type: "at",
|
|
310
|
+
data: {
|
|
311
|
+
id: ("user" in entity && entity.user?.id.toString()) || entityText.slice(1),
|
|
312
|
+
name: ("user" in entity && entity.user?.username) || entityText,
|
|
313
|
+
text: entityText,
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
break;
|
|
317
|
+
case "url":
|
|
318
|
+
case "text_link":
|
|
319
|
+
segments.push({
|
|
320
|
+
type: "link",
|
|
321
|
+
data: {
|
|
322
|
+
url: ("url" in entity && entity.url) || entityText,
|
|
323
|
+
text: entityText,
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
break;
|
|
327
|
+
case "hashtag":
|
|
328
|
+
segments.push({
|
|
329
|
+
type: "text",
|
|
330
|
+
data: { text: entityText },
|
|
331
|
+
});
|
|
332
|
+
break;
|
|
333
|
+
case "bold":
|
|
334
|
+
case "italic":
|
|
335
|
+
case "code":
|
|
336
|
+
case "pre":
|
|
337
|
+
case "underline":
|
|
338
|
+
case "strikethrough":
|
|
339
|
+
segments.push({
|
|
340
|
+
type: "text",
|
|
341
|
+
data: { text: entityText, format: entity.type },
|
|
342
|
+
});
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
segments.push({ type: "text", data: { text: entityText } });
|
|
346
|
+
}
|
|
347
|
+
lastOffset = entity.offset + entity.length;
|
|
348
|
+
}
|
|
349
|
+
// Add remaining text
|
|
350
|
+
if (lastOffset < text.length) {
|
|
351
|
+
const remainingText = text.slice(lastOffset);
|
|
352
|
+
if (remainingText) {
|
|
353
|
+
segments.push({ type: "text", data: { text: remainingText } });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return segments;
|
|
357
|
+
}
|
|
358
|
+
async $sendMessage(options) {
|
|
359
|
+
try {
|
|
360
|
+
const chatId = parseInt(options.id);
|
|
361
|
+
const result = await this.sendContentToChat(chatId, options.content);
|
|
362
|
+
this.pluginLogger.debug(`${this.$config.name} send ${options.type}(${options.id}): ${segment.raw(options.content)}`);
|
|
363
|
+
return result.message_id.toString();
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
this.pluginLogger.error("Failed to send Telegram message:", error);
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async sendContentToChat(chatId, content, extraOptions = {}) {
|
|
371
|
+
if (!Array.isArray(content))
|
|
372
|
+
content = [content];
|
|
373
|
+
let textContent = "";
|
|
374
|
+
let hasMedia = false;
|
|
375
|
+
for (const segment of content) {
|
|
376
|
+
if (typeof segment === "string") {
|
|
377
|
+
textContent += segment;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
const { type, data } = segment;
|
|
381
|
+
switch (type) {
|
|
382
|
+
case "text":
|
|
383
|
+
textContent += data.text || "";
|
|
384
|
+
break;
|
|
385
|
+
case "at":
|
|
386
|
+
if (data.id) {
|
|
387
|
+
textContent += `@${data.name || data.id}`;
|
|
388
|
+
}
|
|
389
|
+
break;
|
|
390
|
+
case "image":
|
|
391
|
+
hasMedia = true;
|
|
392
|
+
if (data.file_id) {
|
|
393
|
+
// Send by file_id
|
|
394
|
+
return await this.telegram.sendPhoto(chatId, data.file_id, {
|
|
395
|
+
caption: textContent || undefined,
|
|
396
|
+
...extraOptions,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
else if (data.url) {
|
|
400
|
+
// Send by URL
|
|
401
|
+
return await this.telegram.sendPhoto(chatId, data.url, {
|
|
402
|
+
caption: textContent || undefined,
|
|
403
|
+
...extraOptions,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
else if (data.file) {
|
|
407
|
+
// Send by file path
|
|
408
|
+
return await this.telegram.sendPhoto(chatId, { source: data.file }, {
|
|
409
|
+
caption: textContent || undefined,
|
|
410
|
+
...extraOptions,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
case "video":
|
|
415
|
+
hasMedia = true;
|
|
416
|
+
if (data.file_id) {
|
|
417
|
+
return await this.telegram.sendVideo(chatId, data.file_id, {
|
|
418
|
+
caption: textContent || undefined,
|
|
419
|
+
...extraOptions,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
else if (data.url) {
|
|
423
|
+
return await this.telegram.sendVideo(chatId, data.url, {
|
|
424
|
+
caption: textContent || undefined,
|
|
425
|
+
...extraOptions,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
else if (data.file) {
|
|
429
|
+
return await this.telegram.sendVideo(chatId, { source: data.file }, {
|
|
430
|
+
caption: textContent || undefined,
|
|
431
|
+
...extraOptions,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
break;
|
|
435
|
+
case "audio":
|
|
436
|
+
hasMedia = true;
|
|
437
|
+
if (data.file_id) {
|
|
438
|
+
return await this.telegram.sendAudio(chatId, data.file_id, {
|
|
439
|
+
caption: textContent || undefined,
|
|
440
|
+
...extraOptions,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
else if (data.url) {
|
|
444
|
+
return await this.telegram.sendAudio(chatId, data.url, {
|
|
445
|
+
caption: textContent || undefined,
|
|
446
|
+
...extraOptions,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
else if (data.file) {
|
|
450
|
+
return await this.telegram.sendAudio(chatId, { source: data.file }, {
|
|
451
|
+
caption: textContent || undefined,
|
|
452
|
+
...extraOptions,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
break;
|
|
456
|
+
case "voice":
|
|
457
|
+
hasMedia = true;
|
|
458
|
+
if (data.file_id) {
|
|
459
|
+
return await this.telegram.sendVoice(chatId, data.file_id, {
|
|
460
|
+
caption: textContent || undefined,
|
|
461
|
+
...extraOptions,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
else if (data.url) {
|
|
465
|
+
return await this.telegram.sendVoice(chatId, data.url, {
|
|
466
|
+
caption: textContent || undefined,
|
|
467
|
+
...extraOptions,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
else if (data.file) {
|
|
471
|
+
return await this.telegram.sendVoice(chatId, { source: data.file }, {
|
|
472
|
+
caption: textContent || undefined,
|
|
473
|
+
...extraOptions,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
case "file":
|
|
478
|
+
hasMedia = true;
|
|
479
|
+
if (data.file_id) {
|
|
480
|
+
return await this.telegram.sendDocument(chatId, data.file_id, {
|
|
481
|
+
caption: textContent || undefined,
|
|
482
|
+
...extraOptions,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
else if (data.url) {
|
|
486
|
+
return await this.telegram.sendDocument(chatId, data.url, {
|
|
487
|
+
caption: textContent || undefined,
|
|
488
|
+
...extraOptions,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
else if (data.file) {
|
|
492
|
+
return await this.telegram.sendDocument(chatId, { source: data.file }, {
|
|
493
|
+
caption: textContent || undefined,
|
|
494
|
+
...extraOptions,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
case "sticker":
|
|
499
|
+
if (data.file_id) {
|
|
500
|
+
hasMedia = true;
|
|
501
|
+
return await this.telegram.sendSticker(chatId, data.file_id, extraOptions);
|
|
502
|
+
}
|
|
503
|
+
break;
|
|
504
|
+
case "location":
|
|
505
|
+
return await this.telegram.sendLocation(chatId, data.latitude, data.longitude, extraOptions);
|
|
506
|
+
case "reply":
|
|
507
|
+
return await this.telegram.sendMessage(chatId, data.id, {
|
|
508
|
+
reply_parameters: { message_id: parseInt(data.id) },
|
|
509
|
+
...extraOptions,
|
|
510
|
+
});
|
|
511
|
+
default:
|
|
512
|
+
// Unknown segment type, add as text
|
|
513
|
+
textContent += data.text || `[${type}]`;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
// If no media was sent, send as text message
|
|
517
|
+
if (!hasMedia && textContent.trim()) {
|
|
518
|
+
return await this.telegram.sendMessage(chatId, textContent.trim(), extraOptions);
|
|
519
|
+
}
|
|
520
|
+
// If neither media nor text was sent, this is an error
|
|
521
|
+
throw new Error("TelegramBot.$sendMessage: No media or text content to send.");
|
|
522
|
+
}
|
|
523
|
+
async $recallMessage(id) {
|
|
524
|
+
// Telegram requires both chat_id and message_id to delete a message
|
|
525
|
+
// The Bot interface only provides message_id, making recall impossible
|
|
526
|
+
// Users should use message.$recall() instead, which has the full context
|
|
527
|
+
throw new Error("TelegramBot.$recallMessage: Message recall not supported without chat_id. " +
|
|
528
|
+
"Use message.$recall() method instead, which contains the required context.");
|
|
529
|
+
}
|
|
530
|
+
// ==================== 群组管理 API ====================
|
|
531
|
+
/**
|
|
532
|
+
* 踢出用户
|
|
533
|
+
* @param chatId 聊天 ID
|
|
534
|
+
* @param userId 用户 ID
|
|
535
|
+
* @param untilDate 封禁截止时间(Unix 时间戳),0 表示永久
|
|
536
|
+
*/
|
|
537
|
+
async kickMember(chatId, userId, untilDate) {
|
|
538
|
+
try {
|
|
539
|
+
await this.telegram.banChatMember(chatId, userId, untilDate);
|
|
540
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 踢出用户 ${userId} 从聊天 ${chatId}`);
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 踢出用户失败:`, error);
|
|
545
|
+
throw error;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* 解除封禁
|
|
550
|
+
* @param chatId 聊天 ID
|
|
551
|
+
* @param userId 用户 ID
|
|
552
|
+
*/
|
|
553
|
+
async unbanMember(chatId, userId) {
|
|
554
|
+
try {
|
|
555
|
+
await this.telegram.unbanChatMember(chatId, userId, { only_if_banned: true });
|
|
556
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 解除用户 ${userId} 的封禁(聊天 ${chatId})`);
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 解除封禁失败:`, error);
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* 限制用户权限(禁言等)
|
|
566
|
+
* @param chatId 聊天 ID
|
|
567
|
+
* @param userId 用户 ID
|
|
568
|
+
* @param permissions 权限设置
|
|
569
|
+
* @param untilDate 限制截止时间
|
|
570
|
+
*/
|
|
571
|
+
async restrictMember(chatId, userId, permissions, untilDate) {
|
|
572
|
+
try {
|
|
573
|
+
await this.telegram.restrictChatMember(chatId, userId, {
|
|
574
|
+
permissions,
|
|
575
|
+
until_date: untilDate,
|
|
576
|
+
});
|
|
577
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 限制用户 ${userId} 权限(聊天 ${chatId})`);
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 限制权限失败:`, error);
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* 禁言用户
|
|
587
|
+
* @param chatId 聊天 ID
|
|
588
|
+
* @param userId 用户 ID
|
|
589
|
+
* @param duration 禁言时长(秒),0 表示解除禁言
|
|
590
|
+
*/
|
|
591
|
+
async muteMember(chatId, userId, duration = 600) {
|
|
592
|
+
try {
|
|
593
|
+
if (duration === 0) {
|
|
594
|
+
// 解除禁言 - 恢复发送消息权限
|
|
595
|
+
await this.telegram.restrictChatMember(chatId, userId, {
|
|
596
|
+
permissions: {
|
|
597
|
+
can_send_messages: true,
|
|
598
|
+
can_send_audios: true,
|
|
599
|
+
can_send_documents: true,
|
|
600
|
+
can_send_photos: true,
|
|
601
|
+
can_send_videos: true,
|
|
602
|
+
can_send_video_notes: true,
|
|
603
|
+
can_send_voice_notes: true,
|
|
604
|
+
can_send_polls: true,
|
|
605
|
+
can_send_other_messages: true,
|
|
606
|
+
can_add_web_page_previews: true,
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 解除用户 ${userId} 禁言(聊天 ${chatId})`);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
const untilDate = Math.floor(Date.now() / 1000) + duration;
|
|
613
|
+
await this.telegram.restrictChatMember(chatId, userId, {
|
|
614
|
+
permissions: {
|
|
615
|
+
can_send_messages: false,
|
|
616
|
+
can_send_audios: false,
|
|
617
|
+
can_send_documents: false,
|
|
618
|
+
can_send_photos: false,
|
|
619
|
+
can_send_videos: false,
|
|
620
|
+
can_send_video_notes: false,
|
|
621
|
+
can_send_voice_notes: false,
|
|
622
|
+
can_send_polls: false,
|
|
623
|
+
can_send_other_messages: false,
|
|
624
|
+
can_add_web_page_previews: false,
|
|
625
|
+
},
|
|
626
|
+
until_date: untilDate,
|
|
627
|
+
});
|
|
628
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 禁言用户 ${userId} ${duration}秒(聊天 ${chatId})`);
|
|
629
|
+
}
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
catch (error) {
|
|
633
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 禁言操作失败:`, error);
|
|
634
|
+
throw error;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* 提升/降级管理员
|
|
639
|
+
* @param chatId 聊天 ID
|
|
640
|
+
* @param userId 用户 ID
|
|
641
|
+
* @param promote 是否提升为管理员
|
|
642
|
+
*/
|
|
643
|
+
async setAdmin(chatId, userId, promote = true) {
|
|
644
|
+
try {
|
|
645
|
+
if (promote) {
|
|
646
|
+
await this.telegram.promoteChatMember(chatId, userId, {
|
|
647
|
+
can_manage_chat: true,
|
|
648
|
+
can_delete_messages: true,
|
|
649
|
+
can_manage_video_chats: true,
|
|
650
|
+
can_restrict_members: true,
|
|
651
|
+
can_promote_members: false,
|
|
652
|
+
can_change_info: true,
|
|
653
|
+
can_invite_users: true,
|
|
654
|
+
can_pin_messages: true,
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
await this.telegram.promoteChatMember(chatId, userId, {
|
|
659
|
+
can_manage_chat: false,
|
|
660
|
+
can_delete_messages: false,
|
|
661
|
+
can_manage_video_chats: false,
|
|
662
|
+
can_restrict_members: false,
|
|
663
|
+
can_promote_members: false,
|
|
664
|
+
can_change_info: false,
|
|
665
|
+
can_invite_users: false,
|
|
666
|
+
can_pin_messages: false,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} ${promote ? '提升' : '降级'}用户 ${userId} 为管理员(聊天 ${chatId})`);
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 设置管理员失败:`, error);
|
|
674
|
+
throw error;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* 设置聊天标题
|
|
679
|
+
* @param chatId 聊天 ID
|
|
680
|
+
* @param title 新标题
|
|
681
|
+
*/
|
|
682
|
+
async setChatTitle(chatId, title) {
|
|
683
|
+
try {
|
|
684
|
+
await this.telegram.setChatTitle(chatId, title);
|
|
685
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 设置聊天 ${chatId} 标题为 "${title}"`);
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 设置标题失败:`, error);
|
|
690
|
+
throw error;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* 设置聊天描述
|
|
695
|
+
* @param chatId 聊天 ID
|
|
696
|
+
* @param description 新描述
|
|
697
|
+
*/
|
|
698
|
+
async setChatDescription(chatId, description) {
|
|
699
|
+
try {
|
|
700
|
+
await this.telegram.setChatDescription(chatId, description);
|
|
701
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 设置聊天 ${chatId} 描述`);
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 设置描述失败:`, error);
|
|
706
|
+
throw error;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* 置顶消息
|
|
711
|
+
* @param chatId 聊天 ID
|
|
712
|
+
* @param messageId 消息 ID
|
|
713
|
+
*/
|
|
714
|
+
async pinMessage(chatId, messageId) {
|
|
715
|
+
try {
|
|
716
|
+
await this.telegram.pinChatMessage(chatId, messageId);
|
|
717
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 置顶消息 ${messageId}(聊天 ${chatId})`);
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
catch (error) {
|
|
721
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 置顶消息失败:`, error);
|
|
722
|
+
throw error;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* 取消置顶消息
|
|
727
|
+
* @param chatId 聊天 ID
|
|
728
|
+
* @param messageId 消息 ID(可选,不提供则取消所有置顶)
|
|
729
|
+
*/
|
|
730
|
+
async unpinMessage(chatId, messageId) {
|
|
731
|
+
try {
|
|
732
|
+
if (messageId) {
|
|
733
|
+
await this.telegram.unpinChatMessage(chatId, messageId);
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
await this.telegram.unpinAllChatMessages(chatId);
|
|
737
|
+
}
|
|
738
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 取消置顶消息(聊天 ${chatId})`);
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
catch (error) {
|
|
742
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 取消置顶失败:`, error);
|
|
743
|
+
throw error;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* 获取聊天信息
|
|
748
|
+
* @param chatId 聊天 ID
|
|
749
|
+
*/
|
|
750
|
+
async getChatInfo(chatId) {
|
|
751
|
+
try {
|
|
752
|
+
return await this.telegram.getChat(chatId);
|
|
753
|
+
}
|
|
754
|
+
catch (error) {
|
|
755
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 获取聊天信息失败:`, error);
|
|
756
|
+
throw error;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* 获取聊天成员
|
|
761
|
+
* @param chatId 聊天 ID
|
|
762
|
+
* @param userId 用户 ID
|
|
763
|
+
*/
|
|
764
|
+
async getChatMember(chatId, userId) {
|
|
765
|
+
try {
|
|
766
|
+
return await this.telegram.getChatMember(chatId, userId);
|
|
767
|
+
}
|
|
768
|
+
catch (error) {
|
|
769
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 获取成员信息失败:`, error);
|
|
770
|
+
throw error;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* 获取管理员列表
|
|
775
|
+
* @param chatId 聊天 ID
|
|
776
|
+
*/
|
|
777
|
+
async getChatAdmins(chatId) {
|
|
778
|
+
try {
|
|
779
|
+
return await this.telegram.getChatAdministrators(chatId);
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 获取管理员列表失败:`, error);
|
|
783
|
+
throw error;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* 获取聊天成员数量
|
|
788
|
+
* @param chatId 聊天 ID
|
|
789
|
+
*/
|
|
790
|
+
async getChatMemberCount(chatId) {
|
|
791
|
+
try {
|
|
792
|
+
return await this.telegram.getChatMembersCount(chatId);
|
|
793
|
+
}
|
|
794
|
+
catch (error) {
|
|
795
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 获取成员数量失败:`, error);
|
|
796
|
+
throw error;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* 创建邀请链接
|
|
801
|
+
* @param chatId 聊天 ID
|
|
802
|
+
*/
|
|
803
|
+
async createInviteLink(chatId) {
|
|
804
|
+
try {
|
|
805
|
+
const link = await this.telegram.createChatInviteLink(chatId, {});
|
|
806
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 创建邀请链接(聊天 ${chatId})`);
|
|
807
|
+
return link.invite_link;
|
|
808
|
+
}
|
|
809
|
+
catch (error) {
|
|
810
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 创建邀请链接失败:`, error);
|
|
811
|
+
throw error;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
async sendPoll(chatId, question, options, isAnonymous = true, allowsMultipleAnswers = false) {
|
|
815
|
+
try {
|
|
816
|
+
const result = await this.telegram.sendPoll(chatId, question, options, {
|
|
817
|
+
is_anonymous: isAnonymous,
|
|
818
|
+
allows_multiple_answers: allowsMultipleAnswers,
|
|
819
|
+
});
|
|
820
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 发送投票到 ${chatId}`);
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 发送投票失败:`, error);
|
|
825
|
+
throw error;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
async setMessageReaction(chatId, messageId, reaction) {
|
|
829
|
+
try {
|
|
830
|
+
await this.telegram.callApi('setMessageReaction', {
|
|
831
|
+
chat_id: chatId,
|
|
832
|
+
message_id: messageId,
|
|
833
|
+
reaction: [{ type: 'emoji', emoji: reaction }],
|
|
834
|
+
});
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
catch (error) {
|
|
838
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 设置反应失败:`, error);
|
|
839
|
+
throw error;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
async sendStickerMessage(chatId, sticker) {
|
|
843
|
+
try {
|
|
844
|
+
const result = await this.telegram.sendSticker(chatId, sticker);
|
|
845
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 发送贴纸到 ${chatId}`);
|
|
846
|
+
return result;
|
|
847
|
+
}
|
|
848
|
+
catch (error) {
|
|
849
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 发送贴纸失败:`, error);
|
|
850
|
+
throw error;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
async setChatPermissionsAll(chatId, permissions) {
|
|
854
|
+
try {
|
|
855
|
+
await this.telegram.setChatPermissions(chatId, permissions);
|
|
856
|
+
this.pluginLogger.info(`Telegram Bot ${this.$id} 设置聊天 ${chatId} 权限`);
|
|
857
|
+
return true;
|
|
858
|
+
}
|
|
859
|
+
catch (error) {
|
|
860
|
+
this.pluginLogger.error(`Telegram Bot ${this.$id} 设置聊天权限失败:`, error);
|
|
861
|
+
throw error;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
//# sourceMappingURL=bot.js.map
|