@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
|
@@ -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 @@
|
|
|
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 { 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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,205 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|