@overpod/mcp-telegram 1.24.0 → 1.25.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 CHANGED
@@ -5,7 +5,58 @@ 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
- ## [Unreleased]
8
+ ## [1.25.0] - 2026-04-20
9
+
10
+ ### Added
11
+ - **Scheduled messages** — `telegram-get-scheduled`, `telegram-delete-scheduled`
12
+ - **Threads & replies** — `telegram-get-replies` for channel post comments
13
+ - **Message links** — `telegram-get-message-link` returns public t.me URL for a message
14
+ - **Mentions & unread reactions** — `telegram-get-unread-mentions`, `telegram-get-unread-reactions`
15
+ - **Translate** — `telegram-translate-message` (requires Telegram Premium)
16
+ - **Typing indicator** — `telegram-send-typing` with configurable action
17
+ - **Dialog management** — `telegram-archive-chat`, `telegram-pin-chat`, `telegram-mark-dialog-unread`
18
+ - **Drafts** — `telegram-save-draft`, `telegram-get-drafts`, `telegram-clear-drafts`
19
+ - **Saved Messages dialogs** — `telegram-get-saved-dialogs` for the new per-peer Saved Messages folders
20
+ - **Admin log** — `telegram-get-admin-log` for channel/supergroup moderation history
21
+ - **Reactions catalog** — `telegram-set-default-reaction`, `telegram-get-top-reactions`, `telegram-get-recent-reactions`
22
+ - **Chat permissions** — `telegram-set-chat-permissions` for default banned rights
23
+ - **Slow mode** — `telegram-set-slow-mode` for supergroups
24
+ - **Forum topics CRUD** — `telegram-create-topic`, `telegram-edit-topic`, `telegram-delete-topic`
25
+ - **Web page preview** — `telegram-get-web-preview` to inspect link previews before sending
26
+
27
+ ### Fixed
28
+ - `telegram-set-chat-permissions` now merges with the chat's current `defaultBannedRights` — omitted flags keep their current state instead of being silently cleared
29
+ - `telegram-clear-drafts` requires `chatId` (single-chat) or `confirmAllChats: true` to wipe drafts account-wide, preventing accidental loss of all drafts in one call
30
+ - `telegram-get-unread-mentions` and `telegram-get-unread-reactions` are now annotated as `WRITE` — they mark the listed items as read on the server
31
+ - `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
32
+ - `telegram-delete-scheduled` caps `messageIds` at 1–100 positive integers
33
+ - `telegram-set-default-reaction` validates `emoji` length (1–8 characters)
34
+ - `telegram-get-web-preview` rejects non-`http(s)` URLs, preventing use as an SSRF proxy
35
+ - `telegram-send-typing` throttles non-`cancel` actions to once per 10 seconds per chat
36
+ - `telegram-get-saved-dialogs` no longer returns a hard-coded `unreadCount: 0`
37
+ - `telegram-create-topic` now reads the new topic ID from `UpdateNewChannelMessage` (authoritative) and fails loudly if neither source is available
38
+ - `telegram-save-draft` drops `replyTo` when the draft text is empty, avoiding `MESSAGE_EMPTY` errors when clearing drafts
39
+ - Removed unused `chatMap` build in `getAdminLog`
40
+
41
+ ## [1.24.1] - 2026-04-20
42
+
43
+ ### Changed
44
+ - Dependencies bumped to latest: `@modelcontextprotocol/sdk` 1.28.0 → 1.29.0, `dotenv` 17.3.1 → 17.4.2, `@biomejs/biome` 2.4.9 → 2.4.12, `typescript` 6.0.2 → 6.0.3, `@types/node` 25.5.0 → 25.6.0
45
+ - `biome.json` migrated to schema 2.4.12
46
+
47
+ ## [1.24.0] - 2026-04-06
48
+
49
+ ### Added
50
+ - **Sticker tools** — 5 new tools (59 total): `telegram-get-sticker-set`, `telegram-search-sticker-sets`, `telegram-get-installed-stickers`, `telegram-send-sticker`, `telegram-get-recent-stickers`
51
+ - **Pre-built binaries** — zero-dependency standalone executables for Linux (x64/ARM64), macOS (x64/ARM64), Windows (x64)
52
+ - **Documentation site** — VitePress-based docs at overpod.github.io/mcp-telegram with i18n (English, Russian, Chinese)
53
+
54
+ ## [1.23.0] - 2026-04-05
55
+
56
+ ### Added
57
+ - 11 new tools (22 total): `telegram-send-reaction`, `telegram-edit-message`, `telegram-delete-message`, `telegram-forward-message`, `telegram-mark-as-read`, `telegram-get-dialogs`, `telegram-get-chat-info`, `telegram-send-file`, `telegram-add-contact`, `telegram-create-poll`, `telegram-manage-topics`
58
+ - Account management tools: `telegram-get-sessions`, `telegram-terminate-session`, `telegram-set-privacy`, `telegram-set-auto-delete`, `telegram-update-profile`
59
+ - Better entity resolution for channels and supergroups
9
60
 
10
61
  ## [1.22.0] - 2026-04-01
11
62
 
@@ -257,28 +308,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
257
308
  - Session persistence
258
309
  - GramJS/MTProto integration
259
310
 
260
- [Unreleased]: https://github.com/overpod/mcp-telegram/compare/v1.19.0...HEAD
261
- [1.19.0]: https://github.com/overpod/mcp-telegram/compare/v1.18.0...v1.19.0
262
- [1.18.0]: https://github.com/overpod/mcp-telegram/compare/v1.17.0...v1.18.0
263
- [1.17.0]: https://github.com/overpod/mcp-telegram/compare/v1.16.0...v1.17.0
264
- [1.16.0]: https://github.com/overpod/mcp-telegram/compare/v1.15.0...v1.16.0
265
- [1.15.0]: https://github.com/overpod/mcp-telegram/compare/v1.14.0...v1.15.0
266
- [1.14.0]: https://github.com/overpod/mcp-telegram/compare/v1.13.0...v1.14.0
267
- [1.13.0]: https://github.com/overpod/mcp-telegram/compare/v1.12.0...v1.13.0
268
- [1.12.0]: https://github.com/overpod/mcp-telegram/compare/v1.11.1...v1.12.0
269
- [1.11.1]: https://github.com/overpod/mcp-telegram/compare/v1.11.0...v1.11.1
270
- [1.11.0]: https://github.com/overpod/mcp-telegram/compare/v1.10.1...v1.11.0
271
- [1.10.1]: https://github.com/overpod/mcp-telegram/compare/v1.10.0...v1.10.1
272
- [1.10.0]: https://github.com/overpod/mcp-telegram/compare/v1.9.0...v1.10.0
273
- [1.9.0]: https://github.com/overpod/mcp-telegram/compare/v1.8.1...v1.9.0
274
- [1.8.1]: https://github.com/overpod/mcp-telegram/compare/v1.8.0...v1.8.1
275
- [1.8.0]: https://github.com/overpod/mcp-telegram/compare/v1.7.0...v1.8.0
276
- [1.7.0]: https://github.com/overpod/mcp-telegram/compare/v1.6.0...v1.7.0
277
- [1.6.0]: https://github.com/overpod/mcp-telegram/compare/v1.5.0...v1.6.0
278
- [1.5.0]: https://github.com/overpod/mcp-telegram/compare/v1.4.0...v1.5.0
279
- [1.4.0]: https://github.com/overpod/mcp-telegram/compare/v1.3.1...v1.4.0
280
- [1.3.1]: https://github.com/overpod/mcp-telegram/compare/v1.3.0...v1.3.1
281
- [1.3.0]: https://github.com/overpod/mcp-telegram/compare/v1.2.0...v1.3.0
282
- [1.2.0]: https://github.com/overpod/mcp-telegram/compare/v1.1.0...v1.2.0
283
- [1.1.0]: https://github.com/overpod/mcp-telegram/compare/v1.0.0...v1.1.0
284
- [1.0.0]: https://github.com/overpod/mcp-telegram/releases/tag/v1.0.0
311
+ [Unreleased]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.24.0...HEAD
312
+ [1.24.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.23.0...v1.24.0
313
+ [1.23.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.22.0...v1.23.0
314
+ [1.22.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.21.0...v1.22.0
315
+ [1.21.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.20.0...v1.21.0
316
+ [1.20.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.19.0...v1.20.0
317
+ [1.19.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.18.0...v1.19.0
318
+ [1.18.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.17.0...v1.18.0
319
+ [1.17.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.16.0...v1.17.0
320
+ [1.16.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.15.0...v1.16.0
321
+ [1.15.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.14.0...v1.15.0
322
+ [1.14.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.13.0...v1.14.0
323
+ [1.13.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.12.0...v1.13.0
324
+ [1.12.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.11.1...v1.12.0
325
+ [1.11.1]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.11.0...v1.11.1
326
+ [1.11.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.10.1...v1.11.0
327
+ [1.10.1]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.10.0...v1.10.1
328
+ [1.10.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.9.0...v1.10.0
329
+ [1.9.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.8.1...v1.9.0
330
+ [1.8.1]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.8.0...v1.8.1
331
+ [1.8.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.7.0...v1.8.0
332
+ [1.7.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.6.0...v1.7.0
333
+ [1.6.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.5.0...v1.6.0
334
+ [1.5.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.4.0...v1.5.0
335
+ [1.4.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.3.1...v1.4.0
336
+ [1.3.1]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.3.0...v1.3.1
337
+ [1.3.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.2.0...v1.3.0
338
+ [1.2.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.1.0...v1.2.0
339
+ [1.1.0]: https://github.com/mcp-telegram/mcp-telegram/compare/v1.0.0...v1.1.0
340
+ [1.0.0]: https://github.com/mcp-telegram/mcp-telegram/releases/tag/v1.0.0
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
9
  [![mcp-telegram MCP server](https://glama.ai/mcp/servers/overpod/mcp-telegram/badges/score.svg)](https://glama.ai/mcp/servers/overpod/mcp-telegram)
10
10
 
11
- > **Hosted version available!** Don't want to self-host? Use [mcp-telegram.com](https://mcp-telegram.com) -- connect Telegram to Claude.ai or ChatGPT in 30 seconds with QR code. No API keys needed.
11
+ > **[📖 Documentation](https://mcp-telegram.github.io/mcp-telegram/)** · **[☁️ Cloud version](https://mcp-telegram.com)** connect Telegram to Claude.ai or ChatGPT in 30 seconds with QR code, no API keys needed.
12
12
 
13
13
  <p align="center">
14
14
  <img src="assets/demo.gif" alt="MCP Telegram demo — connect and summarize chats in Claude" width="700">
@@ -18,10 +18,14 @@ An MCP (Model Context Protocol) server that connects AI assistants like Claude t
18
18
 
19
19
  ## Features
20
20
 
21
+ - **Comprehensive tool coverage** -- the most full-featured Telegram MCP server available
21
22
  - **MTProto protocol** -- direct Telegram API access, not the limited Bot API
22
23
  - **Userbot** -- operates as your personal account, not a bot
23
- - **Full-featured** -- messaging, reactions, polls, scheduled messages, media, contacts, and more
24
+ - **Full-featured** -- messaging, reactions, polls, scheduled messages, stickers, media, contacts, and more
24
25
  - **Forum Topics** -- list topics, read per-topic messages, send to specific topics, per-topic unread counts
26
+ - **Stickers** -- search sticker sets, browse installed/recent stickers, send stickers to any chat
27
+ - **Account management** -- update profile, manage privacy settings, sessions, auto-delete timers
28
+ - **Global search** -- search messages across all chats at once
25
29
  - **QR code login** -- authenticate by scanning a QR code in the Telegram app
26
30
  - **Session persistence** -- login once, stay connected across restarts
27
31
  - **Human-readable output** -- sender names are resolved, not just numeric IDs
@@ -132,7 +136,7 @@ mcp-telegram login # QR login
132
136
 
133
137
  ### Pre-built binary (no runtime needed)
134
138
 
135
- Download from [Releases](https://github.com/overpod/mcp-telegram/releases) — standalone single-file binaries, zero dependencies:
139
+ Download from [Releases](https://github.com/mcp-telegram/mcp-telegram/releases) — standalone single-file binaries, zero dependencies:
136
140
 
137
141
  | Platform | Server | Login CLI |
138
142
  |----------|--------|-----------|
@@ -144,8 +148,8 @@ Download from [Releases](https://github.com/overpod/mcp-telegram/releases) — s
144
148
 
145
149
  ```bash
146
150
  # Download (example for Linux x64)
147
- curl -L -o mcp-telegram https://github.com/overpod/mcp-telegram/releases/latest/download/mcp-telegram-linux-x64
148
- curl -L -o mcp-telegram-login https://github.com/overpod/mcp-telegram/releases/latest/download/mcp-telegram-login-linux-x64
151
+ curl -L -o mcp-telegram https://github.com/mcp-telegram/mcp-telegram/releases/latest/download/mcp-telegram-linux-x64
152
+ curl -L -o mcp-telegram-login https://github.com/mcp-telegram/mcp-telegram/releases/latest/download/mcp-telegram-login-linux-x64
149
153
  chmod +x mcp-telegram mcp-telegram-login
150
154
 
151
155
  # Login
@@ -158,7 +162,7 @@ TELEGRAM_API_ID=YOUR_ID TELEGRAM_API_HASH=YOUR_HASH ./mcp-telegram-login
158
162
  ### From source
159
163
 
160
164
  ```bash
161
- git clone https://github.com/overpod/mcp-telegram.git
165
+ git clone https://github.com/mcp-telegram/mcp-telegram.git
162
166
  cd mcp-telegram
163
167
  npm install && npm run build
164
168
  ```
@@ -166,7 +170,7 @@ npm install && npm run build
166
170
  ### Docker
167
171
 
168
172
  ```bash
169
- docker build -t mcp-telegram https://github.com/overpod/mcp-telegram.git
173
+ docker build -t mcp-telegram https://github.com/mcp-telegram/mcp-telegram.git
170
174
  ```
171
175
 
172
176
  Login (interactive terminal required):
@@ -304,9 +308,29 @@ const telegramMcp = new MCPClient({
304
308
 
305
309
  All tools are auto-discoverable via MCP — your AI client will see the full list with parameters and descriptions when connected.
306
310
 
307
- **Categories:** authentication, messaging (send, edit, delete, forward, schedule, polls), reading (chats, messages, search, unread), forum topics, group management (create, edit, invite, kick, ban, admin, leave), contacts & moderation, user profiles, reactions, media.
308
-
309
- > **Tip**: Ask your AI assistant *"What Telegram tools are available?"* to get the current list with parameters.
311
+ | Category | Tools |
312
+ |----------|-------|
313
+ | **Auth** | `telegram-status`, `telegram-login` |
314
+ | **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` |
315
+ | **Scheduled** | `telegram-get-scheduled`, `telegram-delete-scheduled` |
316
+ | **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` |
317
+ | **Drafts** | `telegram-save-draft`, `telegram-get-drafts`, `telegram-clear-drafts` |
318
+ | **Forum Topics** | `telegram-list-topics`, `telegram-read-topic-messages`, `telegram-create-topic`, `telegram-edit-topic`, `telegram-delete-topic` |
319
+ | **Polls** | `telegram-create-poll` |
320
+ | **Reactions** | `telegram-send-reaction`, `telegram-get-reactions`, `telegram-set-default-reaction`, `telegram-get-top-reactions`, `telegram-get-recent-reactions` |
321
+ | **Stickers** | `telegram-send-sticker`, `telegram-get-installed-stickers`, `telegram-get-recent-stickers`, `telegram-get-sticker-set`, `telegram-search-sticker-sets` |
322
+ | **Media** | `telegram-send-file`, `telegram-download-media`, `telegram-get-profile-photo`, `telegram-get-web-preview` |
323
+ | **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` |
324
+ | **Chat Info** | `telegram-get-chat-info`, `telegram-get-chat-members`, `telegram-get-chat-folders` |
325
+ | **Invite Links** | `telegram-create-invite-link`, `telegram-get-invite-links`, `telegram-revoke-invite-link` |
326
+ | **Contacts** | `telegram-get-contacts`, `telegram-add-contact`, `telegram-get-contact-requests` |
327
+ | **Moderation** | `telegram-block-user`, `telegram-unblock-user`, `telegram-report-spam` |
328
+ | **Profiles** | `telegram-get-profile`, `telegram-update-profile` |
329
+ | **Account** | `telegram-get-sessions`, `telegram-terminate-session`, `telegram-set-privacy`, `telegram-set-auto-delete` |
330
+ | **Pinning** | `telegram-pin-message`, `telegram-unpin-message` |
331
+ | **Chat Settings** | `telegram-mute-chat`, `telegram-archive-chat`, `telegram-pin-chat`, `telegram-mark-dialog-unread` |
332
+
333
+ > **Tip**: Ask your AI assistant *"What Telegram tools are available?"* to get the full list with parameters and descriptions.
310
334
 
311
335
  ## Development
312
336
 
@@ -332,9 +356,11 @@ src/
332
356
  messages.ts -- Send, read, search, edit, delete, forward
333
357
  chats.ts -- Chat listing, group management, admin
334
358
  contacts.ts -- Contacts, profiles, moderation
335
- media.ts -- Files, photos
359
+ media.ts -- Files, photos, downloads
336
360
  reactions.ts -- Reactions
337
361
  extras.ts -- Pin, schedule, polls, topics
362
+ stickers.ts -- Sticker sets, send, search, browse
363
+ account.ts -- Sessions, privacy, auto-delete, profile, chat mute/folders, invite links
338
364
  shared.ts -- Shared utilities
339
365
  ```
340
366
 
@@ -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,23 @@
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 { reactionToEmoji } from "../telegram-client.js";
6
+ describe("reactionToEmoji", () => {
7
+ it("returns emoticon for ReactionEmoji", () => {
8
+ const r = new Api.ReactionEmoji({ emoticon: "👍" });
9
+ assert.strictEqual(reactionToEmoji(r), "👍");
10
+ });
11
+ it("returns custom:<id> for ReactionCustomEmoji", () => {
12
+ const r = new Api.ReactionCustomEmoji({ documentId: bigInt(12345) });
13
+ assert.strictEqual(reactionToEmoji(r), "custom:12345");
14
+ });
15
+ it("returns star for ReactionPaid", () => {
16
+ const r = new Api.ReactionPaid();
17
+ assert.strictEqual(reactionToEmoji(r), "⭐");
18
+ });
19
+ it("returns null for ReactionEmpty", () => {
20
+ const r = new Api.ReactionEmpty();
21
+ assert.strictEqual(reactionToEmoji(r), null);
22
+ });
23
+ });
@@ -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 { mergeBannedRights, TelegramService } from "../telegram-client.js";
6
+ describe("mergeBannedRights", () => {
7
+ it("preserves omitted flags from current rights", () => {
8
+ const current = { pinMessages: true, inviteUsers: true, sendMessages: false };
9
+ const merged = mergeBannedRights(current, { sendMessages: false });
10
+ assert.strictEqual(merged.sendMessages, true, "user-specified denial applies");
11
+ assert.strictEqual(merged.pinMessages, true, "omitted flag stays banned");
12
+ assert.strictEqual(merged.inviteUsers, true, "omitted flag stays banned");
13
+ assert.strictEqual(merged.sendMedia, false, "omitted flag with no current stays unbanned");
14
+ });
15
+ it("user-provided value overrides current", () => {
16
+ const current = { pinMessages: true };
17
+ const merged = mergeBannedRights(current, { pinMessages: true });
18
+ assert.strictEqual(merged.pinMessages, false, "pinMessages:true allowed -> not banned");
19
+ });
20
+ it("fills missing flags with false when current is undefined", () => {
21
+ const merged = mergeBannedRights(undefined, { sendMessages: true });
22
+ assert.strictEqual(merged.sendMessages, false);
23
+ assert.strictEqual(merged.sendMedia, false);
24
+ assert.strictEqual(merged.pinMessages, false);
25
+ });
26
+ it("covers all nineteen flags (10 exposed + 9 extra preserved)", () => {
27
+ const merged = mergeBannedRights(undefined, {});
28
+ const keys = Object.keys(merged).sort();
29
+ assert.deepStrictEqual(keys, [
30
+ "changeInfo",
31
+ "embedLinks",
32
+ "inviteUsers",
33
+ "manageTopics",
34
+ "pinMessages",
35
+ "sendAudios",
36
+ "sendDocs",
37
+ "sendGames",
38
+ "sendGifs",
39
+ "sendInline",
40
+ "sendMedia",
41
+ "sendMessages",
42
+ "sendPhotos",
43
+ "sendPlain",
44
+ "sendPolls",
45
+ "sendRoundvideos",
46
+ "sendStickers",
47
+ "sendVideos",
48
+ "sendVoices",
49
+ ]);
50
+ });
51
+ });
52
+ describe("TelegramService.setChatPermissions", () => {
53
+ it("merges new permissions with existing defaultBannedRights", async () => {
54
+ const existingRights = new Api.ChatBannedRights({
55
+ untilDate: 0,
56
+ pinMessages: true,
57
+ inviteUsers: true,
58
+ });
59
+ const channel = new Api.Channel({
60
+ id: bigInt(12345),
61
+ title: "test",
62
+ photo: new Api.ChatPhotoEmpty(),
63
+ date: 0,
64
+ accessHash: bigInt(1),
65
+ defaultBannedRights: existingRights,
66
+ });
67
+ const inputChannel = new Api.InputPeerChannel({ channelId: bigInt(12345), accessHash: bigInt(1) });
68
+ const invocations = [];
69
+ const fakeClient = {
70
+ invoke: async (req) => {
71
+ invocations.push(req);
72
+ if (req instanceof Api.channels.GetFullChannel) {
73
+ return new Api.messages.ChatFull({
74
+ fullChat: new Api.ChannelFull({
75
+ id: bigInt(12345),
76
+ about: "",
77
+ readInboxMaxId: 0,
78
+ readOutboxMaxId: 0,
79
+ unreadCount: 0,
80
+ chatPhoto: new Api.PhotoEmpty({ id: bigInt(0) }),
81
+ notifySettings: new Api.PeerNotifySettings({}),
82
+ pts: 0,
83
+ botInfo: [],
84
+ }),
85
+ chats: [channel],
86
+ users: [],
87
+ });
88
+ }
89
+ return undefined;
90
+ },
91
+ getInputEntity: async () => inputChannel,
92
+ };
93
+ const service = new TelegramService(1, "hash");
94
+ const internals = service;
95
+ internals.client = fakeClient;
96
+ internals.connected = true;
97
+ internals.resolveChat = async () => channel;
98
+ await service.setChatPermissions("12345", { sendMessages: false });
99
+ const editCall = invocations.find((r) => r instanceof Api.messages.EditChatDefaultBannedRights);
100
+ assert.ok(editCall, "EditChatDefaultBannedRights was invoked");
101
+ const rights = editCall.bannedRights;
102
+ assert.strictEqual(rights.sendMessages, true, "sendMessages becomes banned");
103
+ assert.strictEqual(rights.pinMessages, true, "pinMessages stays banned (preserved)");
104
+ assert.strictEqual(rights.inviteUsers, true, "inviteUsers stays banned (preserved)");
105
+ assert.strictEqual(rights.sendMedia, false, "omitted flag with no prior value stays unbanned");
106
+ });
107
+ });
@@ -1,5 +1,22 @@
1
+ import bigInt from "big-integer";
1
2
  import { TelegramClient } from "telegram";
2
3
  import { Api } from "telegram/tl/index.js";
4
+ export declare function describeAdminLogAction(action: Api.TypeChannelAdminLogEventAction): string;
5
+ export declare function describeAdminLogDetails(action: Api.TypeChannelAdminLogEventAction, describeUser: (userId: bigInt.BigInteger) => string): string;
6
+ export declare function reactionToEmoji(reaction: Api.TypeReaction): string | null;
7
+ export type ChatPermissions = {
8
+ sendMessages?: boolean;
9
+ sendMedia?: boolean;
10
+ sendStickers?: boolean;
11
+ sendGifs?: boolean;
12
+ sendPolls?: boolean;
13
+ sendInline?: boolean;
14
+ embedLinks?: boolean;
15
+ changeInfo?: boolean;
16
+ inviteUsers?: boolean;
17
+ pinMessages?: boolean;
18
+ };
19
+ export declare function mergeBannedRights(current: Record<string, unknown> | undefined | null, permissions: ChatPermissions): Record<string, boolean>;
3
20
  export declare class TelegramService {
4
21
  private client;
5
22
  private apiId;
@@ -8,6 +25,7 @@ export declare class TelegramService {
8
25
  private connected;
9
26
  private sessionPath;
10
27
  private rateLimiter;
28
+ private lastTypingAt;
11
29
  lastError: string;
12
30
  get sessionDir(): string;
13
31
  getClient(): TelegramClient | null;
@@ -87,8 +105,6 @@ export declare class TelegramService {
87
105
  blockUser(userId: string): Promise<void>;
88
106
  reportSpam(chatId: string): Promise<void>;
89
107
  markAsRead(chatId: string): Promise<void>;
90
- private static TYPING_ACTIONS;
91
- setTyping(chatId: string, action?: keyof typeof TelegramService.TYPING_ACTIONS): Promise<void>;
92
108
  getMessageById(chatId: string, messageId: number): Promise<{
93
109
  id: number;
94
110
  text: string;
@@ -108,6 +124,68 @@ export declare class TelegramService {
108
124
  forwardMessage(fromChatId: string, toChatId: string, messageIds: number[]): Promise<void>;
109
125
  editMessage(chatId: string, messageId: number, newText: string): Promise<void>;
110
126
  deleteMessages(chatId: string, messageIds: number[]): Promise<void>;
127
+ getScheduledMessages(chatId: string): Promise<Array<{
128
+ id: number;
129
+ date: string;
130
+ text: string;
131
+ media?: {
132
+ type: string;
133
+ fileName?: string;
134
+ size?: number;
135
+ };
136
+ }>>;
137
+ deleteScheduledMessages(chatId: string, messageIds: number[]): Promise<void>;
138
+ getReplies(chatId: string, messageId: number, limit?: number): Promise<Array<{
139
+ id: number;
140
+ text: string;
141
+ sender: string;
142
+ date: string;
143
+ media?: {
144
+ type: string;
145
+ fileName?: string;
146
+ size?: number;
147
+ };
148
+ reactions?: {
149
+ emoji: string;
150
+ count: number;
151
+ me: boolean;
152
+ }[];
153
+ }>>;
154
+ getMessageLink(chatId: string, messageId: number, thread?: boolean): Promise<string>;
155
+ getUnreadMentions(chatId: string, limit?: number): Promise<Array<{
156
+ id: number;
157
+ text: string;
158
+ sender: string;
159
+ date: string;
160
+ media?: {
161
+ type: string;
162
+ fileName?: string;
163
+ size?: number;
164
+ };
165
+ reactions?: {
166
+ emoji: string;
167
+ count: number;
168
+ me: boolean;
169
+ }[];
170
+ }>>;
171
+ getUnreadReactions(chatId: string, limit?: number): Promise<Array<{
172
+ id: number;
173
+ text: string;
174
+ sender: string;
175
+ date: string;
176
+ media?: {
177
+ type: string;
178
+ fileName?: string;
179
+ size?: number;
180
+ };
181
+ reactions?: {
182
+ emoji: string;
183
+ count: number;
184
+ me: boolean;
185
+ }[];
186
+ }>>;
187
+ translateText(chatId: string, messageIds: number[], toLang: string): Promise<string[]>;
188
+ sendTyping(chatId: string, action?: "typing" | "upload_photo" | "upload_document" | "cancel"): Promise<void>;
111
189
  /**
112
190
  * Resolve a chat by ID, username, or display name.
113
191
  * Falls back to searching user's dialogs if getEntity() fails.
@@ -258,6 +336,13 @@ export declare class TelegramService {
258
336
  }[];
259
337
  total: number;
260
338
  }>;
339
+ setDefaultReaction(emoji: string): Promise<void>;
340
+ getTopReactions(limit: number): Promise<Array<{
341
+ emoji: string;
342
+ }>>;
343
+ getRecentReactions(limit: number): Promise<Array<{
344
+ emoji: string;
345
+ }>>;
261
346
  sendScheduledMessage(chatId: string, text: string, scheduleDate: number, replyTo?: number, parseMode?: "md" | "html"): Promise<void>;
262
347
  createPoll(chatId: string, question: string, answers: string[], options?: {
263
348
  multipleChoice?: boolean;
@@ -327,6 +412,30 @@ export declare class TelegramService {
327
412
  removeAdmin(chatId: string, userId: string): Promise<void>;
328
413
  unblockUser(userId: string): Promise<void>;
329
414
  muteChat(chatId: string, muteUntil: number): Promise<void>;
415
+ archiveChat(chatId: string, archive: boolean): Promise<void>;
416
+ pinDialog(chatId: string, pin: boolean): Promise<void>;
417
+ markDialogUnread(chatId: string, unread: boolean): Promise<void>;
418
+ getAdminLog(chatId: string, limit?: number, q?: string): Promise<Array<{
419
+ id: string;
420
+ date: string;
421
+ userId: string;
422
+ userName: string;
423
+ action: string;
424
+ details: string;
425
+ }>>;
426
+ setChatPermissions(chatId: string, permissions: ChatPermissions): Promise<void>;
427
+ setSlowMode(chatId: string, seconds: number): Promise<void>;
428
+ createForumTopic(chatId: string, title: string, iconColor?: number, iconEmojiId?: string): Promise<{
429
+ id: number;
430
+ title: string;
431
+ }>;
432
+ editForumTopic(chatId: string, topicId: number, options: {
433
+ title?: string;
434
+ iconEmojiId?: string;
435
+ closed?: boolean;
436
+ hidden?: boolean;
437
+ }): Promise<void>;
438
+ deleteForumTopic(chatId: string, topicId: number): Promise<void>;
330
439
  exportInviteLink(chatId: string, options?: {
331
440
  expireDate?: number;
332
441
  usageLimit?: number;
@@ -391,6 +500,26 @@ export declare class TelegramService {
391
500
  count: number;
392
501
  }>>;
393
502
  sendSticker(chatId: string, stickerSetShortName: string, stickerIndex: number, replyTo?: number): Promise<Api.Message | Api.UpdateShortSentMessage | undefined>;
503
+ saveDraft(chatId: string, text: string, replyTo?: number): Promise<void>;
504
+ getAllDrafts(): Promise<Array<{
505
+ chatId: string;
506
+ chatTitle: string;
507
+ text: string;
508
+ date: string;
509
+ }>>;
510
+ clearAllDrafts(): Promise<void>;
511
+ getSavedDialogs(limit: number): Promise<Array<{
512
+ peerId: string;
513
+ peerTitle: string;
514
+ lastMsgId: number;
515
+ }>>;
516
+ getWebPreview(url: string): Promise<{
517
+ type: string;
518
+ url?: string;
519
+ title?: string;
520
+ description?: string;
521
+ siteName?: string;
522
+ } | null>;
394
523
  getRecentStickers(): Promise<Array<{
395
524
  id: string;
396
525
  accessHash: string;