Skip to content

Commit 44500a7

Browse files
committed
fix: include generic status code in failure DM
Signed-off-by: Michael Cramer <michael@bigmichi1.de>
1 parent ffe9d86 commit 44500a7

4 files changed

Lines changed: 38 additions & 10 deletions

File tree

src/bot/bot.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,11 @@ client.on(Events.MessageCreate, async (message) => {
197197
// Deduplicate in case the same code appears multiple times in one message.
198198
const uniqueCodes = [...new Set(foundCodes)];
199199

200-
// Persist all found codes to pending_codes immediately so /catchup can
201-
// recover them if auto-redeem has no enabled users or the API fails.
202-
// addPendingCode returns true only for a newly inserted row, giving us
203-
// at-most-once insert semantics (onConflictDoNothing) that also eliminate
200+
// Persist all found codes to pending_codes in a single batch INSERT.
201+
// onConflictDoNothing gives at-most-once insert semantics and eliminates
204202
// the TOCTOU race of a pre-read snapshot under concurrent MessageCreate events.
205-
const newCodes: string[] = [];
206-
for (const code of uniqueCodes) {
207-
const isNew = await codeManager.addPendingCode(code);
208-
if (isNew) newCodes.push(code);
209-
}
203+
// Only newly inserted codes trigger DM notifications.
204+
const newCodes = await codeManager.addNewPendingCodes(uniqueCodes);
210205

211206
// DM users who opted in for code-detection notifications (independent of autoredeem).
212207
// A single bulk query resolves per-recipient redeemed status, then a short delay

src/bot/database/codeManager.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,24 @@ describe('pending codes', () => {
313313
await codeManager.clearPendingCodes(USER_A);
314314
expect(await codeManager.getPendingCodes()).toEqual(['PEND5678EFGH']);
315315
});
316+
317+
test('addNewPendingCodes returns only newly inserted codes', async () => {
318+
const result = await codeManager.addNewPendingCodes(['CODE1111AAAA', 'CODE2222BBBB']);
319+
expect(result).toContain('CODE1111AAAA');
320+
expect(result).toContain('CODE2222BBBB');
321+
});
322+
323+
test('addNewPendingCodes skips already-present codes', async () => {
324+
await codeManager.addPendingCode('CODE1111AAAA');
325+
const result = await codeManager.addNewPendingCodes(['CODE1111AAAA', 'CODE2222BBBB']);
326+
expect(result).not.toContain('CODE1111AAAA');
327+
expect(result).toContain('CODE2222BBBB');
328+
});
329+
330+
test('addNewPendingCodes returns empty array for empty input', async () => {
331+
const result = await codeManager.addNewPendingCodes([]);
332+
expect(result).toEqual([]);
333+
});
316334
});
317335

318336
// ---------------------------------------------------------------------------

src/bot/database/codeManager.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,21 @@ class CodeManager {
261261
return rows.length > 0;
262262
}
263263

264+
/**
265+
* Batch-insert multiple codes into pending_codes in a single query.
266+
* Returns only the codes that were newly inserted (already-present codes are skipped via onConflictDoNothing).
267+
*/
268+
async addNewPendingCodes(codes: string[]): Promise<string[]> {
269+
if (codes.length === 0) return [];
270+
const rows = db
271+
.insert(pendingCodes)
272+
.values(codes.map((code) => ({ code, discordId: null })))
273+
.onConflictDoNothing()
274+
.returning({ code: pendingCodes.code })
275+
.all();
276+
return rows.map((r) => r.code);
277+
}
278+
264279
async getPendingCodes(discordId?: string): Promise<string[]> {
265280
const results = discordId
266281
? db.select({ code: pendingCodes.code }).from(pendingCodes).where(eq(pendingCodes.discordId, discordId)).orderBy(sql`${pendingCodes.foundAt} ASC`).all()

src/bot/handlers/autoRedeemer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ async function redeemCodeForUser(code: string, credentials: UserCredentials): Pr
175175
logger.error(
176176
`[AUTO REDEEMER] submitCode returned GenericResponse status=${generic.status} for code ${code}, user ${discordId}`
177177
);
178-
if (credentials.dmOnFailure) sendFailureDm(discordId, code, 'Server Error');
178+
if (credentials.dmOnFailure) sendFailureDm(discordId, code, `Server Error (status ${generic.status})`);
179179
return;
180180
}
181181
}

0 commit comments

Comments
 (0)