@overpod/mcp-telegram 1.25.0 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +35 -5
- package/dist/__tests__/approve-join-request.test.d.ts +1 -0
- package/dist/__tests__/approve-join-request.test.js +107 -0
- package/dist/__tests__/boosts.test.d.ts +1 -0
- package/dist/__tests__/boosts.test.js +310 -0
- package/dist/__tests__/broadcast-stats.test.d.ts +1 -0
- package/dist/__tests__/broadcast-stats.test.js +172 -0
- package/dist/__tests__/business-chat-links.test.d.ts +1 -0
- package/dist/__tests__/business-chat-links.test.js +102 -0
- package/dist/__tests__/get-message-buttons.test.d.ts +1 -0
- package/dist/__tests__/get-message-buttons.test.js +122 -0
- package/dist/__tests__/group-calls.test.d.ts +1 -0
- package/dist/__tests__/group-calls.test.js +503 -0
- package/dist/__tests__/inline-query-send.test.d.ts +1 -0
- package/dist/__tests__/inline-query-send.test.js +94 -0
- package/dist/__tests__/inline-query.test.d.ts +1 -0
- package/dist/__tests__/inline-query.test.js +115 -0
- package/dist/__tests__/megagroup-stats.test.d.ts +1 -0
- package/dist/__tests__/megagroup-stats.test.js +166 -0
- package/dist/__tests__/press-button.test.d.ts +1 -0
- package/dist/__tests__/press-button.test.js +123 -0
- package/dist/__tests__/quick-replies.test.d.ts +1 -0
- package/dist/__tests__/quick-replies.test.js +245 -0
- package/dist/__tests__/set-chat-reactions.test.d.ts +1 -0
- package/dist/__tests__/set-chat-reactions.test.js +129 -0
- package/dist/__tests__/stars-status.test.d.ts +1 -0
- package/dist/__tests__/stars-status.test.js +205 -0
- package/dist/__tests__/stars-transactions.test.d.ts +1 -0
- package/dist/__tests__/stars-transactions.test.js +82 -0
- package/dist/__tests__/stories.test.d.ts +1 -0
- package/dist/__tests__/stories.test.js +361 -0
- package/dist/__tests__/toggle-anti-spam.test.d.ts +1 -0
- package/dist/__tests__/toggle-anti-spam.test.js +80 -0
- package/dist/__tests__/toggle-channel-signatures.test.d.ts +1 -0
- package/dist/__tests__/toggle-channel-signatures.test.js +80 -0
- package/dist/__tests__/toggle-forum-mode.test.d.ts +1 -0
- package/dist/__tests__/toggle-forum-mode.test.js +80 -0
- package/dist/__tests__/toggle-prehistory-hidden.test.d.ts +1 -0
- package/dist/__tests__/toggle-prehistory-hidden.test.js +80 -0
- package/dist/__tests__/updates.test.d.ts +1 -0
- package/dist/__tests__/updates.test.js +221 -0
- package/dist/rate-limiter.d.ts +8 -2
- package/dist/rate-limiter.js +15 -8
- package/dist/telegram-client.d.ts +580 -0
- package/dist/telegram-client.js +1322 -0
- package/dist/tools/account.js +16 -0
- package/dist/tools/boosts.d.ts +3 -0
- package/dist/tools/boosts.js +65 -0
- package/dist/tools/chats.js +150 -0
- package/dist/tools/group-calls.d.ts +4 -0
- package/dist/tools/group-calls.js +77 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/messages.js +192 -0
- package/dist/tools/quick-replies.d.ts +4 -0
- package/dist/tools/quick-replies.js +58 -0
- package/dist/tools/reactions.js +43 -0
- package/dist/tools/stars.d.ts +4 -0
- package/dist/tools/stars.js +71 -0
- package/dist/tools/stories.d.ts +3 -0
- package/dist/tools/stories.js +107 -0
- package/package.json +1 -1
|
@@ -0,0 +1,172 @@
|
|
|
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 { summarizeBroadcastStats, TelegramService } from "../telegram-client.js";
|
|
6
|
+
function makeService(entity, invokeImpl, invocations) {
|
|
7
|
+
const fakeClient = {
|
|
8
|
+
invoke: async (req) => {
|
|
9
|
+
invocations.push(req);
|
|
10
|
+
return invokeImpl(req);
|
|
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
|
+
function fakeStats() {
|
|
21
|
+
// Build a plain object that structurally matches BroadcastStats.
|
|
22
|
+
// We cast through unknown so tests don't depend on GramJS constructor quirks.
|
|
23
|
+
return {
|
|
24
|
+
period: { minDate: 1000, maxDate: 2000 },
|
|
25
|
+
followers: { current: 100, previous: 80 },
|
|
26
|
+
viewsPerPost: { current: 500, previous: 400 },
|
|
27
|
+
sharesPerPost: { current: 10, previous: 8 },
|
|
28
|
+
reactionsPerPost: { current: 25, previous: 20 },
|
|
29
|
+
viewsPerStory: { current: 50, previous: 45 },
|
|
30
|
+
sharesPerStory: { current: 3, previous: 2 },
|
|
31
|
+
reactionsPerStory: { current: 7, previous: 6 },
|
|
32
|
+
enabledNotifications: { part: 40, total: 100 },
|
|
33
|
+
growthGraph: new Api.StatsGraphAsync({ token: "growth-token" }),
|
|
34
|
+
followersGraph: new Api.StatsGraphAsync({ token: "followers-token" }),
|
|
35
|
+
muteGraph: new Api.StatsGraphError({ error: "unavailable" }),
|
|
36
|
+
topHoursGraph: new Api.StatsGraph({
|
|
37
|
+
json: new Api.DataJSON({ data: '{"x":[1,2,3]}' }),
|
|
38
|
+
zoomToken: "zoom",
|
|
39
|
+
}),
|
|
40
|
+
interactionsGraph: new Api.StatsGraphAsync({ token: "inter-token" }),
|
|
41
|
+
ivInteractionsGraph: new Api.StatsGraphAsync({ token: "iv-token" }),
|
|
42
|
+
viewsBySourceGraph: new Api.StatsGraphAsync({ token: "views-src-token" }),
|
|
43
|
+
newFollowersBySourceGraph: new Api.StatsGraphAsync({ token: "new-src-token" }),
|
|
44
|
+
languagesGraph: new Api.StatsGraphAsync({ token: "lang-token" }),
|
|
45
|
+
reactionsByEmotionGraph: new Api.StatsGraphAsync({ token: "react-token" }),
|
|
46
|
+
storyInteractionsGraph: new Api.StatsGraphAsync({ token: "story-inter-token" }),
|
|
47
|
+
storyReactionsByEmotionGraph: new Api.StatsGraphAsync({ token: "story-react-token" }),
|
|
48
|
+
recentPostsInteractions: [
|
|
49
|
+
new Api.PostInteractionCountersMessage({ msgId: 42, views: 500, forwards: 3, reactions: 12 }),
|
|
50
|
+
new Api.PostInteractionCountersStory({ storyId: 7, views: 50, forwards: 1, reactions: 4 }),
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
describe("summarizeBroadcastStats", () => {
|
|
55
|
+
it("produces compact summary without graphs by default", () => {
|
|
56
|
+
const summary = summarizeBroadcastStats(fakeStats(), false);
|
|
57
|
+
assert.strictEqual(summary.graphs, undefined);
|
|
58
|
+
assert.deepStrictEqual(summary.period, { minDate: 1000, maxDate: 2000 });
|
|
59
|
+
assert.deepStrictEqual(summary.followers, { current: 100, previous: 80 });
|
|
60
|
+
assert.deepStrictEqual(summary.viewsPerPost, { current: 500, previous: 400 });
|
|
61
|
+
assert.strictEqual(summary.enabledNotifications.percent, 40);
|
|
62
|
+
assert.strictEqual(summary.recentPostsInteractions.length, 2);
|
|
63
|
+
assert.deepStrictEqual(summary.recentPostsInteractions[0], {
|
|
64
|
+
kind: "message",
|
|
65
|
+
msgId: 42,
|
|
66
|
+
views: 500,
|
|
67
|
+
forwards: 3,
|
|
68
|
+
reactions: 12,
|
|
69
|
+
});
|
|
70
|
+
assert.deepStrictEqual(summary.recentPostsInteractions[1], {
|
|
71
|
+
kind: "story",
|
|
72
|
+
storyId: 7,
|
|
73
|
+
views: 50,
|
|
74
|
+
forwards: 1,
|
|
75
|
+
reactions: 4,
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
it("handles zero-total enabled notifications without dividing by zero", () => {
|
|
79
|
+
const stats = fakeStats();
|
|
80
|
+
stats.enabledNotifications = {
|
|
81
|
+
part: 0,
|
|
82
|
+
total: 0,
|
|
83
|
+
};
|
|
84
|
+
const summary = summarizeBroadcastStats(stats, false);
|
|
85
|
+
assert.strictEqual(summary.enabledNotifications.percent, 0);
|
|
86
|
+
});
|
|
87
|
+
it("includes and decodes graphs when requested", () => {
|
|
88
|
+
const summary = summarizeBroadcastStats(fakeStats(), true);
|
|
89
|
+
assert.ok(summary.graphs);
|
|
90
|
+
const graphs = summary.graphs ?? {};
|
|
91
|
+
assert.deepStrictEqual(graphs.growth, { type: "async", token: "growth-token" });
|
|
92
|
+
assert.deepStrictEqual(graphs.mute, { type: "error", error: "unavailable" });
|
|
93
|
+
assert.deepStrictEqual(graphs.topHours, { type: "data", data: { x: [1, 2, 3] }, zoomToken: "zoom" });
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe("TelegramService.getBroadcastStats", () => {
|
|
97
|
+
function broadcastChannel() {
|
|
98
|
+
return new Api.Channel({
|
|
99
|
+
id: bigInt(12345),
|
|
100
|
+
title: "broadcast",
|
|
101
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
102
|
+
date: 0,
|
|
103
|
+
accessHash: bigInt(1),
|
|
104
|
+
broadcast: true,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
it("invokes stats.GetBroadcastStats and returns compact summary", async () => {
|
|
108
|
+
const invocations = [];
|
|
109
|
+
const service = makeService(broadcastChannel(), async () => fakeStats(), invocations);
|
|
110
|
+
const summary = await service.getBroadcastStats("12345");
|
|
111
|
+
const call = invocations.find((r) => r instanceof Api.stats.GetBroadcastStats);
|
|
112
|
+
assert.ok(call, "GetBroadcastStats was invoked");
|
|
113
|
+
assert.strictEqual(summary.followers.current, 100);
|
|
114
|
+
assert.strictEqual(summary.graphs, undefined);
|
|
115
|
+
});
|
|
116
|
+
it("passes dark=true when requested", async () => {
|
|
117
|
+
const invocations = [];
|
|
118
|
+
const service = makeService(broadcastChannel(), async () => fakeStats(), invocations);
|
|
119
|
+
await service.getBroadcastStats("12345", { dark: true });
|
|
120
|
+
const call = invocations.find((r) => r instanceof Api.stats.GetBroadcastStats);
|
|
121
|
+
assert.ok(call);
|
|
122
|
+
assert.strictEqual(call.dark, true);
|
|
123
|
+
});
|
|
124
|
+
it("includes graphs when includeGraphs=true", async () => {
|
|
125
|
+
const invocations = [];
|
|
126
|
+
const service = makeService(broadcastChannel(), async () => fakeStats(), invocations);
|
|
127
|
+
const summary = await service.getBroadcastStats("12345", { includeGraphs: true });
|
|
128
|
+
assert.ok(summary.graphs);
|
|
129
|
+
assert.strictEqual(summary.graphs?.growth.type, "async");
|
|
130
|
+
});
|
|
131
|
+
it("rejects supergroups with hint to use megagroup stats", async () => {
|
|
132
|
+
const megagroup = new Api.Channel({
|
|
133
|
+
id: bigInt(33333),
|
|
134
|
+
title: "supergroup",
|
|
135
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
136
|
+
date: 0,
|
|
137
|
+
accessHash: bigInt(1),
|
|
138
|
+
megagroup: true,
|
|
139
|
+
});
|
|
140
|
+
const invocations = [];
|
|
141
|
+
const service = makeService(megagroup, async () => fakeStats(), invocations);
|
|
142
|
+
await assert.rejects(service.getBroadcastStats("33333"), /telegram-get-megagroup-stats/);
|
|
143
|
+
assert.strictEqual(invocations.find((r) => r instanceof Api.stats.GetBroadcastStats), undefined);
|
|
144
|
+
});
|
|
145
|
+
it("rejects non-channel entities", async () => {
|
|
146
|
+
const chat = new Api.Chat({
|
|
147
|
+
id: bigInt(44444),
|
|
148
|
+
title: "basic group",
|
|
149
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
150
|
+
participantsCount: 5,
|
|
151
|
+
date: 0,
|
|
152
|
+
version: 0,
|
|
153
|
+
});
|
|
154
|
+
const invocations = [];
|
|
155
|
+
const service = makeService(chat, async () => fakeStats(), invocations);
|
|
156
|
+
await assert.rejects(service.getBroadcastStats("44444"), /channels/);
|
|
157
|
+
});
|
|
158
|
+
it("surfaces admin-required errors with a clearer message", async () => {
|
|
159
|
+
const invocations = [];
|
|
160
|
+
const service = makeService(broadcastChannel(), async () => {
|
|
161
|
+
throw new Error("CHAT_ADMIN_REQUIRED");
|
|
162
|
+
}, invocations);
|
|
163
|
+
await assert.rejects(service.getBroadcastStats("12345"), /admin rights/i);
|
|
164
|
+
});
|
|
165
|
+
it("surfaces stats-unavailable errors with premium hint", async () => {
|
|
166
|
+
const invocations = [];
|
|
167
|
+
const service = makeService(broadcastChannel(), async () => {
|
|
168
|
+
throw new Error("STATS_UNAVAILABLE");
|
|
169
|
+
}, invocations);
|
|
170
|
+
await assert.rejects(service.getBroadcastStats("12345"), /Premium/i);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { Api } from "telegram/tl/index.js";
|
|
4
|
+
import { summarizeBusinessChatLink, summarizeBusinessChatLinks, TelegramService } from "../telegram-client.js";
|
|
5
|
+
function makeService(invocations, responder) {
|
|
6
|
+
const fakeClient = {
|
|
7
|
+
invoke: async (req) => {
|
|
8
|
+
invocations.push(req);
|
|
9
|
+
return responder(req);
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
const service = new TelegramService(1, "hash");
|
|
13
|
+
const internals = service;
|
|
14
|
+
internals.client = fakeClient;
|
|
15
|
+
internals.connected = true;
|
|
16
|
+
return service;
|
|
17
|
+
}
|
|
18
|
+
describe("summarizeBusinessChatLink", () => {
|
|
19
|
+
it("maps link, message, title, views and entity count", () => {
|
|
20
|
+
const link = new Api.BusinessChatLink({
|
|
21
|
+
link: "https://t.me/message/abc",
|
|
22
|
+
message: "Hello there",
|
|
23
|
+
title: "Welcome",
|
|
24
|
+
views: 42,
|
|
25
|
+
entities: [
|
|
26
|
+
new Api.MessageEntityBold({ offset: 0, length: 5 }),
|
|
27
|
+
new Api.MessageEntityItalic({ offset: 6, length: 5 }),
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
const out = summarizeBusinessChatLink(link);
|
|
31
|
+
assert.strictEqual(out.link, "https://t.me/message/abc");
|
|
32
|
+
assert.strictEqual(out.message, "Hello there");
|
|
33
|
+
assert.strictEqual(out.title, "Welcome");
|
|
34
|
+
assert.strictEqual(out.views, 42);
|
|
35
|
+
assert.strictEqual(out.entityCount, 2);
|
|
36
|
+
});
|
|
37
|
+
it("leaves title undefined and entityCount 0 when entities missing", () => {
|
|
38
|
+
const link = new Api.BusinessChatLink({
|
|
39
|
+
link: "https://t.me/message/xyz",
|
|
40
|
+
message: "Plain",
|
|
41
|
+
views: 0,
|
|
42
|
+
});
|
|
43
|
+
const out = summarizeBusinessChatLink(link);
|
|
44
|
+
assert.strictEqual(out.title, undefined);
|
|
45
|
+
assert.strictEqual(out.entityCount, 0);
|
|
46
|
+
assert.strictEqual(out.views, 0);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe("summarizeBusinessChatLinks", () => {
|
|
50
|
+
it("computes count from links length and maps each entry", () => {
|
|
51
|
+
const resp = new Api.account.BusinessChatLinks({
|
|
52
|
+
links: [
|
|
53
|
+
new Api.BusinessChatLink({
|
|
54
|
+
link: "https://t.me/message/a",
|
|
55
|
+
message: "Hi",
|
|
56
|
+
views: 1,
|
|
57
|
+
}),
|
|
58
|
+
new Api.BusinessChatLink({
|
|
59
|
+
link: "https://t.me/message/b",
|
|
60
|
+
message: "Hello",
|
|
61
|
+
title: "Label",
|
|
62
|
+
views: 5,
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
chats: [],
|
|
66
|
+
users: [],
|
|
67
|
+
});
|
|
68
|
+
const out = summarizeBusinessChatLinks(resp);
|
|
69
|
+
assert.strictEqual(out.count, 2);
|
|
70
|
+
assert.strictEqual(out.links.length, 2);
|
|
71
|
+
assert.strictEqual(out.links[0].link, "https://t.me/message/a");
|
|
72
|
+
assert.strictEqual(out.links[1].title, "Label");
|
|
73
|
+
});
|
|
74
|
+
it("handles empty link list", () => {
|
|
75
|
+
const resp = new Api.account.BusinessChatLinks({ links: [], chats: [], users: [] });
|
|
76
|
+
const out = summarizeBusinessChatLinks(resp);
|
|
77
|
+
assert.strictEqual(out.count, 0);
|
|
78
|
+
assert.deepStrictEqual(out.links, []);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe("TelegramService.getBusinessChatLinks", () => {
|
|
82
|
+
it("invokes account.GetBusinessChatLinks and returns summary", async () => {
|
|
83
|
+
const invocations = [];
|
|
84
|
+
const service = makeService(invocations, () => new Api.account.BusinessChatLinks({
|
|
85
|
+
links: [
|
|
86
|
+
new Api.BusinessChatLink({
|
|
87
|
+
link: "https://t.me/message/foo",
|
|
88
|
+
message: "hey",
|
|
89
|
+
views: 9,
|
|
90
|
+
}),
|
|
91
|
+
],
|
|
92
|
+
chats: [],
|
|
93
|
+
users: [],
|
|
94
|
+
}));
|
|
95
|
+
const out = await service.getBusinessChatLinks();
|
|
96
|
+
const call = invocations.find((r) => r instanceof Api.account.GetBusinessChatLinks);
|
|
97
|
+
assert.ok(call);
|
|
98
|
+
assert.strictEqual(out.count, 1);
|
|
99
|
+
assert.strictEqual(out.links[0].link, "https://t.me/message/foo");
|
|
100
|
+
assert.strictEqual(out.links[0].views, 9);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
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(chatEntity, messages) {
|
|
7
|
+
const fakeClient = {
|
|
8
|
+
getMessages: async (_entity, _opts) => messages,
|
|
9
|
+
};
|
|
10
|
+
const service = new TelegramService(1, "hash");
|
|
11
|
+
const internals = service;
|
|
12
|
+
internals.client = fakeClient;
|
|
13
|
+
internals.connected = true;
|
|
14
|
+
internals.resolveChat = async () => chatEntity;
|
|
15
|
+
return service;
|
|
16
|
+
}
|
|
17
|
+
function makeChannel(id) {
|
|
18
|
+
return new Api.Channel({
|
|
19
|
+
id: bigInt(id),
|
|
20
|
+
title: "t",
|
|
21
|
+
photo: new Api.ChatPhotoEmpty(),
|
|
22
|
+
date: 0,
|
|
23
|
+
accessHash: bigInt(1),
|
|
24
|
+
megagroup: true,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function makeMessage(id, markup) {
|
|
28
|
+
return new Api.Message({
|
|
29
|
+
id,
|
|
30
|
+
peerId: new Api.PeerChannel({ channelId: bigInt(100) }),
|
|
31
|
+
date: 0,
|
|
32
|
+
message: "hi",
|
|
33
|
+
replyMarkup: markup,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function inlineMarkup(buttons) {
|
|
37
|
+
return new Api.ReplyInlineMarkup({
|
|
38
|
+
rows: buttons.map((row) => new Api.KeyboardButtonRow({ buttons: row })),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
describe("TelegramService.getMessageButtons", () => {
|
|
42
|
+
it("returns empty buttons and markupType='none' when message has no markup", async () => {
|
|
43
|
+
const msg = makeMessage(1, undefined);
|
|
44
|
+
const service = makeService(makeChannel(100), [msg]);
|
|
45
|
+
const res = await service.getMessageButtons("100", 1);
|
|
46
|
+
assert.deepStrictEqual(res, { markupType: "none", buttons: [] });
|
|
47
|
+
});
|
|
48
|
+
it("describes a callback button with base64 data", async () => {
|
|
49
|
+
const data = Buffer.from("vote_yes");
|
|
50
|
+
const msg = makeMessage(42, inlineMarkup([[new Api.KeyboardButtonCallback({ text: "Yes", data, requiresPassword: false })]]));
|
|
51
|
+
const service = makeService(makeChannel(100), [msg]);
|
|
52
|
+
const res = await service.getMessageButtons("100", 42);
|
|
53
|
+
assert.strictEqual(res.markupType, "ReplyInlineMarkup");
|
|
54
|
+
assert.strictEqual(res.buttons.length, 1);
|
|
55
|
+
const b = res.buttons[0];
|
|
56
|
+
assert.strictEqual(b.row, 0);
|
|
57
|
+
assert.strictEqual(b.col, 0);
|
|
58
|
+
assert.strictEqual(b.type, "KeyboardButtonCallback");
|
|
59
|
+
assert.strictEqual(b.label, "Yes");
|
|
60
|
+
assert.ok(b.data, "data is set");
|
|
61
|
+
assert.strictEqual(Buffer.from(b.data, "base64").toString(), "vote_yes");
|
|
62
|
+
assert.strictEqual(b.requiresPassword, undefined);
|
|
63
|
+
});
|
|
64
|
+
it("flags requiresPassword callback buttons", async () => {
|
|
65
|
+
const msg = makeMessage(1, inlineMarkup([
|
|
66
|
+
[new Api.KeyboardButtonCallback({ text: "Admin", data: Buffer.from("x"), requiresPassword: true })],
|
|
67
|
+
]));
|
|
68
|
+
const service = makeService(makeChannel(100), [msg]);
|
|
69
|
+
const res = await service.getMessageButtons("100", 1);
|
|
70
|
+
assert.strictEqual(res.buttons[0].requiresPassword, true);
|
|
71
|
+
});
|
|
72
|
+
it("describes URL, switch-inline and copy buttons with type-specific fields", async () => {
|
|
73
|
+
const msg = makeMessage(2, inlineMarkup([
|
|
74
|
+
[
|
|
75
|
+
new Api.KeyboardButtonUrl({ text: "Open", url: "https://x.test" }),
|
|
76
|
+
new Api.KeyboardButtonSwitchInline({ text: "Inline", query: "q", samePeer: true }),
|
|
77
|
+
],
|
|
78
|
+
[new Api.KeyboardButtonCopy({ text: "Copy", copyText: "abc" })],
|
|
79
|
+
]));
|
|
80
|
+
const service = makeService(makeChannel(100), [msg]);
|
|
81
|
+
const res = await service.getMessageButtons("100", 2);
|
|
82
|
+
assert.strictEqual(res.buttons.length, 3);
|
|
83
|
+
const [urlB, inlineB, copyB] = res.buttons;
|
|
84
|
+
assert.strictEqual(urlB.type, "KeyboardButtonUrl");
|
|
85
|
+
assert.strictEqual(urlB.url, "https://x.test");
|
|
86
|
+
assert.strictEqual(urlB.row, 0);
|
|
87
|
+
assert.strictEqual(urlB.col, 0);
|
|
88
|
+
assert.strictEqual(inlineB.type, "KeyboardButtonSwitchInline");
|
|
89
|
+
assert.strictEqual(inlineB.switchQuery, "q");
|
|
90
|
+
assert.strictEqual(inlineB.samePeer, true);
|
|
91
|
+
assert.strictEqual(inlineB.row, 0);
|
|
92
|
+
assert.strictEqual(inlineB.col, 1);
|
|
93
|
+
assert.strictEqual(copyB.type, "KeyboardButtonCopy");
|
|
94
|
+
assert.strictEqual(copyB.copyText, "abc");
|
|
95
|
+
assert.strictEqual(copyB.row, 1);
|
|
96
|
+
assert.strictEqual(copyB.col, 0);
|
|
97
|
+
});
|
|
98
|
+
it("preserves markupType for non-inline reply keyboards with buttons", async () => {
|
|
99
|
+
const markup = new Api.ReplyKeyboardMarkup({
|
|
100
|
+
rows: [new Api.KeyboardButtonRow({ buttons: [new Api.KeyboardButton({ text: "x" })] })],
|
|
101
|
+
});
|
|
102
|
+
const msg = makeMessage(3, markup);
|
|
103
|
+
const service = makeService(makeChannel(100), [msg]);
|
|
104
|
+
const res = await service.getMessageButtons("100", 3);
|
|
105
|
+
assert.strictEqual(res.markupType, "ReplyKeyboardMarkup");
|
|
106
|
+
assert.strictEqual(res.buttons.length, 1);
|
|
107
|
+
assert.strictEqual(res.buttons[0].type, "KeyboardButton");
|
|
108
|
+
assert.strictEqual(res.buttons[0].label, "x");
|
|
109
|
+
});
|
|
110
|
+
it("returns empty buttons for ReplyKeyboardHide-like markup without rows", async () => {
|
|
111
|
+
const markup = new Api.ReplyKeyboardHide({ selective: false });
|
|
112
|
+
const msg = makeMessage(4, markup);
|
|
113
|
+
const service = makeService(makeChannel(100), [msg]);
|
|
114
|
+
const res = await service.getMessageButtons("100", 4);
|
|
115
|
+
assert.strictEqual(res.markupType, "ReplyKeyboardHide");
|
|
116
|
+
assert.deepStrictEqual(res.buttons, []);
|
|
117
|
+
});
|
|
118
|
+
it("rejects when message is not found", async () => {
|
|
119
|
+
const service = makeService(makeChannel(100), []);
|
|
120
|
+
await assert.rejects(service.getMessageButtons("100", 999), /not found/i);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|