grammy-broadcast 2.0.2
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/LICENSE +21 -0
- package/Readme.md +603 -0
- package/dist/index.d.mts +180 -0
- package/dist/index.d.ts +180 -0
- package/dist/index.js +884 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +884 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +33 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } var _class; var _class2; var _class3;// src/middleware.ts
|
|
2
|
+
var _grammy = require('grammy');
|
|
3
|
+
|
|
4
|
+
// src/utils.ts
|
|
5
|
+
function sleep(milli) {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
setTimeout(resolve, milli);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function buildProgressBtnText(percent, chars = 10) {
|
|
11
|
+
let progress = Math.floor(percent * chars);
|
|
12
|
+
let empty = chars - progress;
|
|
13
|
+
console.log({ progress, empty });
|
|
14
|
+
return "\u2588".repeat(progress) + "\u2591".repeat(empty) + ` (${Math.floor(percent * 1e3) / 10}%)`;
|
|
15
|
+
}
|
|
16
|
+
function buildProgressText(error, sent, total, rateLimit) {
|
|
17
|
+
const rateText = rateLimit !== void 0 ? `
|
|
18
|
+
\u26A1 Rate: ${rateLimit} msg/sec` : "";
|
|
19
|
+
return `\u231B Progress: ${error + sent}/${total}
|
|
20
|
+
\u2705 Sent: ${sent}
|
|
21
|
+
\u274C Error: ${error} (${Math.floor(error / total * 1e4) / 100}%)${rateText}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/middleware.ts
|
|
25
|
+
function getMiddleware(options) {
|
|
26
|
+
var _a;
|
|
27
|
+
const middleware = new (0, _grammy.Composer)();
|
|
28
|
+
let broadcastMiddleware;
|
|
29
|
+
if ((_a = options.sudoUsers) == null ? void 0 : _a.length) {
|
|
30
|
+
broadcastMiddleware = middleware.filter((ctx) => {
|
|
31
|
+
var _a2;
|
|
32
|
+
if ((_a2 = ctx.from) == null ? void 0 : _a2.id) {
|
|
33
|
+
return options.sudoUsers.includes(ctx.from.id);
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
});
|
|
37
|
+
} else if (typeof options.hasPermission === "function") {
|
|
38
|
+
broadcastMiddleware = middleware.filter(options.hasPermission);
|
|
39
|
+
} else {
|
|
40
|
+
broadcastMiddleware = middleware;
|
|
41
|
+
}
|
|
42
|
+
broadcastMiddleware.command([options.cmds.broadcast, options.cmds.copy, options.cmds.forward], async (ctx) => {
|
|
43
|
+
var _a2;
|
|
44
|
+
let [command, ...args] = ctx.message.text.substring(1).split(" ");
|
|
45
|
+
let type;
|
|
46
|
+
let filter;
|
|
47
|
+
if (command === options.cmds.broadcast) {
|
|
48
|
+
if (args.length < 1) {
|
|
49
|
+
return ctx.reply(`Usage: /${options.cmds.broadcast} <type> [filter]
|
|
50
|
+
|
|
51
|
+
\`type\` should be copy or forward
|
|
52
|
+
\`filter\` is anything that want to passed to getBroadcastChats
|
|
53
|
+
`, {
|
|
54
|
+
parse_mode: "Markdown"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
type = args[0];
|
|
58
|
+
filter = args.slice(1).join(" ");
|
|
59
|
+
} else if (command === options.cmds.copy) {
|
|
60
|
+
type = "copy";
|
|
61
|
+
filter = args.join(" ");
|
|
62
|
+
} else if (command === options.cmds.forward) {
|
|
63
|
+
type = "forward";
|
|
64
|
+
filter = args.join(" ");
|
|
65
|
+
}
|
|
66
|
+
if (!["copy", "forward"].includes(type)) {
|
|
67
|
+
return ctx.reply(`Invalid type ${type}`);
|
|
68
|
+
}
|
|
69
|
+
let brdId = Math.random().toString(36).substring(7);
|
|
70
|
+
if (!ctx.message.reply_to_message) {
|
|
71
|
+
return ctx.reply("Reply to a message");
|
|
72
|
+
}
|
|
73
|
+
await options.storage.hset(options.keyPrefix + "info:" + brdId, {
|
|
74
|
+
type,
|
|
75
|
+
chatFilter: filter,
|
|
76
|
+
message_ids: (_a2 = ctx.message.reply_to_message) == null ? void 0 : _a2.message_id.toString(),
|
|
77
|
+
chat_id: ctx.chat.id.toString(),
|
|
78
|
+
user_id: ctx.from.id.toString(),
|
|
79
|
+
id: brdId,
|
|
80
|
+
error: "0",
|
|
81
|
+
sent: "0",
|
|
82
|
+
botId: ctx.me.id.toString(),
|
|
83
|
+
total: "-1"
|
|
84
|
+
});
|
|
85
|
+
return ctx.reply(`
|
|
86
|
+
Ready to broadcast!
|
|
87
|
+
currently 1 message is in queue
|
|
88
|
+
for send multi message in this broadcast reply this command to another message
|
|
89
|
+
<code>/${options.cmds.addmsg} ${brdId}</code>
|
|
90
|
+
`, {
|
|
91
|
+
parse_mode: "HTML",
|
|
92
|
+
reply_markup: new (0, _grammy.InlineKeyboard)().text("Preview", "brd:preview:" + brdId).text("Pin", `brd:pin:${brdId}`).row().text("Start", "brd:start:" + brdId).text("Cancel", "brd:stop:" + brdId)
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
broadcastMiddleware.command(options.cmds.addmsg, async (ctx) => {
|
|
96
|
+
var _a2;
|
|
97
|
+
let args = ctx.message.text.split(" ").slice(1);
|
|
98
|
+
if (args.length < 1) {
|
|
99
|
+
return ctx.reply(`Usage: /${options.cmds.addmsg} <id>`);
|
|
100
|
+
}
|
|
101
|
+
let brdId = args[0];
|
|
102
|
+
if (!ctx.message.reply_to_message) {
|
|
103
|
+
return ctx.reply("Reply to a message");
|
|
104
|
+
}
|
|
105
|
+
let newMsgId = (_a2 = ctx.message.reply_to_message) == null ? void 0 : _a2.message_id;
|
|
106
|
+
let messageIds = await options.storage.hget(options.keyPrefix + "info:" + brdId, "message_ids");
|
|
107
|
+
if (!messageIds) {
|
|
108
|
+
return ctx.reply("Broadcast not found");
|
|
109
|
+
}
|
|
110
|
+
let currentIds = messageIds.split("_").map((e) => Number.parseInt(e));
|
|
111
|
+
if (Math.max(newMsgId, ...currentIds) !== newMsgId) {
|
|
112
|
+
return ctx.reply("Message should be newer than previous messages");
|
|
113
|
+
}
|
|
114
|
+
if (currentIds.includes(newMsgId)) {
|
|
115
|
+
return ctx.reply("Message already in queue");
|
|
116
|
+
}
|
|
117
|
+
currentIds.push(newMsgId);
|
|
118
|
+
await options.storage.hset(options.keyPrefix + "info:" + brdId, "message_ids", currentIds.join("_"));
|
|
119
|
+
let isPin = await options.storage.hget(options.keyPrefix + "info:" + brdId, "pin");
|
|
120
|
+
return ctx.reply(`Message added to queue
|
|
121
|
+
|
|
122
|
+
Messages Count ${currentIds.length}`, {
|
|
123
|
+
reply_markup: new (0, _grammy.InlineKeyboard)().text("Preview", "brd:preview:" + brdId).text(`Pin${isPin ? " \u2705" : ""}`, `brd:pin:${brdId}`).row().text("Start", "brd:start:" + brdId).text("Cancel", "brd:stop:" + brdId)
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
broadcastMiddleware.callbackQuery(/brd:progress:(\w+)/, async (ctx) => {
|
|
127
|
+
let info = await options.storage.hgetall(options.keyPrefix + "info:" + ctx.match[1]);
|
|
128
|
+
return ctx.answerCallbackQuery(
|
|
129
|
+
{
|
|
130
|
+
text: buildProgressText(+info.error, +info.sent, +info.total),
|
|
131
|
+
show_alert: true
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
broadcastMiddleware.callbackQuery(/brd:pause:(\w+)/, async (ctx) => {
|
|
136
|
+
await options.storage.hset(options.keyPrefix + "info:" + ctx.match[1], "paused", "1");
|
|
137
|
+
return ctx.editMessageText("Broadcast paused!", {
|
|
138
|
+
reply_markup: new (0, _grammy.InlineKeyboard)().text("Resume", "brd:resume:" + ctx.match[1]).text("Stop", "brd:stop:" + ctx.match[1])
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
broadcastMiddleware.callbackQuery(/brd:resume:(\w+)/, async (ctx) => {
|
|
142
|
+
await options.storage.hdel(options.keyPrefix + "info:" + ctx.match[1], "paused");
|
|
143
|
+
return ctx.editMessageText(
|
|
144
|
+
"Lets begin...",
|
|
145
|
+
{
|
|
146
|
+
reply_markup: new (0, _grammy.InlineKeyboard)().text("Pause", "brd:pause:" + ctx.match[1]).text("Stop", "brd:stop:" + ctx.match[1])
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
broadcastMiddleware.callbackQuery(/brd:pin:(\w+)/, async (ctx) => {
|
|
151
|
+
let brdId = ctx.match[1];
|
|
152
|
+
let isPin = await options.storage.hget(options.keyPrefix + "info:" + brdId, "pin");
|
|
153
|
+
if (isPin) {
|
|
154
|
+
await options.storage.hdel(options.keyPrefix + "info:" + brdId, "pin");
|
|
155
|
+
} else {
|
|
156
|
+
await options.storage.hset(options.keyPrefix + "info:" + brdId, "pin", "1");
|
|
157
|
+
}
|
|
158
|
+
return ctx.editMessageReplyMarkup({
|
|
159
|
+
reply_markup: new (0, _grammy.InlineKeyboard)().text("Preview", "brd:preview:" + brdId).text(`Pin${!isPin ? " \u2705" : ""}`, `brd:pin:${brdId}`).row().text("Start", "brd:start:" + brdId).text("Cancel", "brd:stop:" + brdId)
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
broadcastMiddleware.callbackQuery(/brd:preview:(\w+)/, async (ctx) => {
|
|
163
|
+
let info = await options.storage.hgetall(options.keyPrefix + "info:" + ctx.match[1]);
|
|
164
|
+
if (!info.message_ids) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
let messageIds = info.message_ids.split("_").map((e) => Number.parseInt(e));
|
|
168
|
+
if (info.type === "copy") {
|
|
169
|
+
let msgs = await ctx.copyMessages(info.chat_id, messageIds);
|
|
170
|
+
if (info.pin) {
|
|
171
|
+
await ctx.pinChatMessage(msgs.pop().message_id);
|
|
172
|
+
}
|
|
173
|
+
} else if (info.type === "forward") {
|
|
174
|
+
let msgs = await ctx.forwardMessages(info.chat_id, messageIds);
|
|
175
|
+
if (info.pin) {
|
|
176
|
+
await ctx.pinChatMessage(msgs.pop().message_id);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
broadcastMiddleware.callbackQuery(/brd:start:(\w+)/, async (ctx) => {
|
|
181
|
+
let id = ctx.match[1];
|
|
182
|
+
await options.storage.rpush(options.keyPrefix + "list", id);
|
|
183
|
+
return ctx.editMessageText(`Broadcast added to queue it takes some time to start...`, {
|
|
184
|
+
reply_markup: new (0, _grammy.InlineKeyboard)().text("Pause", "brd:pause:" + id).text("Stop", "brd:stop:" + id)
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
broadcastMiddleware.callbackQuery(/brd:stop:(\w+)/, async (ctx) => {
|
|
188
|
+
return ctx.editMessageReplyMarkup({
|
|
189
|
+
reply_markup: new (0, _grammy.InlineKeyboard)().text("Sure?").row().text("Yes", "brd:stop_confirm:" + ctx.match[1]).text("No", `brd:stop_cancel:${ctx.match[1]}`)
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
broadcastMiddleware.callbackQuery(/brd:stop_cancel/, async (ctx) => {
|
|
193
|
+
return ctx.editMessageReplyMarkup(
|
|
194
|
+
{
|
|
195
|
+
reply_markup: new (0, _grammy.InlineKeyboard)().text("Pause", "brd:pause:" + ctx.match[1])
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
broadcastMiddleware.callbackQuery(/brd:stop_confirm:(\w+)/, async (ctx) => {
|
|
200
|
+
let id = ctx.match[1];
|
|
201
|
+
await options.storage.del(options.keyPrefix + "chats:" + id);
|
|
202
|
+
await options.storage.del(options.keyPrefix + "info:" + id);
|
|
203
|
+
await options.storage.lrem(options.keyPrefix + "list", 1, id);
|
|
204
|
+
return ctx.editMessageText("Broadcast stopped");
|
|
205
|
+
});
|
|
206
|
+
return middleware;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/broadcast.queue.ts
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
// src/initChats.queue.ts
|
|
213
|
+
var ChatsFetcher = class {
|
|
214
|
+
constructor(options) {
|
|
215
|
+
this.options = options;
|
|
216
|
+
}
|
|
217
|
+
async fetchChats(broadcast, progressCallback) {
|
|
218
|
+
let chatOffset = +(broadcast.chatOffset || "0");
|
|
219
|
+
if (progressCallback) {
|
|
220
|
+
await progressCallback(broadcast);
|
|
221
|
+
}
|
|
222
|
+
while (true) {
|
|
223
|
+
let chatIds = await this.options.getBroadcastChats(+broadcast.botId, chatOffset, this.options.chunkSize, broadcast.chatFilter);
|
|
224
|
+
await this.options.storage.rpush(this.options.keyPrefix + "chats:" + broadcast.id, ...chatIds.map(String));
|
|
225
|
+
chatOffset += chatIds.length;
|
|
226
|
+
broadcast.chatOffset = chatOffset.toString();
|
|
227
|
+
await this.options.storage.hset(this.options.keyPrefix + "info:" + broadcast.id, "chatOffset", chatOffset.toString());
|
|
228
|
+
if (progressCallback) {
|
|
229
|
+
await progressCallback(broadcast);
|
|
230
|
+
}
|
|
231
|
+
if (chatIds.length < this.options.chunkSize) {
|
|
232
|
+
await this.options.storage.hset(this.options.keyPrefix + "info:" + broadcast.id, "total", chatOffset.toString());
|
|
233
|
+
broadcast.total = chatOffset.toString();
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/broadcast.queue.ts
|
|
241
|
+
var RateLimiter = (_class = class {
|
|
242
|
+
__init() {this.queue = []}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
__init2() {this.refillTimer = null}
|
|
248
|
+
__init3() {this.lastRefillTime = Date.now()}
|
|
249
|
+
constructor(requestsPerSecond) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
|
|
250
|
+
this.initialMaxTokens = requestsPerSecond;
|
|
251
|
+
this.maxTokens = requestsPerSecond;
|
|
252
|
+
this.tokens = requestsPerSecond;
|
|
253
|
+
this.refillInterval = requestsPerSecond >= 100 ? 10 : 100;
|
|
254
|
+
this.startRefill();
|
|
255
|
+
}
|
|
256
|
+
getCurrentRate() {
|
|
257
|
+
return this.maxTokens;
|
|
258
|
+
}
|
|
259
|
+
reduceRate(factor = 0.5) {
|
|
260
|
+
const newRate = Math.max(1, Math.floor(this.maxTokens * factor));
|
|
261
|
+
console.log(`Reducing rate limit from ${this.maxTokens}/sec to ${newRate}/sec`);
|
|
262
|
+
this.maxTokens = newRate;
|
|
263
|
+
this.refillInterval = newRate >= 100 ? 10 : 100;
|
|
264
|
+
this.tokens = Math.min(this.tokens, this.maxTokens);
|
|
265
|
+
}
|
|
266
|
+
resetRate() {
|
|
267
|
+
if (this.maxTokens !== this.initialMaxTokens) {
|
|
268
|
+
console.log(`Resetting rate limit from ${this.maxTokens}/sec to ${this.initialMaxTokens}/sec`);
|
|
269
|
+
this.maxTokens = this.initialMaxTokens;
|
|
270
|
+
this.refillInterval = this.initialMaxTokens >= 100 ? 10 : 100;
|
|
271
|
+
this.tokens = Math.min(this.tokens, this.maxTokens);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
startRefill() {
|
|
275
|
+
if (this.refillTimer) return;
|
|
276
|
+
this.refillTimer = setInterval(() => {
|
|
277
|
+
const now = Date.now();
|
|
278
|
+
const elapsed = now - this.lastRefillTime;
|
|
279
|
+
const tokensToAdd = Math.floor(this.maxTokens * elapsed / 1e3);
|
|
280
|
+
if (tokensToAdd > 0) {
|
|
281
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
|
|
282
|
+
this.lastRefillTime = now;
|
|
283
|
+
this.processQueue();
|
|
284
|
+
}
|
|
285
|
+
}, this.refillInterval);
|
|
286
|
+
}
|
|
287
|
+
processQueue() {
|
|
288
|
+
while (this.queue.length > 0 && this.tokens > 0) {
|
|
289
|
+
this.tokens--;
|
|
290
|
+
const resolve = this.queue.shift();
|
|
291
|
+
resolve();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async acquire() {
|
|
295
|
+
const now = Date.now();
|
|
296
|
+
const elapsed = now - this.lastRefillTime;
|
|
297
|
+
if (elapsed >= 1e3) {
|
|
298
|
+
this.tokens = this.maxTokens;
|
|
299
|
+
this.lastRefillTime = now;
|
|
300
|
+
} else if (elapsed > 0) {
|
|
301
|
+
const tokensToAdd = Math.floor(this.maxTokens * elapsed / 1e3);
|
|
302
|
+
if (tokensToAdd > 0) {
|
|
303
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
|
|
304
|
+
this.lastRefillTime = now;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (this.tokens > 0) {
|
|
308
|
+
this.tokens--;
|
|
309
|
+
return Promise.resolve();
|
|
310
|
+
}
|
|
311
|
+
return new Promise((resolve) => {
|
|
312
|
+
this.queue.push(resolve);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
destroy() {
|
|
316
|
+
if (this.refillTimer) {
|
|
317
|
+
clearInterval(this.refillTimer);
|
|
318
|
+
this.refillTimer = null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}, _class);
|
|
322
|
+
var BroadcastQueue = (_class2 = class {
|
|
323
|
+
constructor(options) {;_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);
|
|
324
|
+
this.options = options;
|
|
325
|
+
const requestsPerSecond = this.options.allowPaidBroadcast ? 1e3 : 30;
|
|
326
|
+
this.rateLimiter = new RateLimiter(requestsPerSecond);
|
|
327
|
+
}
|
|
328
|
+
__init4() {this.reportIds = {}}
|
|
329
|
+
__init5() {this.lastReports = {}}
|
|
330
|
+
__init6() {this.processingBroadcasts = /* @__PURE__ */ new Set()}
|
|
331
|
+
|
|
332
|
+
__init7() {this.lastRateLimitTime = 0}
|
|
333
|
+
async checkBroadcasts() {
|
|
334
|
+
let broadcasts = await this.options.storage.lrange(this.options.keyPrefix + "list", 0, -1);
|
|
335
|
+
let newBroadcasts = broadcasts.filter((id) => !this.processingBroadcasts.has(id));
|
|
336
|
+
if (newBroadcasts.length > 0) {
|
|
337
|
+
newBroadcasts.forEach((id) => this.processingBroadcasts.add(id));
|
|
338
|
+
Promise.all(newBroadcasts.map(
|
|
339
|
+
(broadcastId) => this.sendBroadcast(broadcastId).finally(() => {
|
|
340
|
+
this.processingBroadcasts.delete(broadcastId);
|
|
341
|
+
})
|
|
342
|
+
)).catch((err) => {
|
|
343
|
+
console.error("Error processing broadcasts:", err);
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
setTimeout(this.checkBroadcasts.bind(this), this.options.checkQueueInterval);
|
|
347
|
+
}
|
|
348
|
+
async sendBroadcast(id) {
|
|
349
|
+
let broadcastInfo = await this.options.storage.hgetall(this.options.keyPrefix + "info:" + id);
|
|
350
|
+
if (broadcastInfo.total === "-1") {
|
|
351
|
+
console.log("fetching chats");
|
|
352
|
+
let fetcher = new ChatsFetcher(this.options);
|
|
353
|
+
await fetcher.fetchChats(broadcastInfo, async (info) => {
|
|
354
|
+
await this.sendProgress(info, false, "fetching");
|
|
355
|
+
});
|
|
356
|
+
broadcastInfo = await this.options.storage.hgetall(this.options.keyPrefix + "info:" + id);
|
|
357
|
+
}
|
|
358
|
+
if (!broadcastInfo) {
|
|
359
|
+
await this.options.storage.lrem(this.options.keyPrefix + "list", 1, id);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
let chats = await this.options.storage.lpop(this.options.keyPrefix + "chats:" + id, this.options.chunkSize);
|
|
363
|
+
if (broadcastInfo.paused) return;
|
|
364
|
+
if (!(chats == null ? void 0 : chats.length)) {
|
|
365
|
+
await this.options.storage.del(this.options.keyPrefix + "chats:" + id);
|
|
366
|
+
await this.options.storage.del(this.options.keyPrefix + "info:" + id);
|
|
367
|
+
await this.options.storage.lrem(this.options.keyPrefix + "list", 1, id);
|
|
368
|
+
await this.sendProgress(broadcastInfo, true);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
broadcastInfo.sent = broadcastInfo.sent || "0";
|
|
372
|
+
broadcastInfo.error = broadcastInfo.error || "0";
|
|
373
|
+
const sendPromises = chats.map(async (chat) => {
|
|
374
|
+
await this.rateLimiter.acquire();
|
|
375
|
+
let isSent = await this.sendToChat(chat, broadcastInfo);
|
|
376
|
+
if (isSent) {
|
|
377
|
+
broadcastInfo.sent = (+broadcastInfo.sent + 1).toString();
|
|
378
|
+
await this.options.storage.hincrby(this.options.keyPrefix + "info:" + id, "sent", 1);
|
|
379
|
+
} else {
|
|
380
|
+
broadcastInfo.error = (+broadcastInfo.error + 1).toString();
|
|
381
|
+
await this.options.storage.hincrby(this.options.keyPrefix + "info:" + id, "error", 1);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
await Promise.all(sendPromises);
|
|
385
|
+
await this.sendProgress(broadcastInfo);
|
|
386
|
+
await this.sendBroadcast(id);
|
|
387
|
+
}
|
|
388
|
+
async sendProgress(broadcastInfo, finished = false, stage = "broadcasting") {
|
|
389
|
+
if (this.options.progressCallback) {
|
|
390
|
+
this.options.progressCallback(
|
|
391
|
+
broadcastInfo.id,
|
|
392
|
+
+broadcastInfo.sent || 0,
|
|
393
|
+
+broadcastInfo.error || 0,
|
|
394
|
+
+broadcastInfo.total || 0
|
|
395
|
+
);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
let api = await this.options.getApi(+broadcastInfo.botId);
|
|
399
|
+
let progressText;
|
|
400
|
+
let percent;
|
|
401
|
+
let title;
|
|
402
|
+
if (stage === "fetching") {
|
|
403
|
+
let chatOffset = +(broadcastInfo.chatOffset || "0");
|
|
404
|
+
let currentTotal = broadcastInfo.total === "-1" ? chatOffset : +broadcastInfo.total;
|
|
405
|
+
progressText = `\u{1F50D} Fetching chats...
|
|
406
|
+
\u{1F4CA} Chats fetched: ${chatOffset}/${currentTotal}`;
|
|
407
|
+
percent = 0;
|
|
408
|
+
title = "\u{1F50D} Fetching Chats";
|
|
409
|
+
} else {
|
|
410
|
+
let error = +broadcastInfo.error || 0;
|
|
411
|
+
let sent = +broadcastInfo.sent || 0;
|
|
412
|
+
let total = +broadcastInfo.total || 0;
|
|
413
|
+
percent = total > 0 ? (error + sent) / total : 0;
|
|
414
|
+
const currentRate = this.rateLimiter.getCurrentRate();
|
|
415
|
+
progressText = buildProgressText(error, sent, total, currentRate);
|
|
416
|
+
title = finished ? "\u2705 Broadcast Finished" : "\u231B Broadcasting";
|
|
417
|
+
}
|
|
418
|
+
let replyMarkup = new (0, _grammy.InlineKeyboard)().text(buildProgressBtnText(percent), `brd:progress:${broadcastInfo.id}`).row().text("Pause", `brd:pause:${broadcastInfo.id}`).text("Stop", `brd:stop:${broadcastInfo.id}`);
|
|
419
|
+
if (finished) {
|
|
420
|
+
await api.sendMessage(broadcastInfo.chat_id, `${title}
|
|
421
|
+
${progressText}`);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
let msgId = this.reportIds[broadcastInfo.id];
|
|
425
|
+
if (!msgId) {
|
|
426
|
+
let msg = await api.sendMessage(broadcastInfo.chat_id, `${title}
|
|
427
|
+
${progressText}`, {
|
|
428
|
+
reply_markup: replyMarkup
|
|
429
|
+
});
|
|
430
|
+
this.reportIds[broadcastInfo.id] = msg.message_id;
|
|
431
|
+
this.lastReports[broadcastInfo.id] = /* @__PURE__ */ new Date();
|
|
432
|
+
} else {
|
|
433
|
+
let lastReport = this.lastReports[broadcastInfo.id];
|
|
434
|
+
if (lastReport && Date.now() - lastReport.getTime() < this.options.reportFrequency) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
await api.editMessageText(broadcastInfo.chat_id, msgId, `${title}
|
|
438
|
+
${progressText}`, {
|
|
439
|
+
reply_markup: replyMarkup
|
|
440
|
+
});
|
|
441
|
+
this.lastReports[broadcastInfo.id] = /* @__PURE__ */ new Date();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
async sendToChat(chatId, broadcastInfo) {
|
|
445
|
+
var _a;
|
|
446
|
+
let msgIds = (_a = broadcastInfo.message_ids) == null ? void 0 : _a.split("_").map((e) => parseInt(e));
|
|
447
|
+
let api = await this.options.getApi(+broadcastInfo.botId);
|
|
448
|
+
try {
|
|
449
|
+
let msgId;
|
|
450
|
+
const paidBroadcastOptions = this.options.allowPaidBroadcast ? { allow_paid_broadcast: true } : {};
|
|
451
|
+
if (broadcastInfo.type === "text") {
|
|
452
|
+
let msg = await api.sendMessage(chatId, broadcastInfo.text, paidBroadcastOptions);
|
|
453
|
+
msgId = msg.message_id;
|
|
454
|
+
} else if (broadcastInfo.type === "forward") {
|
|
455
|
+
let msgs = await api.forwardMessages(chatId, broadcastInfo.chat_id, msgIds, paidBroadcastOptions);
|
|
456
|
+
msgId = msgs.pop().message_id;
|
|
457
|
+
} else if (broadcastInfo.type === "copy") {
|
|
458
|
+
let msgs = await api.copyMessages(chatId, broadcastInfo.chat_id, msgIds, paidBroadcastOptions);
|
|
459
|
+
msgId = msgs.pop().message_id;
|
|
460
|
+
}
|
|
461
|
+
if (broadcastInfo.pin) {
|
|
462
|
+
await api.pinChatMessage(chatId, msgId, { disable_notification: true, ...paidBroadcastOptions });
|
|
463
|
+
}
|
|
464
|
+
return true;
|
|
465
|
+
} catch (err) {
|
|
466
|
+
let retry = await this.handleError(+broadcastInfo.botId, chatId, err);
|
|
467
|
+
if (retry) {
|
|
468
|
+
await this.sendToChat(chatId, broadcastInfo);
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
async handleError(botId, chatId, error) {
|
|
475
|
+
var _a;
|
|
476
|
+
try {
|
|
477
|
+
const message = "description" in error ? error.description : error.message;
|
|
478
|
+
const errorMessage = message.toLowerCase();
|
|
479
|
+
const setRestricted = ((_a = this.options.setRestricted) == null ? void 0 : _a.bind(null, botId, chatId)) || ((reason) => {
|
|
480
|
+
console.log(`ChatId: ${chatId} is restricted for reason: ${reason} you didn't handled this error`);
|
|
481
|
+
});
|
|
482
|
+
if (errorMessage.includes("blocked")) {
|
|
483
|
+
setRestricted("block");
|
|
484
|
+
}
|
|
485
|
+
if (errorMessage.includes("deactivated")) {
|
|
486
|
+
setRestricted("deactivated");
|
|
487
|
+
}
|
|
488
|
+
if (errorMessage.includes("kicked")) {
|
|
489
|
+
setRestricted("banned");
|
|
490
|
+
}
|
|
491
|
+
if (errorMessage.includes("restricted")) {
|
|
492
|
+
setRestricted("restricted");
|
|
493
|
+
}
|
|
494
|
+
if (errorMessage.includes("initiate conversation")) {
|
|
495
|
+
setRestricted("no-conv");
|
|
496
|
+
}
|
|
497
|
+
if ("parameters" in error) {
|
|
498
|
+
if (error.parameters.retry_after) {
|
|
499
|
+
const retryAfter = error.parameters.retry_after;
|
|
500
|
+
console.log(`Rate limited by Telegram for ${retryAfter} secs`);
|
|
501
|
+
await sleep(retryAfter * 1e3);
|
|
502
|
+
const now = Date.now();
|
|
503
|
+
const timeSinceLastLimit = now - this.lastRateLimitTime;
|
|
504
|
+
this.lastRateLimitTime = now;
|
|
505
|
+
if (timeSinceLastLimit < 6e4) {
|
|
506
|
+
this.rateLimiter.reduceRate(0.5);
|
|
507
|
+
} else {
|
|
508
|
+
this.rateLimiter.reduceRate(0.7);
|
|
509
|
+
}
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
} catch (err) {
|
|
514
|
+
console.log("HandlerError error: ", err);
|
|
515
|
+
} finally {
|
|
516
|
+
}
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
}, _class2);
|
|
520
|
+
|
|
521
|
+
// src/storage.ts
|
|
522
|
+
var Storage = class {
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// src/storage/redis.storage.ts
|
|
526
|
+
var RedisStorage = class extends Storage {
|
|
527
|
+
constructor(redis) {
|
|
528
|
+
super();
|
|
529
|
+
this.redis = redis;
|
|
530
|
+
}
|
|
531
|
+
async lrange(key, start, end) {
|
|
532
|
+
return await this.redis.lrange(key, start, end);
|
|
533
|
+
}
|
|
534
|
+
async hgetall(key) {
|
|
535
|
+
return await this.redis.hgetall(key);
|
|
536
|
+
}
|
|
537
|
+
async lrem(key, count, value) {
|
|
538
|
+
await this.redis.lrem(key, count, value);
|
|
539
|
+
}
|
|
540
|
+
async lpop(key, count) {
|
|
541
|
+
const result = await this.redis.lpop(key, count);
|
|
542
|
+
if (result === null) return null;
|
|
543
|
+
return Array.isArray(result) ? result : [result];
|
|
544
|
+
}
|
|
545
|
+
async del(...keys) {
|
|
546
|
+
await this.redis.del(...keys);
|
|
547
|
+
}
|
|
548
|
+
async hincrby(key, field, increment) {
|
|
549
|
+
await this.redis.hincrby(key, field, increment);
|
|
550
|
+
}
|
|
551
|
+
async hset(key, fieldOrObject, value) {
|
|
552
|
+
if (typeof fieldOrObject === "string") {
|
|
553
|
+
await this.redis.hset(key, fieldOrObject, value);
|
|
554
|
+
} else {
|
|
555
|
+
await this.redis.hset(key, fieldOrObject);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async hget(key, field) {
|
|
559
|
+
return await this.redis.hget(key, field);
|
|
560
|
+
}
|
|
561
|
+
async hdel(key, ...fields) {
|
|
562
|
+
await this.redis.hdel(key, ...fields);
|
|
563
|
+
}
|
|
564
|
+
async rpush(key, ...values) {
|
|
565
|
+
await this.redis.rpush(key, ...values);
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// src/storage/memory.storage.ts
|
|
570
|
+
var MemoryStorage = (_class3 = class extends Storage {constructor(...args2) { super(...args2); _class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this); }
|
|
571
|
+
__init8() {this.lists = /* @__PURE__ */ new Map()}
|
|
572
|
+
__init9() {this.hashes = /* @__PURE__ */ new Map()}
|
|
573
|
+
async lrange(key, start, end) {
|
|
574
|
+
const list = this.lists.get(key) || [];
|
|
575
|
+
const actualEnd = end === -1 ? list.length : end + 1;
|
|
576
|
+
return list.slice(start, actualEnd);
|
|
577
|
+
}
|
|
578
|
+
async hgetall(key) {
|
|
579
|
+
const hash = this.hashes.get(key);
|
|
580
|
+
if (!hash) return {};
|
|
581
|
+
const result = {};
|
|
582
|
+
hash.forEach((value, field) => {
|
|
583
|
+
result[field] = value;
|
|
584
|
+
});
|
|
585
|
+
return result;
|
|
586
|
+
}
|
|
587
|
+
async lrem(key, count, value) {
|
|
588
|
+
const list = this.lists.get(key);
|
|
589
|
+
if (!list) return;
|
|
590
|
+
if (count === 0) {
|
|
591
|
+
const filtered = list.filter((item) => item !== value);
|
|
592
|
+
this.lists.set(key, filtered);
|
|
593
|
+
} else if (count > 0) {
|
|
594
|
+
let removed = 0;
|
|
595
|
+
const filtered = list.filter((item) => {
|
|
596
|
+
if (item === value && removed < count) {
|
|
597
|
+
removed++;
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
return true;
|
|
601
|
+
});
|
|
602
|
+
this.lists.set(key, filtered);
|
|
603
|
+
} else {
|
|
604
|
+
let removed = 0;
|
|
605
|
+
const filtered = list.reverse().filter((item) => {
|
|
606
|
+
if (item === value && removed < Math.abs(count)) {
|
|
607
|
+
removed++;
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
return true;
|
|
611
|
+
}).reverse();
|
|
612
|
+
this.lists.set(key, filtered);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
async lpop(key, count) {
|
|
616
|
+
const list = this.lists.get(key);
|
|
617
|
+
if (!list || list.length === 0) return null;
|
|
618
|
+
const popped = list.splice(0, count);
|
|
619
|
+
if (list.length === 0) {
|
|
620
|
+
this.lists.delete(key);
|
|
621
|
+
}
|
|
622
|
+
return popped;
|
|
623
|
+
}
|
|
624
|
+
async del(...keys) {
|
|
625
|
+
for (const key of keys) {
|
|
626
|
+
this.lists.delete(key);
|
|
627
|
+
this.hashes.delete(key);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
async hincrby(key, field, increment) {
|
|
631
|
+
let hash = this.hashes.get(key);
|
|
632
|
+
if (!hash) {
|
|
633
|
+
hash = /* @__PURE__ */ new Map();
|
|
634
|
+
this.hashes.set(key, hash);
|
|
635
|
+
}
|
|
636
|
+
const currentValue = hash.get(field) || "0";
|
|
637
|
+
const newValue = (parseInt(currentValue, 10) + increment).toString();
|
|
638
|
+
hash.set(field, newValue);
|
|
639
|
+
}
|
|
640
|
+
async hset(key, fieldOrObject, value) {
|
|
641
|
+
let hash = this.hashes.get(key);
|
|
642
|
+
if (!hash) {
|
|
643
|
+
hash = /* @__PURE__ */ new Map();
|
|
644
|
+
this.hashes.set(key, hash);
|
|
645
|
+
}
|
|
646
|
+
if (typeof fieldOrObject === "string") {
|
|
647
|
+
hash.set(fieldOrObject, value);
|
|
648
|
+
} else {
|
|
649
|
+
Object.entries(fieldOrObject).forEach(([field, val]) => {
|
|
650
|
+
hash.set(field, val);
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
async hget(key, field) {
|
|
655
|
+
const hash = this.hashes.get(key);
|
|
656
|
+
if (!hash) return null;
|
|
657
|
+
return hash.get(field) || null;
|
|
658
|
+
}
|
|
659
|
+
async hdel(key, ...fields) {
|
|
660
|
+
const hash = this.hashes.get(key);
|
|
661
|
+
if (!hash) return;
|
|
662
|
+
fields.forEach((field) => hash.delete(field));
|
|
663
|
+
if (hash.size === 0) {
|
|
664
|
+
this.hashes.delete(key);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
async rpush(key, ...values) {
|
|
668
|
+
let list = this.lists.get(key);
|
|
669
|
+
if (!list) {
|
|
670
|
+
list = [];
|
|
671
|
+
this.lists.set(key, list);
|
|
672
|
+
}
|
|
673
|
+
list.push(...values);
|
|
674
|
+
}
|
|
675
|
+
}, _class3);
|
|
676
|
+
|
|
677
|
+
// src/storage/file.storage.ts
|
|
678
|
+
var _promises = require('fs/promises'); var fs = _interopRequireWildcard(_promises);
|
|
679
|
+
var _path = require('path'); var path = _interopRequireWildcard(_path);
|
|
680
|
+
var FileStorage = class extends Storage {
|
|
681
|
+
|
|
682
|
+
constructor(baseDir = "./broadcast-storage") {
|
|
683
|
+
super();
|
|
684
|
+
this.baseDir = baseDir;
|
|
685
|
+
}
|
|
686
|
+
async ensureDir() {
|
|
687
|
+
try {
|
|
688
|
+
await fs.mkdir(this.baseDir, { recursive: true });
|
|
689
|
+
} catch (error) {
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
getListPath(key) {
|
|
693
|
+
return path.join(this.baseDir, `list_${key.replace(/[^a-zA-Z0-9]/g, "_")}.json`);
|
|
694
|
+
}
|
|
695
|
+
getHashPath(key) {
|
|
696
|
+
return path.join(this.baseDir, `hash_${key.replace(/[^a-zA-Z0-9]/g, "_")}.json`);
|
|
697
|
+
}
|
|
698
|
+
async readList(key) {
|
|
699
|
+
await this.ensureDir();
|
|
700
|
+
const filePath = this.getListPath(key);
|
|
701
|
+
try {
|
|
702
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
703
|
+
return JSON.parse(content);
|
|
704
|
+
} catch (error) {
|
|
705
|
+
return [];
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async writeList(key, list) {
|
|
709
|
+
await this.ensureDir();
|
|
710
|
+
const filePath = this.getListPath(key);
|
|
711
|
+
if (list.length === 0) {
|
|
712
|
+
try {
|
|
713
|
+
await fs.unlink(filePath);
|
|
714
|
+
} catch (error) {
|
|
715
|
+
}
|
|
716
|
+
} else {
|
|
717
|
+
await fs.writeFile(filePath, JSON.stringify(list), "utf-8");
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
async readHash(key) {
|
|
721
|
+
await this.ensureDir();
|
|
722
|
+
const filePath = this.getHashPath(key);
|
|
723
|
+
try {
|
|
724
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
725
|
+
return JSON.parse(content);
|
|
726
|
+
} catch (error) {
|
|
727
|
+
return {};
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
async writeHash(key, hash) {
|
|
731
|
+
await this.ensureDir();
|
|
732
|
+
const filePath = this.getHashPath(key);
|
|
733
|
+
if (Object.keys(hash).length === 0) {
|
|
734
|
+
try {
|
|
735
|
+
await fs.unlink(filePath);
|
|
736
|
+
} catch (error) {
|
|
737
|
+
}
|
|
738
|
+
} else {
|
|
739
|
+
await fs.writeFile(filePath, JSON.stringify(hash), "utf-8");
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async lrange(key, start, end) {
|
|
743
|
+
const list = await this.readList(key);
|
|
744
|
+
const actualEnd = end === -1 ? list.length : end + 1;
|
|
745
|
+
return list.slice(start, actualEnd);
|
|
746
|
+
}
|
|
747
|
+
async hgetall(key) {
|
|
748
|
+
return await this.readHash(key);
|
|
749
|
+
}
|
|
750
|
+
async lrem(key, count, value) {
|
|
751
|
+
const list = await this.readList(key);
|
|
752
|
+
if (count === 0) {
|
|
753
|
+
const filtered = list.filter((item) => item !== value);
|
|
754
|
+
await this.writeList(key, filtered);
|
|
755
|
+
} else if (count > 0) {
|
|
756
|
+
let removed = 0;
|
|
757
|
+
const filtered = list.filter((item) => {
|
|
758
|
+
if (item === value && removed < count) {
|
|
759
|
+
removed++;
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
return true;
|
|
763
|
+
});
|
|
764
|
+
await this.writeList(key, filtered);
|
|
765
|
+
} else {
|
|
766
|
+
let removed = 0;
|
|
767
|
+
const filtered = list.reverse().filter((item) => {
|
|
768
|
+
if (item === value && removed < Math.abs(count)) {
|
|
769
|
+
removed++;
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
return true;
|
|
773
|
+
}).reverse();
|
|
774
|
+
await this.writeList(key, filtered);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
async lpop(key, count) {
|
|
778
|
+
const list = await this.readList(key);
|
|
779
|
+
if (list.length === 0) return null;
|
|
780
|
+
const popped = list.splice(0, count);
|
|
781
|
+
await this.writeList(key, list);
|
|
782
|
+
return popped;
|
|
783
|
+
}
|
|
784
|
+
async del(...keys) {
|
|
785
|
+
for (const key of keys) {
|
|
786
|
+
const listPath = this.getListPath(key);
|
|
787
|
+
const hashPath = this.getHashPath(key);
|
|
788
|
+
try {
|
|
789
|
+
await fs.unlink(listPath);
|
|
790
|
+
} catch (error) {
|
|
791
|
+
}
|
|
792
|
+
try {
|
|
793
|
+
await fs.unlink(hashPath);
|
|
794
|
+
} catch (error) {
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
async hincrby(key, field, increment) {
|
|
799
|
+
const hash = await this.readHash(key);
|
|
800
|
+
const currentValue = hash[field] || "0";
|
|
801
|
+
hash[field] = (parseInt(currentValue, 10) + increment).toString();
|
|
802
|
+
await this.writeHash(key, hash);
|
|
803
|
+
}
|
|
804
|
+
async hset(key, fieldOrObject, value) {
|
|
805
|
+
const hash = await this.readHash(key);
|
|
806
|
+
if (typeof fieldOrObject === "string") {
|
|
807
|
+
hash[fieldOrObject] = value;
|
|
808
|
+
} else {
|
|
809
|
+
Object.assign(hash, fieldOrObject);
|
|
810
|
+
}
|
|
811
|
+
await this.writeHash(key, hash);
|
|
812
|
+
}
|
|
813
|
+
async hget(key, field) {
|
|
814
|
+
const hash = await this.readHash(key);
|
|
815
|
+
return hash[field] || null;
|
|
816
|
+
}
|
|
817
|
+
async hdel(key, ...fields) {
|
|
818
|
+
const hash = await this.readHash(key);
|
|
819
|
+
fields.forEach((field) => delete hash[field]);
|
|
820
|
+
await this.writeHash(key, hash);
|
|
821
|
+
}
|
|
822
|
+
async rpush(key, ...values) {
|
|
823
|
+
const list = await this.readList(key);
|
|
824
|
+
list.push(...values);
|
|
825
|
+
await this.writeList(key, list);
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
// src/index.ts
|
|
830
|
+
var defaultOptions = {
|
|
831
|
+
chunkSize: 100,
|
|
832
|
+
keyPrefix: "brdc:",
|
|
833
|
+
reportFrequency: 60 * 1e3,
|
|
834
|
+
progressCallback: null,
|
|
835
|
+
setRestricted: null,
|
|
836
|
+
checkQueueInterval: 60 * 1e3,
|
|
837
|
+
hasPermission: null,
|
|
838
|
+
allowPaidBroadcast: false,
|
|
839
|
+
cmds: {
|
|
840
|
+
broadcast: "broadcast",
|
|
841
|
+
copy: "copy",
|
|
842
|
+
forward: "forward",
|
|
843
|
+
addmsg: "addmsg"
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
var Broadcaster = class _Broadcaster {
|
|
847
|
+
constructor(options) {
|
|
848
|
+
this.options = options;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
static getInstance(options) {
|
|
852
|
+
if (_Broadcaster._instance) {
|
|
853
|
+
return _Broadcaster._instance;
|
|
854
|
+
}
|
|
855
|
+
let instance = new _Broadcaster(options);
|
|
856
|
+
const queue = new BroadcastQueue(options);
|
|
857
|
+
queue.checkBroadcasts().then(() => {
|
|
858
|
+
});
|
|
859
|
+
_Broadcaster._instance = instance;
|
|
860
|
+
return instance;
|
|
861
|
+
}
|
|
862
|
+
getMiddleware() {
|
|
863
|
+
return getMiddleware(this.options);
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
function createBroadcaster(options) {
|
|
867
|
+
const allOptions = {
|
|
868
|
+
...defaultOptions,
|
|
869
|
+
cmds: {
|
|
870
|
+
...defaultOptions.cmds,
|
|
871
|
+
...options.cmds
|
|
872
|
+
},
|
|
873
|
+
...options
|
|
874
|
+
};
|
|
875
|
+
return Broadcaster.getInstance(allOptions);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
exports.FileStorage = FileStorage; exports.MemoryStorage = MemoryStorage; exports.RedisStorage = RedisStorage; exports.Storage = Storage; exports.createBroadcaster = createBroadcaster;
|
|
884
|
+
//# sourceMappingURL=index.js.map
|