Fix duplicate responses caused by reconnect-stacking loops
WhatsApp reconnections called startMessageLoop/startSchedulerLoop/ startIpcWatcher and setInterval again without stopping the previous instances, creating parallel loops that processed the same messages. Add guard flags so each loop starts only once per process lifetime. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
30
src/index.ts
30
src/index.ts
@@ -52,6 +52,10 @@ let registeredGroups: Record<string, RegisteredGroup> = {};
|
|||||||
let lastAgentTimestamp: Record<string, string> = {};
|
let lastAgentTimestamp: Record<string, string> = {};
|
||||||
// LID to phone number mapping (WhatsApp now sends LID JIDs for self-chats)
|
// LID to phone number mapping (WhatsApp now sends LID JIDs for self-chats)
|
||||||
let lidToPhoneMap: Record<string, string> = {};
|
let lidToPhoneMap: Record<string, string> = {};
|
||||||
|
// Guards to prevent duplicate loops on WhatsApp reconnect
|
||||||
|
let messageLoopRunning = false;
|
||||||
|
let ipcWatcherRunning = false;
|
||||||
|
let groupSyncTimerStarted = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate a JID from LID format to phone format if we have a mapping.
|
* Translate a JID from LID format to phone format if we have a mapping.
|
||||||
@@ -292,6 +296,12 @@ async function sendMessage(jid: string, text: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startIpcWatcher(): void {
|
function startIpcWatcher(): void {
|
||||||
|
if (ipcWatcherRunning) {
|
||||||
|
logger.debug('IPC watcher already running, skipping duplicate start');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipcWatcherRunning = true;
|
||||||
|
|
||||||
const ipcBaseDir = path.join(DATA_DIR, 'ipc');
|
const ipcBaseDir = path.join(DATA_DIR, 'ipc');
|
||||||
fs.mkdirSync(ipcBaseDir, { recursive: true });
|
fs.mkdirSync(ipcBaseDir, { recursive: true });
|
||||||
|
|
||||||
@@ -697,12 +707,15 @@ async function connectWhatsApp(): Promise<void> {
|
|||||||
syncGroupMetadata().catch((err) =>
|
syncGroupMetadata().catch((err) =>
|
||||||
logger.error({ err }, 'Initial group sync failed'),
|
logger.error({ err }, 'Initial group sync failed'),
|
||||||
);
|
);
|
||||||
// Set up daily sync timer
|
// Set up daily sync timer (only once)
|
||||||
setInterval(() => {
|
if (!groupSyncTimerStarted) {
|
||||||
syncGroupMetadata().catch((err) =>
|
groupSyncTimerStarted = true;
|
||||||
logger.error({ err }, 'Periodic group sync failed'),
|
setInterval(() => {
|
||||||
);
|
syncGroupMetadata().catch((err) =>
|
||||||
}, GROUP_SYNC_INTERVAL_MS);
|
logger.error({ err }, 'Periodic group sync failed'),
|
||||||
|
);
|
||||||
|
}, GROUP_SYNC_INTERVAL_MS);
|
||||||
|
}
|
||||||
startSchedulerLoop({
|
startSchedulerLoop({
|
||||||
sendMessage,
|
sendMessage,
|
||||||
registeredGroups: () => registeredGroups,
|
registeredGroups: () => registeredGroups,
|
||||||
@@ -745,6 +758,11 @@ async function connectWhatsApp(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function startMessageLoop(): Promise<void> {
|
async function startMessageLoop(): Promise<void> {
|
||||||
|
if (messageLoopRunning) {
|
||||||
|
logger.debug('Message loop already running, skipping duplicate start');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
messageLoopRunning = true;
|
||||||
logger.info(`NanoClaw running (trigger: @${ASSISTANT_NAME})`);
|
logger.info(`NanoClaw running (trigger: @${ASSISTANT_NAME})`);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|||||||
@@ -141,7 +141,14 @@ async function runTask(
|
|||||||
updateTaskAfterRun(task.id, nextRun, resultSummary);
|
updateTaskAfterRun(task.id, nextRun, resultSummary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let schedulerRunning = false;
|
||||||
|
|
||||||
export function startSchedulerLoop(deps: SchedulerDependencies): void {
|
export function startSchedulerLoop(deps: SchedulerDependencies): void {
|
||||||
|
if (schedulerRunning) {
|
||||||
|
logger.debug('Scheduler loop already running, skipping duplicate start');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
schedulerRunning = true;
|
||||||
logger.info('Scheduler loop started');
|
logger.info('Scheduler loop started');
|
||||||
|
|
||||||
const loop = async () => {
|
const loop = async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user