Fix opt-out asymmetric 8/9-digit bypass letting sends reach blocked numbers
isOptedOut/addOptOut/removeOptOut used equivalentPhoneNumbers, which is not symmetric for a 12-digit local outside the 6-9 range vs its 13-digit counterpart (same root cause already fixed for shared-number detection). An opt-out registered against one form of such a number silently failed to block sends to a lead whose number was stored in the other form, on both the manual send path (reserveOutreachSend) and, independently, in two inlined checks that bypassed isOptedOut entirely: the last-mile recheck in campaign-worker.mjs's reserveCampaignQueueSend, and the opt-out NOT EXISTS clause in campaign-queue.mjs's candidate query. All three now use the symmetric canonicalPhoneVariants key already introduced for shared-number overrides, and the worker's last-mile check now calls isOptedOut directly instead of re-deriving its own number list. Found via a full-project audit; reproduced and verified before fixing.
This commit is contained in:
@@ -624,3 +624,31 @@ test('variantes 8 e 9 digitos do mesmo numero contam como compartilhado na fila'
|
||||
assert.equal(queue.some(({ lead_id }) => [nineDigits, eightDigits].includes(lead_id)), false);
|
||||
db.close();
|
||||
});
|
||||
|
||||
// Opt-out registrado na outra forma de um par 12/13 digitos assimetrico
|
||||
// precisa excluir o lead da fila nos dois sentidos (mesma logica de chave
|
||||
// canonica ja usada para numero compartilhado nesta consulta).
|
||||
test('opt-out na outra forma de um par 12/13 digitos assimetrico exclui o lead da fila', async () => {
|
||||
for (const [leadNumero, optOutNumero] of [
|
||||
['557533334444', '5575933334444'],
|
||||
['5575933334444', '557533334444']
|
||||
]) {
|
||||
const { db, campaign } = setupCampaign();
|
||||
const lead = insertLead(db, { key: 'assim-optout', whatsapp: leadNumero, score: 100 });
|
||||
confirmed(db, leadNumero);
|
||||
addOptOut(db, optOutNumero, 'pediu para nao receber mais');
|
||||
|
||||
await prepareDailyQueue({
|
||||
db,
|
||||
whatsapp: noSendWhatsapp(async () => { throw new Error('numero possui cache'); }),
|
||||
campaignId: campaign.id,
|
||||
now: new Date('2026-07-29T14:00:00-03:00')
|
||||
});
|
||||
assert.equal(
|
||||
listCampaignQueue(db, campaign.id).some((row) => row.lead_id === lead),
|
||||
false,
|
||||
`${leadNumero} / opt-out em ${optOutNumero}`
|
||||
);
|
||||
db.close();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user