diff --git a/index.js b/index.js index 9ad9a75..d4cb05d 100644 --- a/index.js +++ b/index.js @@ -980,23 +980,39 @@ async function runBatchUpload(chatId, triggerMsg = null) { saveJson(STATE_FILE, state); await maybeSendFailureAlert(state); + const elapsedMsFinal = Date.now() - startMs; + const elapsedSecFinal = Math.round(elapsedMsFinal / 1000); + const speedMbpsFinal = elapsedMsFinal > 0 + ? Number(bytesToMb(uploadedBytes)) / (elapsedMsFinal / 1000) + : 0; + const finalCombinedMessage = [ + "✅ Upload complete", + `Batch: ${folderLabel}`, + `Total files: ${total}`, + `Uploaded: ${uploadedCount}`, + `Failed: ${failedCount}`, + `Retry queued: ${retryItems.length}`, + `Total size: ${bytesToMb(totalBytes)} MB`, + `📁 Drive link: ${folderLink}`, + "", + `⏫ Uploading ${total} file(s) to ${folderLabel}...`, + `${formatProgressBar(uploadedBytes, totalBytes)}`, + `${total}/${total} | Elapsed: ${elapsedSecFinal}s | ETA: 0s`, + `Uploaded: ${bytesToMb(uploadedBytes)}/${bytesToMb(totalBytes)} MB | Speed: ${speedMbpsFinal.toFixed(2)} MB/s`, + "", + "Next: send more files directly, or type start | list | done", + ].join("\n"); + clearBatch(chatId); - await sendChatOrReply( - chatId, - [ - "✅ Upload complete", - `Batch: *${folderLabel}*`, - `Total files: *${total}*`, - `Uploaded: *${uploadedCount}*`, - `Failed: *${failedCount}*`, - `Retry queued: *${retryItems.length}*`, - `Total size: *${bytesToMb(totalBytes)} MB*`, - `📁 Drive link: ${folderLink}`, - "", - "Next: send more files directly, or type start | list | done", - ].join("\n"), - null - ); + try { + if (progressMsg?.edit) { + await progressMsg.edit(finalCombinedMessage); + } else { + await sendChatOrReply(chatId, finalCombinedMessage, null); + } + } catch { + await sendChatOrReply(chatId, finalCombinedMessage, null); + } logInfo("Batch upload complete", { chatId, folderLabel, uploadedCount, failedCount, totalBytes }); } catch (err) { const b = BATCH.get(chatId); @@ -1134,6 +1150,8 @@ client.on("disconnected", (reason) => logWarn("WhatsApp disconnected", { reason client.on("message", async (msg) => { try { + if (isStatusOrBroadcastMessage(msg)) return; + lastMessageAt = Date.now(); const text = (msg.body || "").trim(); const chatId = msg.from; @@ -1427,8 +1445,6 @@ client.on("message", async (msg) => { if (msg.hasMedia) { batch.activeDownloads = (batch.activeDownloads || 0) + 1; touchBatch(chatId); - let statusMsg = null; - let statusTimer = null; try { const mediaSizeBytes = Number(msg?._data?.size || msg?._data?.fileSize || 0); const maxBytes = MAX_MEDIA_MB > 0 ? MAX_MEDIA_MB * 1024 * 1024 : 0; @@ -1444,32 +1460,12 @@ client.on("message", async (msg) => { } const statusStart = Date.now(); - const statusFrames = ["⏳", "⌛", "🔄", "⏬"]; - let statusIndex = 0; - try { - statusMsg = await msg.reply("⏳ Downloading... 0m 0s"); - logInfo("Media download started", { chatId }); - statusTimer = setInterval(async () => { - if (!statusMsg) return; - const elapsed = formatElapsedSec(Date.now() - statusStart); - const icon = statusFrames[statusIndex % statusFrames.length]; - statusIndex += 1; - try { - await statusMsg.edit(`${icon} Downloading... ${elapsed}`); - } catch { } - logInfo("Media download heartbeat", { chatId, elapsed }); - }, 5000); - } catch { } + logInfo("Media download started", { chatId }); let media = null; let lastErr = null; for (let attempt = 1; attempt <= DOWNLOAD_MAX_RETRIES + 1; attempt += 1) { try { - if (attempt > 1 && statusMsg) { - try { - await statusMsg.edit(`🔁 Retrying download (${attempt}/${DOWNLOAD_MAX_RETRIES + 1})...`); - } catch { } - } media = await downloadMediaWithTimeout(msg, DOWNLOAD_TIMEOUT_MS); if (media && media.data) break; lastErr = new Error("empty media data"); @@ -1482,11 +1478,6 @@ client.on("message", async (msg) => { } if (!media || !media.data) { - if (statusMsg) { - try { - await statusMsg.edit("❌ Download failed. Try sending as document."); - } catch { } - } await msg.reply("❌ Could not download the attachment."); logWarn("Media download failed", { chatId, error: lastErr?.message }); return; @@ -1504,11 +1495,6 @@ client.on("message", async (msg) => { try { fs.rmSync(filePath, { force: true }); } catch { } - if (statusMsg) { - try { - await statusMsg.edit(`⚠️ Duplicate skipped: ${sourceName} (${bytesToDisplay(fileSizeBytes)})`); - } catch { } - } await msg.reply("⚠️ Duplicate file skipped (same filename + size in this batch)."); logInfo("Duplicate skipped", { chatId, sourceName, sizeBytes: fileSizeBytes }); return; @@ -1534,17 +1520,11 @@ client.on("message", async (msg) => { incrementStats(state, { filesQueued: 1 }); saveJson(STATE_FILE, state); - if (statusMsg) { - try { - await statusMsg.edit(`✅ ${batch.files.length} file(s) queued`); - } catch { } - } try { await msg.react("✅"); } catch { } logInfo("File queued", { chatId, filename, batchCount: batch.files.length }); } finally { - if (statusTimer) clearInterval(statusTimer); const b = BATCH.get(chatId); if (b) { b.activeDownloads = Math.max(0, (b.activeDownloads || 0) - 1); @@ -1625,6 +1605,15 @@ function parseCommand(rawText) { return null; } +function isStatusOrBroadcastMessage(msg) { + const from = String(msg?.from || "").toLowerCase(); + const to = String(msg?.to || "").toLowerCase(); + if (from === "status@broadcast" || to === "status@broadcast") return true; + if (from.endsWith("@broadcast") || to.endsWith("@broadcast")) return true; + if (msg?.isStatus === true) return true; + return false; +} + function buildAutoBatchFolderName() { const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, ""); return `Batch_${stamp}`;