feat: add private Discord progress and explicit shared delivery controls

This commit is contained in:
syntaxbullet
2026-09-08 08:23:55 +02:00
parent 5dbec04b04
commit a1ae65634b
11 changed files with 318 additions and 39 deletions

View File

@@ -2,9 +2,9 @@ import { generateKeyPairSync, sign } from 'node:crypto';
import { eq } from 'drizzle-orm';
import { fixture } from '../habits/test-fixture';
import { users } from '../db/schema';
import { createDiscordRoutes } from './routes';
import { createDiscordRoutes, type DeferredDispatch } from './routes';
import type { Interaction } from './types';
export function discordFixture(remindersAvailable = false) {
export function discordFixture(setup?: boolean | ((f: ReturnType<typeof fixture>, now: () => number) => DeferredDispatch)) {
const f = fixture();
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
const key = publicKey.export({ format: 'der', type: 'spki' }).subarray(-32).toString('hex');
@@ -13,17 +13,19 @@ export function discordFixture(remindersAvailable = false) {
f.db.update(users).set({ discordId: alice }).where(eq(users.id, 'alice')).run();
f.db.update(users).set({ discordId: bob }).where(eq(users.id, 'bob')).run();
const changed: string[] = [];
let app = createDiscordRoutes(f.db, { publicKey: key, applicationId, origin: f.origin, remindersAvailable }, () => time, id => changed.push(id));
const dispatch = typeof setup === "function" ? setup(f, () => time) : undefined;
const remindersAvailable = setup === true;
let app = createDiscordRoutes(f.db, { publicKey: key, applicationId, origin: f.origin, remindersAvailable }, () => time, id => changed.push(id), dispatch);
function payload(data: Partial<Interaction>, guild = false, user = alice): Interaction {
const profile = { id: user, username: user === alice ? 'alice' : 'bob' };
return { id: String(++sequence), application_id: applicationId, type: 2, ...(guild ? { guild_id: '444444444444444444', member: { user: profile } } : { user: profile }), ...data };
return { id: String(++sequence), application_id: applicationId, type: 2, token: 'test-interaction-token', ...(guild ? { guild_id: '444444444444444444', member: { user: profile } } : { user: profile }), ...data };
}
async function request(i: Interaction, headers: Record<string, string> = {}) {
const body = JSON.stringify(i), timestamp = String(time / 1000);
return app.request('http://localhost/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Signature-Timestamp': timestamp, 'X-Signature-Ed25519': sign(null, Buffer.from(timestamp + body), privateKey).toString('hex'), ...headers }, body });
}
async function run(data: Partial<Interaction>, guild = false, user = alice): Promise<any> { return (await request(payload(data, guild, user))).json(); }
return { ...f, alice, bob, changed, payload, requestDiscord: request, run, restart: () => { app = createDiscordRoutes(f.db, { publicKey: key, applicationId, origin: f.origin, remindersAvailable }, () => time, id => changed.push(id)); }, setDiscordTime: (value: string) => { time = Date.parse(value); f.setTime(value); } };
return { ...f, alice, bob, changed, payload, requestDiscord: request, run, restart: () => { app = createDiscordRoutes(f.db, { publicKey: key, applicationId, origin: f.origin, remindersAvailable }, () => time, id => changed.push(id), dispatch); }, setDiscordTime: (value: string) => { time = Date.parse(value); f.setTime(value); } };
}
export function control(reply: any, label: string): string {
const item = reply.data.components.flatMap((r: any) => r.components ?? []).find((c: any) => c.label === label || c.placeholder === label);