api: share a spam check from its start
ci / check (push) Successful in 1m11s
ci / images (push) Successful in 21s
ci / check (pull_request) Successful in 1m9s
ci / images (pull_request) Successful in 33s

SD5 follow-up (14 §4): checks for the same number were shared only once
they reached the IPQualityScore request, after reading the cache and the
pause state. A check that finished in between cost a second request
(CI: "five at once cost one request" failed). Now shared before anything
is awaited.
This commit is contained in:
iamdoubz committed 2026-10-27 18:32:04 -05:00
1 parent 6b0c5aa827
commit 70083f8f83
1 file changed
+15 -8
+15 -8
View File
@@ -48,11 +48,23 @@ function warn(d: SpamDeps, provider: string, err: unknown) {
* IPQualityScore first when it's set up, then the FTC list (owner, 2026-10-27). `cachedOnly`:
* the cache and the local FTC copy, never a request out (call history, 14 §6).
*/
export async function checkNumber(
export function checkNumber(
d: SpamDeps,
number: string,
opts: { cachedOnly?: boolean } = {},
): Promise<SpamCheck | null> {
if (opts.cachedOnly) return check(d, number, true);
// Shared from the start, before anything is awaited: else a check that finishes between
// another's cache read and its look here would cost a second request.
let p = pending.get(number);
if (!p) {
p = check(d, number, false).finally(() => pending.delete(number));
pending.set(number, p);
}
return p;
}
async function check(d: SpamDeps, number: string, cachedOnly: boolean): Promise<SpamCheck | null> {
if (!isCheckable(number)) return null;
const s = await getSetting(d.db, 'spam');
if (!s.enabled) return null;
@@ -63,13 +75,8 @@ export async function checkNumber(
.where(and(eq(spamLookups.number, number), gt(spamLookups.expiresAt, sql`now()`)));
if (hit) return { risk: hit.risk as SpamRisk, provider: 'ipqs', info: hit.info as IpqsInfo };
// Paused (14 §3): the cache still answers, IPQualityScore isn't asked.
if (!opts.cachedOnly && !(await getSetting(d.db, 'spam.state')).ipqsPausedAt) {
let p = pending.get(number);
if (!p) {
p = askIpqs(d, s, s.ipqs.keyEnc, number).finally(() => pending.delete(number));
pending.set(number, p);
}
const answer = await p;
if (!cachedOnly && !(await getSetting(d.db, 'spam.state')).ipqsPausedAt) {
const answer = await askIpqs(d, s, s.ipqs.keyEnc, number);
if (answer) return answer;
}
}