@overpod/mcp-telegram 1.24.1 → 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 +67 -1
- package/README.md +45 -13
- package/dist/__tests__/admin-log.test.d.ts +1 -0
- package/dist/__tests__/admin-log.test.js +41 -0
- 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__/reactions.test.d.ts +1 -0
- package/dist/__tests__/reactions.test.js +23 -0
- package/dist/__tests__/set-chat-permissions-merge.test.d.ts +1 -0
- package/dist/__tests__/set-chat-permissions-merge.test.js +107 -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 +711 -2
- package/dist/telegram-client.js +2167 -99
- package/dist/tools/account.js +108 -0
- package/dist/tools/boosts.d.ts +3 -0
- package/dist/tools/boosts.js +65 -0
- package/dist/tools/chats.js +388 -1
- 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/media.js +120 -1
- package/dist/tools/messages.js +379 -0
- package/dist/tools/quick-replies.d.ts +4 -0
- package/dist/tools/quick-replies.js +58 -0
- package/dist/tools/reactions.js +102 -1
- 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/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,73 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [1.26.0] - 2026-04-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Phase 2 — Admin Toggles, Customization, Stats (8 tools)**
|
|
12
|
+
- `telegram-toggle-channel-signatures` — toggle post signatures on a channel
|
|
13
|
+
- `telegram-toggle-anti-spam` — toggle native anti-spam in a supergroup (`ban_users` admin)
|
|
14
|
+
- `telegram-toggle-forum-mode` — enable/disable forum mode on a supergroup (disable requires `confirm: true` — destructive, removes all topics)
|
|
15
|
+
- `telegram-approve-join-request` — approve or reject a single chat join request
|
|
16
|
+
- `telegram-toggle-prehistory-hidden` — show/hide pre-history for new supergroup members
|
|
17
|
+
- `telegram-set-chat-reactions` — set allowed reactions on a chat (`all` / `some` / `none`)
|
|
18
|
+
- `telegram-get-broadcast-stats` — channel stats overview (Premium admin may be required; pass `includeGraphs: true` for raw series)
|
|
19
|
+
- `telegram-get-megagroup-stats` — supergroup stats overview (rate-limited by Telegram to ~1 req/30 min per channel)
|
|
20
|
+
- **Phase 3 — Inline Bots, Buttons, Real-Time Updates (7 tools)**
|
|
21
|
+
- `telegram-inline-query` — query an inline bot in a chat context (queryId TTL ≈ 1 min)
|
|
22
|
+
- `telegram-inline-query-send` — send an inline bot result by queryId + result id
|
|
23
|
+
- `telegram-press-button` — press a callback button on a message by row/col or raw data
|
|
24
|
+
- `telegram-get-message-buttons` — list a message's reply-markup buttons with indices and types
|
|
25
|
+
- `telegram-get-state` — initialize a polling cursor (`pts`, `qts`, `date`, `seq`)
|
|
26
|
+
- `telegram-get-updates` — fetch global updates since a known cursor via `updates.GetDifference`; returns `{newMessages, deletedMessageIds, otherUpdates, state, isFinal}` and surfaces `DifferenceTooLong` as a history-fallback hint
|
|
27
|
+
- `telegram-get-channel-updates` — per-channel polling via `updates.GetChannelDifference`
|
|
28
|
+
- Cursors are client-owned (stateless server) — the agent stores `{pts, qts, date}` between calls
|
|
29
|
+
- **Phase 4 ship — Stories, Boosts, Business (8 tools)**
|
|
30
|
+
- `telegram-get-all-stories` — list stories across peers with pagination state
|
|
31
|
+
- `telegram-get-peer-stories` — list stories posted by one peer (compact, media refs only)
|
|
32
|
+
- `telegram-get-stories-by-id` — fetch specific story items by id
|
|
33
|
+
- `telegram-get-story-views` — list views on your own stories (Premium for full stats)
|
|
34
|
+
- `telegram-get-my-boosts` — list boost slots assigned by your account
|
|
35
|
+
- `telegram-get-boosts-status` — boost status for a channel/supergroup
|
|
36
|
+
- `telegram-get-boosts-list` — list boosters for a channel (admin)
|
|
37
|
+
- `telegram-get-business-chat-links` — list your Telegram Business chat links
|
|
38
|
+
- **Phase 4 opt-in (env-gated, 6 tools)** — registered only when the corresponding flag is set:
|
|
39
|
+
- `MCP_TELEGRAM_ENABLE_GROUP_CALLS=1` → `telegram-get-group-call`, `telegram-get-group-call-participants`
|
|
40
|
+
- `MCP_TELEGRAM_ENABLE_STARS=1` → `telegram-get-stars-status`, `telegram-get-stars-transactions`
|
|
41
|
+
- `MCP_TELEGRAM_ENABLE_QUICK_REPLIES=1` → `telegram-get-quick-replies`, `telegram-get-quick-reply-messages`
|
|
42
|
+
|
|
43
|
+
## [1.25.0] - 2026-04-20
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- **Scheduled messages** — `telegram-get-scheduled`, `telegram-delete-scheduled`
|
|
47
|
+
- **Threads & replies** — `telegram-get-replies` for channel post comments
|
|
48
|
+
- **Message links** — `telegram-get-message-link` returns public t.me URL for a message
|
|
49
|
+
- **Mentions & unread reactions** — `telegram-get-unread-mentions`, `telegram-get-unread-reactions`
|
|
50
|
+
- **Translate** — `telegram-translate-message` (requires Telegram Premium)
|
|
51
|
+
- **Typing indicator** — `telegram-send-typing` with configurable action
|
|
52
|
+
- **Dialog management** — `telegram-archive-chat`, `telegram-pin-chat`, `telegram-mark-dialog-unread`
|
|
53
|
+
- **Drafts** — `telegram-save-draft`, `telegram-get-drafts`, `telegram-clear-drafts`
|
|
54
|
+
- **Saved Messages dialogs** — `telegram-get-saved-dialogs` for the new per-peer Saved Messages folders
|
|
55
|
+
- **Admin log** — `telegram-get-admin-log` for channel/supergroup moderation history
|
|
56
|
+
- **Reactions catalog** — `telegram-set-default-reaction`, `telegram-get-top-reactions`, `telegram-get-recent-reactions`
|
|
57
|
+
- **Chat permissions** — `telegram-set-chat-permissions` for default banned rights
|
|
58
|
+
- **Slow mode** — `telegram-set-slow-mode` for supergroups
|
|
59
|
+
- **Forum topics CRUD** — `telegram-create-topic`, `telegram-edit-topic`, `telegram-delete-topic`
|
|
60
|
+
- **Web page preview** — `telegram-get-web-preview` to inspect link previews before sending
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
- `telegram-set-chat-permissions` now merges with the chat's current `defaultBannedRights` — omitted flags keep their current state instead of being silently cleared
|
|
64
|
+
- `telegram-clear-drafts` requires `chatId` (single-chat) or `confirmAllChats: true` to wipe drafts account-wide, preventing accidental loss of all drafts in one call
|
|
65
|
+
- `telegram-get-unread-mentions` and `telegram-get-unread-reactions` are now annotated as `WRITE` — they mark the listed items as read on the server
|
|
66
|
+
- `telegram-translate-message` is now annotated as `WRITE` (consumes Premium translate quota); `toLang` is validated against an ISO-639 / locale pattern and `messageIds` is capped at 1–100 positive integers
|
|
67
|
+
- `telegram-delete-scheduled` caps `messageIds` at 1–100 positive integers
|
|
68
|
+
- `telegram-set-default-reaction` validates `emoji` length (1–8 characters)
|
|
69
|
+
- `telegram-get-web-preview` rejects non-`http(s)` URLs, preventing use as an SSRF proxy
|
|
70
|
+
- `telegram-send-typing` throttles non-`cancel` actions to once per 10 seconds per chat
|
|
71
|
+
- `telegram-get-saved-dialogs` no longer returns a hard-coded `unreadCount: 0`
|
|
72
|
+
- `telegram-create-topic` now reads the new topic ID from `UpdateNewChannelMessage` (authoritative) and fails loudly if neither source is available
|
|
73
|
+
- `telegram-save-draft` drops `replyTo` when the draft text is empty, avoiding `MESSAGE_EMPTY` errors when clearing drafts
|
|
74
|
+
- Removed unused `chatMap` build in `getAdminLog`
|
|
9
75
|
|
|
10
76
|
## [1.24.1] - 2026-04-20
|
|
11
77
|
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ An MCP (Model Context Protocol) server that connects AI assistants like Claude t
|
|
|
18
18
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
|
-
- **
|
|
21
|
+
- **Comprehensive tool coverage** -- the most full-featured Telegram MCP server available (80+ tools)
|
|
22
22
|
- **MTProto protocol** -- direct Telegram API access, not the limited Bot API
|
|
23
23
|
- **Userbot** -- operates as your personal account, not a bot
|
|
24
24
|
- **Full-featured** -- messaging, reactions, polls, scheduled messages, stickers, media, contacts, and more
|
|
@@ -26,6 +26,12 @@ An MCP (Model Context Protocol) server that connects AI assistants like Claude t
|
|
|
26
26
|
- **Stickers** -- search sticker sets, browse installed/recent stickers, send stickers to any chat
|
|
27
27
|
- **Account management** -- update profile, manage privacy settings, sessions, auto-delete timers
|
|
28
28
|
- **Global search** -- search messages across all chats at once
|
|
29
|
+
- **Real-time polling** -- fetch updates via stateless cursors; agent owns `{pts, qts, date}` state
|
|
30
|
+
- **Inline bots & buttons** -- query inline bots, send results, press callback buttons
|
|
31
|
+
- **Stories** -- read stories from peers, get story view stats
|
|
32
|
+
- **Admin controls** -- toggle channel signatures, anti-spam, forum mode, prehistory; approve join requests
|
|
33
|
+
- **Stats** -- channel and supergroup analytics (GetBroadcastStats / GetMegagroupStats)
|
|
34
|
+
- **Boosts & Business** -- boost status, boosters list, Telegram Business chat links
|
|
29
35
|
- **QR code login** -- authenticate by scanning a QR code in the Telegram app
|
|
30
36
|
- **Session persistence** -- login once, stay connected across restarts
|
|
31
37
|
- **Human-readable output** -- sender names are resolved, not just numeric IDs
|
|
@@ -304,21 +310,23 @@ const telegramMcp = new MCPClient({
|
|
|
304
310
|
});
|
|
305
311
|
```
|
|
306
312
|
|
|
307
|
-
## Tools
|
|
313
|
+
## Tools
|
|
308
314
|
|
|
309
315
|
All tools are auto-discoverable via MCP — your AI client will see the full list with parameters and descriptions when connected.
|
|
310
316
|
|
|
311
317
|
| Category | Tools |
|
|
312
318
|
|----------|-------|
|
|
313
319
|
| **Auth** | `telegram-status`, `telegram-login` |
|
|
314
|
-
| **Messaging** | `telegram-send-message`, `telegram-edit-message`, `telegram-delete-message`, `telegram-forward-message`, `telegram-send-scheduled` |
|
|
315
|
-
| **
|
|
316
|
-
| **
|
|
320
|
+
| **Messaging** | `telegram-send-message`, `telegram-edit-message`, `telegram-delete-message`, `telegram-forward-message`, `telegram-send-scheduled`, `telegram-send-typing`, `telegram-translate-message`, `telegram-get-message-link` |
|
|
321
|
+
| **Scheduled** | `telegram-get-scheduled`, `telegram-delete-scheduled` |
|
|
322
|
+
| **Reading** | `telegram-list-chats`, `telegram-read-messages`, `telegram-search-messages`, `telegram-search-global`, `telegram-search-chats`, `telegram-get-unread`, `telegram-mark-as-read`, `telegram-get-replies`, `telegram-get-unread-mentions`, `telegram-get-unread-reactions`, `telegram-get-saved-dialogs` |
|
|
323
|
+
| **Drafts** | `telegram-save-draft`, `telegram-get-drafts`, `telegram-clear-drafts` |
|
|
324
|
+
| **Forum Topics** | `telegram-list-topics`, `telegram-read-topic-messages`, `telegram-create-topic`, `telegram-edit-topic`, `telegram-delete-topic` |
|
|
317
325
|
| **Polls** | `telegram-create-poll` |
|
|
318
|
-
| **Reactions** | `telegram-send-reaction`, `telegram-get-reactions` |
|
|
326
|
+
| **Reactions** | `telegram-send-reaction`, `telegram-get-reactions`, `telegram-set-default-reaction`, `telegram-get-top-reactions`, `telegram-get-recent-reactions` |
|
|
319
327
|
| **Stickers** | `telegram-send-sticker`, `telegram-get-installed-stickers`, `telegram-get-recent-stickers`, `telegram-get-sticker-set`, `telegram-search-sticker-sets` |
|
|
320
|
-
| **Media** | `telegram-send-file`, `telegram-download-media`, `telegram-get-profile-photo` |
|
|
321
|
-
| **Groups** | `telegram-create-group`, `telegram-edit-group`, `telegram-invite-to-group`, `telegram-join-chat`, `telegram-leave-group`, `telegram-kick-user`, `telegram-ban-user`, `telegram-unban-user`, `telegram-set-admin`, `telegram-remove-admin`, `telegram-get-my-role` |
|
|
328
|
+
| **Media** | `telegram-send-file`, `telegram-download-media`, `telegram-get-profile-photo`, `telegram-get-web-preview` |
|
|
329
|
+
| **Groups** | `telegram-create-group`, `telegram-edit-group`, `telegram-invite-to-group`, `telegram-join-chat`, `telegram-leave-group`, `telegram-kick-user`, `telegram-ban-user`, `telegram-unban-user`, `telegram-set-admin`, `telegram-remove-admin`, `telegram-get-my-role`, `telegram-set-chat-permissions`, `telegram-set-slow-mode`, `telegram-get-admin-log` |
|
|
322
330
|
| **Chat Info** | `telegram-get-chat-info`, `telegram-get-chat-members`, `telegram-get-chat-folders` |
|
|
323
331
|
| **Invite Links** | `telegram-create-invite-link`, `telegram-get-invite-links`, `telegram-revoke-invite-link` |
|
|
324
332
|
| **Contacts** | `telegram-get-contacts`, `telegram-add-contact`, `telegram-get-contact-requests` |
|
|
@@ -326,10 +334,29 @@ All tools are auto-discoverable via MCP — your AI client will see the full lis
|
|
|
326
334
|
| **Profiles** | `telegram-get-profile`, `telegram-update-profile` |
|
|
327
335
|
| **Account** | `telegram-get-sessions`, `telegram-terminate-session`, `telegram-set-privacy`, `telegram-set-auto-delete` |
|
|
328
336
|
| **Pinning** | `telegram-pin-message`, `telegram-unpin-message` |
|
|
329
|
-
| **Chat Settings** | `telegram-mute-chat` |
|
|
337
|
+
| **Chat Settings** | `telegram-mute-chat`, `telegram-archive-chat`, `telegram-pin-chat`, `telegram-mark-dialog-unread` |
|
|
338
|
+
| **Admin Toggles** | `telegram-toggle-channel-signatures`, `telegram-toggle-anti-spam`, `telegram-toggle-forum-mode`, `telegram-toggle-prehistory-hidden`, `telegram-set-chat-reactions`, `telegram-approve-join-request` |
|
|
339
|
+
| **Stats** | `telegram-get-broadcast-stats`, `telegram-get-megagroup-stats` |
|
|
340
|
+
| **Inline Bots & Buttons** | `telegram-inline-query`, `telegram-inline-query-send`, `telegram-press-button`, `telegram-get-message-buttons` |
|
|
341
|
+
| **Real-Time Polling** | `telegram-get-state`, `telegram-get-updates`, `telegram-get-channel-updates` |
|
|
342
|
+
| **Stories** | `telegram-get-all-stories`, `telegram-get-peer-stories`, `telegram-get-stories-by-id`, `telegram-get-story-views` |
|
|
343
|
+
| **Boosts & Business** | `telegram-get-my-boosts`, `telegram-get-boosts-status`, `telegram-get-boosts-list`, `telegram-get-business-chat-links` |
|
|
344
|
+
| **Opt-in (env-gated)** | `telegram-get-group-call`, `telegram-get-group-call-participants` (requires `MCP_TELEGRAM_ENABLE_GROUP_CALLS=1`), `telegram-get-stars-status`, `telegram-get-stars-transactions` (requires `MCP_TELEGRAM_ENABLE_STARS=1`), `telegram-get-quick-replies`, `telegram-get-quick-reply-messages` (requires `MCP_TELEGRAM_ENABLE_QUICK_REPLIES=1`) |
|
|
330
345
|
|
|
331
346
|
> **Tip**: Ask your AI assistant *"What Telegram tools are available?"* to get the full list with parameters and descriptions.
|
|
332
347
|
|
|
348
|
+
## Optional Features
|
|
349
|
+
|
|
350
|
+
Some tools are disabled by default and must be opted in via environment variables:
|
|
351
|
+
|
|
352
|
+
| Variable | Value | Tools enabled |
|
|
353
|
+
|----------|-------|---------------|
|
|
354
|
+
| `MCP_TELEGRAM_ENABLE_GROUP_CALLS` | `1` | `telegram-get-group-call`, `telegram-get-group-call-participants` |
|
|
355
|
+
| `MCP_TELEGRAM_ENABLE_STARS` | `1` | `telegram-get-stars-status`, `telegram-get-stars-transactions` |
|
|
356
|
+
| `MCP_TELEGRAM_ENABLE_QUICK_REPLIES` | `1` | `telegram-get-quick-replies`, `telegram-get-quick-reply-messages` |
|
|
357
|
+
|
|
358
|
+
Add these to your `.env` file or MCP client config to enable them.
|
|
359
|
+
|
|
333
360
|
## Development
|
|
334
361
|
|
|
335
362
|
```bash
|
|
@@ -351,14 +378,19 @@ src/
|
|
|
351
378
|
qr-login-cli.ts -- CLI utility for QR code login
|
|
352
379
|
tools/ -- Modular tool definitions
|
|
353
380
|
auth.ts -- Connection & login
|
|
354
|
-
messages.ts -- Send, read, search, edit, delete, forward
|
|
355
|
-
chats.ts -- Chat listing, group management, admin
|
|
381
|
+
messages.ts -- Send, read, search, edit, delete, forward; inline bots; real-time polling
|
|
382
|
+
chats.ts -- Chat listing, group management, admin toggles, stats
|
|
356
383
|
contacts.ts -- Contacts, profiles, moderation
|
|
357
384
|
media.ts -- Files, photos, downloads
|
|
358
|
-
reactions.ts -- Reactions
|
|
385
|
+
reactions.ts -- Reactions, set-chat-reactions
|
|
359
386
|
extras.ts -- Pin, schedule, polls, topics
|
|
360
387
|
stickers.ts -- Sticker sets, send, search, browse
|
|
361
|
-
account.ts -- Sessions, privacy, auto-delete, profile, chat mute/folders, invite links
|
|
388
|
+
account.ts -- Sessions, privacy, auto-delete, profile, chat mute/folders, invite links, business chat links
|
|
389
|
+
boosts.ts -- Boost status, my boosts, boosters list
|
|
390
|
+
stories.ts -- Stories: list all, peer, by-id, view stats
|
|
391
|
+
group-calls.ts -- Group call info and participants (opt-in: MCP_TELEGRAM_ENABLE_GROUP_CALLS)
|
|
392
|
+
stars.ts -- Stars wallet status and transactions (opt-in: MCP_TELEGRAM_ENABLE_STARS)
|
|
393
|
+
quick-replies.ts -- Quick replies and messages (opt-in: MCP_TELEGRAM_ENABLE_QUICK_REPLIES)
|
|
362
394
|
shared.ts -- Shared utilities
|
|
363
395
|
```
|
|
364
396
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { Api } from "telegram/tl/index.js";
|
|
4
|
+
import { describeAdminLogAction, describeAdminLogDetails } from "../telegram-client.js";
|
|
5
|
+
describe("describeAdminLogAction", () => {
|
|
6
|
+
it("converts ChangeTitle to snake_case", () => {
|
|
7
|
+
const action = new Api.ChannelAdminLogEventActionChangeTitle({ prevValue: "a", newValue: "b" });
|
|
8
|
+
assert.strictEqual(describeAdminLogAction(action), "change_title");
|
|
9
|
+
});
|
|
10
|
+
it("converts ParticipantJoin to snake_case", () => {
|
|
11
|
+
const action = new Api.ChannelAdminLogEventActionParticipantJoin();
|
|
12
|
+
assert.strictEqual(describeAdminLogAction(action), "participant_join");
|
|
13
|
+
});
|
|
14
|
+
it("handles ToggleSlowMode", () => {
|
|
15
|
+
const action = new Api.ChannelAdminLogEventActionToggleSlowMode({ prevValue: 0, newValue: 30 });
|
|
16
|
+
assert.strictEqual(describeAdminLogAction(action), "toggle_slow_mode");
|
|
17
|
+
});
|
|
18
|
+
it("handles ChangeHistoryTTL without splitting acronym", () => {
|
|
19
|
+
const action = new Api.ChannelAdminLogEventActionChangeHistoryTTL({ prevValue: 0, newValue: 86400 });
|
|
20
|
+
assert.strictEqual(describeAdminLogAction(action), "change_history_ttl");
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe("describeAdminLogDetails", () => {
|
|
24
|
+
const describeUser = (id) => `user_${id.toString()}`;
|
|
25
|
+
it("formats title change", () => {
|
|
26
|
+
const action = new Api.ChannelAdminLogEventActionChangeTitle({ prevValue: "old", newValue: "new" });
|
|
27
|
+
assert.strictEqual(describeAdminLogDetails(action, describeUser), '"old" → "new"');
|
|
28
|
+
});
|
|
29
|
+
it("formats username change", () => {
|
|
30
|
+
const action = new Api.ChannelAdminLogEventActionChangeUsername({ prevValue: "old", newValue: "new" });
|
|
31
|
+
assert.strictEqual(describeAdminLogDetails(action, describeUser), "@old → @new");
|
|
32
|
+
});
|
|
33
|
+
it("formats slow mode change", () => {
|
|
34
|
+
const action = new Api.ChannelAdminLogEventActionToggleSlowMode({ prevValue: 0, newValue: 30 });
|
|
35
|
+
assert.strictEqual(describeAdminLogDetails(action, describeUser), "0s → 30s");
|
|
36
|
+
});
|
|
37
|
+
it("returns empty string for unknown actions", () => {
|
|
38
|
+
const action = new Api.ChannelAdminLogEventActionParticipantJoin();
|
|
39
|
+
assert.strictEqual(describeAdminLogDetails(action, describeUser), "");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import bigInt from "big-integer";
|
|
4
|
+
import { Api } from "telegram/tl/index.js";
|
|
5
|
+
import { TelegramService } from "../telegram-client.js";
|
|
6
|
+
function makeService(entity, user, invocations) {
|
|
7
|
+
const fakeClient = {
|
|
8
|
+
invoke: async (req) => {
|
|
9
|
+
invocations.push(req);
|
|
10
|
+
return undefined;
|
|
11
|
+
},
|
|
12
|
+
getEntity: async (_id) => user,
|
|
13
|
+
};
|
|
14
|
+
const service = new TelegramService(1, "hash");
|
|
15
|
+
const internals = service;
|
|
16
|
+
internals.client = fakeClient;
|
|
17
|
+
internals.connected = true;
|
|
18
|
+
internals.resolveChat = async () => entity;
|
|
19
|
+
return service;
|
|
20
|
+
}
|
|
21
|
+
describe("TelegramService.approveChatJoinRequest", () => {
|
|
22
|
+
const makeUser = (id) => new Api.User({
|
|
23
|
+
id: bigInt(id),
|
|
24
|
+
accessHash: bigInt(42),
|
|
25
|
+
firstName: "Test",
|
|
26
|
+
});
|
|
27
|
+
it("invokes HideChatJoinRequest with approved=true for channel peer", async () => {
|
|
28
|
+
const megagroup = new Api.Channel({
|
|
29
|
+
id: bigInt(10000),
|
|
30
|
+
title: "sg",
|
|
31
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
32
|
+
date: 0,
|
|
33
|
+
accessHash: bigInt(1),
|
|
34
|
+
megagroup: true,
|
|
35
|
+
});
|
|
36
|
+
const invocations = [];
|
|
37
|
+
const service = makeService(megagroup, makeUser(555), invocations);
|
|
38
|
+
await service.approveChatJoinRequest("10000", "555", true);
|
|
39
|
+
const call = invocations.find((r) => r instanceof Api.messages.HideChatJoinRequest);
|
|
40
|
+
assert.ok(call, "HideChatJoinRequest was invoked");
|
|
41
|
+
assert.strictEqual(call.approved, true);
|
|
42
|
+
assert.ok(call.userId instanceof Api.InputUser);
|
|
43
|
+
assert.strictEqual(call.userId.userId.toString(), "555");
|
|
44
|
+
});
|
|
45
|
+
it("invokes HideChatJoinRequest with approved=false (denied)", async () => {
|
|
46
|
+
const megagroup = new Api.Channel({
|
|
47
|
+
id: bigInt(20000),
|
|
48
|
+
title: "sg",
|
|
49
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
50
|
+
date: 0,
|
|
51
|
+
accessHash: bigInt(1),
|
|
52
|
+
megagroup: true,
|
|
53
|
+
});
|
|
54
|
+
const invocations = [];
|
|
55
|
+
const service = makeService(megagroup, makeUser(777), invocations);
|
|
56
|
+
await service.approveChatJoinRequest("20000", "777", false);
|
|
57
|
+
const call = invocations.find((r) => r instanceof Api.messages.HideChatJoinRequest);
|
|
58
|
+
assert.ok(call);
|
|
59
|
+
assert.strictEqual(call.approved, false);
|
|
60
|
+
});
|
|
61
|
+
it("rejects basic Chat peer (basic groups do not support join requests)", async () => {
|
|
62
|
+
const chat = new Api.Chat({
|
|
63
|
+
id: bigInt(33333),
|
|
64
|
+
title: "basic group",
|
|
65
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
66
|
+
participantsCount: 5,
|
|
67
|
+
date: 0,
|
|
68
|
+
version: 0,
|
|
69
|
+
});
|
|
70
|
+
const invocations = [];
|
|
71
|
+
const service = makeService(chat, makeUser(888), invocations);
|
|
72
|
+
await assert.rejects(service.approveChatJoinRequest("33333", "888", true), /supergroups and channels/i);
|
|
73
|
+
assert.strictEqual(invocations.find((r) => r instanceof Api.messages.HideChatJoinRequest), undefined, "no API call for basic group");
|
|
74
|
+
});
|
|
75
|
+
it("rejects when user resolves to non-User entity", async () => {
|
|
76
|
+
const megagroup = new Api.Channel({
|
|
77
|
+
id: bigInt(44444),
|
|
78
|
+
title: "sg",
|
|
79
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
80
|
+
date: 0,
|
|
81
|
+
accessHash: bigInt(1),
|
|
82
|
+
megagroup: true,
|
|
83
|
+
});
|
|
84
|
+
const notAUser = new Api.Chat({
|
|
85
|
+
id: bigInt(99),
|
|
86
|
+
title: "not a user",
|
|
87
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
88
|
+
participantsCount: 1,
|
|
89
|
+
date: 0,
|
|
90
|
+
version: 0,
|
|
91
|
+
});
|
|
92
|
+
const invocations = [];
|
|
93
|
+
const service = makeService(megagroup, notAUser, invocations);
|
|
94
|
+
await assert.rejects(service.approveChatJoinRequest("44444", "99", true), /not a user/i);
|
|
95
|
+
assert.strictEqual(invocations.find((r) => r instanceof Api.messages.HideChatJoinRequest), undefined, "no API call when target is not a user");
|
|
96
|
+
});
|
|
97
|
+
it("rejects when chat entity is a private user (not a group/channel)", async () => {
|
|
98
|
+
const userPeer = new Api.User({
|
|
99
|
+
id: bigInt(1),
|
|
100
|
+
accessHash: bigInt(1),
|
|
101
|
+
firstName: "Peer",
|
|
102
|
+
});
|
|
103
|
+
const invocations = [];
|
|
104
|
+
const service = makeService(userPeer, makeUser(123), invocations);
|
|
105
|
+
await assert.rejects(service.approveChatJoinRequest("1", "123", true), /supergroups and channels/i);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import bigInt from "big-integer";
|
|
4
|
+
import { Api } from "telegram/tl/index.js";
|
|
5
|
+
import { summarizeBoost, summarizeBoostsList, summarizeBoostsStatus, summarizeMyBoost, summarizeMyBoosts, summarizePrepaidGiveaway, TelegramService, } from "../telegram-client.js";
|
|
6
|
+
function makeService(invocations, responder) {
|
|
7
|
+
const fakeClient = {
|
|
8
|
+
invoke: async (req) => {
|
|
9
|
+
invocations.push(req);
|
|
10
|
+
return responder(req);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
const service = new TelegramService(1, "hash");
|
|
14
|
+
const internals = service;
|
|
15
|
+
internals.client = fakeClient;
|
|
16
|
+
internals.connected = true;
|
|
17
|
+
return service;
|
|
18
|
+
}
|
|
19
|
+
describe("summarizeMyBoost", () => {
|
|
20
|
+
it("maps slot, peer, dates and cooldown", () => {
|
|
21
|
+
const boost = new Api.MyBoost({
|
|
22
|
+
slot: 2,
|
|
23
|
+
peer: new Api.PeerChannel({ channelId: bigInt(900) }),
|
|
24
|
+
date: 1710000000,
|
|
25
|
+
expires: 1720000000,
|
|
26
|
+
cooldownUntilDate: 1715000000,
|
|
27
|
+
});
|
|
28
|
+
const out = summarizeMyBoost(boost);
|
|
29
|
+
assert.strictEqual(out.slot, 2);
|
|
30
|
+
assert.deepStrictEqual(out.peer, { kind: "channel", id: "900" });
|
|
31
|
+
assert.strictEqual(out.date, 1710000000);
|
|
32
|
+
assert.strictEqual(out.expires, 1720000000);
|
|
33
|
+
assert.strictEqual(out.cooldownUntilDate, 1715000000);
|
|
34
|
+
});
|
|
35
|
+
it("leaves peer undefined when boost is unassigned", () => {
|
|
36
|
+
const boost = new Api.MyBoost({
|
|
37
|
+
slot: 1,
|
|
38
|
+
date: 100,
|
|
39
|
+
expires: 200,
|
|
40
|
+
});
|
|
41
|
+
const out = summarizeMyBoost(boost);
|
|
42
|
+
assert.strictEqual(out.slot, 1);
|
|
43
|
+
assert.strictEqual(out.peer, undefined);
|
|
44
|
+
assert.strictEqual(out.cooldownUntilDate, undefined);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe("summarizeMyBoosts", () => {
|
|
48
|
+
it("computes count from myBoosts length and maps each entry", () => {
|
|
49
|
+
const resp = new Api.premium.MyBoosts({
|
|
50
|
+
myBoosts: [
|
|
51
|
+
new Api.MyBoost({
|
|
52
|
+
slot: 1,
|
|
53
|
+
peer: new Api.PeerChannel({ channelId: bigInt(10) }),
|
|
54
|
+
date: 1,
|
|
55
|
+
expires: 2,
|
|
56
|
+
}),
|
|
57
|
+
new Api.MyBoost({ slot: 2, date: 3, expires: 4 }),
|
|
58
|
+
],
|
|
59
|
+
chats: [],
|
|
60
|
+
users: [],
|
|
61
|
+
});
|
|
62
|
+
const out = summarizeMyBoosts(resp);
|
|
63
|
+
assert.strictEqual(out.count, 2);
|
|
64
|
+
assert.strictEqual(out.myBoosts.length, 2);
|
|
65
|
+
assert.deepStrictEqual(out.myBoosts[0].peer, { kind: "channel", id: "10" });
|
|
66
|
+
assert.strictEqual(out.myBoosts[1].peer, undefined);
|
|
67
|
+
});
|
|
68
|
+
it("handles empty boost list", () => {
|
|
69
|
+
const resp = new Api.premium.MyBoosts({ myBoosts: [], chats: [], users: [] });
|
|
70
|
+
const out = summarizeMyBoosts(resp);
|
|
71
|
+
assert.strictEqual(out.count, 0);
|
|
72
|
+
assert.deepStrictEqual(out.myBoosts, []);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe("summarizePrepaidGiveaway", () => {
|
|
76
|
+
it("maps premium PrepaidGiveaway (months + quantity)", () => {
|
|
77
|
+
const g = new Api.PrepaidGiveaway({
|
|
78
|
+
id: bigInt(123),
|
|
79
|
+
months: 3,
|
|
80
|
+
quantity: 10,
|
|
81
|
+
date: 1700000000,
|
|
82
|
+
});
|
|
83
|
+
const out = summarizePrepaidGiveaway(g);
|
|
84
|
+
assert.deepStrictEqual(out, {
|
|
85
|
+
kind: "premium",
|
|
86
|
+
id: "123",
|
|
87
|
+
months: 3,
|
|
88
|
+
quantity: 10,
|
|
89
|
+
date: 1700000000,
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
it("maps PrepaidStarsGiveaway with stars + boosts", () => {
|
|
93
|
+
const g = new Api.PrepaidStarsGiveaway({
|
|
94
|
+
id: bigInt(456),
|
|
95
|
+
stars: bigInt(5000),
|
|
96
|
+
quantity: 20,
|
|
97
|
+
boosts: 4,
|
|
98
|
+
date: 1710000000,
|
|
99
|
+
});
|
|
100
|
+
const out = summarizePrepaidGiveaway(g);
|
|
101
|
+
assert.deepStrictEqual(out, {
|
|
102
|
+
kind: "stars",
|
|
103
|
+
id: "456",
|
|
104
|
+
stars: "5000",
|
|
105
|
+
quantity: 20,
|
|
106
|
+
boosts: 4,
|
|
107
|
+
date: 1710000000,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe("summarizeBoostsStatus", () => {
|
|
112
|
+
it("maps core counters and boost url", () => {
|
|
113
|
+
const resp = new Api.premium.BoostsStatus({
|
|
114
|
+
level: 3,
|
|
115
|
+
currentLevelBoosts: 10,
|
|
116
|
+
boosts: 15,
|
|
117
|
+
giftBoosts: 2,
|
|
118
|
+
nextLevelBoosts: 25,
|
|
119
|
+
boostUrl: "https://t.me/boost/test",
|
|
120
|
+
myBoost: true,
|
|
121
|
+
myBoostSlots: [1, 2],
|
|
122
|
+
});
|
|
123
|
+
const out = summarizeBoostsStatus(resp);
|
|
124
|
+
assert.strictEqual(out.level, 3);
|
|
125
|
+
assert.strictEqual(out.boosts, 15);
|
|
126
|
+
assert.strictEqual(out.currentLevelBoosts, 10);
|
|
127
|
+
assert.strictEqual(out.nextLevelBoosts, 25);
|
|
128
|
+
assert.strictEqual(out.giftBoosts, 2);
|
|
129
|
+
assert.strictEqual(out.boostUrl, "https://t.me/boost/test");
|
|
130
|
+
assert.strictEqual(out.myBoost, true);
|
|
131
|
+
assert.deepStrictEqual(out.myBoostSlots, [1, 2]);
|
|
132
|
+
assert.strictEqual(out.premiumAudience, undefined);
|
|
133
|
+
assert.strictEqual(out.prepaidGiveaways, undefined);
|
|
134
|
+
});
|
|
135
|
+
it("includes premiumAudience and prepaidGiveaways when present", () => {
|
|
136
|
+
const resp = new Api.premium.BoostsStatus({
|
|
137
|
+
level: 1,
|
|
138
|
+
currentLevelBoosts: 0,
|
|
139
|
+
boosts: 5,
|
|
140
|
+
boostUrl: "https://t.me/boost/x",
|
|
141
|
+
premiumAudience: new Api.StatsPercentValue({ part: 2, total: 100 }),
|
|
142
|
+
prepaidGiveaways: [new Api.PrepaidGiveaway({ id: bigInt(1), months: 6, quantity: 5, date: 111 })],
|
|
143
|
+
});
|
|
144
|
+
const out = summarizeBoostsStatus(resp);
|
|
145
|
+
assert.deepStrictEqual(out.premiumAudience, { part: 2, total: 100 });
|
|
146
|
+
assert.ok(out.prepaidGiveaways);
|
|
147
|
+
assert.strictEqual(out.prepaidGiveaways?.length, 1);
|
|
148
|
+
assert.strictEqual(out.prepaidGiveaways?.[0].kind, "premium");
|
|
149
|
+
assert.strictEqual(out.prepaidGiveaways?.[0].id, "1");
|
|
150
|
+
});
|
|
151
|
+
it("omits empty prepaidGiveaways list", () => {
|
|
152
|
+
const resp = new Api.premium.BoostsStatus({
|
|
153
|
+
level: 0,
|
|
154
|
+
currentLevelBoosts: 0,
|
|
155
|
+
boosts: 0,
|
|
156
|
+
boostUrl: "https://t.me/boost/y",
|
|
157
|
+
prepaidGiveaways: [],
|
|
158
|
+
});
|
|
159
|
+
const out = summarizeBoostsStatus(resp);
|
|
160
|
+
assert.strictEqual(out.prepaidGiveaways, undefined);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe("TelegramService.getBoostsStatus", () => {
|
|
164
|
+
it("invokes premium.GetBoostsStatus with resolved peer and returns summary", async () => {
|
|
165
|
+
const invocations = [];
|
|
166
|
+
const service = makeService(invocations, () => new Api.premium.BoostsStatus({
|
|
167
|
+
level: 2,
|
|
168
|
+
currentLevelBoosts: 5,
|
|
169
|
+
boosts: 7,
|
|
170
|
+
boostUrl: "https://t.me/boost/foo",
|
|
171
|
+
}));
|
|
172
|
+
const internals = service;
|
|
173
|
+
internals.resolvePeer = async (_id) => new Api.InputPeerChannel({ channelId: bigInt(500), accessHash: bigInt(0) });
|
|
174
|
+
const out = await service.getBoostsStatus("@foo");
|
|
175
|
+
const call = invocations.find((r) => r instanceof Api.premium.GetBoostsStatus);
|
|
176
|
+
assert.ok(call);
|
|
177
|
+
assert.strictEqual(out.level, 2);
|
|
178
|
+
assert.strictEqual(out.boosts, 7);
|
|
179
|
+
assert.strictEqual(out.boostUrl, "https://t.me/boost/foo");
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
describe("summarizeBoost", () => {
|
|
183
|
+
it("maps core boost fields and converts bigInt ids to strings", () => {
|
|
184
|
+
const boost = new Api.Boost({
|
|
185
|
+
id: "boost-1",
|
|
186
|
+
userId: bigInt(123),
|
|
187
|
+
date: 1700000000,
|
|
188
|
+
expires: 1702000000,
|
|
189
|
+
gift: true,
|
|
190
|
+
giveaway: false,
|
|
191
|
+
unclaimed: false,
|
|
192
|
+
giveawayMsgId: 55,
|
|
193
|
+
usedGiftSlug: "slug-abc",
|
|
194
|
+
multiplier: 2,
|
|
195
|
+
stars: bigInt(500),
|
|
196
|
+
});
|
|
197
|
+
const out = summarizeBoost(boost);
|
|
198
|
+
assert.strictEqual(out.id, "boost-1");
|
|
199
|
+
assert.strictEqual(out.userId, "123");
|
|
200
|
+
assert.strictEqual(out.date, 1700000000);
|
|
201
|
+
assert.strictEqual(out.expires, 1702000000);
|
|
202
|
+
assert.strictEqual(out.gift, true);
|
|
203
|
+
assert.strictEqual(out.giveaway, false);
|
|
204
|
+
assert.strictEqual(out.unclaimed, false);
|
|
205
|
+
assert.strictEqual(out.giveawayMsgId, 55);
|
|
206
|
+
assert.strictEqual(out.usedGiftSlug, "slug-abc");
|
|
207
|
+
assert.strictEqual(out.multiplier, 2);
|
|
208
|
+
assert.strictEqual(out.stars, "500");
|
|
209
|
+
});
|
|
210
|
+
it("leaves optional fields undefined when missing", () => {
|
|
211
|
+
const boost = new Api.Boost({
|
|
212
|
+
id: "boost-2",
|
|
213
|
+
date: 10,
|
|
214
|
+
expires: 20,
|
|
215
|
+
});
|
|
216
|
+
const out = summarizeBoost(boost);
|
|
217
|
+
assert.strictEqual(out.id, "boost-2");
|
|
218
|
+
assert.strictEqual(out.userId, undefined);
|
|
219
|
+
assert.strictEqual(out.stars, undefined);
|
|
220
|
+
assert.strictEqual(out.multiplier, undefined);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe("summarizeBoostsList", () => {
|
|
224
|
+
it("maps count, boosts and nextOffset", () => {
|
|
225
|
+
const resp = new Api.premium.BoostsList({
|
|
226
|
+
count: 2,
|
|
227
|
+
boosts: [
|
|
228
|
+
new Api.Boost({ id: "a", userId: bigInt(1), date: 1, expires: 2 }),
|
|
229
|
+
new Api.Boost({ id: "b", giveaway: true, date: 3, expires: 4 }),
|
|
230
|
+
],
|
|
231
|
+
nextOffset: "cursor-xyz",
|
|
232
|
+
users: [],
|
|
233
|
+
});
|
|
234
|
+
const out = summarizeBoostsList(resp);
|
|
235
|
+
assert.strictEqual(out.count, 2);
|
|
236
|
+
assert.strictEqual(out.boosts.length, 2);
|
|
237
|
+
assert.strictEqual(out.boosts[0].id, "a");
|
|
238
|
+
assert.strictEqual(out.boosts[0].userId, "1");
|
|
239
|
+
assert.strictEqual(out.boosts[1].giveaway, true);
|
|
240
|
+
assert.strictEqual(out.nextOffset, "cursor-xyz");
|
|
241
|
+
});
|
|
242
|
+
it("handles empty boosts and missing nextOffset", () => {
|
|
243
|
+
const resp = new Api.premium.BoostsList({
|
|
244
|
+
count: 0,
|
|
245
|
+
boosts: [],
|
|
246
|
+
users: [],
|
|
247
|
+
});
|
|
248
|
+
const out = summarizeBoostsList(resp);
|
|
249
|
+
assert.strictEqual(out.count, 0);
|
|
250
|
+
assert.deepStrictEqual(out.boosts, []);
|
|
251
|
+
assert.strictEqual(out.nextOffset, undefined);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
describe("TelegramService.getBoostsList", () => {
|
|
255
|
+
it("invokes premium.GetBoostsList with defaults (empty offset, limit 50)", async () => {
|
|
256
|
+
const invocations = [];
|
|
257
|
+
const service = makeService(invocations, () => new Api.premium.BoostsList({
|
|
258
|
+
count: 1,
|
|
259
|
+
boosts: [new Api.Boost({ id: "boost-1", userId: bigInt(7), date: 10, expires: 20 })],
|
|
260
|
+
nextOffset: "next",
|
|
261
|
+
users: [],
|
|
262
|
+
}));
|
|
263
|
+
const internals = service;
|
|
264
|
+
internals.resolvePeer = async (_id) => new Api.InputPeerChannel({ channelId: bigInt(500), accessHash: bigInt(0) });
|
|
265
|
+
const out = await service.getBoostsList("@foo");
|
|
266
|
+
const call = invocations.find((r) => r instanceof Api.premium.GetBoostsList);
|
|
267
|
+
assert.ok(call);
|
|
268
|
+
assert.strictEqual(call.offset, "");
|
|
269
|
+
assert.strictEqual(call.limit, 50);
|
|
270
|
+
assert.strictEqual(call.gifts, undefined);
|
|
271
|
+
assert.strictEqual(out.count, 1);
|
|
272
|
+
assert.strictEqual(out.boosts[0].id, "boost-1");
|
|
273
|
+
assert.strictEqual(out.nextOffset, "next");
|
|
274
|
+
});
|
|
275
|
+
it("passes gifts/offset/limit through to GetBoostsList", async () => {
|
|
276
|
+
const invocations = [];
|
|
277
|
+
const service = makeService(invocations, () => new Api.premium.BoostsList({ count: 0, boosts: [], users: [] }));
|
|
278
|
+
const internals = service;
|
|
279
|
+
internals.resolvePeer = async (_id) => new Api.InputPeerChannel({ channelId: bigInt(1), accessHash: bigInt(0) });
|
|
280
|
+
await service.getBoostsList("@bar", { gifts: true, offset: "cur", limit: 10 });
|
|
281
|
+
const call = invocations.find((r) => r instanceof Api.premium.GetBoostsList);
|
|
282
|
+
assert.ok(call);
|
|
283
|
+
assert.strictEqual(call.gifts, true);
|
|
284
|
+
assert.strictEqual(call.offset, "cur");
|
|
285
|
+
assert.strictEqual(call.limit, 10);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe("TelegramService.getMyBoosts", () => {
|
|
289
|
+
it("invokes premium.GetMyBoosts and returns summary", async () => {
|
|
290
|
+
const invocations = [];
|
|
291
|
+
const service = makeService(invocations, () => new Api.premium.MyBoosts({
|
|
292
|
+
myBoosts: [
|
|
293
|
+
new Api.MyBoost({
|
|
294
|
+
slot: 1,
|
|
295
|
+
peer: new Api.PeerChannel({ channelId: bigInt(42) }),
|
|
296
|
+
date: 100,
|
|
297
|
+
expires: 200,
|
|
298
|
+
}),
|
|
299
|
+
],
|
|
300
|
+
chats: [],
|
|
301
|
+
users: [],
|
|
302
|
+
}));
|
|
303
|
+
const out = await service.getMyBoosts();
|
|
304
|
+
const call = invocations.find((r) => r instanceof Api.premium.GetMyBoosts);
|
|
305
|
+
assert.ok(call);
|
|
306
|
+
assert.strictEqual(out.count, 1);
|
|
307
|
+
assert.strictEqual(out.myBoosts[0].slot, 1);
|
|
308
|
+
assert.deepStrictEqual(out.myBoosts[0].peer, { kind: "channel", id: "42" });
|
|
309
|
+
});
|
|
310
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|