Deploy: 2026-07-15 04:57:29
This commit is contained in:
@@ -6,6 +6,7 @@ const NodeCache = require('node-cache');
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const throttleManager = require('./throttle-manager');
|
||||
|
||||
class InMemoryStore {
|
||||
constructor() {
|
||||
@@ -573,44 +574,40 @@ function convertToOggOpus(base64Audio) {
|
||||
/**
|
||||
* Send a message using an active session
|
||||
*/
|
||||
async function sendMessage(session_key, phone, message, mediaUrl = null, audioBase64 = null, mimetype = null, imageBase64 = null) {
|
||||
const sock = sessions.get(session_key);
|
||||
if (!sock) {
|
||||
throw new Error(`Session ${session_key} is not active or connected`);
|
||||
}
|
||||
|
||||
// Use the LID JID if we have a mapping for this phone number.
|
||||
// This is CRITICAL: Signal E2EE sessions are bound to the LID JID,
|
||||
// so replying to the phone JID creates a separate session that
|
||||
// conflicts with the LID session, causing "Waiting for this message".
|
||||
let jid;
|
||||
/**
|
||||
* Resolve the correct JID for a phone number (handles LID privacy scheme)
|
||||
*/
|
||||
async function resolveJid(sock, phone) {
|
||||
if (phone.includes('@')) {
|
||||
jid = phone;
|
||||
} else if (phoneToLid.has(phone)) {
|
||||
jid = phoneToLid.get(phone);
|
||||
console.log(`[LID] Routing reply to ${phone} via LID: ${jid}`);
|
||||
} else {
|
||||
// Query WhatsApp to resolve the correct JID/LID for this phone number.
|
||||
// This is CRITICAL for newer WhatsApp versions (especially iOS) to prevent
|
||||
// E2EE session conflicts and the "Waiting for this message" decryption delay.
|
||||
try {
|
||||
console.log(`[JID Resolve] Resolving JID for ${phone}...`);
|
||||
const [result] = await sock.onWhatsApp(phone);
|
||||
if (result && result.exists) {
|
||||
jid = result.jid;
|
||||
console.log(`[JID Resolve] Successfully resolved JID for ${phone}: ${jid}`);
|
||||
// Cache it for subsequent messages
|
||||
if (jid.endsWith('@lid')) {
|
||||
phoneToLid.set(phone, jid);
|
||||
}
|
||||
} else {
|
||||
jid = `${phone}@s.whatsapp.net`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[JID Resolve] Failed to query onWhatsApp for ${phone}:`, err.message);
|
||||
jid = `${phone}@s.whatsapp.net`;
|
||||
}
|
||||
return phone;
|
||||
}
|
||||
if (phoneToLid.has(phone)) {
|
||||
const lid = phoneToLid.get(phone);
|
||||
console.log(`[LID] Routing reply to ${phone} via LID: ${lid}`);
|
||||
return lid;
|
||||
}
|
||||
// Query WhatsApp to resolve the correct JID/LID for this phone number.
|
||||
try {
|
||||
console.log(`[JID Resolve] Resolving JID for ${phone}...`);
|
||||
const [result] = await sock.onWhatsApp(phone);
|
||||
if (result && result.exists) {
|
||||
console.log(`[JID Resolve] Successfully resolved JID for ${phone}: ${result.jid}`);
|
||||
if (result.jid.endsWith('@lid')) {
|
||||
phoneToLid.set(phone, result.jid);
|
||||
}
|
||||
return result.jid;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[JID Resolve] Failed to query onWhatsApp for ${phone}:`, err.message);
|
||||
}
|
||||
return `${phone}@s.whatsapp.net`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal raw send — performs the actual sock.sendMessage without throttling.
|
||||
* Used inside the throttle queue's sendFn callback.
|
||||
*/
|
||||
async function _rawSend(sock, jid, message, mediaUrl, audioBase64, mimetype, imageBase64) {
|
||||
let sentMsg;
|
||||
|
||||
if (imageBase64) {
|
||||
@@ -631,7 +628,6 @@ async function sendMessage(session_key, phone, message, mediaUrl = null, audioBa
|
||||
finalMime = 'audio/ogg; codecs=opus';
|
||||
} catch (err) {
|
||||
console.error(`[Baileys] FFmpeg conversion failed:`, err.message);
|
||||
// Fallback to sending as normal audio if conversion fails
|
||||
finalMime = 'audio/mp4';
|
||||
}
|
||||
}
|
||||
@@ -642,7 +638,7 @@ async function sendMessage(session_key, phone, message, mediaUrl = null, audioBa
|
||||
sentMsg = await sock.sendMessage(jid, {
|
||||
audio: buffer,
|
||||
mimetype: finalMime,
|
||||
ptt: !isMp3 // PTT enabled for OGG/MP4, disabled for raw MP3
|
||||
ptt: !isMp3
|
||||
});
|
||||
} else if (mediaUrl) {
|
||||
const ext = mediaUrl.split('.').pop().toLowerCase();
|
||||
@@ -669,6 +665,37 @@ async function sendMessage(session_key, phone, message, mediaUrl = null, audioBa
|
||||
return sentMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message using an active session — routed through ThrottleManager
|
||||
* for human-like delays, typing indicators, and anti-ban protection.
|
||||
*/
|
||||
async function sendMessage(session_key, phone, message, mediaUrl = null, audioBase64 = null, mimetype = null, imageBase64 = null) {
|
||||
const sock = sessions.get(session_key);
|
||||
if (!sock) {
|
||||
throw new Error(`Session ${session_key} is not active or connected`);
|
||||
}
|
||||
|
||||
const jid = await resolveJid(sock, phone);
|
||||
|
||||
// Determine message type for throttle delay calculation
|
||||
let msgType = 'auto_reply';
|
||||
if (audioBase64) msgType = 'voice_reply';
|
||||
|
||||
// Build the content descriptor for typing delay calculation
|
||||
const content = { text: message || '' };
|
||||
|
||||
// Enqueue through ThrottleManager for anti-ban protection
|
||||
const sentMsg = await throttleManager.enqueue(session_key, {
|
||||
jid,
|
||||
sock,
|
||||
content,
|
||||
type: msgType,
|
||||
sendFn: () => _rawSend(sock, jid, message, mediaUrl, audioBase64, mimetype, imageBase64)
|
||||
});
|
||||
|
||||
return sentMsg;
|
||||
}
|
||||
|
||||
async function checkContact(session_key, phone) {
|
||||
const sock = sessions.get(session_key);
|
||||
if (!sock) {
|
||||
@@ -770,6 +797,7 @@ module.exports = {
|
||||
sendMessage,
|
||||
getActiveSessions,
|
||||
checkContact,
|
||||
exportChatHistory
|
||||
exportChatHistory,
|
||||
throttleManager
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user