@overpod/mcp-telegram 1.25.0 → 1.26.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/CHANGELOG.md +35 -0
- package/README.md +35 -5
- package/dist/__tests__/approve-join-request.test.d.ts +1 -0
- package/dist/__tests__/approve-join-request.test.js +107 -0
- package/dist/__tests__/boosts.test.d.ts +1 -0
- package/dist/__tests__/boosts.test.js +310 -0
- package/dist/__tests__/broadcast-stats.test.d.ts +1 -0
- package/dist/__tests__/broadcast-stats.test.js +172 -0
- package/dist/__tests__/business-chat-links.test.d.ts +1 -0
- package/dist/__tests__/business-chat-links.test.js +102 -0
- package/dist/__tests__/get-message-buttons.test.d.ts +1 -0
- package/dist/__tests__/get-message-buttons.test.js +122 -0
- package/dist/__tests__/group-calls.test.d.ts +1 -0
- package/dist/__tests__/group-calls.test.js +503 -0
- package/dist/__tests__/inline-query-send.test.d.ts +1 -0
- package/dist/__tests__/inline-query-send.test.js +94 -0
- package/dist/__tests__/inline-query.test.d.ts +1 -0
- package/dist/__tests__/inline-query.test.js +115 -0
- package/dist/__tests__/megagroup-stats.test.d.ts +1 -0
- package/dist/__tests__/megagroup-stats.test.js +166 -0
- package/dist/__tests__/press-button.test.d.ts +1 -0
- package/dist/__tests__/press-button.test.js +123 -0
- package/dist/__tests__/quick-replies.test.d.ts +1 -0
- package/dist/__tests__/quick-replies.test.js +245 -0
- package/dist/__tests__/set-chat-reactions.test.d.ts +1 -0
- package/dist/__tests__/set-chat-reactions.test.js +129 -0
- package/dist/__tests__/stars-status.test.d.ts +1 -0
- package/dist/__tests__/stars-status.test.js +205 -0
- package/dist/__tests__/stars-transactions.test.d.ts +1 -0
- package/dist/__tests__/stars-transactions.test.js +82 -0
- package/dist/__tests__/stories.test.d.ts +1 -0
- package/dist/__tests__/stories.test.js +361 -0
- package/dist/__tests__/toggle-anti-spam.test.d.ts +1 -0
- package/dist/__tests__/toggle-anti-spam.test.js +80 -0
- package/dist/__tests__/toggle-channel-signatures.test.d.ts +1 -0
- package/dist/__tests__/toggle-channel-signatures.test.js +80 -0
- package/dist/__tests__/toggle-forum-mode.test.d.ts +1 -0
- package/dist/__tests__/toggle-forum-mode.test.js +80 -0
- package/dist/__tests__/toggle-prehistory-hidden.test.d.ts +1 -0
- package/dist/__tests__/toggle-prehistory-hidden.test.js +80 -0
- package/dist/__tests__/updates.test.d.ts +1 -0
- package/dist/__tests__/updates.test.js +221 -0
- package/dist/rate-limiter.d.ts +8 -2
- package/dist/rate-limiter.js +15 -8
- package/dist/telegram-client.d.ts +580 -0
- package/dist/telegram-client.js +1322 -0
- package/dist/tools/account.js +16 -0
- package/dist/tools/boosts.d.ts +3 -0
- package/dist/tools/boosts.js +65 -0
- package/dist/tools/chats.js +150 -0
- package/dist/tools/group-calls.d.ts +4 -0
- package/dist/tools/group-calls.js +77 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/messages.js +192 -0
- package/dist/tools/quick-replies.d.ts +4 -0
- package/dist/tools/quick-replies.js +58 -0
- package/dist/tools/reactions.js +43 -0
- package/dist/tools/stars.d.ts +4 -0
- package/dist/tools/stars.js +71 -0
- package/dist/tools/stories.d.ts +3 -0
- package/dist/tools/stories.js +107 -0
- package/package.json +1 -1
package/dist/telegram-client.js
CHANGED
|
@@ -134,6 +134,129 @@ export function reactionToEmoji(reaction) {
|
|
|
134
134
|
return "⭐";
|
|
135
135
|
return null;
|
|
136
136
|
}
|
|
137
|
+
function absValue(v) {
|
|
138
|
+
return { current: v?.current ?? 0, previous: v?.previous ?? 0 };
|
|
139
|
+
}
|
|
140
|
+
function compactGraph(g) {
|
|
141
|
+
if (g instanceof Api.StatsGraphAsync)
|
|
142
|
+
return { type: "async", token: g.token };
|
|
143
|
+
if (g instanceof Api.StatsGraphError)
|
|
144
|
+
return { type: "error", error: g.error };
|
|
145
|
+
if (g instanceof Api.StatsGraph) {
|
|
146
|
+
let parsed = g.json?.data;
|
|
147
|
+
if (typeof parsed === "string") {
|
|
148
|
+
try {
|
|
149
|
+
parsed = JSON.parse(parsed);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// leave raw string
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { type: "data", data: parsed, zoomToken: g.zoomToken };
|
|
156
|
+
}
|
|
157
|
+
const any = g;
|
|
158
|
+
if (typeof any.token === "string")
|
|
159
|
+
return { type: "async", token: any.token };
|
|
160
|
+
if (typeof any.error === "string")
|
|
161
|
+
return { type: "error", error: any.error };
|
|
162
|
+
return { type: "data", data: any.json?.data, zoomToken: any.zoomToken };
|
|
163
|
+
}
|
|
164
|
+
export function summarizeMegagroupStats(stats, includeGraphs) {
|
|
165
|
+
const summary = {
|
|
166
|
+
period: {
|
|
167
|
+
minDate: stats.period?.minDate ?? 0,
|
|
168
|
+
maxDate: stats.period?.maxDate ?? 0,
|
|
169
|
+
},
|
|
170
|
+
members: absValue(stats.members),
|
|
171
|
+
messages: absValue(stats.messages),
|
|
172
|
+
viewers: absValue(stats.viewers),
|
|
173
|
+
posters: absValue(stats.posters),
|
|
174
|
+
topPosters: (stats.topPosters ?? []).map((p) => ({
|
|
175
|
+
userId: p.userId?.toString() ?? "",
|
|
176
|
+
messages: p.messages,
|
|
177
|
+
avgChars: p.avgChars,
|
|
178
|
+
})),
|
|
179
|
+
topAdmins: (stats.topAdmins ?? []).map((a) => ({
|
|
180
|
+
userId: a.userId?.toString() ?? "",
|
|
181
|
+
deleted: a.deleted,
|
|
182
|
+
kicked: a.kicked,
|
|
183
|
+
banned: a.banned,
|
|
184
|
+
})),
|
|
185
|
+
topInviters: (stats.topInviters ?? []).map((i) => ({
|
|
186
|
+
userId: i.userId?.toString() ?? "",
|
|
187
|
+
invitations: i.invitations,
|
|
188
|
+
})),
|
|
189
|
+
};
|
|
190
|
+
if (includeGraphs) {
|
|
191
|
+
summary.graphs = {
|
|
192
|
+
growth: compactGraph(stats.growthGraph),
|
|
193
|
+
members: compactGraph(stats.membersGraph),
|
|
194
|
+
newMembersBySource: compactGraph(stats.newMembersBySourceGraph),
|
|
195
|
+
languages: compactGraph(stats.languagesGraph),
|
|
196
|
+
messages: compactGraph(stats.messagesGraph),
|
|
197
|
+
actions: compactGraph(stats.actionsGraph),
|
|
198
|
+
topHours: compactGraph(stats.topHoursGraph),
|
|
199
|
+
weekdays: compactGraph(stats.weekdaysGraph),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return summary;
|
|
203
|
+
}
|
|
204
|
+
export function summarizeBroadcastStats(stats, includeGraphs) {
|
|
205
|
+
const enabled = stats.enabledNotifications;
|
|
206
|
+
const part = enabled?.part ?? 0;
|
|
207
|
+
const total = enabled?.total ?? 0;
|
|
208
|
+
const percent = total > 0 ? (part / total) * 100 : 0;
|
|
209
|
+
const summary = {
|
|
210
|
+
period: {
|
|
211
|
+
minDate: stats.period?.minDate ?? 0,
|
|
212
|
+
maxDate: stats.period?.maxDate ?? 0,
|
|
213
|
+
},
|
|
214
|
+
followers: absValue(stats.followers),
|
|
215
|
+
viewsPerPost: absValue(stats.viewsPerPost),
|
|
216
|
+
sharesPerPost: absValue(stats.sharesPerPost),
|
|
217
|
+
reactionsPerPost: absValue(stats.reactionsPerPost),
|
|
218
|
+
viewsPerStory: absValue(stats.viewsPerStory),
|
|
219
|
+
sharesPerStory: absValue(stats.sharesPerStory),
|
|
220
|
+
reactionsPerStory: absValue(stats.reactionsPerStory),
|
|
221
|
+
enabledNotifications: { part, total, percent },
|
|
222
|
+
recentPostsInteractions: (stats.recentPostsInteractions ?? []).map((p) => {
|
|
223
|
+
if (p instanceof Api.PostInteractionCountersStory) {
|
|
224
|
+
return {
|
|
225
|
+
kind: "story",
|
|
226
|
+
storyId: p.storyId,
|
|
227
|
+
views: p.views,
|
|
228
|
+
forwards: p.forwards,
|
|
229
|
+
reactions: p.reactions,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const m = p;
|
|
233
|
+
return {
|
|
234
|
+
kind: "message",
|
|
235
|
+
msgId: m.msgId,
|
|
236
|
+
views: m.views,
|
|
237
|
+
forwards: m.forwards,
|
|
238
|
+
reactions: m.reactions,
|
|
239
|
+
};
|
|
240
|
+
}),
|
|
241
|
+
};
|
|
242
|
+
if (includeGraphs) {
|
|
243
|
+
summary.graphs = {
|
|
244
|
+
growth: compactGraph(stats.growthGraph),
|
|
245
|
+
followers: compactGraph(stats.followersGraph),
|
|
246
|
+
mute: compactGraph(stats.muteGraph),
|
|
247
|
+
topHours: compactGraph(stats.topHoursGraph),
|
|
248
|
+
interactions: compactGraph(stats.interactionsGraph),
|
|
249
|
+
ivInteractions: compactGraph(stats.ivInteractionsGraph),
|
|
250
|
+
viewsBySource: compactGraph(stats.viewsBySourceGraph),
|
|
251
|
+
newFollowersBySource: compactGraph(stats.newFollowersBySourceGraph),
|
|
252
|
+
languages: compactGraph(stats.languagesGraph),
|
|
253
|
+
reactionsByEmotion: compactGraph(stats.reactionsByEmotionGraph),
|
|
254
|
+
storyInteractions: compactGraph(stats.storyInteractionsGraph),
|
|
255
|
+
storyReactionsByEmotion: compactGraph(stats.storyReactionsByEmotionGraph),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return summary;
|
|
259
|
+
}
|
|
137
260
|
const BANNED_RIGHT_FLAGS = [
|
|
138
261
|
"sendMessages",
|
|
139
262
|
"sendMedia",
|
|
@@ -175,6 +298,621 @@ export function mergeBannedRights(current, permissions) {
|
|
|
175
298
|
}
|
|
176
299
|
return result;
|
|
177
300
|
}
|
|
301
|
+
export function describeKeyboardButton(button, row, col) {
|
|
302
|
+
const base = {
|
|
303
|
+
row,
|
|
304
|
+
col,
|
|
305
|
+
type: button.className,
|
|
306
|
+
label: "text" in button && typeof button.text === "string" ? button.text : "",
|
|
307
|
+
};
|
|
308
|
+
if (button instanceof Api.KeyboardButtonCallback) {
|
|
309
|
+
base.data = Buffer.from(button.data).toString("base64");
|
|
310
|
+
if (button.requiresPassword)
|
|
311
|
+
base.requiresPassword = true;
|
|
312
|
+
return base;
|
|
313
|
+
}
|
|
314
|
+
if (button instanceof Api.KeyboardButtonUrl) {
|
|
315
|
+
base.url = button.url;
|
|
316
|
+
return base;
|
|
317
|
+
}
|
|
318
|
+
if (button instanceof Api.KeyboardButtonUrlAuth) {
|
|
319
|
+
base.url = button.url;
|
|
320
|
+
base.buttonId = button.buttonId;
|
|
321
|
+
return base;
|
|
322
|
+
}
|
|
323
|
+
if (button instanceof Api.KeyboardButtonSwitchInline) {
|
|
324
|
+
base.switchQuery = button.query;
|
|
325
|
+
base.samePeer = Boolean(button.samePeer);
|
|
326
|
+
return base;
|
|
327
|
+
}
|
|
328
|
+
if (button instanceof Api.KeyboardButtonWebView || button instanceof Api.KeyboardButtonSimpleWebView) {
|
|
329
|
+
base.url = button.url;
|
|
330
|
+
return base;
|
|
331
|
+
}
|
|
332
|
+
if (button instanceof Api.KeyboardButtonUserProfile) {
|
|
333
|
+
base.userId = button.userId?.toString();
|
|
334
|
+
return base;
|
|
335
|
+
}
|
|
336
|
+
if (button instanceof Api.KeyboardButtonRequestPoll) {
|
|
337
|
+
if (button.quiz)
|
|
338
|
+
base.quiz = true;
|
|
339
|
+
return base;
|
|
340
|
+
}
|
|
341
|
+
if (button instanceof Api.KeyboardButtonRequestPeer) {
|
|
342
|
+
base.buttonId = button.buttonId;
|
|
343
|
+
return base;
|
|
344
|
+
}
|
|
345
|
+
if (button instanceof Api.KeyboardButtonCopy) {
|
|
346
|
+
base.copyText = button.copyText;
|
|
347
|
+
return base;
|
|
348
|
+
}
|
|
349
|
+
return base;
|
|
350
|
+
}
|
|
351
|
+
export function peerToCompact(peer) {
|
|
352
|
+
if (!peer)
|
|
353
|
+
return undefined;
|
|
354
|
+
if (peer instanceof Api.PeerUser)
|
|
355
|
+
return { kind: "user", id: peer.userId.toString() };
|
|
356
|
+
if (peer instanceof Api.PeerChat)
|
|
357
|
+
return { kind: "chat", id: peer.chatId.toString() };
|
|
358
|
+
if (peer instanceof Api.PeerChannel)
|
|
359
|
+
return { kind: "channel", id: peer.channelId.toString() };
|
|
360
|
+
return undefined;
|
|
361
|
+
}
|
|
362
|
+
function summarizeMessageForUpdates(msg) {
|
|
363
|
+
if (msg instanceof Api.MessageEmpty)
|
|
364
|
+
return null;
|
|
365
|
+
const peer = peerToCompact(msg.peerId);
|
|
366
|
+
if (!peer)
|
|
367
|
+
return null;
|
|
368
|
+
const fromId = peerToCompact(msg.fromId);
|
|
369
|
+
const date = msg.date ?? 0;
|
|
370
|
+
if (msg instanceof Api.Message) {
|
|
371
|
+
return { id: msg.id, peer, fromId, date, text: msg.message ?? "", isService: false };
|
|
372
|
+
}
|
|
373
|
+
if (msg instanceof Api.MessageService) {
|
|
374
|
+
return {
|
|
375
|
+
id: msg.id,
|
|
376
|
+
peer,
|
|
377
|
+
fromId,
|
|
378
|
+
date,
|
|
379
|
+
text: `[${msg.action?.className ?? "service"}]`,
|
|
380
|
+
isService: true,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
function collectDeletedMessageIds(updates) {
|
|
386
|
+
const out = [];
|
|
387
|
+
for (const u of updates) {
|
|
388
|
+
if (u instanceof Api.UpdateDeleteMessages) {
|
|
389
|
+
out.push({ messageIds: u.messages });
|
|
390
|
+
}
|
|
391
|
+
else if (u instanceof Api.UpdateDeleteChannelMessages) {
|
|
392
|
+
out.push({
|
|
393
|
+
peer: { kind: "channel", id: u.channelId.toString() },
|
|
394
|
+
messageIds: u.messages,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return out;
|
|
399
|
+
}
|
|
400
|
+
export function summarizeUpdatesDifference(diff, cursor) {
|
|
401
|
+
if (diff instanceof Api.updates.DifferenceEmpty) {
|
|
402
|
+
return {
|
|
403
|
+
state: { pts: cursor.pts, qts: cursor.qts, date: diff.date, seq: diff.seq },
|
|
404
|
+
isFinal: true,
|
|
405
|
+
newMessages: [],
|
|
406
|
+
deletedMessageIds: [],
|
|
407
|
+
otherUpdates: [],
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
if (diff instanceof Api.updates.DifferenceTooLong) {
|
|
411
|
+
return {
|
|
412
|
+
state: { pts: diff.pts, qts: cursor.qts, date: cursor.date, seq: 0 },
|
|
413
|
+
isFinal: true,
|
|
414
|
+
newMessages: [],
|
|
415
|
+
deletedMessageIds: [],
|
|
416
|
+
otherUpdates: [],
|
|
417
|
+
fallback: {
|
|
418
|
+
kind: "tooLong",
|
|
419
|
+
suggestedAction: "gap too large — call telegram-read-messages per chat or telegram-get-state to resync",
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const isFinal = diff instanceof Api.updates.Difference;
|
|
424
|
+
const state = isFinal
|
|
425
|
+
? diff.state
|
|
426
|
+
: diff.intermediateState;
|
|
427
|
+
const newMessages = (diff.newMessages ?? [])
|
|
428
|
+
.map(summarizeMessageForUpdates)
|
|
429
|
+
.filter((m) => m !== null);
|
|
430
|
+
const otherUpdates = diff.otherUpdates ?? [];
|
|
431
|
+
return {
|
|
432
|
+
state: {
|
|
433
|
+
pts: state.pts,
|
|
434
|
+
qts: state.qts,
|
|
435
|
+
date: state.date,
|
|
436
|
+
seq: state.seq,
|
|
437
|
+
unreadCount: state.unreadCount,
|
|
438
|
+
},
|
|
439
|
+
isFinal,
|
|
440
|
+
newMessages,
|
|
441
|
+
deletedMessageIds: collectDeletedMessageIds(otherUpdates),
|
|
442
|
+
otherUpdates: otherUpdates.map((u) => ({ type: u.className })),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
export function summarizeChannelDifference(diff, channelId, fallbackPts) {
|
|
446
|
+
if (diff instanceof Api.updates.ChannelDifferenceEmpty) {
|
|
447
|
+
return {
|
|
448
|
+
channelId,
|
|
449
|
+
pts: diff.pts,
|
|
450
|
+
isFinal: Boolean(diff.final),
|
|
451
|
+
timeout: diff.timeout,
|
|
452
|
+
newMessages: [],
|
|
453
|
+
otherUpdates: [],
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (diff instanceof Api.updates.ChannelDifferenceTooLong) {
|
|
457
|
+
const freshPts = diff.dialog instanceof Api.Dialog ? (diff.dialog.pts ?? fallbackPts) : fallbackPts;
|
|
458
|
+
return {
|
|
459
|
+
channelId,
|
|
460
|
+
pts: freshPts,
|
|
461
|
+
isFinal: Boolean(diff.final),
|
|
462
|
+
timeout: diff.timeout,
|
|
463
|
+
newMessages: (diff.messages ?? [])
|
|
464
|
+
.map(summarizeMessageForUpdates)
|
|
465
|
+
.filter((m) => m !== null),
|
|
466
|
+
otherUpdates: [],
|
|
467
|
+
fallback: {
|
|
468
|
+
kind: "tooLong",
|
|
469
|
+
suggestedAction: "channel gap too large — dialog snapshot returned; call telegram-read-messages for full history",
|
|
470
|
+
},
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (diff instanceof Api.updates.ChannelDifference) {
|
|
474
|
+
return {
|
|
475
|
+
channelId,
|
|
476
|
+
pts: diff.pts,
|
|
477
|
+
isFinal: Boolean(diff.final),
|
|
478
|
+
timeout: diff.timeout,
|
|
479
|
+
newMessages: (diff.newMessages ?? [])
|
|
480
|
+
.map(summarizeMessageForUpdates)
|
|
481
|
+
.filter((m) => m !== null),
|
|
482
|
+
otherUpdates: (diff.otherUpdates ?? []).map((u) => ({ type: u.className })),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
channelId,
|
|
487
|
+
pts: fallbackPts,
|
|
488
|
+
isFinal: false,
|
|
489
|
+
newMessages: [],
|
|
490
|
+
otherUpdates: [],
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
export function summarizeMyBoost(boost) {
|
|
494
|
+
const b = boost;
|
|
495
|
+
return {
|
|
496
|
+
slot: b.slot,
|
|
497
|
+
peer: peerToCompact(b.peer),
|
|
498
|
+
date: b.date,
|
|
499
|
+
expires: b.expires,
|
|
500
|
+
cooldownUntilDate: b.cooldownUntilDate,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
export function summarizeMyBoosts(result) {
|
|
504
|
+
const boosts = result.myBoosts ?? [];
|
|
505
|
+
return {
|
|
506
|
+
count: boosts.length,
|
|
507
|
+
myBoosts: boosts.map(summarizeMyBoost),
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
export function summarizePrepaidGiveaway(g) {
|
|
511
|
+
if (g instanceof Api.PrepaidStarsGiveaway) {
|
|
512
|
+
return {
|
|
513
|
+
kind: "stars",
|
|
514
|
+
id: g.id.toString(),
|
|
515
|
+
quantity: g.quantity,
|
|
516
|
+
date: g.date,
|
|
517
|
+
stars: g.stars.toString(),
|
|
518
|
+
boosts: g.boosts,
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
const p = g;
|
|
522
|
+
return {
|
|
523
|
+
kind: "premium",
|
|
524
|
+
id: p.id.toString(),
|
|
525
|
+
quantity: p.quantity,
|
|
526
|
+
date: p.date,
|
|
527
|
+
months: p.months,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
export function summarizeBoostsStatus(result) {
|
|
531
|
+
const r = result;
|
|
532
|
+
const out = {
|
|
533
|
+
level: r.level,
|
|
534
|
+
boosts: r.boosts,
|
|
535
|
+
currentLevelBoosts: r.currentLevelBoosts,
|
|
536
|
+
nextLevelBoosts: r.nextLevelBoosts,
|
|
537
|
+
giftBoosts: r.giftBoosts,
|
|
538
|
+
boostUrl: r.boostUrl,
|
|
539
|
+
myBoost: r.myBoost,
|
|
540
|
+
myBoostSlots: r.myBoostSlots,
|
|
541
|
+
};
|
|
542
|
+
if (r.premiumAudience) {
|
|
543
|
+
out.premiumAudience = { part: r.premiumAudience.part, total: r.premiumAudience.total };
|
|
544
|
+
}
|
|
545
|
+
if (r.prepaidGiveaways && r.prepaidGiveaways.length > 0) {
|
|
546
|
+
out.prepaidGiveaways = r.prepaidGiveaways.map(summarizePrepaidGiveaway);
|
|
547
|
+
}
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
550
|
+
export function summarizeBoost(boost) {
|
|
551
|
+
const b = boost;
|
|
552
|
+
return {
|
|
553
|
+
id: b.id,
|
|
554
|
+
userId: b.userId?.toString(),
|
|
555
|
+
date: b.date,
|
|
556
|
+
expires: b.expires,
|
|
557
|
+
gift: b.gift,
|
|
558
|
+
giveaway: b.giveaway,
|
|
559
|
+
unclaimed: b.unclaimed,
|
|
560
|
+
giveawayMsgId: b.giveawayMsgId,
|
|
561
|
+
usedGiftSlug: b.usedGiftSlug,
|
|
562
|
+
multiplier: b.multiplier,
|
|
563
|
+
stars: b.stars?.toString(),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
export function summarizeBoostsList(result) {
|
|
567
|
+
const r = result;
|
|
568
|
+
return {
|
|
569
|
+
count: r.count,
|
|
570
|
+
boosts: (r.boosts ?? []).map(summarizeBoost),
|
|
571
|
+
nextOffset: r.nextOffset,
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
export function summarizeBusinessChatLink(link) {
|
|
575
|
+
const l = link;
|
|
576
|
+
return {
|
|
577
|
+
link: l.link,
|
|
578
|
+
message: l.message,
|
|
579
|
+
title: l.title,
|
|
580
|
+
views: l.views,
|
|
581
|
+
entityCount: l.entities?.length ?? 0,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
export function summarizeBusinessChatLinks(result) {
|
|
585
|
+
const r = result;
|
|
586
|
+
const links = r.links ?? [];
|
|
587
|
+
return {
|
|
588
|
+
count: links.length,
|
|
589
|
+
links: links.map(summarizeBusinessChatLink),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
export function summarizeGroupCallInfo(call) {
|
|
593
|
+
if (call instanceof Api.GroupCallDiscarded) {
|
|
594
|
+
return {
|
|
595
|
+
kind: "discarded",
|
|
596
|
+
id: call.id.toString(),
|
|
597
|
+
accessHash: call.accessHash.toString(),
|
|
598
|
+
duration: call.duration,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
const c = call;
|
|
602
|
+
return {
|
|
603
|
+
kind: "active",
|
|
604
|
+
id: c.id.toString(),
|
|
605
|
+
accessHash: c.accessHash.toString(),
|
|
606
|
+
participantsCount: c.participantsCount,
|
|
607
|
+
title: c.title,
|
|
608
|
+
scheduleDate: c.scheduleDate,
|
|
609
|
+
recordStartDate: c.recordStartDate,
|
|
610
|
+
streamDcId: c.streamDcId,
|
|
611
|
+
unmutedVideoCount: c.unmutedVideoCount,
|
|
612
|
+
unmutedVideoLimit: c.unmutedVideoLimit,
|
|
613
|
+
version: c.version,
|
|
614
|
+
joinMuted: c.joinMuted,
|
|
615
|
+
canChangeJoinMuted: c.canChangeJoinMuted,
|
|
616
|
+
joinDateAsc: c.joinDateAsc,
|
|
617
|
+
scheduleStartSubscribed: c.scheduleStartSubscribed,
|
|
618
|
+
canStartVideo: c.canStartVideo,
|
|
619
|
+
recordVideoActive: c.recordVideoActive,
|
|
620
|
+
rtmpStream: c.rtmpStream,
|
|
621
|
+
listenersHidden: c.listenersHidden,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
export function summarizeGroupCallParticipant(p) {
|
|
625
|
+
const gp = p;
|
|
626
|
+
return {
|
|
627
|
+
peer: peerToCompact(gp.peer),
|
|
628
|
+
date: gp.date,
|
|
629
|
+
activeDate: gp.activeDate,
|
|
630
|
+
source: gp.source,
|
|
631
|
+
volume: gp.volume,
|
|
632
|
+
muted: gp.muted,
|
|
633
|
+
left: gp.left,
|
|
634
|
+
canSelfUnmute: gp.canSelfUnmute,
|
|
635
|
+
justJoined: gp.justJoined,
|
|
636
|
+
self: gp.self,
|
|
637
|
+
mutedByYou: gp.mutedByYou,
|
|
638
|
+
volumeByAdmin: gp.volumeByAdmin,
|
|
639
|
+
videoJoined: gp.videoJoined,
|
|
640
|
+
about: gp.about,
|
|
641
|
+
raiseHandRating: gp.raiseHandRating?.toString(),
|
|
642
|
+
hasVideo: gp.video ? true : undefined,
|
|
643
|
+
hasPresentation: gp.presentation ? true : undefined,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
export function summarizeGroupCall(result) {
|
|
647
|
+
const r = result;
|
|
648
|
+
return {
|
|
649
|
+
call: summarizeGroupCallInfo(r.call),
|
|
650
|
+
participants: (r.participants ?? []).map(summarizeGroupCallParticipant),
|
|
651
|
+
participantsNextOffset: r.participantsNextOffset || undefined,
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
export function summarizeGroupCallParticipants(result) {
|
|
655
|
+
const r = result;
|
|
656
|
+
return {
|
|
657
|
+
count: r.count,
|
|
658
|
+
participants: (r.participants ?? []).map(summarizeGroupCallParticipant),
|
|
659
|
+
nextOffset: r.nextOffset || undefined,
|
|
660
|
+
version: r.version,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
export function summarizeStarsAmount(amount) {
|
|
664
|
+
const a = amount;
|
|
665
|
+
return { amount: a.amount.toString(), nanos: a.nanos };
|
|
666
|
+
}
|
|
667
|
+
export function summarizeStarsTransactionPeer(peer) {
|
|
668
|
+
if (peer instanceof Api.StarsTransactionPeerAppStore)
|
|
669
|
+
return { kind: "appStore" };
|
|
670
|
+
if (peer instanceof Api.StarsTransactionPeerPlayMarket)
|
|
671
|
+
return { kind: "playMarket" };
|
|
672
|
+
if (peer instanceof Api.StarsTransactionPeerPremiumBot)
|
|
673
|
+
return { kind: "premiumBot" };
|
|
674
|
+
if (peer instanceof Api.StarsTransactionPeerFragment)
|
|
675
|
+
return { kind: "fragment" };
|
|
676
|
+
if (peer instanceof Api.StarsTransactionPeerAds)
|
|
677
|
+
return { kind: "ads" };
|
|
678
|
+
if (peer instanceof Api.StarsTransactionPeerAPI)
|
|
679
|
+
return { kind: "api" };
|
|
680
|
+
if (peer instanceof Api.StarsTransactionPeer)
|
|
681
|
+
return { kind: "peer", peer: peerToCompact(peer.peer) };
|
|
682
|
+
return { kind: "unsupported" };
|
|
683
|
+
}
|
|
684
|
+
export function summarizeStarsTransaction(tx) {
|
|
685
|
+
const t = tx;
|
|
686
|
+
return {
|
|
687
|
+
id: t.id,
|
|
688
|
+
stars: summarizeStarsAmount(t.stars),
|
|
689
|
+
date: t.date,
|
|
690
|
+
peer: summarizeStarsTransactionPeer(t.peer),
|
|
691
|
+
refund: t.refund,
|
|
692
|
+
pending: t.pending,
|
|
693
|
+
failed: t.failed,
|
|
694
|
+
gift: t.gift,
|
|
695
|
+
reaction: t.reaction,
|
|
696
|
+
title: t.title,
|
|
697
|
+
description: t.description,
|
|
698
|
+
msgId: t.msgId,
|
|
699
|
+
subscriptionPeriod: t.subscriptionPeriod,
|
|
700
|
+
giveawayPostId: t.giveawayPostId,
|
|
701
|
+
transactionDate: t.transactionDate,
|
|
702
|
+
transactionUrl: t.transactionUrl,
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
export function summarizeStarsSubscription(sub) {
|
|
706
|
+
const s = sub;
|
|
707
|
+
const pricing = s.pricing;
|
|
708
|
+
return {
|
|
709
|
+
id: s.id,
|
|
710
|
+
peer: peerToCompact(s.peer),
|
|
711
|
+
untilDate: s.untilDate,
|
|
712
|
+
pricing: { period: pricing.period, amount: pricing.amount.toString() },
|
|
713
|
+
canceled: s.canceled,
|
|
714
|
+
canRefulfill: s.canRefulfill,
|
|
715
|
+
missingBalance: s.missingBalance,
|
|
716
|
+
botCanceled: s.botCanceled,
|
|
717
|
+
chatInviteHash: s.chatInviteHash,
|
|
718
|
+
title: s.title,
|
|
719
|
+
invoiceSlug: s.invoiceSlug,
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
export function summarizeQuickReply(reply) {
|
|
723
|
+
const r = reply;
|
|
724
|
+
return {
|
|
725
|
+
shortcutId: r.shortcutId,
|
|
726
|
+
shortcut: r.shortcut,
|
|
727
|
+
topMessage: r.topMessage,
|
|
728
|
+
count: r.count,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
export function summarizeQuickReplies(result) {
|
|
732
|
+
if (result instanceof Api.messages.QuickRepliesNotModified) {
|
|
733
|
+
return { notModified: true };
|
|
734
|
+
}
|
|
735
|
+
const r = result;
|
|
736
|
+
return { quickReplies: r.quickReplies.map(summarizeQuickReply) };
|
|
737
|
+
}
|
|
738
|
+
export function summarizeQuickReplyMessage(msg) {
|
|
739
|
+
if (msg instanceof Api.MessageEmpty)
|
|
740
|
+
return null;
|
|
741
|
+
const base = msg;
|
|
742
|
+
const fromId = peerToCompact(base.fromId);
|
|
743
|
+
const replyHeader = base.replyTo;
|
|
744
|
+
const replyToMsgId = replyHeader instanceof Api.MessageReplyHeader ? replyHeader.replyToMsgId : undefined;
|
|
745
|
+
if (msg instanceof Api.Message) {
|
|
746
|
+
return {
|
|
747
|
+
id: msg.id,
|
|
748
|
+
date: msg.date,
|
|
749
|
+
text: msg.message ?? "",
|
|
750
|
+
isService: false,
|
|
751
|
+
fromId,
|
|
752
|
+
replyToMsgId,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
if (msg instanceof Api.MessageService) {
|
|
756
|
+
return {
|
|
757
|
+
id: msg.id,
|
|
758
|
+
date: msg.date,
|
|
759
|
+
text: `[${msg.action?.className ?? "service"}]`,
|
|
760
|
+
isService: true,
|
|
761
|
+
fromId,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
export function summarizeQuickReplyMessages(result) {
|
|
767
|
+
if (result instanceof Api.messages.MessagesNotModified) {
|
|
768
|
+
return { notModified: true, count: result.count };
|
|
769
|
+
}
|
|
770
|
+
const rawMessages = result
|
|
771
|
+
.messages;
|
|
772
|
+
const messages = rawMessages.map(summarizeQuickReplyMessage).filter((m) => m !== null);
|
|
773
|
+
const count = result instanceof Api.messages.Messages
|
|
774
|
+
? messages.length
|
|
775
|
+
: result.count;
|
|
776
|
+
return { count, messages };
|
|
777
|
+
}
|
|
778
|
+
export function summarizeStarsStatus(result) {
|
|
779
|
+
const r = result;
|
|
780
|
+
const out = {
|
|
781
|
+
balance: summarizeStarsAmount(r.balance),
|
|
782
|
+
subscriptionsNextOffset: r.subscriptionsNextOffset || undefined,
|
|
783
|
+
subscriptionsMissingBalance: r.subscriptionsMissingBalance?.toString(),
|
|
784
|
+
nextOffset: r.nextOffset || undefined,
|
|
785
|
+
};
|
|
786
|
+
if (r.subscriptions && r.subscriptions.length > 0) {
|
|
787
|
+
out.subscriptions = r.subscriptions.map(summarizeStarsSubscription);
|
|
788
|
+
}
|
|
789
|
+
if (r.history && r.history.length > 0) {
|
|
790
|
+
out.history = r.history.map(summarizeStarsTransaction);
|
|
791
|
+
}
|
|
792
|
+
return out;
|
|
793
|
+
}
|
|
794
|
+
export function summarizeStoryItem(item) {
|
|
795
|
+
if (item instanceof Api.StoryItemDeleted) {
|
|
796
|
+
return { id: item.id, kind: "deleted" };
|
|
797
|
+
}
|
|
798
|
+
if (item instanceof Api.StoryItemSkipped) {
|
|
799
|
+
return {
|
|
800
|
+
id: item.id,
|
|
801
|
+
kind: "skipped",
|
|
802
|
+
date: item.date,
|
|
803
|
+
expireDate: item.expireDate,
|
|
804
|
+
closeFriends: item.closeFriends,
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
const story = item;
|
|
808
|
+
return {
|
|
809
|
+
id: story.id,
|
|
810
|
+
kind: "active",
|
|
811
|
+
date: story.date,
|
|
812
|
+
expireDate: story.expireDate,
|
|
813
|
+
caption: story.caption,
|
|
814
|
+
mediaType: story.media?.className,
|
|
815
|
+
pinned: story.pinned,
|
|
816
|
+
public: story.public,
|
|
817
|
+
closeFriends: story.closeFriends,
|
|
818
|
+
edited: story.edited,
|
|
819
|
+
noforwards: story.noforwards,
|
|
820
|
+
fromId: peerToCompact(story.fromId),
|
|
821
|
+
viewsCount: story.views?.viewsCount,
|
|
822
|
+
reactionsCount: story.views?.reactionsCount,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
export function summarizePeerStories(ps) {
|
|
826
|
+
const peer = peerToCompact(ps.peer);
|
|
827
|
+
if (!peer)
|
|
828
|
+
return null;
|
|
829
|
+
return {
|
|
830
|
+
peer,
|
|
831
|
+
maxReadId: ps.maxReadId,
|
|
832
|
+
stories: (ps.stories ?? []).map(summarizeStoryItem),
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
export function summarizeStoriesById(result) {
|
|
836
|
+
return {
|
|
837
|
+
count: result.count,
|
|
838
|
+
stories: (result.stories ?? []).map(summarizeStoryItem),
|
|
839
|
+
pinnedToTop: result.pinnedToTop,
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
export function summarizeStoryView(view) {
|
|
843
|
+
if (view instanceof Api.StoryViewPublicForward) {
|
|
844
|
+
const msg = view.message;
|
|
845
|
+
const messageId = msg instanceof Api.MessageEmpty ? undefined : msg?.id;
|
|
846
|
+
const peer = msg instanceof Api.MessageEmpty
|
|
847
|
+
? undefined
|
|
848
|
+
: peerToCompact(msg?.peerId);
|
|
849
|
+
return {
|
|
850
|
+
kind: "publicForward",
|
|
851
|
+
messageId,
|
|
852
|
+
peer,
|
|
853
|
+
blocked: view.blocked,
|
|
854
|
+
blockedMyStoriesFrom: view.blockedMyStoriesFrom,
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
if (view instanceof Api.StoryViewPublicRepost) {
|
|
858
|
+
const story = view.story;
|
|
859
|
+
return {
|
|
860
|
+
kind: "publicRepost",
|
|
861
|
+
peer: peerToCompact(view.peerId),
|
|
862
|
+
storyId: story?.id,
|
|
863
|
+
blocked: view.blocked,
|
|
864
|
+
blockedMyStoriesFrom: view.blockedMyStoriesFrom,
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
const v = view;
|
|
868
|
+
return {
|
|
869
|
+
kind: "user",
|
|
870
|
+
userId: v.userId.toString(),
|
|
871
|
+
date: v.date,
|
|
872
|
+
reaction: v.reaction ? reactionToEmoji(v.reaction) : undefined,
|
|
873
|
+
blocked: v.blocked,
|
|
874
|
+
blockedMyStoriesFrom: v.blockedMyStoriesFrom,
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
export function summarizeStoryViewsList(result) {
|
|
878
|
+
const list = result;
|
|
879
|
+
return {
|
|
880
|
+
count: list.count,
|
|
881
|
+
viewsCount: list.viewsCount,
|
|
882
|
+
forwardsCount: list.forwardsCount,
|
|
883
|
+
reactionsCount: list.reactionsCount,
|
|
884
|
+
views: (list.views ?? []).map(summarizeStoryView),
|
|
885
|
+
nextOffset: list.nextOffset,
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
export function summarizeAllStories(result) {
|
|
889
|
+
const stealthMode = result.stealthMode
|
|
890
|
+
? {
|
|
891
|
+
activeUntilDate: result.stealthMode.activeUntilDate,
|
|
892
|
+
cooldownUntilDate: result.stealthMode.cooldownUntilDate,
|
|
893
|
+
}
|
|
894
|
+
: undefined;
|
|
895
|
+
if (result instanceof Api.stories.AllStoriesNotModified) {
|
|
896
|
+
return {
|
|
897
|
+
modified: false,
|
|
898
|
+
state: result.state,
|
|
899
|
+
peerStories: [],
|
|
900
|
+
stealthMode,
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
const all = result;
|
|
904
|
+
const peerStories = (all.peerStories ?? [])
|
|
905
|
+
.map(summarizePeerStories)
|
|
906
|
+
.filter((p) => p !== null);
|
|
907
|
+
return {
|
|
908
|
+
modified: true,
|
|
909
|
+
state: all.state,
|
|
910
|
+
hasMore: all.hasMore,
|
|
911
|
+
count: all.count,
|
|
912
|
+
peerStories,
|
|
913
|
+
stealthMode,
|
|
914
|
+
};
|
|
915
|
+
}
|
|
178
916
|
export class TelegramService {
|
|
179
917
|
client = null;
|
|
180
918
|
apiId;
|
|
@@ -950,6 +1688,9 @@ export class TelegramService {
|
|
|
950
1688
|
*/
|
|
951
1689
|
// biome-ignore lint: GramJS has no proper entity union type
|
|
952
1690
|
async resolvePeer(chatId) {
|
|
1691
|
+
// Normalize '@me' — GramJS only intercepts the plain 'me' string as InputPeerSelf
|
|
1692
|
+
if (chatId === "@me")
|
|
1693
|
+
return "me";
|
|
953
1694
|
// Numeric IDs and @usernames work directly
|
|
954
1695
|
if (/^-?\d+$/.test(chatId) || chatId.startsWith("@"))
|
|
955
1696
|
return chatId;
|
|
@@ -2151,6 +2892,375 @@ export class TelegramService {
|
|
|
2151
2892
|
await this.client?.invoke(new Api.channels.ToggleSlowMode({ channel: entity, seconds }));
|
|
2152
2893
|
}, `setSlowMode ${chatId}`);
|
|
2153
2894
|
}
|
|
2895
|
+
async toggleChannelSignatures(chatId, enabled) {
|
|
2896
|
+
if (!this.client || !this.connected)
|
|
2897
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
2898
|
+
return this.rateLimiter.execute(async () => {
|
|
2899
|
+
const entity = await this.resolveChat(chatId);
|
|
2900
|
+
if (!(entity instanceof Api.Channel)) {
|
|
2901
|
+
throw new Error("Channel signatures are only available for broadcast channels (not groups or supergroups)");
|
|
2902
|
+
}
|
|
2903
|
+
if (entity.megagroup) {
|
|
2904
|
+
throw new Error("Channel signatures are only available for broadcast channels, not supergroups");
|
|
2905
|
+
}
|
|
2906
|
+
await this.client?.invoke(new Api.channels.ToggleSignatures({ channel: entity, signaturesEnabled: enabled }));
|
|
2907
|
+
}, `toggleChannelSignatures ${chatId}`);
|
|
2908
|
+
}
|
|
2909
|
+
async toggleAntiSpam(chatId, enabled) {
|
|
2910
|
+
if (!this.client || !this.connected)
|
|
2911
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
2912
|
+
return this.rateLimiter.execute(async () => {
|
|
2913
|
+
const entity = await this.resolveChat(chatId);
|
|
2914
|
+
if (!(entity instanceof Api.Channel)) {
|
|
2915
|
+
throw new Error("Aggressive anti-spam is only available for supergroups");
|
|
2916
|
+
}
|
|
2917
|
+
if (!entity.megagroup) {
|
|
2918
|
+
throw new Error("Aggressive anti-spam is only available for supergroups, not broadcast channels");
|
|
2919
|
+
}
|
|
2920
|
+
await this.client?.invoke(new Api.channels.ToggleAntiSpam({ channel: entity, enabled }));
|
|
2921
|
+
}, `toggleAntiSpam ${chatId}`);
|
|
2922
|
+
}
|
|
2923
|
+
async toggleForumMode(chatId, enabled) {
|
|
2924
|
+
if (!this.client || !this.connected)
|
|
2925
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
2926
|
+
return this.rateLimiter.execute(async () => {
|
|
2927
|
+
const entity = await this.resolveChat(chatId);
|
|
2928
|
+
if (!(entity instanceof Api.Channel)) {
|
|
2929
|
+
throw new Error("Forum mode is only available for supergroups");
|
|
2930
|
+
}
|
|
2931
|
+
if (!entity.megagroup) {
|
|
2932
|
+
throw new Error("Forum mode is only available for supergroups, not broadcast channels");
|
|
2933
|
+
}
|
|
2934
|
+
await this.client?.invoke(new Api.channels.ToggleForum({ channel: entity, enabled }));
|
|
2935
|
+
}, `toggleForumMode ${chatId}`);
|
|
2936
|
+
}
|
|
2937
|
+
async togglePrehistoryHidden(chatId, hidden) {
|
|
2938
|
+
if (!this.client || !this.connected)
|
|
2939
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
2940
|
+
return this.rateLimiter.execute(async () => {
|
|
2941
|
+
const entity = await this.resolveChat(chatId);
|
|
2942
|
+
if (!(entity instanceof Api.Channel)) {
|
|
2943
|
+
throw new Error("Prehistory visibility is only available for supergroups");
|
|
2944
|
+
}
|
|
2945
|
+
if (!entity.megagroup) {
|
|
2946
|
+
throw new Error("Prehistory visibility is only available for supergroups, not broadcast channels");
|
|
2947
|
+
}
|
|
2948
|
+
await this.client?.invoke(new Api.channels.TogglePreHistoryHidden({ channel: entity, enabled: hidden }));
|
|
2949
|
+
}, `togglePrehistoryHidden ${chatId}`);
|
|
2950
|
+
}
|
|
2951
|
+
async setChatAvailableReactions(chatId, reactions) {
|
|
2952
|
+
if (!this.client || !this.connected)
|
|
2953
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
2954
|
+
return this.rateLimiter.execute(async () => {
|
|
2955
|
+
const entity = await this.resolveChat(chatId);
|
|
2956
|
+
if (!(entity instanceof Api.Channel) && !(entity instanceof Api.Chat)) {
|
|
2957
|
+
throw new Error("Chat reactions can only be configured for groups, supergroups, and channels");
|
|
2958
|
+
}
|
|
2959
|
+
let availableReactions;
|
|
2960
|
+
if (reactions.type === "all") {
|
|
2961
|
+
availableReactions = new Api.ChatReactionsAll({ allowCustom: reactions.allowCustom });
|
|
2962
|
+
}
|
|
2963
|
+
else if (reactions.type === "none") {
|
|
2964
|
+
availableReactions = new Api.ChatReactionsNone();
|
|
2965
|
+
}
|
|
2966
|
+
else {
|
|
2967
|
+
if (reactions.emoji.length === 0) {
|
|
2968
|
+
throw new Error('reactions.emoji must be non-empty when type is "some" (use type:"none" to disable)');
|
|
2969
|
+
}
|
|
2970
|
+
availableReactions = new Api.ChatReactionsSome({
|
|
2971
|
+
reactions: reactions.emoji.map((emoticon) => new Api.ReactionEmoji({ emoticon })),
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
await this.client?.invoke(new Api.messages.SetChatAvailableReactions({ peer: entity, availableReactions }));
|
|
2975
|
+
}, `setChatAvailableReactions ${chatId}`);
|
|
2976
|
+
}
|
|
2977
|
+
async approveChatJoinRequest(chatId, userId, approved) {
|
|
2978
|
+
if (!this.client || !this.connected)
|
|
2979
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
2980
|
+
return this.rateLimiter.execute(async () => {
|
|
2981
|
+
const entity = await this.resolveChat(chatId);
|
|
2982
|
+
if (!(entity instanceof Api.Channel)) {
|
|
2983
|
+
throw new Error("Join request approval is only supported for supergroups and channels, not basic groups");
|
|
2984
|
+
}
|
|
2985
|
+
const user = await this.client?.getEntity(userId);
|
|
2986
|
+
if (!(user instanceof Api.User)) {
|
|
2987
|
+
throw new Error("Target is not a user");
|
|
2988
|
+
}
|
|
2989
|
+
const inputUser = new Api.InputUser({ userId: user.id, accessHash: user.accessHash ?? bigInt.zero });
|
|
2990
|
+
await this.client?.invoke(new Api.messages.HideChatJoinRequest({ peer: entity, userId: inputUser, approved }));
|
|
2991
|
+
}, `approveChatJoinRequest ${chatId}/${userId}`);
|
|
2992
|
+
}
|
|
2993
|
+
async getInlineBotResults(bot, chatId, query, offset) {
|
|
2994
|
+
if (!this.client || !this.connected)
|
|
2995
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
2996
|
+
return this.rateLimiter.execute(async () => {
|
|
2997
|
+
const peer = await this.resolveChat(chatId);
|
|
2998
|
+
const botEntity = await this.client?.getEntity(bot);
|
|
2999
|
+
if (!(botEntity instanceof Api.User)) {
|
|
3000
|
+
throw new Error(`'${bot}' is not a user/bot`);
|
|
3001
|
+
}
|
|
3002
|
+
if (!botEntity.bot) {
|
|
3003
|
+
throw new Error(`'${bot}' is not a bot (inline queries require a bot account)`);
|
|
3004
|
+
}
|
|
3005
|
+
const inputBot = new Api.InputUser({
|
|
3006
|
+
userId: botEntity.id,
|
|
3007
|
+
accessHash: botEntity.accessHash ?? bigInt.zero,
|
|
3008
|
+
});
|
|
3009
|
+
const result = await this.client?.invoke(new Api.messages.GetInlineBotResults({
|
|
3010
|
+
bot: inputBot,
|
|
3011
|
+
peer,
|
|
3012
|
+
query,
|
|
3013
|
+
offset: offset ?? "",
|
|
3014
|
+
}));
|
|
3015
|
+
if (!result)
|
|
3016
|
+
throw new Error("No inline bot results returned");
|
|
3017
|
+
return {
|
|
3018
|
+
queryId: result.queryId.toString(),
|
|
3019
|
+
nextOffset: result.nextOffset,
|
|
3020
|
+
cacheTime: result.cacheTime,
|
|
3021
|
+
gallery: result.gallery === true,
|
|
3022
|
+
results: result.results.map((r) => {
|
|
3023
|
+
if (r instanceof Api.BotInlineResult) {
|
|
3024
|
+
return { id: r.id, type: r.type, title: r.title, description: r.description, url: r.url };
|
|
3025
|
+
}
|
|
3026
|
+
const mr = r;
|
|
3027
|
+
return { id: mr.id, type: mr.type, title: mr.title, description: mr.description };
|
|
3028
|
+
}),
|
|
3029
|
+
};
|
|
3030
|
+
}, `getInlineBotResults via ${bot}`);
|
|
3031
|
+
}
|
|
3032
|
+
async sendInlineBotResult(chatId, queryId, resultId, options) {
|
|
3033
|
+
if (!this.client || !this.connected)
|
|
3034
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3035
|
+
return this.rateLimiter.execute(async () => {
|
|
3036
|
+
const peer = await this.resolveChat(chatId);
|
|
3037
|
+
const randomId = bigInt(Math.floor(Math.random() * 1e15));
|
|
3038
|
+
const replyTo = options?.replyTo ? new Api.InputReplyToMessage({ replyToMsgId: options.replyTo }) : undefined;
|
|
3039
|
+
const result = await this.client?.invoke(new Api.messages.SendInlineBotResult({
|
|
3040
|
+
peer,
|
|
3041
|
+
queryId: bigInt(queryId),
|
|
3042
|
+
id: resultId,
|
|
3043
|
+
randomId,
|
|
3044
|
+
...(replyTo ? { replyTo } : {}),
|
|
3045
|
+
...(options?.silent ? { silent: true } : {}),
|
|
3046
|
+
...(options?.hideVia ? { hideVia: true } : {}),
|
|
3047
|
+
...(options?.clearDraft ? { clearDraft: true } : {}),
|
|
3048
|
+
}));
|
|
3049
|
+
if (!result)
|
|
3050
|
+
throw new Error("No response from SendInlineBotResult");
|
|
3051
|
+
if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
|
|
3052
|
+
for (const update of result.updates) {
|
|
3053
|
+
if (update instanceof Api.UpdateMessageID && update.randomId?.equals(randomId)) {
|
|
3054
|
+
return { messageId: update.id };
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
if (result instanceof Api.UpdateShortSentMessage) {
|
|
3059
|
+
return { messageId: result.id };
|
|
3060
|
+
}
|
|
3061
|
+
return { messageId: 0 };
|
|
3062
|
+
}, `sendInlineBotResult ${resultId} to ${chatId}`);
|
|
3063
|
+
}
|
|
3064
|
+
async pressButton(chatId, messageId, options) {
|
|
3065
|
+
if (!this.client || !this.connected)
|
|
3066
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3067
|
+
return this.rateLimiter.execute(async () => {
|
|
3068
|
+
const entity = await this.resolveChat(chatId);
|
|
3069
|
+
let data;
|
|
3070
|
+
if (options.buttonIndex) {
|
|
3071
|
+
const { row, column } = options.buttonIndex;
|
|
3072
|
+
const messages = await this.client?.getMessages(entity, { ids: [messageId] });
|
|
3073
|
+
const msg = messages?.[0];
|
|
3074
|
+
if (!msg)
|
|
3075
|
+
throw new Error(`Message ${messageId} not found in ${chatId}`);
|
|
3076
|
+
const markup = msg.replyMarkup;
|
|
3077
|
+
if (!markup)
|
|
3078
|
+
throw new Error(`Message ${messageId} has no reply markup`);
|
|
3079
|
+
if (!(markup instanceof Api.ReplyInlineMarkup)) {
|
|
3080
|
+
throw new Error(`Message ${messageId} reply markup is ${markup.className} (only ReplyInlineMarkup has callable buttons)`);
|
|
3081
|
+
}
|
|
3082
|
+
const rowEntry = markup.rows[row];
|
|
3083
|
+
if (!rowEntry)
|
|
3084
|
+
throw new Error(`Row ${row} out of bounds (message has ${markup.rows.length} rows)`);
|
|
3085
|
+
const button = rowEntry.buttons[column];
|
|
3086
|
+
if (!button) {
|
|
3087
|
+
throw new Error(`Column ${column} out of bounds in row ${row} (row has ${rowEntry.buttons.length} buttons)`);
|
|
3088
|
+
}
|
|
3089
|
+
if (!(button instanceof Api.KeyboardButtonCallback)) {
|
|
3090
|
+
throw new Error(`Button at (${row},${column}) is ${button.className}, not callable — use the appropriate tool for URL/switch-inline/game buttons`);
|
|
3091
|
+
}
|
|
3092
|
+
if (button.requiresPassword) {
|
|
3093
|
+
throw new Error(`Button at (${row},${column}) requires 2FA password confirmation — not supported by telegram-press-button`);
|
|
3094
|
+
}
|
|
3095
|
+
data = Buffer.from(button.data);
|
|
3096
|
+
}
|
|
3097
|
+
else if (options.data !== undefined) {
|
|
3098
|
+
data = Buffer.from(options.data, "base64");
|
|
3099
|
+
}
|
|
3100
|
+
else {
|
|
3101
|
+
throw new Error("Either buttonIndex or data must be provided");
|
|
3102
|
+
}
|
|
3103
|
+
const answer = await this.client?.invoke(new Api.messages.GetBotCallbackAnswer({
|
|
3104
|
+
peer: entity,
|
|
3105
|
+
msgId: messageId,
|
|
3106
|
+
data,
|
|
3107
|
+
}));
|
|
3108
|
+
if (!answer)
|
|
3109
|
+
throw new Error("No callback answer returned");
|
|
3110
|
+
return {
|
|
3111
|
+
alert: answer.alert,
|
|
3112
|
+
hasUrl: answer.hasUrl,
|
|
3113
|
+
nativeUi: answer.nativeUi,
|
|
3114
|
+
message: answer.message,
|
|
3115
|
+
url: answer.url,
|
|
3116
|
+
cacheTime: answer.cacheTime,
|
|
3117
|
+
};
|
|
3118
|
+
}, `pressButton ${chatId}/${messageId}`);
|
|
3119
|
+
}
|
|
3120
|
+
async getMessageButtons(chatId, messageId) {
|
|
3121
|
+
if (!this.client || !this.connected)
|
|
3122
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3123
|
+
return this.rateLimiter.execute(async () => {
|
|
3124
|
+
const entity = await this.resolveChat(chatId);
|
|
3125
|
+
const messages = await this.client?.getMessages(entity, { ids: [messageId] });
|
|
3126
|
+
const msg = messages?.[0];
|
|
3127
|
+
if (!msg)
|
|
3128
|
+
throw new Error(`Message ${messageId} not found in ${chatId}`);
|
|
3129
|
+
const markup = msg.replyMarkup;
|
|
3130
|
+
if (!markup) {
|
|
3131
|
+
return { markupType: "none", buttons: [] };
|
|
3132
|
+
}
|
|
3133
|
+
if (!(markup instanceof Api.ReplyInlineMarkup) && !(markup instanceof Api.ReplyKeyboardMarkup)) {
|
|
3134
|
+
return { markupType: markup.className, buttons: [] };
|
|
3135
|
+
}
|
|
3136
|
+
const buttons = [];
|
|
3137
|
+
markup.rows.forEach((rowEntry, row) => {
|
|
3138
|
+
rowEntry.buttons.forEach((button, col) => {
|
|
3139
|
+
buttons.push(describeKeyboardButton(button, row, col));
|
|
3140
|
+
});
|
|
3141
|
+
});
|
|
3142
|
+
return { markupType: markup.className, buttons };
|
|
3143
|
+
}, `getMessageButtons ${chatId}/${messageId}`);
|
|
3144
|
+
}
|
|
3145
|
+
async getBroadcastStats(chatId, options) {
|
|
3146
|
+
if (!this.client || !this.connected)
|
|
3147
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3148
|
+
return this.rateLimiter.execute(async () => {
|
|
3149
|
+
const entity = await this.resolveChat(chatId);
|
|
3150
|
+
if (!(entity instanceof Api.Channel)) {
|
|
3151
|
+
throw new Error("Broadcast stats are only available for channels");
|
|
3152
|
+
}
|
|
3153
|
+
if (entity.megagroup) {
|
|
3154
|
+
throw new Error("Broadcast stats are only available for broadcast channels, not supergroups (use telegram-get-megagroup-stats)");
|
|
3155
|
+
}
|
|
3156
|
+
let result;
|
|
3157
|
+
try {
|
|
3158
|
+
const response = await this.client?.invoke(new Api.stats.GetBroadcastStats({ channel: entity, dark: options?.dark }));
|
|
3159
|
+
if (!response) {
|
|
3160
|
+
throw new Error("channel has no stats (may require Telegram Premium admin)");
|
|
3161
|
+
}
|
|
3162
|
+
result = response;
|
|
3163
|
+
}
|
|
3164
|
+
catch (e) {
|
|
3165
|
+
const msg = e.message ?? String(e);
|
|
3166
|
+
if (/CHAT_ADMIN_REQUIRED|ADMIN_RANK_INVALID/i.test(msg)) {
|
|
3167
|
+
throw new Error("Access denied: channel stats require admin rights (and may require Telegram Premium)");
|
|
3168
|
+
}
|
|
3169
|
+
if (/STATS_UNAVAILABLE|BROADCAST_REQUIRED|PARTICIPANTS_TOO_FEW/i.test(msg)) {
|
|
3170
|
+
throw new Error("channel has no stats (may require Telegram Premium admin)");
|
|
3171
|
+
}
|
|
3172
|
+
throw e;
|
|
3173
|
+
}
|
|
3174
|
+
return summarizeBroadcastStats(result, options?.includeGraphs === true);
|
|
3175
|
+
}, `getBroadcastStats ${chatId}`);
|
|
3176
|
+
}
|
|
3177
|
+
async getMegagroupStats(chatId, options) {
|
|
3178
|
+
if (!this.client || !this.connected)
|
|
3179
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3180
|
+
return this.rateLimiter.execute(async () => {
|
|
3181
|
+
const entity = await this.resolveChat(chatId);
|
|
3182
|
+
if (!(entity instanceof Api.Channel)) {
|
|
3183
|
+
throw new Error("Megagroup stats are only available for supergroups");
|
|
3184
|
+
}
|
|
3185
|
+
if (!entity.megagroup) {
|
|
3186
|
+
throw new Error("Megagroup stats are only available for supergroups, not broadcast channels (use telegram-get-broadcast-stats)");
|
|
3187
|
+
}
|
|
3188
|
+
let result;
|
|
3189
|
+
try {
|
|
3190
|
+
const response = await this.client?.invoke(new Api.stats.GetMegagroupStats({ channel: entity, dark: options?.dark }));
|
|
3191
|
+
if (!response) {
|
|
3192
|
+
throw new Error("supergroup has no stats yet (needs more activity/members)");
|
|
3193
|
+
}
|
|
3194
|
+
result = response;
|
|
3195
|
+
}
|
|
3196
|
+
catch (e) {
|
|
3197
|
+
const msg = e.message ?? String(e);
|
|
3198
|
+
if (/CHAT_ADMIN_REQUIRED|ADMIN_RANK_INVALID/i.test(msg)) {
|
|
3199
|
+
throw new Error("Access denied: supergroup stats require admin rights");
|
|
3200
|
+
}
|
|
3201
|
+
if (/STATS_UNAVAILABLE|PARTICIPANTS_TOO_FEW|MEGAGROUP_REQUIRED/i.test(msg)) {
|
|
3202
|
+
throw new Error("supergroup has no stats yet (needs more activity/members)");
|
|
3203
|
+
}
|
|
3204
|
+
throw e;
|
|
3205
|
+
}
|
|
3206
|
+
return summarizeMegagroupStats(result, options?.includeGraphs === true);
|
|
3207
|
+
}, `getMegagroupStats ${chatId}`, { throwOnFloodWait: true });
|
|
3208
|
+
}
|
|
3209
|
+
async getUpdatesState() {
|
|
3210
|
+
if (!this.client || !this.connected)
|
|
3211
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3212
|
+
return this.rateLimiter.execute(async () => {
|
|
3213
|
+
const state = await this.client?.invoke(new Api.updates.GetState());
|
|
3214
|
+
if (!state)
|
|
3215
|
+
throw new Error("updates.GetState returned no state");
|
|
3216
|
+
return {
|
|
3217
|
+
pts: state.pts,
|
|
3218
|
+
qts: state.qts,
|
|
3219
|
+
date: state.date,
|
|
3220
|
+
seq: state.seq,
|
|
3221
|
+
unreadCount: state.unreadCount,
|
|
3222
|
+
};
|
|
3223
|
+
}, "getUpdatesState");
|
|
3224
|
+
}
|
|
3225
|
+
async getUpdates(cursor) {
|
|
3226
|
+
if (!this.client || !this.connected)
|
|
3227
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3228
|
+
const ptsLimit = Math.min(cursor.ptsLimit ?? 100, 1000);
|
|
3229
|
+
const ptsTotalLimit = Math.min(cursor.ptsTotalLimit ?? 1000, 1000);
|
|
3230
|
+
return this.rateLimiter.execute(async () => {
|
|
3231
|
+
const diff = await this.client?.invoke(new Api.updates.GetDifference({
|
|
3232
|
+
pts: cursor.pts,
|
|
3233
|
+
date: cursor.date,
|
|
3234
|
+
qts: cursor.qts,
|
|
3235
|
+
ptsLimit,
|
|
3236
|
+
ptsTotalLimit,
|
|
3237
|
+
}));
|
|
3238
|
+
if (!diff)
|
|
3239
|
+
throw new Error("updates.GetDifference returned nothing");
|
|
3240
|
+
return summarizeUpdatesDifference(diff, cursor);
|
|
3241
|
+
}, "getUpdates");
|
|
3242
|
+
}
|
|
3243
|
+
async getChannelUpdates(chatId, cursor) {
|
|
3244
|
+
if (!this.client || !this.connected)
|
|
3245
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3246
|
+
const limit = Math.min(cursor.limit ?? 100, 1_000);
|
|
3247
|
+
return this.rateLimiter.execute(async () => {
|
|
3248
|
+
const entity = await this.resolveChat(chatId);
|
|
3249
|
+
if (!(entity instanceof Api.Channel)) {
|
|
3250
|
+
throw new Error("Channel updates are only available for channels/supergroups");
|
|
3251
|
+
}
|
|
3252
|
+
const diff = await this.client?.invoke(new Api.updates.GetChannelDifference({
|
|
3253
|
+
channel: entity,
|
|
3254
|
+
filter: new Api.ChannelMessagesFilterEmpty(),
|
|
3255
|
+
pts: cursor.pts,
|
|
3256
|
+
limit,
|
|
3257
|
+
force: cursor.force,
|
|
3258
|
+
}));
|
|
3259
|
+
if (!diff)
|
|
3260
|
+
throw new Error("updates.GetChannelDifference returned nothing");
|
|
3261
|
+
return summarizeChannelDifference(diff, entity.id.toString(), cursor.pts);
|
|
3262
|
+
}, `getChannelUpdates ${chatId}`);
|
|
3263
|
+
}
|
|
2154
3264
|
async createForumTopic(chatId, title, iconColor, iconEmojiId) {
|
|
2155
3265
|
if (!this.client || !this.connected)
|
|
2156
3266
|
throw new Error(NOT_CONNECTED_ERROR);
|
|
@@ -2690,4 +3800,216 @@ export class TelegramService {
|
|
|
2690
3800
|
emoji: emojiMap.get(doc.id.toString()) || "",
|
|
2691
3801
|
}));
|
|
2692
3802
|
}
|
|
3803
|
+
async getAllStories(options) {
|
|
3804
|
+
if (!this.client || !this.connected)
|
|
3805
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3806
|
+
return this.rateLimiter.execute(async () => {
|
|
3807
|
+
const response = await this.client?.invoke(new Api.stories.GetAllStories({
|
|
3808
|
+
next: options?.next,
|
|
3809
|
+
hidden: options?.hidden,
|
|
3810
|
+
state: options?.state,
|
|
3811
|
+
}));
|
|
3812
|
+
if (!response)
|
|
3813
|
+
throw new Error("stories.GetAllStories returned nothing");
|
|
3814
|
+
return summarizeAllStories(response);
|
|
3815
|
+
}, "getAllStories");
|
|
3816
|
+
}
|
|
3817
|
+
async getPeerStories(chatId) {
|
|
3818
|
+
if (!this.client || !this.connected)
|
|
3819
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3820
|
+
const peer = await this.resolvePeer(chatId);
|
|
3821
|
+
return this.rateLimiter.execute(async () => {
|
|
3822
|
+
const response = await this.client?.invoke(new Api.stories.GetPeerStories({ peer }));
|
|
3823
|
+
if (!response)
|
|
3824
|
+
throw new Error("stories.GetPeerStories returned nothing");
|
|
3825
|
+
return summarizePeerStories(response.stories);
|
|
3826
|
+
}, `getPeerStories ${chatId}`);
|
|
3827
|
+
}
|
|
3828
|
+
async getStoriesById(chatId, ids) {
|
|
3829
|
+
if (!this.client || !this.connected)
|
|
3830
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3831
|
+
const peer = await this.resolvePeer(chatId);
|
|
3832
|
+
return this.rateLimiter.execute(async () => {
|
|
3833
|
+
const response = await this.client?.invoke(new Api.stories.GetStoriesByID({ peer, id: ids }));
|
|
3834
|
+
if (!response)
|
|
3835
|
+
throw new Error("stories.GetStoriesByID returned nothing");
|
|
3836
|
+
return summarizeStoriesById(response);
|
|
3837
|
+
}, `getStoriesById ${chatId}`);
|
|
3838
|
+
}
|
|
3839
|
+
async getStoryViewsList(chatId, options) {
|
|
3840
|
+
if (!this.client || !this.connected)
|
|
3841
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3842
|
+
const peer = await this.resolvePeer(chatId);
|
|
3843
|
+
return this.rateLimiter.execute(async () => {
|
|
3844
|
+
const response = await this.client?.invoke(new Api.stories.GetStoryViewsList({
|
|
3845
|
+
peer,
|
|
3846
|
+
id: options.id,
|
|
3847
|
+
q: options.q,
|
|
3848
|
+
justContacts: options.justContacts,
|
|
3849
|
+
reactionsFirst: options.reactionsFirst,
|
|
3850
|
+
forwardsFirst: options.forwardsFirst,
|
|
3851
|
+
offset: options.offset ?? "",
|
|
3852
|
+
limit: options.limit ?? 50,
|
|
3853
|
+
}));
|
|
3854
|
+
if (!response)
|
|
3855
|
+
throw new Error("stories.GetStoryViewsList returned nothing");
|
|
3856
|
+
return summarizeStoryViewsList(response);
|
|
3857
|
+
}, `getStoryViewsList ${chatId}/${options.id}`);
|
|
3858
|
+
}
|
|
3859
|
+
async getMyBoosts() {
|
|
3860
|
+
if (!this.client || !this.connected)
|
|
3861
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3862
|
+
return this.rateLimiter.execute(async () => {
|
|
3863
|
+
const response = await this.client?.invoke(new Api.premium.GetMyBoosts());
|
|
3864
|
+
if (!response)
|
|
3865
|
+
throw new Error("premium.GetMyBoosts returned nothing");
|
|
3866
|
+
return summarizeMyBoosts(response);
|
|
3867
|
+
}, "getMyBoosts");
|
|
3868
|
+
}
|
|
3869
|
+
async getBoostsStatus(chatId) {
|
|
3870
|
+
if (!this.client || !this.connected)
|
|
3871
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3872
|
+
const peer = await this.resolvePeer(chatId);
|
|
3873
|
+
return this.rateLimiter.execute(async () => {
|
|
3874
|
+
const response = await this.client?.invoke(new Api.premium.GetBoostsStatus({ peer }));
|
|
3875
|
+
if (!response)
|
|
3876
|
+
throw new Error("premium.GetBoostsStatus returned nothing");
|
|
3877
|
+
return summarizeBoostsStatus(response);
|
|
3878
|
+
}, `getBoostsStatus ${chatId}`);
|
|
3879
|
+
}
|
|
3880
|
+
async getBoostsList(chatId, options = {}) {
|
|
3881
|
+
if (!this.client || !this.connected)
|
|
3882
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3883
|
+
const peer = await this.resolvePeer(chatId);
|
|
3884
|
+
return this.rateLimiter.execute(async () => {
|
|
3885
|
+
const response = await this.client?.invoke(new Api.premium.GetBoostsList({
|
|
3886
|
+
peer,
|
|
3887
|
+
gifts: options.gifts,
|
|
3888
|
+
offset: options.offset ?? "",
|
|
3889
|
+
limit: options.limit ?? 50,
|
|
3890
|
+
}));
|
|
3891
|
+
if (!response)
|
|
3892
|
+
throw new Error("premium.GetBoostsList returned nothing");
|
|
3893
|
+
return summarizeBoostsList(response);
|
|
3894
|
+
}, `getBoostsList ${chatId}`);
|
|
3895
|
+
}
|
|
3896
|
+
async getBusinessChatLinks() {
|
|
3897
|
+
if (!this.client || !this.connected)
|
|
3898
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3899
|
+
return this.rateLimiter.execute(async () => {
|
|
3900
|
+
const response = await this.client?.invoke(new Api.account.GetBusinessChatLinks());
|
|
3901
|
+
if (!response)
|
|
3902
|
+
throw new Error("account.GetBusinessChatLinks returned nothing");
|
|
3903
|
+
return summarizeBusinessChatLinks(response);
|
|
3904
|
+
}, "getBusinessChatLinks");
|
|
3905
|
+
}
|
|
3906
|
+
async getGroupCall(chatId, options = {}) {
|
|
3907
|
+
if (!this.client || !this.connected)
|
|
3908
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3909
|
+
return this.rateLimiter.execute(async () => {
|
|
3910
|
+
const call = await this.resolveInputGroupCall(chatId);
|
|
3911
|
+
const response = await this.client?.invoke(new Api.phone.GetGroupCall({ call, limit: options.limit ?? 0 }));
|
|
3912
|
+
if (!response)
|
|
3913
|
+
throw new Error("phone.GetGroupCall returned nothing");
|
|
3914
|
+
return summarizeGroupCall(response);
|
|
3915
|
+
}, `getGroupCall ${chatId}`);
|
|
3916
|
+
}
|
|
3917
|
+
async getGroupCallParticipants(chatId, options = {}) {
|
|
3918
|
+
if (!this.client || !this.connected)
|
|
3919
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3920
|
+
return this.rateLimiter.execute(async () => {
|
|
3921
|
+
const call = await this.resolveInputGroupCall(chatId);
|
|
3922
|
+
const ids = [];
|
|
3923
|
+
for (const id of options.ids ?? []) {
|
|
3924
|
+
ids.push(await this.resolvePeer(id));
|
|
3925
|
+
}
|
|
3926
|
+
const response = await this.client?.invoke(new Api.phone.GetGroupParticipants({
|
|
3927
|
+
call,
|
|
3928
|
+
ids,
|
|
3929
|
+
sources: options.sources ?? [],
|
|
3930
|
+
offset: options.offset ?? "",
|
|
3931
|
+
limit: options.limit ?? 100,
|
|
3932
|
+
}));
|
|
3933
|
+
if (!response)
|
|
3934
|
+
throw new Error("phone.GetGroupParticipants returned nothing");
|
|
3935
|
+
return summarizeGroupCallParticipants(response);
|
|
3936
|
+
}, `getGroupCallParticipants ${chatId}`);
|
|
3937
|
+
}
|
|
3938
|
+
async getStarsStatus(chatId) {
|
|
3939
|
+
if (!this.client || !this.connected)
|
|
3940
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3941
|
+
const peer = await this.resolvePeer(chatId);
|
|
3942
|
+
return this.rateLimiter.execute(async () => {
|
|
3943
|
+
const response = await this.client?.invoke(new Api.payments.GetStarsStatus({ peer }));
|
|
3944
|
+
if (!response)
|
|
3945
|
+
throw new Error("payments.GetStarsStatus returned nothing");
|
|
3946
|
+
return summarizeStarsStatus(response);
|
|
3947
|
+
}, `getStarsStatus ${chatId}`);
|
|
3948
|
+
}
|
|
3949
|
+
async getStarsTransactions(chatId, options = {}) {
|
|
3950
|
+
if (!this.client || !this.connected)
|
|
3951
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3952
|
+
const peer = await this.resolvePeer(chatId);
|
|
3953
|
+
return this.rateLimiter.execute(async () => {
|
|
3954
|
+
const response = await this.client?.invoke(new Api.payments.GetStarsTransactions({
|
|
3955
|
+
peer,
|
|
3956
|
+
inbound: options.inbound,
|
|
3957
|
+
outbound: options.outbound,
|
|
3958
|
+
ascending: options.ascending,
|
|
3959
|
+
subscriptionId: options.subscriptionId,
|
|
3960
|
+
offset: options.offset ?? "",
|
|
3961
|
+
limit: options.limit ?? 50,
|
|
3962
|
+
}));
|
|
3963
|
+
if (!response)
|
|
3964
|
+
throw new Error("payments.GetStarsTransactions returned nothing");
|
|
3965
|
+
return summarizeStarsStatus(response);
|
|
3966
|
+
}, `getStarsTransactions ${chatId}`);
|
|
3967
|
+
}
|
|
3968
|
+
async getQuickReplies(hash) {
|
|
3969
|
+
if (!this.client || !this.connected)
|
|
3970
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3971
|
+
return this.rateLimiter.execute(async () => {
|
|
3972
|
+
const response = await this.client?.invoke(new Api.messages.GetQuickReplies({ hash: hash ? bigInt(hash) : bigInt(0) }));
|
|
3973
|
+
if (!response)
|
|
3974
|
+
throw new Error("messages.GetQuickReplies returned nothing");
|
|
3975
|
+
return summarizeQuickReplies(response);
|
|
3976
|
+
}, "getQuickReplies");
|
|
3977
|
+
}
|
|
3978
|
+
async getQuickReplyMessages(shortcutId, options = {}) {
|
|
3979
|
+
if (!this.client || !this.connected)
|
|
3980
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3981
|
+
return this.rateLimiter.execute(async () => {
|
|
3982
|
+
const response = await this.client?.invoke(new Api.messages.GetQuickReplyMessages({
|
|
3983
|
+
shortcutId,
|
|
3984
|
+
id: options.ids,
|
|
3985
|
+
hash: options.hash ? bigInt(options.hash) : bigInt(0),
|
|
3986
|
+
}));
|
|
3987
|
+
if (!response)
|
|
3988
|
+
throw new Error("messages.GetQuickReplyMessages returned nothing");
|
|
3989
|
+
return summarizeQuickReplyMessages(response);
|
|
3990
|
+
}, `getQuickReplyMessages ${shortcutId}`);
|
|
3991
|
+
}
|
|
3992
|
+
async resolveInputGroupCall(chatId) {
|
|
3993
|
+
const entity = await this.resolveChat(chatId);
|
|
3994
|
+
let call;
|
|
3995
|
+
if (entity instanceof Api.Channel) {
|
|
3996
|
+
const full = await this.client?.invoke(new Api.channels.GetFullChannel({ channel: entity }));
|
|
3997
|
+
if (full?.fullChat instanceof Api.ChannelFull) {
|
|
3998
|
+
call = full.fullChat.call;
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
else if (entity instanceof Api.Chat) {
|
|
4002
|
+
const full = await this.client?.invoke(new Api.messages.GetFullChat({ chatId: entity.id }));
|
|
4003
|
+
if (full?.fullChat instanceof Api.ChatFull) {
|
|
4004
|
+
call = full.fullChat.call;
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
else {
|
|
4008
|
+
throw new Error("Group calls are only available for groups/supergroups/channels");
|
|
4009
|
+
}
|
|
4010
|
+
if (!call) {
|
|
4011
|
+
throw new Error(`No active group call in chat ${chatId}`);
|
|
4012
|
+
}
|
|
4013
|
+
return call;
|
|
4014
|
+
}
|
|
2693
4015
|
}
|