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;