@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,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 { TelegramService } from "../telegram-client.js";
|
|
6
|
-
function makeService(entity, user, invocations) {
|
|
7
|
-
const fakeClient = {
|
|
8
|
-
invoke: async (req) => {
|
|
9
|
-
invocations.push(req);
|
|
10
|
-
return undefined;
|
|
11
|
-
},
|
|
12
|
-
getEntity: async (_id) => user,
|
|
13
|
-
};
|
|
14
|
-
const service = new TelegramService(1, "hash");
|
|
15
|
-
const internals = service;
|
|
16
|
-
internals.client = fakeClient;
|
|
17
|
-
internals.connected = true;
|
|
18
|
-
internals.resolveChat = async () => entity;
|
|
19
|
-
return service;
|
|
20
|
-
}
|
|
21
|
-
describe("TelegramService.approveChatJoinRequest", () => {
|
|
22
|
-
const makeUser = (id) => new Api.User({
|
|
23
|
-
id: bigInt(id),
|
|
24
|
-
accessHash: bigInt(42),
|
|
25
|
-
firstName: "Test",
|
|
26
|
-
});
|
|
27
|
-
it("invokes HideChatJoinRequest with approved=true for channel peer", async () => {
|
|
28
|
-
const megagroup = new Api.Channel({
|
|
29
|
-
id: bigInt(10000),
|
|
30
|
-
title: "sg",
|
|
31
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
32
|
-
date: 0,
|
|
33
|
-
accessHash: bigInt(1),
|
|
34
|
-
megagroup: true,
|
|
35
|
-
});
|
|
36
|
-
const invocations = [];
|
|
37
|
-
const service = makeService(megagroup, makeUser(555), invocations);
|
|
38
|
-
await service.approveChatJoinRequest("10000", "555", true);
|
|
39
|
-
const call = invocations.find((r) => r instanceof Api.messages.HideChatJoinRequest);
|
|
40
|
-
assert.ok(call, "HideChatJoinRequest was invoked");
|
|
41
|
-
assert.strictEqual(call.approved, true);
|
|
42
|
-
assert.ok(call.userId instanceof Api.InputUser);
|
|
43
|
-
assert.strictEqual(call.userId.userId.toString(), "555");
|
|
44
|
-
});
|
|
45
|
-
it("invokes HideChatJoinRequest with approved=false (denied)", async () => {
|
|
46
|
-
const megagroup = new Api.Channel({
|
|
47
|
-
id: bigInt(20000),
|
|
48
|
-
title: "sg",
|
|
49
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
50
|
-
date: 0,
|
|
51
|
-
accessHash: bigInt(1),
|
|
52
|
-
megagroup: true,
|
|
53
|
-
});
|
|
54
|
-
const invocations = [];
|
|
55
|
-
const service = makeService(megagroup, makeUser(777), invocations);
|
|
56
|
-
await service.approveChatJoinRequest("20000", "777", false);
|
|
57
|
-
const call = invocations.find((r) => r instanceof Api.messages.HideChatJoinRequest);
|
|
58
|
-
assert.ok(call);
|
|
59
|
-
assert.strictEqual(call.approved, false);
|
|
60
|
-
});
|
|
61
|
-
it("rejects basic Chat peer (basic groups do not support join requests)", async () => {
|
|
62
|
-
const chat = new Api.Chat({
|
|
63
|
-
id: bigInt(33333),
|
|
64
|
-
title: "basic group",
|
|
65
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
66
|
-
participantsCount: 5,
|
|
67
|
-
date: 0,
|
|
68
|
-
version: 0,
|
|
69
|
-
});
|
|
70
|
-
const invocations = [];
|
|
71
|
-
const service = makeService(chat, makeUser(888), invocations);
|
|
72
|
-
await assert.rejects(service.approveChatJoinRequest("33333", "888", true), /supergroups and channels/i);
|
|
73
|
-
assert.strictEqual(invocations.find((r) => r instanceof Api.messages.HideChatJoinRequest), undefined, "no API call for basic group");
|
|
74
|
-
});
|
|
75
|
-
it("rejects when user resolves to non-User entity", async () => {
|
|
76
|
-
const megagroup = new Api.Channel({
|
|
77
|
-
id: bigInt(44444),
|
|
78
|
-
title: "sg",
|
|
79
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
80
|
-
date: 0,
|
|
81
|
-
accessHash: bigInt(1),
|
|
82
|
-
megagroup: true,
|
|
83
|
-
});
|
|
84
|
-
const notAUser = new Api.Chat({
|
|
85
|
-
id: bigInt(99),
|
|
86
|
-
title: "not a user",
|
|
87
|
-
photo: new Api.ChatPhotoEmpty(),
|
|
88
|
-
participantsCount: 1,
|
|
89
|
-
date: 0,
|
|
90
|
-
version: 0,
|
|
91
|
-
});
|
|
92
|
-
const invocations = [];
|
|
93
|
-
const service = makeService(megagroup, notAUser, invocations);
|
|
94
|
-
await assert.rejects(service.approveChatJoinRequest("44444", "99", true), /not a user/i);
|
|
95
|
-
assert.strictEqual(invocations.find((r) => r instanceof Api.messages.HideChatJoinRequest), undefined, "no API call when target is not a user");
|
|
96
|
-
});
|
|
97
|
-
it("rejects when chat entity is a private user (not a group/channel)", async () => {
|
|
98
|
-
const userPeer = new Api.User({
|
|
99
|
-
id: bigInt(1),
|
|
100
|
-
accessHash: bigInt(1),
|
|
101
|
-
firstName: "Peer",
|
|
102
|
-
});
|
|
103
|
-
const invocations = [];
|
|
104
|
-
const service = makeService(userPeer, makeUser(123), invocations);
|
|
105
|
-
await assert.rejects(service.approveChatJoinRequest("1", "123", true), /supergroups and channels/i);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,310 +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 { summarizeBoost, summarizeBoostsList, summarizeBoostsStatus, summarizeMyBoost, summarizeMyBoosts, summarizePrepaidGiveaway, TelegramService, } from "../telegram-client.js";
|
|
6
|
-
function makeService(invocations, responder) {
|
|
7
|
-
const fakeClient = {
|
|
8
|
-
invoke: async (req) => {
|
|
9
|
-
invocations.push(req);
|
|
10
|
-
return responder(req);
|
|
11
|
-
},
|
|
12
|
-
};
|
|
13
|
-
const service = new TelegramService(1, "hash");
|
|
14
|
-
const internals = service;
|
|
15
|
-
internals.client = fakeClient;
|
|
16
|
-
internals.connected = true;
|
|
17
|
-
return service;
|
|
18
|
-
}
|
|
19
|
-
describe("summarizeMyBoost", () => {
|
|
20
|
-
it("maps slot, peer, dates and cooldown", () => {
|
|
21
|
-
const boost = new Api.MyBoost({
|
|
22
|
-
slot: 2,
|
|
23
|
-
peer: new Api.PeerChannel({ channelId: bigInt(900) }),
|
|
24
|
-
date: 1710000000,
|
|
25
|
-
expires: 1720000000,
|
|
26
|
-
cooldownUntilDate: 1715000000,
|
|
27
|
-
});
|
|
28
|
-
const out = summarizeMyBoost(boost);
|
|
29
|
-
assert.strictEqual(out.slot, 2);
|
|
30
|
-
assert.deepStrictEqual(out.peer, { kind: "channel", id: "900" });
|
|
31
|
-
assert.strictEqual(out.date, 1710000000);
|
|
32
|
-
assert.strictEqual(out.expires, 1720000000);
|
|
33
|
-
assert.strictEqual(out.cooldownUntilDate, 1715000000);
|
|
34
|
-
});
|
|
35
|
-
it("leaves peer undefined when boost is unassigned", () => {
|
|
36
|
-
const boost = new Api.MyBoost({
|
|
37
|
-
slot: 1,
|
|
38
|
-
date: 100,
|
|
39
|
-
expires: 200,
|
|
40
|
-
});
|
|
41
|
-
const out = summarizeMyBoost(boost);
|
|
42
|
-
assert.strictEqual(out.slot, 1);
|
|
43
|
-
assert.strictEqual(out.peer, undefined);
|
|
44
|
-
assert.strictEqual(out.cooldownUntilDate, undefined);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
describe("summarizeMyBoosts", () => {
|
|
48
|
-
it("computes count from myBoosts length and maps each entry", () => {
|
|
49
|
-
const resp = new Api.premium.MyBoosts({
|
|
50
|
-
myBoosts: [
|
|
51
|
-
new Api.MyBoost({
|
|
52
|
-
slot: 1,
|
|
53
|
-
peer: new Api.PeerChannel({ channelId: bigInt(10) }),
|
|
54
|
-
date: 1,
|
|
55
|
-
expires: 2,
|
|
56
|
-
}),
|
|
57
|
-
new Api.MyBoost({ slot: 2, date: 3, expires: 4 }),
|
|
58
|
-
],
|
|
59
|
-
chats: [],
|
|
60
|
-
users: [],
|
|
61
|
-
});
|
|
62
|
-
const out = summarizeMyBoosts(resp);
|
|
63
|
-
assert.strictEqual(out.count, 2);
|
|
64
|
-
assert.strictEqual(out.myBoosts.length, 2);
|
|
65
|
-
assert.deepStrictEqual(out.myBoosts[0].peer, { kind: "channel", id: "10" });
|
|
66
|
-
assert.strictEqual(out.myBoosts[1].peer, undefined);
|
|
67
|
-
});
|
|
68
|
-
it("handles empty boost list", () => {
|
|
69
|
-
const resp = new Api.premium.MyBoosts({ myBoosts: [], chats: [], users: [] });
|
|
70
|
-
const out = summarizeMyBoosts(resp);
|
|
71
|
-
assert.strictEqual(out.count, 0);
|
|
72
|
-
assert.deepStrictEqual(out.myBoosts, []);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
describe("summarizePrepaidGiveaway", () => {
|
|
76
|
-
it("maps premium PrepaidGiveaway (months + quantity)", () => {
|
|
77
|
-
const g = new Api.PrepaidGiveaway({
|
|
78
|
-
id: bigInt(123),
|
|
79
|
-
months: 3,
|
|
80
|
-
quantity: 10,
|
|
81
|
-
date: 1700000000,
|
|
82
|
-
});
|
|
83
|
-
const out = summarizePrepaidGiveaway(g);
|
|
84
|
-
assert.deepStrictEqual(out, {
|
|
85
|
-
kind: "premium",
|
|
86
|
-
id: "123",
|
|
87
|
-
months: 3,
|
|
88
|
-
quantity: 10,
|
|
89
|
-
date: 1700000000,
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
it("maps PrepaidStarsGiveaway with stars + boosts", () => {
|
|
93
|
-
const g = new Api.PrepaidStarsGiveaway({
|
|
94
|
-
id: bigInt(456),
|
|
95
|
-
stars: bigInt(5000),
|
|
96
|
-
quantity: 20,
|
|
97
|
-
boosts: 4,
|
|
98
|
-
date: 1710000000,
|
|
99
|
-
});
|
|
100
|
-
const out = summarizePrepaidGiveaway(g);
|
|
101
|
-
assert.deepStrictEqual(out, {
|
|
102
|
-
kind: "stars",
|
|
103
|
-
id: "456",
|
|
104
|
-
stars: "5000",
|
|
105
|
-
quantity: 20,
|
|
106
|
-
boosts: 4,
|
|
107
|
-
date: 1710000000,
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
describe("summarizeBoostsStatus", () => {
|
|
112
|
-
it("maps core counters and boost url", () => {
|
|
113
|
-
const resp = new Api.premium.BoostsStatus({
|
|
114
|
-
level: 3,
|
|
115
|
-
currentLevelBoosts: 10,
|
|
116
|
-
boosts: 15,
|
|
117
|
-
giftBoosts: 2,
|
|
118
|
-
nextLevelBoosts: 25,
|
|
119
|
-
boostUrl: "https://t.me/boost/test",
|
|
120
|
-
myBoost: true,
|
|
121
|
-
myBoostSlots: [1, 2],
|
|
122
|
-
});
|
|
123
|
-
const out = summarizeBoostsStatus(resp);
|
|
124
|
-
assert.strictEqual(out.level, 3);
|
|
125
|
-
assert.strictEqual(out.boosts, 15);
|
|
126
|
-
assert.strictEqual(out.currentLevelBoosts, 10);
|
|
127
|
-
assert.strictEqual(out.nextLevelBoosts, 25);
|
|
128
|
-
assert.strictEqual(out.giftBoosts, 2);
|
|
129
|
-
assert.strictEqual(out.boostUrl, "https://t.me/boost/test");
|
|
130
|
-
assert.strictEqual(out.myBoost, true);
|
|
131
|
-
assert.deepStrictEqual(out.myBoostSlots, [1, 2]);
|
|
132
|
-
assert.strictEqual(out.premiumAudience, undefined);
|
|
133
|
-
assert.strictEqual(out.prepaidGiveaways, undefined);
|
|
134
|
-
});
|
|
135
|
-
it("includes premiumAudience and prepaidGiveaways when present", () => {
|
|
136
|
-
const resp = new Api.premium.BoostsStatus({
|
|
137
|
-
level: 1,
|
|
138
|
-
currentLevelBoosts: 0,
|
|
139
|
-
boosts: 5,
|
|
140
|
-
boostUrl: "https://t.me/boost/x",
|
|
141
|
-
premiumAudience: new Api.StatsPercentValue({ part: 2, total: 100 }),
|
|
142
|
-
prepaidGiveaways: [new Api.PrepaidGiveaway({ id: bigInt(1), months: 6, quantity: 5, date: 111 })],
|
|
143
|
-
});
|
|
144
|
-
const out = summarizeBoostsStatus(resp);
|
|
145
|
-
assert.deepStrictEqual(out.premiumAudience, { part: 2, total: 100 });
|
|
146
|
-
assert.ok(out.prepaidGiveaways);
|
|
147
|
-
assert.strictEqual(out.prepaidGiveaways?.length, 1);
|
|
148
|
-
assert.strictEqual(out.prepaidGiveaways?.[0].kind, "premium");
|
|
149
|
-
assert.strictEqual(out.prepaidGiveaways?.[0].id, "1");
|
|
150
|
-
});
|
|
151
|
-
it("omits empty prepaidGiveaways list", () => {
|
|
152
|
-
const resp = new Api.premium.BoostsStatus({
|
|
153
|
-
level: 0,
|
|
154
|
-
currentLevelBoosts: 0,
|
|
155
|
-
boosts: 0,
|
|
156
|
-
boostUrl: "https://t.me/boost/y",
|
|
157
|
-
prepaidGiveaways: [],
|
|
158
|
-
});
|
|
159
|
-
const out = summarizeBoostsStatus(resp);
|
|
160
|
-
assert.strictEqual(out.prepaidGiveaways, undefined);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
describe("TelegramService.getBoostsStatus", () => {
|
|
164
|
-
it("invokes premium.GetBoostsStatus with resolved peer and returns summary", async () => {
|
|
165
|
-
const invocations = [];
|
|
166
|
-
const service = makeService(invocations, () => new Api.premium.BoostsStatus({
|
|
167
|
-
level: 2,
|
|
168
|
-
currentLevelBoosts: 5,
|
|
169
|
-
boosts: 7,
|
|
170
|
-
boostUrl: "https://t.me/boost/foo",
|
|
171
|
-
}));
|
|
172
|
-
const internals = service;
|
|
173
|
-
internals.resolvePeer = async (_id) => new Api.InputPeerChannel({ channelId: bigInt(500), accessHash: bigInt(0) });
|
|
174
|
-
const out = await service.getBoostsStatus("@foo");
|
|
175
|
-
const call = invocations.find((r) => r instanceof Api.premium.GetBoostsStatus);
|
|
176
|
-
assert.ok(call);
|
|
177
|
-
assert.strictEqual(out.level, 2);
|
|
178
|
-
assert.strictEqual(out.boosts, 7);
|
|
179
|
-
assert.strictEqual(out.boostUrl, "https://t.me/boost/foo");
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
describe("summarizeBoost", () => {
|
|
183
|
-
it("maps core boost fields and converts bigInt ids to strings", () => {
|
|
184
|
-
const boost = new Api.Boost({
|
|
185
|
-
id: "boost-1",
|
|
186
|
-
userId: bigInt(123),
|
|
187
|
-
date: 1700000000,
|
|
188
|
-
expires: 1702000000,
|
|
189
|
-
gift: true,
|
|
190
|
-
giveaway: false,
|
|
191
|
-
unclaimed: false,
|
|
192
|
-
giveawayMsgId: 55,
|
|
193
|
-
usedGiftSlug: "slug-abc",
|
|
194
|
-
multiplier: 2,
|
|
195
|
-
stars: bigInt(500),
|
|
196
|
-
});
|
|
197
|
-
const out = summarizeBoost(boost);
|
|
198
|
-
assert.strictEqual(out.id, "boost-1");
|
|
199
|
-
assert.strictEqual(out.userId, "123");
|
|
200
|
-
assert.strictEqual(out.date, 1700000000);
|
|
201
|
-
assert.strictEqual(out.expires, 1702000000);
|
|
202
|
-
assert.strictEqual(out.gift, true);
|
|
203
|
-
assert.strictEqual(out.giveaway, false);
|
|
204
|
-
assert.strictEqual(out.unclaimed, false);
|
|
205
|
-
assert.strictEqual(out.giveawayMsgId, 55);
|
|
206
|
-
assert.strictEqual(out.usedGiftSlug, "slug-abc");
|
|
207
|
-
assert.strictEqual(out.multiplier, 2);
|
|
208
|
-
assert.strictEqual(out.stars, "500");
|
|
209
|
-
});
|
|
210
|
-
it("leaves optional fields undefined when missing", () => {
|
|
211
|
-
const boost = new Api.Boost({
|
|
212
|
-
id: "boost-2",
|
|
213
|
-
date: 10,
|
|
214
|
-
expires: 20,
|
|
215
|
-
});
|
|
216
|
-
const out = summarizeBoost(boost);
|
|
217
|
-
assert.strictEqual(out.id, "boost-2");
|
|
218
|
-
assert.strictEqual(out.userId, undefined);
|
|
219
|
-
assert.strictEqual(out.stars, undefined);
|
|
220
|
-
assert.strictEqual(out.multiplier, undefined);
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
describe("summarizeBoostsList", () => {
|
|
224
|
-
it("maps count, boosts and nextOffset", () => {
|
|
225
|
-
const resp = new Api.premium.BoostsList({
|
|
226
|
-
count: 2,
|
|
227
|
-
boosts: [
|
|
228
|
-
new Api.Boost({ id: "a", userId: bigInt(1), date: 1, expires: 2 }),
|
|
229
|
-
new Api.Boost({ id: "b", giveaway: true, date: 3, expires: 4 }),
|
|
230
|
-
],
|
|
231
|
-
nextOffset: "cursor-xyz",
|
|
232
|
-
users: [],
|
|
233
|
-
});
|
|
234
|
-
const out = summarizeBoostsList(resp);
|
|
235
|
-
assert.strictEqual(out.count, 2);
|
|
236
|
-
assert.strictEqual(out.boosts.length, 2);
|
|
237
|
-
assert.strictEqual(out.boosts[0].id, "a");
|
|
238
|
-
assert.strictEqual(out.boosts[0].userId, "1");
|
|
239
|
-
assert.strictEqual(out.boosts[1].giveaway, true);
|
|
240
|
-
assert.strictEqual(out.nextOffset, "cursor-xyz");
|
|
241
|
-
});
|
|
242
|
-
it("handles empty boosts and missing nextOffset", () => {
|
|
243
|
-
const resp = new Api.premium.BoostsList({
|
|
244
|
-
count: 0,
|
|
245
|
-
boosts: [],
|
|
246
|
-
users: [],
|
|
247
|
-
});
|
|
248
|
-
const out = summarizeBoostsList(resp);
|
|
249
|
-
assert.strictEqual(out.count, 0);
|
|
250
|
-
assert.deepStrictEqual(out.boosts, []);
|
|
251
|
-
assert.strictEqual(out.nextOffset, undefined);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
describe("TelegramService.getBoostsList", () => {
|
|
255
|
-
it("invokes premium.GetBoostsList with defaults (empty offset, limit 50)", async () => {
|
|
256
|
-
const invocations = [];
|
|
257
|
-
const service = makeService(invocations, () => new Api.premium.BoostsList({
|
|
258
|
-
count: 1,
|
|
259
|
-
boosts: [new Api.Boost({ id: "boost-1", userId: bigInt(7), date: 10, expires: 20 })],
|
|
260
|
-
nextOffset: "next",
|
|
261
|
-
users: [],
|
|
262
|
-
}));
|
|
263
|
-
const internals = service;
|
|
264
|
-
internals.resolvePeer = async (_id) => new Api.InputPeerChannel({ channelId: bigInt(500), accessHash: bigInt(0) });
|
|
265
|
-
const out = await service.getBoostsList("@foo");
|
|
266
|
-
const call = invocations.find((r) => r instanceof Api.premium.GetBoostsList);
|
|
267
|
-
assert.ok(call);
|
|
268
|
-
assert.strictEqual(call.offset, "");
|
|
269
|
-
assert.strictEqual(call.limit, 50);
|
|
270
|
-
assert.strictEqual(call.gifts, undefined);
|
|
271
|
-
assert.strictEqual(out.count, 1);
|
|
272
|
-
assert.strictEqual(out.boosts[0].id, "boost-1");
|
|
273
|
-
assert.strictEqual(out.nextOffset, "next");
|
|
274
|
-
});
|
|
275
|
-
it("passes gifts/offset/limit through to GetBoostsList", async () => {
|
|
276
|
-
const invocations = [];
|
|
277
|
-
const service = makeService(invocations, () => new Api.premium.BoostsList({ count: 0, boosts: [], users: [] }));
|
|
278
|
-
const internals = service;
|
|
279
|
-
internals.resolvePeer = async (_id) => new Api.InputPeerChannel({ channelId: bigInt(1), accessHash: bigInt(0) });
|
|
280
|
-
await service.getBoostsList("@bar", { gifts: true, offset: "cur", limit: 10 });
|
|
281
|
-
const call = invocations.find((r) => r instanceof Api.premium.GetBoostsList);
|
|
282
|
-
assert.ok(call);
|
|
283
|
-
assert.strictEqual(call.gifts, true);
|
|
284
|
-
assert.strictEqual(call.offset, "cur");
|
|
285
|
-
assert.strictEqual(call.limit, 10);
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
describe("TelegramService.getMyBoosts", () => {
|
|
289
|
-
it("invokes premium.GetMyBoosts and returns summary", async () => {
|
|
290
|
-
const invocations = [];
|
|
291
|
-
const service = makeService(invocations, () => new Api.premium.MyBoosts({
|
|
292
|
-
myBoosts: [
|
|
293
|
-
new Api.MyBoost({
|
|
294
|
-
slot: 1,
|
|
295
|
-
peer: new Api.PeerChannel({ channelId: bigInt(42) }),
|
|
296
|
-
date: 100,
|
|
297
|
-
expires: 200,
|
|
298
|
-
}),
|
|
299
|
-
],
|
|
300
|
-
chats: [],
|
|
301
|
-
users: [],
|
|
302
|
-
}));
|
|
303
|
-
const out = await service.getMyBoosts();
|
|
304
|
-
const call = invocations.find((r) => r instanceof Api.premium.GetMyBoosts);
|
|
305
|
-
assert.ok(call);
|
|
306
|
-
assert.strictEqual(out.count, 1);
|
|
307
|
-
assert.strictEqual(out.myBoosts[0].slot, 1);
|
|
308
|
-
assert.deepStrictEqual(out.myBoosts[0].peer, { kind: "channel", id: "42" });
|
|
309
|
-
});
|
|
310
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,172 +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 { 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
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|