@overpod/mcp-telegram 1.26.0 → 1.27.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/dist/client.d.ts +12 -0
- package/dist/client.js +136 -0
- package/dist/index.js +15 -24
- package/dist/ipc-protocol.d.ts +19 -0
- package/dist/ipc-protocol.js +22 -0
- package/dist/lock.d.ts +12 -0
- package/dist/lock.js +83 -0
- package/dist/master.d.ts +1 -0
- package/dist/master.js +105 -0
- package/dist/rate-limiter.d.ts +1 -1
- package/dist/rate-limiter.js +8 -8
- package/dist/telegram-client.d.ts +6 -470
- package/dist/telegram-client.js +17 -874
- package/dist/telegram-helpers.d.ts +470 -0
- package/dist/telegram-helpers.js +870 -0
- package/dist/tools/account.js +7 -7
- package/dist/tools/boosts.js +4 -4
- package/dist/tools/chats.js +7 -7
- package/dist/tools/contacts.js +3 -3
- package/dist/tools/extras.js +3 -3
- package/dist/tools/group-calls.js +3 -3
- package/dist/tools/messages.js +17 -17
- package/dist/tools/quick-replies.js +3 -3
- package/dist/tools/reactions.js +2 -2
- package/dist/tools/shared.d.ts +3 -3
- package/dist/tools/shared.js +8 -7
- package/dist/tools/stars.js +3 -3
- package/dist/tools/stickers.js +5 -5
- package/dist/tools/stories.js +5 -5
- package/package.json +1 -1
- package/dist/__tests__/admin-log.test.d.ts +0 -1
- package/dist/__tests__/admin-log.test.js +0 -41
- package/dist/__tests__/approve-join-request.test.d.ts +0 -1
- package/dist/__tests__/approve-join-request.test.js +0 -107
- package/dist/__tests__/boosts.test.d.ts +0 -1
- package/dist/__tests__/boosts.test.js +0 -310
- package/dist/__tests__/broadcast-stats.test.d.ts +0 -1
- package/dist/__tests__/broadcast-stats.test.js +0 -172
- package/dist/__tests__/business-chat-links.test.d.ts +0 -1
- package/dist/__tests__/business-chat-links.test.js +0 -102
- package/dist/__tests__/get-message-buttons.test.d.ts +0 -1
- package/dist/__tests__/get-message-buttons.test.js +0 -122
- package/dist/__tests__/group-calls.test.d.ts +0 -1
- package/dist/__tests__/group-calls.test.js +0 -503
- package/dist/__tests__/inline-query-send.test.d.ts +0 -1
- package/dist/__tests__/inline-query-send.test.js +0 -94
- package/dist/__tests__/inline-query.test.d.ts +0 -1
- package/dist/__tests__/inline-query.test.js +0 -115
- package/dist/__tests__/megagroup-stats.test.d.ts +0 -1
- package/dist/__tests__/megagroup-stats.test.js +0 -166
- package/dist/__tests__/press-button.test.d.ts +0 -1
- package/dist/__tests__/press-button.test.js +0 -123
- package/dist/__tests__/quick-replies.test.d.ts +0 -1
- package/dist/__tests__/quick-replies.test.js +0 -245
- package/dist/__tests__/rate-limiter.test.d.ts +0 -1
- package/dist/__tests__/rate-limiter.test.js +0 -81
- package/dist/__tests__/reactions.test.d.ts +0 -1
- package/dist/__tests__/reactions.test.js +0 -23
- package/dist/__tests__/set-chat-permissions-merge.test.d.ts +0 -1
- package/dist/__tests__/set-chat-permissions-merge.test.js +0 -107
- package/dist/__tests__/set-chat-reactions.test.d.ts +0 -1
- package/dist/__tests__/set-chat-reactions.test.js +0 -129
- package/dist/__tests__/stars-status.test.d.ts +0 -1
- package/dist/__tests__/stars-status.test.js +0 -205
- package/dist/__tests__/stars-transactions.test.d.ts +0 -1
- package/dist/__tests__/stars-transactions.test.js +0 -82
- package/dist/__tests__/stories.test.d.ts +0 -1
- package/dist/__tests__/stories.test.js +0 -361
- package/dist/__tests__/toggle-anti-spam.test.d.ts +0 -1
- package/dist/__tests__/toggle-anti-spam.test.js +0 -80
- package/dist/__tests__/toggle-channel-signatures.test.d.ts +0 -1
- package/dist/__tests__/toggle-channel-signatures.test.js +0 -80
- package/dist/__tests__/toggle-forum-mode.test.d.ts +0 -1
- package/dist/__tests__/toggle-forum-mode.test.js +0 -80
- package/dist/__tests__/toggle-prehistory-hidden.test.d.ts +0 -1
- package/dist/__tests__/toggle-prehistory-hidden.test.js +0 -80
- package/dist/__tests__/tools/shared.test.d.ts +0 -1
- package/dist/__tests__/tools/shared.test.js +0 -110
- package/dist/__tests__/updates.test.d.ts +0 -1
- package/dist/__tests__/updates.test.js +0 -221
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
3
|
-
import { RateLimiter } from "../rate-limiter.js";
|
|
4
|
-
describe("RateLimiter", () => {
|
|
5
|
-
it("should execute a function successfully", async () => {
|
|
6
|
-
const limiter = new RateLimiter({ maxRequestsPerSecond: 100 });
|
|
7
|
-
const result = await limiter.execute(async () => "success");
|
|
8
|
-
assert.strictEqual(result, "success");
|
|
9
|
-
});
|
|
10
|
-
it("should enforce rate limiting between requests", async () => {
|
|
11
|
-
const limiter = new RateLimiter({ maxRequestsPerSecond: 10 }); // 10 req/s = 100ms between requests
|
|
12
|
-
const start = Date.now();
|
|
13
|
-
await limiter.execute(async () => "first");
|
|
14
|
-
await limiter.execute(async () => "second");
|
|
15
|
-
const elapsed = Date.now() - start;
|
|
16
|
-
assert.ok(elapsed >= 90, `Expected at least 90ms, got ${elapsed}ms`);
|
|
17
|
-
});
|
|
18
|
-
it("should retry on FLOOD_WAIT error", async () => {
|
|
19
|
-
const limiter = new RateLimiter({ maxRetries: 2, maxRequestsPerSecond: 100 });
|
|
20
|
-
let attempts = 0;
|
|
21
|
-
const result = await limiter.execute(async () => {
|
|
22
|
-
attempts++;
|
|
23
|
-
if (attempts < 2) {
|
|
24
|
-
throw new Error("FLOOD_WAIT_1");
|
|
25
|
-
}
|
|
26
|
-
return "success after retry";
|
|
27
|
-
});
|
|
28
|
-
assert.strictEqual(result, "success after retry");
|
|
29
|
-
assert.strictEqual(attempts, 2);
|
|
30
|
-
});
|
|
31
|
-
it("should throw after max retries on FLOOD_WAIT", async () => {
|
|
32
|
-
const limiter = new RateLimiter({ maxRetries: 1, maxRequestsPerSecond: 100 });
|
|
33
|
-
await assert.rejects(async () => {
|
|
34
|
-
await limiter.execute(async () => {
|
|
35
|
-
throw new Error("FLOOD_WAIT_2");
|
|
36
|
-
});
|
|
37
|
-
}, {
|
|
38
|
-
message: /Rate limit exceeded after 1 retries/,
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
it("should retry on network errors with exponential backoff", async () => {
|
|
42
|
-
const limiter = new RateLimiter({
|
|
43
|
-
maxRetries: 2,
|
|
44
|
-
initialRetryDelay: 100,
|
|
45
|
-
maxRequestsPerSecond: 100,
|
|
46
|
-
});
|
|
47
|
-
let attempts = 0;
|
|
48
|
-
const result = await limiter.execute(async () => {
|
|
49
|
-
attempts++;
|
|
50
|
-
if (attempts < 2) {
|
|
51
|
-
throw new Error("TIMEOUT");
|
|
52
|
-
}
|
|
53
|
-
return "recovered";
|
|
54
|
-
});
|
|
55
|
-
assert.strictEqual(result, "recovered");
|
|
56
|
-
assert.strictEqual(attempts, 2);
|
|
57
|
-
});
|
|
58
|
-
it("should not retry on non-retryable errors", async () => {
|
|
59
|
-
const limiter = new RateLimiter({ maxRetries: 3, maxRequestsPerSecond: 100 });
|
|
60
|
-
let attempts = 0;
|
|
61
|
-
await assert.rejects(async () => {
|
|
62
|
-
await limiter.execute(async () => {
|
|
63
|
-
attempts++;
|
|
64
|
-
throw new Error("AUTH_KEY_UNREGISTERED");
|
|
65
|
-
});
|
|
66
|
-
}, {
|
|
67
|
-
message: "AUTH_KEY_UNREGISTERED",
|
|
68
|
-
});
|
|
69
|
-
assert.strictEqual(attempts, 1, "Should not retry non-retryable errors");
|
|
70
|
-
});
|
|
71
|
-
it("should handle FLOOD_WAIT with seconds parsing", async () => {
|
|
72
|
-
const limiter = new RateLimiter({ maxRetries: 1, maxRequestsPerSecond: 100 });
|
|
73
|
-
await assert.rejects(async () => {
|
|
74
|
-
await limiter.execute(async () => {
|
|
75
|
-
throw new Error("FLOOD_WAIT_1");
|
|
76
|
-
});
|
|
77
|
-
}, {
|
|
78
|
-
message: /Telegram requires 1s wait/,
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,23 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,107 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,129 +0,0 @@
|
|
|
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, invocations) {
|
|
7
|
-
const fakeClient = {
|
|
8
|
-
invoke: async (req) => {
|
|
9
|
-
invocations.push(req);
|
|
10
|
-
return undefined;
|
|
11
|
-
},
|
|
12
|
-
};
|
|
13
|
-
const service = new TelegramService(1, "hash");
|
|
14
|
-
const internals = service;
|
|
15
|
-
internals.client = fakeClient;
|
|
16
|
-
internals.connected = true;
|
|
17
|
-
internals.resolveChat = async () => entity;
|
|
18
|
-
return service;
|
|
19
|
-
}
|
|
20
|
-
describe("TelegramService.setChatAvailableReactions", () => {
|
|
21
|
-
it("sends ChatReactionsAll without custom when type='all'", async () => {
|
|
22
|
-
const channel = new Api.Channel({
|
|
23
|
-
id: bigInt(12345),
|
|
24
|
-
title: "channel",
|
|
25
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
26
|
-
date: 0,
|
|
27
|
-
accessHash: bigInt(1),
|
|
28
|
-
megagroup: true,
|
|
29
|
-
});
|
|
30
|
-
const invocations = [];
|
|
31
|
-
const service = makeService(channel, invocations);
|
|
32
|
-
await service.setChatAvailableReactions("12345", { type: "all" });
|
|
33
|
-
const call = invocations.find((r) => r instanceof Api.messages.SetChatAvailableReactions);
|
|
34
|
-
assert.ok(call, "SetChatAvailableReactions was invoked");
|
|
35
|
-
assert.ok(call.availableReactions instanceof Api.ChatReactionsAll);
|
|
36
|
-
assert.strictEqual(call.availableReactions.allowCustom, undefined);
|
|
37
|
-
});
|
|
38
|
-
it("sends ChatReactionsAll with allowCustom=true when requested", async () => {
|
|
39
|
-
const channel = new Api.Channel({
|
|
40
|
-
id: bigInt(22222),
|
|
41
|
-
title: "channel",
|
|
42
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
43
|
-
date: 0,
|
|
44
|
-
accessHash: bigInt(1),
|
|
45
|
-
megagroup: true,
|
|
46
|
-
});
|
|
47
|
-
const invocations = [];
|
|
48
|
-
const service = makeService(channel, invocations);
|
|
49
|
-
await service.setChatAvailableReactions("22222", { type: "all", allowCustom: true });
|
|
50
|
-
const call = invocations.find((r) => r instanceof Api.messages.SetChatAvailableReactions);
|
|
51
|
-
assert.ok(call);
|
|
52
|
-
assert.ok(call.availableReactions instanceof Api.ChatReactionsAll);
|
|
53
|
-
assert.strictEqual(call.availableReactions.allowCustom, true);
|
|
54
|
-
});
|
|
55
|
-
it("sends ChatReactionsNone when type='none'", async () => {
|
|
56
|
-
const channel = new Api.Channel({
|
|
57
|
-
id: bigInt(33333),
|
|
58
|
-
title: "channel",
|
|
59
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
60
|
-
date: 0,
|
|
61
|
-
accessHash: bigInt(1),
|
|
62
|
-
megagroup: true,
|
|
63
|
-
});
|
|
64
|
-
const invocations = [];
|
|
65
|
-
const service = makeService(channel, invocations);
|
|
66
|
-
await service.setChatAvailableReactions("33333", { type: "none" });
|
|
67
|
-
const call = invocations.find((r) => r instanceof Api.messages.SetChatAvailableReactions);
|
|
68
|
-
assert.ok(call);
|
|
69
|
-
assert.ok(call.availableReactions instanceof Api.ChatReactionsNone);
|
|
70
|
-
});
|
|
71
|
-
it("sends ChatReactionsSome with ReactionEmoji list when type='some'", async () => {
|
|
72
|
-
const channel = new Api.Channel({
|
|
73
|
-
id: bigInt(44444),
|
|
74
|
-
title: "channel",
|
|
75
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
76
|
-
date: 0,
|
|
77
|
-
accessHash: bigInt(1),
|
|
78
|
-
megagroup: true,
|
|
79
|
-
});
|
|
80
|
-
const invocations = [];
|
|
81
|
-
const service = makeService(channel, invocations);
|
|
82
|
-
await service.setChatAvailableReactions("44444", { type: "some", emoji: ["👍", "❤️", "🔥"] });
|
|
83
|
-
const call = invocations.find((r) => r instanceof Api.messages.SetChatAvailableReactions);
|
|
84
|
-
assert.ok(call);
|
|
85
|
-
assert.ok(call.availableReactions instanceof Api.ChatReactionsSome);
|
|
86
|
-
const some = call.availableReactions;
|
|
87
|
-
assert.strictEqual(some.reactions.length, 3);
|
|
88
|
-
for (const r of some.reactions) {
|
|
89
|
-
assert.ok(r instanceof Api.ReactionEmoji);
|
|
90
|
-
}
|
|
91
|
-
assert.deepStrictEqual(some.reactions.map((r) => r.emoticon), ["👍", "❤️", "🔥"]);
|
|
92
|
-
});
|
|
93
|
-
it("accepts basic Chat entity (non-channel group)", async () => {
|
|
94
|
-
const chat = new Api.Chat({
|
|
95
|
-
id: bigInt(55555),
|
|
96
|
-
title: "basic group",
|
|
97
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
98
|
-
participantsCount: 5,
|
|
99
|
-
date: 0,
|
|
100
|
-
version: 0,
|
|
101
|
-
});
|
|
102
|
-
const invocations = [];
|
|
103
|
-
const service = makeService(chat, invocations);
|
|
104
|
-
await service.setChatAvailableReactions("55555", { type: "none" });
|
|
105
|
-
const call = invocations.find((r) => r instanceof Api.messages.SetChatAvailableReactions);
|
|
106
|
-
assert.ok(call, "SetChatAvailableReactions was invoked for basic group");
|
|
107
|
-
});
|
|
108
|
-
it("rejects User peer (non-chat)", async () => {
|
|
109
|
-
const user = new Api.User({ id: bigInt(66666), accessHash: bigInt(1), firstName: "Bob" });
|
|
110
|
-
const invocations = [];
|
|
111
|
-
const service = makeService(user, invocations);
|
|
112
|
-
await assert.rejects(service.setChatAvailableReactions("66666", { type: "none" }), /groups, supergroups, and channels/);
|
|
113
|
-
assert.strictEqual(invocations.length, 0);
|
|
114
|
-
});
|
|
115
|
-
it("rejects empty emoji list on type='some'", async () => {
|
|
116
|
-
const channel = new Api.Channel({
|
|
117
|
-
id: bigInt(77777),
|
|
118
|
-
title: "channel",
|
|
119
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
120
|
-
date: 0,
|
|
121
|
-
accessHash: bigInt(1),
|
|
122
|
-
megagroup: true,
|
|
123
|
-
});
|
|
124
|
-
const invocations = [];
|
|
125
|
-
const service = makeService(channel, invocations);
|
|
126
|
-
await assert.rejects(service.setChatAvailableReactions("77777", { type: "some", emoji: [] }), /non-empty/);
|
|
127
|
-
assert.strictEqual(invocations.length, 0);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,205 +0,0 @@
|
|
|
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 { summarizeStarsAmount, summarizeStarsStatus, summarizeStarsSubscription, summarizeStarsTransaction, summarizeStarsTransactionPeer, TelegramService, } from "../telegram-client.js";
|
|
6
|
-
import { isStarsEnabled } from "../tools/stars.js";
|
|
7
|
-
function makeService(invocations, responder) {
|
|
8
|
-
const fakeClient = {
|
|
9
|
-
invoke: async (req) => {
|
|
10
|
-
invocations.push(req);
|
|
11
|
-
return responder(req);
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
const service = new TelegramService(1, "hash");
|
|
15
|
-
const internals = service;
|
|
16
|
-
internals.client = fakeClient;
|
|
17
|
-
internals.connected = true;
|
|
18
|
-
return service;
|
|
19
|
-
}
|
|
20
|
-
describe("summarizeStarsAmount", () => {
|
|
21
|
-
it("stringifies amount (bigInt) and passes nanos through", () => {
|
|
22
|
-
const out = summarizeStarsAmount(new Api.StarsAmount({ amount: bigInt(12345), nanos: 67 }));
|
|
23
|
-
assert.deepStrictEqual(out, { amount: "12345", nanos: 67 });
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe("summarizeStarsTransactionPeer", () => {
|
|
27
|
-
it("maps app/play/premiumBot/fragment/ads/api/unsupported", () => {
|
|
28
|
-
assert.deepStrictEqual(summarizeStarsTransactionPeer(new Api.StarsTransactionPeerAppStore()), { kind: "appStore" });
|
|
29
|
-
assert.deepStrictEqual(summarizeStarsTransactionPeer(new Api.StarsTransactionPeerPlayMarket()), {
|
|
30
|
-
kind: "playMarket",
|
|
31
|
-
});
|
|
32
|
-
assert.deepStrictEqual(summarizeStarsTransactionPeer(new Api.StarsTransactionPeerPremiumBot()), {
|
|
33
|
-
kind: "premiumBot",
|
|
34
|
-
});
|
|
35
|
-
assert.deepStrictEqual(summarizeStarsTransactionPeer(new Api.StarsTransactionPeerFragment()), { kind: "fragment" });
|
|
36
|
-
assert.deepStrictEqual(summarizeStarsTransactionPeer(new Api.StarsTransactionPeerAds()), { kind: "ads" });
|
|
37
|
-
assert.deepStrictEqual(summarizeStarsTransactionPeer(new Api.StarsTransactionPeerAPI()), { kind: "api" });
|
|
38
|
-
assert.deepStrictEqual(summarizeStarsTransactionPeer(new Api.StarsTransactionPeerUnsupported()), {
|
|
39
|
-
kind: "unsupported",
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
it("maps StarsTransactionPeer with a user peer", () => {
|
|
43
|
-
const out = summarizeStarsTransactionPeer(new Api.StarsTransactionPeer({ peer: new Api.PeerUser({ userId: bigInt(42) }) }));
|
|
44
|
-
assert.deepStrictEqual(out, { kind: "peer", peer: { kind: "user", id: "42" } });
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
describe("summarizeStarsTransaction", () => {
|
|
48
|
-
it("maps core fields and flags", () => {
|
|
49
|
-
const tx = new Api.StarsTransaction({
|
|
50
|
-
id: "tx-1",
|
|
51
|
-
stars: new Api.StarsAmount({ amount: bigInt(500), nanos: 0 }),
|
|
52
|
-
date: 1710000000,
|
|
53
|
-
peer: new Api.StarsTransactionPeerAppStore(),
|
|
54
|
-
refund: true,
|
|
55
|
-
gift: true,
|
|
56
|
-
title: "Top-up",
|
|
57
|
-
msgId: 55,
|
|
58
|
-
transactionDate: 1710000100,
|
|
59
|
-
transactionUrl: "https://example.com/x",
|
|
60
|
-
});
|
|
61
|
-
const out = summarizeStarsTransaction(tx);
|
|
62
|
-
assert.strictEqual(out.id, "tx-1");
|
|
63
|
-
assert.deepStrictEqual(out.stars, { amount: "500", nanos: 0 });
|
|
64
|
-
assert.strictEqual(out.date, 1710000000);
|
|
65
|
-
assert.deepStrictEqual(out.peer, { kind: "appStore" });
|
|
66
|
-
assert.strictEqual(out.refund, true);
|
|
67
|
-
assert.strictEqual(out.gift, true);
|
|
68
|
-
assert.strictEqual(out.title, "Top-up");
|
|
69
|
-
assert.strictEqual(out.msgId, 55);
|
|
70
|
-
assert.strictEqual(out.transactionDate, 1710000100);
|
|
71
|
-
assert.strictEqual(out.transactionUrl, "https://example.com/x");
|
|
72
|
-
assert.strictEqual(out.description, undefined);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
describe("summarizeStarsSubscription", () => {
|
|
76
|
-
it("maps id, peer, pricing and flags", () => {
|
|
77
|
-
const sub = new Api.StarsSubscription({
|
|
78
|
-
id: "sub-1",
|
|
79
|
-
peer: new Api.PeerChannel({ channelId: bigInt(100) }),
|
|
80
|
-
untilDate: 1720000000,
|
|
81
|
-
pricing: new Api.StarsSubscriptionPricing({ period: 2592000, amount: bigInt(150) }),
|
|
82
|
-
canceled: true,
|
|
83
|
-
title: "Premium group",
|
|
84
|
-
invoiceSlug: "slug-x",
|
|
85
|
-
});
|
|
86
|
-
const out = summarizeStarsSubscription(sub);
|
|
87
|
-
assert.strictEqual(out.id, "sub-1");
|
|
88
|
-
assert.deepStrictEqual(out.peer, { kind: "channel", id: "100" });
|
|
89
|
-
assert.strictEqual(out.untilDate, 1720000000);
|
|
90
|
-
assert.deepStrictEqual(out.pricing, { period: 2592000, amount: "150" });
|
|
91
|
-
assert.strictEqual(out.canceled, true);
|
|
92
|
-
assert.strictEqual(out.title, "Premium group");
|
|
93
|
-
assert.strictEqual(out.invoiceSlug, "slug-x");
|
|
94
|
-
assert.strictEqual(out.missingBalance, undefined);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
describe("summarizeStarsStatus", () => {
|
|
98
|
-
it("maps balance, history, subscriptions and offsets", () => {
|
|
99
|
-
const resp = new Api.payments.StarsStatus({
|
|
100
|
-
balance: new Api.StarsAmount({ amount: bigInt(1000), nanos: 5 }),
|
|
101
|
-
history: [
|
|
102
|
-
new Api.StarsTransaction({
|
|
103
|
-
id: "t1",
|
|
104
|
-
stars: new Api.StarsAmount({ amount: bigInt(10), nanos: 0 }),
|
|
105
|
-
date: 1,
|
|
106
|
-
peer: new Api.StarsTransactionPeerPremiumBot(),
|
|
107
|
-
}),
|
|
108
|
-
],
|
|
109
|
-
subscriptions: [
|
|
110
|
-
new Api.StarsSubscription({
|
|
111
|
-
id: "s1",
|
|
112
|
-
peer: new Api.PeerChannel({ channelId: bigInt(7) }),
|
|
113
|
-
untilDate: 100,
|
|
114
|
-
pricing: new Api.StarsSubscriptionPricing({ period: 30, amount: bigInt(5) }),
|
|
115
|
-
}),
|
|
116
|
-
],
|
|
117
|
-
subscriptionsNextOffset: "sub-cursor",
|
|
118
|
-
subscriptionsMissingBalance: bigInt(250),
|
|
119
|
-
nextOffset: "tx-cursor",
|
|
120
|
-
chats: [],
|
|
121
|
-
users: [],
|
|
122
|
-
});
|
|
123
|
-
const out = summarizeStarsStatus(resp);
|
|
124
|
-
assert.deepStrictEqual(out.balance, { amount: "1000", nanos: 5 });
|
|
125
|
-
assert.strictEqual(out.history?.length, 1);
|
|
126
|
-
assert.strictEqual(out.history?.[0].id, "t1");
|
|
127
|
-
assert.strictEqual(out.subscriptions?.length, 1);
|
|
128
|
-
assert.strictEqual(out.subscriptions?.[0].id, "s1");
|
|
129
|
-
assert.strictEqual(out.subscriptionsNextOffset, "sub-cursor");
|
|
130
|
-
assert.strictEqual(out.subscriptionsMissingBalance, "250");
|
|
131
|
-
assert.strictEqual(out.nextOffset, "tx-cursor");
|
|
132
|
-
});
|
|
133
|
-
it("omits empty history/subscriptions and empty offset strings", () => {
|
|
134
|
-
const resp = new Api.payments.StarsStatus({
|
|
135
|
-
balance: new Api.StarsAmount({ amount: bigInt(0), nanos: 0 }),
|
|
136
|
-
history: [],
|
|
137
|
-
subscriptions: [],
|
|
138
|
-
subscriptionsNextOffset: "",
|
|
139
|
-
nextOffset: "",
|
|
140
|
-
chats: [],
|
|
141
|
-
users: [],
|
|
142
|
-
});
|
|
143
|
-
const out = summarizeStarsStatus(resp);
|
|
144
|
-
assert.strictEqual(out.history, undefined);
|
|
145
|
-
assert.strictEqual(out.subscriptions, undefined);
|
|
146
|
-
assert.strictEqual(out.subscriptionsNextOffset, undefined);
|
|
147
|
-
assert.strictEqual(out.nextOffset, undefined);
|
|
148
|
-
assert.strictEqual(out.subscriptionsMissingBalance, undefined);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
describe("TelegramService.getStarsStatus", () => {
|
|
152
|
-
it("invokes payments.GetStarsStatus with resolved peer and returns summary", async () => {
|
|
153
|
-
const invocations = [];
|
|
154
|
-
const service = makeService(invocations, () => new Api.payments.StarsStatus({
|
|
155
|
-
balance: new Api.StarsAmount({ amount: bigInt(42), nanos: 0 }),
|
|
156
|
-
chats: [],
|
|
157
|
-
users: [],
|
|
158
|
-
}));
|
|
159
|
-
const internals = service;
|
|
160
|
-
internals.resolvePeer = async (_id) => new Api.InputPeerUser({ userId: bigInt(1), accessHash: bigInt(2) });
|
|
161
|
-
const out = await service.getStarsStatus("me");
|
|
162
|
-
const call = invocations.find((r) => r instanceof Api.payments.GetStarsStatus);
|
|
163
|
-
assert.ok(call);
|
|
164
|
-
assert.deepStrictEqual(out.balance, { amount: "42", nanos: 0 });
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
describe("isStarsEnabled gate", () => {
|
|
168
|
-
it("is off by default", () => {
|
|
169
|
-
const prev = process.env.MCP_TELEGRAM_ENABLE_STARS;
|
|
170
|
-
delete process.env.MCP_TELEGRAM_ENABLE_STARS;
|
|
171
|
-
try {
|
|
172
|
-
assert.strictEqual(isStarsEnabled(), false);
|
|
173
|
-
}
|
|
174
|
-
finally {
|
|
175
|
-
if (prev !== undefined)
|
|
176
|
-
process.env.MCP_TELEGRAM_ENABLE_STARS = prev;
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
it("turns on when env is '1'", () => {
|
|
180
|
-
const prev = process.env.MCP_TELEGRAM_ENABLE_STARS;
|
|
181
|
-
process.env.MCP_TELEGRAM_ENABLE_STARS = "1";
|
|
182
|
-
try {
|
|
183
|
-
assert.strictEqual(isStarsEnabled(), true);
|
|
184
|
-
}
|
|
185
|
-
finally {
|
|
186
|
-
if (prev === undefined)
|
|
187
|
-
delete process.env.MCP_TELEGRAM_ENABLE_STARS;
|
|
188
|
-
else
|
|
189
|
-
process.env.MCP_TELEGRAM_ENABLE_STARS = prev;
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
it("stays off for any non-'1' value", () => {
|
|
193
|
-
const prev = process.env.MCP_TELEGRAM_ENABLE_STARS;
|
|
194
|
-
process.env.MCP_TELEGRAM_ENABLE_STARS = "true";
|
|
195
|
-
try {
|
|
196
|
-
assert.strictEqual(isStarsEnabled(), false);
|
|
197
|
-
}
|
|
198
|
-
finally {
|
|
199
|
-
if (prev === undefined)
|
|
200
|
-
delete process.env.MCP_TELEGRAM_ENABLE_STARS;
|
|
201
|
-
else
|
|
202
|
-
process.env.MCP_TELEGRAM_ENABLE_STARS = prev;
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,82 +0,0 @@
|
|
|
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(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("TelegramService.getStarsTransactions", () => {
|
|
20
|
-
it("invokes payments.GetStarsTransactions with resolved peer, default offset='' and limit=50", async () => {
|
|
21
|
-
const invocations = [];
|
|
22
|
-
const service = makeService(invocations, () => new Api.payments.StarsStatus({
|
|
23
|
-
balance: new Api.StarsAmount({ amount: bigInt(100), nanos: 0 }),
|
|
24
|
-
history: [
|
|
25
|
-
new Api.StarsTransaction({
|
|
26
|
-
id: "tx-A",
|
|
27
|
-
stars: new Api.StarsAmount({ amount: bigInt(25), nanos: 0 }),
|
|
28
|
-
date: 1710000000,
|
|
29
|
-
peer: new Api.StarsTransactionPeerAppStore(),
|
|
30
|
-
}),
|
|
31
|
-
],
|
|
32
|
-
nextOffset: "next-cursor",
|
|
33
|
-
chats: [],
|
|
34
|
-
users: [],
|
|
35
|
-
}));
|
|
36
|
-
const internals = service;
|
|
37
|
-
internals.resolvePeer = async (_id) => new Api.InputPeerUser({ userId: bigInt(1), accessHash: bigInt(2) });
|
|
38
|
-
const out = await service.getStarsTransactions("me");
|
|
39
|
-
const call = invocations.find((r) => r instanceof Api.payments.GetStarsTransactions);
|
|
40
|
-
assert.ok(call, "GetStarsTransactions must be invoked");
|
|
41
|
-
assert.strictEqual(call.offset, "");
|
|
42
|
-
assert.strictEqual(call.limit, 50);
|
|
43
|
-
assert.strictEqual(call.inbound, undefined);
|
|
44
|
-
assert.strictEqual(call.outbound, undefined);
|
|
45
|
-
assert.strictEqual(call.ascending, undefined);
|
|
46
|
-
assert.strictEqual(call.subscriptionId, undefined);
|
|
47
|
-
assert.deepStrictEqual(out.balance, { amount: "100", nanos: 0 });
|
|
48
|
-
assert.strictEqual(out.history?.length, 1);
|
|
49
|
-
assert.strictEqual(out.history?.[0].id, "tx-A");
|
|
50
|
-
assert.strictEqual(out.nextOffset, "next-cursor");
|
|
51
|
-
});
|
|
52
|
-
it("forwards filter and pagination options verbatim", async () => {
|
|
53
|
-
const invocations = [];
|
|
54
|
-
const service = makeService(invocations, () => new Api.payments.StarsStatus({
|
|
55
|
-
balance: new Api.StarsAmount({ amount: bigInt(0), nanos: 0 }),
|
|
56
|
-
chats: [],
|
|
57
|
-
users: [],
|
|
58
|
-
}));
|
|
59
|
-
const internals = service;
|
|
60
|
-
internals.resolvePeer = async (_id) => new Api.InputPeerUser({ userId: bigInt(7), accessHash: bigInt(8) });
|
|
61
|
-
await service.getStarsTransactions("me", {
|
|
62
|
-
inbound: true,
|
|
63
|
-
outbound: false,
|
|
64
|
-
ascending: true,
|
|
65
|
-
subscriptionId: "sub-9",
|
|
66
|
-
offset: "cursor-xyz",
|
|
67
|
-
limit: 25,
|
|
68
|
-
});
|
|
69
|
-
const call = invocations.find((r) => r instanceof Api.payments.GetStarsTransactions);
|
|
70
|
-
assert.ok(call);
|
|
71
|
-
assert.strictEqual(call.inbound, true);
|
|
72
|
-
assert.strictEqual(call.outbound, false);
|
|
73
|
-
assert.strictEqual(call.ascending, true);
|
|
74
|
-
assert.strictEqual(call.subscriptionId, "sub-9");
|
|
75
|
-
assert.strictEqual(call.offset, "cursor-xyz");
|
|
76
|
-
assert.strictEqual(call.limit, 25);
|
|
77
|
-
});
|
|
78
|
-
it("throws when not connected", async () => {
|
|
79
|
-
const service = new TelegramService(1, "hash");
|
|
80
|
-
await assert.rejects(() => service.getStarsTransactions("me"), /Not connected/i);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|