feat(lolschedule): per-chat subscribe/unsubscribe for the daily cron

Replaces the single LOLSCHEDULE_CHAT_ID env var with a KV-backed
subscriber list. New commands /lolschedule_subscribe and
/lolschedule_unsubscribe let each chat opt in/out. The cron now fans
out to every subscriber via Promise.allSettled so one blocked chat
cannot break the others.
This commit is contained in:
2026-04-21 10:23:17 +07:00
committed by Tien Nguyen Minh
parent c69d1749b7
commit 3e4e0e5b6e
7 changed files with 270 additions and 54 deletions
@@ -0,0 +1,43 @@
import { beforeEach, describe, expect, it } from "vitest";
import { createStore } from "../../../src/db/create-store.js";
import {
addSubscriber,
listSubscribers,
removeSubscriber,
} from "../../../src/modules/lolschedule/subscribers.js";
import { makeFakeKv } from "../../fakes/fake-kv-namespace.js";
describe("subscribers", () => {
let db;
beforeEach(() => {
db = createStore("lolschedule", { KV: makeFakeKv() });
});
it("starts empty", async () => {
expect(await listSubscribers(db)).toEqual([]);
});
it("adds a new subscriber and returns true", async () => {
expect(await addSubscriber(db, 42)).toBe(true);
expect(await listSubscribers(db)).toEqual([42]);
});
it("add is idempotent for an already-subscribed chat", async () => {
await addSubscriber(db, 42);
expect(await addSubscriber(db, 42)).toBe(false);
expect(await listSubscribers(db)).toEqual([42]);
});
it("removes an existing subscriber and returns true", async () => {
await addSubscriber(db, 1);
await addSubscriber(db, 2);
expect(await removeSubscriber(db, 1)).toBe(true);
expect(await listSubscribers(db)).toEqual([2]);
});
it("remove on a non-subscriber returns false and is a no-op", async () => {
await addSubscriber(db, 1);
expect(await removeSubscriber(db, 99)).toBe(false);
expect(await listSubscribers(db)).toEqual([1]);
});
});