Spaces:
Paused
Paused
File size: 4,144 Bytes
89ec743 aff1ffd 89ec743 e81c9a5 89ec743 e81c9a5 aff1ffd e81c9a5 aff1ffd 89ec743 e81c9a5 0876dd4 89ec743 e81c9a5 89ec743 e81c9a5 89ec743 aff1ffd e81c9a5 aff1ffd 0876dd4 e81c9a5 0876dd4 89ec743 e81c9a5 89ec743 e81c9a5 aff1ffd 89ec743 aff1ffd e81c9a5 aff1ffd e81c9a5 aff1ffd 89ec743 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | const { Scenes, Markup } = require('telegraf');
const User = require('../../models/User');
const userController = require('../../controllers/userController');
const broadcastScene = new Scenes.WizardScene(
'BROADCAST_SCENE',
// Step 1: Ask for content
async (ctx) => {
// Ensure localization
if (!ctx.i18n) {
const locales = require('../../locales');
const User = require('../../models/User');
const user = await User.findOne({ id: ctx.from.id });
ctx.i18n = locales[user ? user.language : 'uz'];
}
const i18n = ctx.i18n;
ctx.reply(i18n.admin.broadcast_prompt, Markup.keyboard([[i18n.btn_cancel]]).resize());
return ctx.wizard.next();
},
// Step 2: Confirm
(ctx) => {
const i18n = ctx.i18n;
if (ctx.message && (ctx.message.text === '/cancel' || ctx.message.text === i18n.btn_cancel)) {
ctx.scene.leave();
userController.start(ctx, i18n.cancel_process);
return;
}
// Handle Back from Step 3 (Logic if needed, but Step 3 handles it mostly)
if (ctx.message && ctx.message.text === i18n.btn_back_nav) {
ctx.reply(i18n.admin.broadcast_prompt, Markup.keyboard([[i18n.btn_cancel]]).resize());
return ctx.wizard.back();
}
ctx.wizard.state.message = ctx.message; // Save the message object
// Show Preview (Forward copy)
ctx.copyMessage(ctx.from.id, ctx.message.message_id);
ctx.reply(i18n.admin.broadcast_confirm, Markup.inlineKeyboard([
Markup.button.callback(i18n.admin.broadcast_send, "confirm_send"),
Markup.button.callback(i18n.admin.broadcast_change, "back_to_start"),
Markup.button.callback(i18n.btn_cancel, "cancel_send")
]));
return ctx.wizard.next();
},
// Step 3: Action Handler
async (ctx) => {
const i18n = ctx.i18n;
if (ctx.callbackQuery) {
const action = ctx.callbackQuery.data;
if (action === 'cancel_send') {
ctx.deleteMessage();
ctx.scene.leave();
userController.start(ctx, i18n.cancel_process);
return;
} else if (action === 'back_to_start') {
ctx.deleteMessage();
ctx.reply(i18n.admin.broadcast_prompt, Markup.keyboard([[i18n.btn_cancel]]).resize());
return ctx.wizard.back();
} else if (action === 'confirm_send') {
await ctx.deleteMessage();
ctx.reply(i18n.admin.broadcast_sending);
// Start sending
const users = await User.find();
let success = 0;
let blocked = 0;
const messageId = ctx.wizard.state.message.message_id;
// Optimized Broadcast with Batching
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
const BATCH_SIZE = 20;
for (let i = 0; i < users.length; i += BATCH_SIZE) {
const batch = users.slice(i, i + BATCH_SIZE);
await Promise.all(batch.map(async (user) => {
try {
await ctx.copyMessage(user.id, messageId);
success++;
} catch (err) {
blocked++;
}
}));
await sleep(1000); // 1 sec limit to respect telegram API limits
}
ctx.reply(`${i18n.admin.broadcast_finish}\n\n✅: ${success}\n🚫: ${blocked}`);
userController.start(ctx); // Return to menu
return ctx.scene.leave();
}
} else {
// Handle text input during buttonwait?
if (ctx.message && ctx.message.text === i18n.btn_cancel) {
ctx.scene.leave();
userController.start(ctx, i18n.cancel_process);
return;
}
}
}
);
module.exports = broadcastScene;
|