Liegestuetze: pro Stream mitzaehlen, wie viele gemacht sind
Herunterzaehlen mit -1/-5/-10 zaehlt als gemacht, Setzen bleibt eine Korrektur und zaehlt nicht. Der Stand "gemacht" beginnt mit jedem neuen Stream bei 0 (nicht beim Neustart des Hubs mitten im Stream), steht im Overlay unter der offenen Zahl und laesst sich auf der Uebersicht korrigieren. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
eb9eab949e
commit
094a791da2
7 files changed
+213
-79
No files matched your search
+24
-13
@@ -99,7 +99,7 @@ export async function runHub(): Promise<void> {
|
||||
const liegestuetze = createLiegestuetze(db);
|
||||
// Gleich zu Beginn in den Bus, sonst zeigt eine frisch verbundene
|
||||
// Browser-Source 0, bis die naechste Einloesung kommt.
|
||||
bus.publish({ kind: 'liegestuetze', offen: liegestuetze.stand() });
|
||||
bus.publish({ kind: 'liegestuetze', ...liegestuetze.stand() });
|
||||
|
||||
/**
|
||||
* Eine Einloesung abschliessen (FULFILLED) oder die Punkte zurueckgeben
|
||||
@@ -196,8 +196,7 @@ export async function runHub(): Promise<void> {
|
||||
if (ent.aktion === 'emote-regen') bus.publish({ kind: 'hype', score: 5, rate: 40 });
|
||||
if (ent.aktion === 'liegestuetze') {
|
||||
const dazu = ent.plan.anzahl ?? 5;
|
||||
const offen = liegestuetze.aendern(dazu);
|
||||
bus.publish({ kind: 'liegestuetze', offen, dazu, name: e.userName });
|
||||
bus.publish({ kind: 'liegestuetze', ...liegestuetze.aendern(dazu), dazu, name: e.userName });
|
||||
}
|
||||
log.info(`${e.userName} hat "${e.rewardTitle}" eingeloest -> ${ent.aktion}`);
|
||||
void einloesungSetzen(e.rewardId, e.id, 'FULFILLED');
|
||||
@@ -331,7 +330,7 @@ export async function runHub(): Promise<void> {
|
||||
goLive: announcer.lastResult() ?? 'noch nichts',
|
||||
overlayClients: bus.clients(),
|
||||
ttsWartend: warteschlange.liste().length,
|
||||
liegestuetzeOffen: liegestuetze.stand(),
|
||||
liegestuetze: liegestuetze.stand(),
|
||||
ttsStimme: stimmeFehlt.length ? `fehlt: ${stimmeFehlt.join(', ')}` : 'bereit',
|
||||
spikeArmed: spikes.isArmed(),
|
||||
note: state.chatAnonymous
|
||||
@@ -374,23 +373,35 @@ export async function runHub(): Promise<void> {
|
||||
liegestuetze: {
|
||||
stand: () => liegestuetze.stand(),
|
||||
aendern: (n) => {
|
||||
const offen = liegestuetze.aendern(n);
|
||||
bus.publish({ kind: 'liegestuetze', offen });
|
||||
log.info(`Liegestuetze von Hand ${n > 0 ? '+' : ''}${n} -> ${offen} offen`);
|
||||
return offen;
|
||||
const neu = liegestuetze.aendern(n);
|
||||
bus.publish({ kind: 'liegestuetze', ...neu });
|
||||
log.info(`Liegestuetze von Hand ${n > 0 ? '+' : ''}${n} -> ` +
|
||||
`${neu.offen} offen, ${neu.gemacht} gemacht`);
|
||||
return neu;
|
||||
},
|
||||
setzen: (n) => {
|
||||
const offen = liegestuetze.setzen(n);
|
||||
bus.publish({ kind: 'liegestuetze', offen });
|
||||
log.info(`Liegestuetze von Hand gesetzt -> ${offen} offen`);
|
||||
return offen;
|
||||
const neu = liegestuetze.setzen(n);
|
||||
bus.publish({ kind: 'liegestuetze', ...neu });
|
||||
log.info(`Liegestuetze von Hand gesetzt -> ${neu.offen} offen`);
|
||||
return neu;
|
||||
},
|
||||
gemachtSetzen: (n) => {
|
||||
const neu = liegestuetze.gemachtSetzen(n);
|
||||
bus.publish({ kind: 'liegestuetze', ...neu });
|
||||
log.info(`Liegestuetze gemacht von Hand gesetzt -> ${neu.gemacht}`);
|
||||
return neu;
|
||||
},
|
||||
},
|
||||
});
|
||||
void server;
|
||||
|
||||
// ---- Zuschauerzahlen und Stream-Sitzungen
|
||||
void pollStream(cfg, repo, state, bus, abort.signal, (stream) => announcer.announce(stream));
|
||||
void pollStream(cfg, repo, state, bus, abort.signal, (stream) => {
|
||||
announcer.announce(stream);
|
||||
// Nur bei einem wirklich neuen Stream, nicht beim Neustart des Hubs
|
||||
// mitten im Stream: dann wird die Sitzung uebernommen, nicht gestartet.
|
||||
bus.publish({ kind: 'liegestuetze', ...liegestuetze.neuerStream() });
|
||||
});
|
||||
|
||||
// ---- EventSub erst, wenn ein Konto verbunden ist. Ohne Nutzer-Token
|
||||
// wuerde der Verbindungsversuch im Minutentakt scheitern und nichts als
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Der Zaehler haelt fest, was Zuschauer mit Kanalpunkten bezahlt haben. Er
|
||||
// darf weder beim Neustart vergessen noch ins Negative laufen.
|
||||
// Der Zaehler haelt fest, was Zuschauer mit Kanalpunkten bezahlt haben und
|
||||
// was Abdu davon geschafft hat. Er darf weder beim Neustart vergessen noch
|
||||
// ins Negative laufen, und eine Korrektur ist keine Liegestuetze.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
@@ -25,23 +26,43 @@ function oeffnen(file: string): DatabaseSync {
|
||||
|
||||
test('Liegestuetze: zaehlt hoch und herunter, nie unter 0', () => {
|
||||
const lz = createLiegestuetze(oeffnen(datei()));
|
||||
assert.equal(lz.stand(), 0);
|
||||
assert.equal(lz.aendern(10), 10);
|
||||
assert.equal(lz.aendern(10), 20);
|
||||
assert.equal(lz.aendern(-5), 15);
|
||||
assert.equal(lz.aendern(-100), 0);
|
||||
assert.equal(lz.setzen(42), 42);
|
||||
assert.equal(lz.setzen(-3), 0);
|
||||
assert.equal(lz.setzen(10_000_000), HOECHSTENS);
|
||||
assert.deepEqual(lz.stand(), { offen: 0, gemacht: 0 });
|
||||
assert.equal(lz.aendern(10).offen, 10);
|
||||
assert.equal(lz.aendern(10).offen, 20);
|
||||
assert.equal(lz.aendern(-5).offen, 15);
|
||||
assert.equal(lz.aendern(-100).offen, 0);
|
||||
assert.equal(lz.setzen(42).offen, 42);
|
||||
assert.equal(lz.setzen(-3).offen, 0);
|
||||
assert.equal(lz.setzen(10_000_000).offen, HOECHSTENS);
|
||||
});
|
||||
|
||||
test('Liegestuetze: Herunterzaehlen ist gemacht, Setzen nicht', () => {
|
||||
const lz = createLiegestuetze(oeffnen(datei()));
|
||||
lz.aendern(20);
|
||||
assert.deepEqual(lz.aendern(-5), { offen: 15, gemacht: 5 });
|
||||
// Nur was wirklich offen war, kann gemacht sein.
|
||||
assert.deepEqual(lz.aendern(-100), { offen: 0, gemacht: 20 });
|
||||
lz.aendern(10);
|
||||
assert.deepEqual(lz.setzen(3), { offen: 3, gemacht: 20 }, 'Korrektur zaehlt nicht');
|
||||
assert.deepEqual(lz.gemachtSetzen(7), { offen: 3, gemacht: 7 });
|
||||
});
|
||||
|
||||
test('Liegestuetze: neuer Stream setzt gemacht zurueck, offen bleibt', () => {
|
||||
const lz = createLiegestuetze(oeffnen(datei()));
|
||||
lz.aendern(30);
|
||||
lz.aendern(-10);
|
||||
assert.deepEqual(lz.neuerStream(), { offen: 20, gemacht: 0 });
|
||||
});
|
||||
|
||||
test('Liegestuetze: der Stand ueberlebt einen Neustart', () => {
|
||||
const file = datei();
|
||||
const erst = oeffnen(file);
|
||||
createLiegestuetze(erst).aendern(30);
|
||||
const lz = createLiegestuetze(erst);
|
||||
lz.aendern(30);
|
||||
lz.aendern(-5);
|
||||
erst.close();
|
||||
|
||||
assert.equal(createLiegestuetze(oeffnen(file)).stand(), 30);
|
||||
assert.deepEqual(createLiegestuetze(oeffnen(file)).stand(), { offen: 25, gemacht: 5 });
|
||||
});
|
||||
|
||||
test('Belohnung mit Aktion liegestuetze wird ausgefuehrt und traegt die Anzahl', () => {
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
// Offene Liegestuetze aus Kanalpunkten.
|
||||
// Liegestuetze aus Kanalpunkten: was offen ist und was in diesem Stream
|
||||
// schon gemacht wurde.
|
||||
//
|
||||
// Eine Einloesung zaehlt hoch, Abdu zaehlt auf der Uebersichtsseite wieder
|
||||
// herunter, wenn er sie gemacht hat - oder korrigiert, wenn etwas nicht
|
||||
// stimmt. Der Stand liegt in der Datenbank: was die Zuschauer bezahlt haben,
|
||||
// darf ein Neustart nicht loeschen.
|
||||
// Eine Einloesung zaehlt `offen` hoch. Abdu zaehlt auf der Uebersichtsseite
|
||||
// herunter, wenn er sie gemacht hat - das wandert nach `gemacht`. Setzen ist
|
||||
// dagegen eine Korrektur und zaehlt nicht als gemacht: wer einen Tippfehler
|
||||
// behebt, hat dabei keine Liegestuetze gemacht.
|
||||
//
|
||||
// Beides liegt in der Datenbank: was die Zuschauer bezahlt haben, darf ein
|
||||
// Neustart nicht loeschen. `gemacht` beginnt mit jedem neuen Stream bei 0.
|
||||
|
||||
import type { Db } from '#db/index.ts';
|
||||
|
||||
/** Mehr glaubt niemand, und ein Tippfehler mit sechs Nullen soll nicht im Bild stehen. */
|
||||
export const HOECHSTENS = 99_999;
|
||||
|
||||
export type LiegestuetzStand = { offen: number; gemacht: number };
|
||||
|
||||
export type Liegestuetze = {
|
||||
stand: () => number;
|
||||
/** Zaehlt um `n` hoch oder (negativ) herunter. Nie unter 0. Gibt den neuen Stand zurueck. */
|
||||
aendern: (n: number) => number;
|
||||
/** Setzt den Stand fest. Gibt den neuen Stand zurueck. */
|
||||
setzen: (n: number) => number;
|
||||
stand: () => LiegestuetzStand;
|
||||
/**
|
||||
* Zaehlt `offen` um `n` hoch oder (negativ) herunter, nie unter 0. Was
|
||||
* dabei wirklich abgezogen wurde, kommt zu `gemacht`.
|
||||
*/
|
||||
aendern: (n: number) => LiegestuetzStand;
|
||||
/** Setzt `offen` fest. Korrektur, zaehlt nicht als gemacht. */
|
||||
setzen: (n: number) => LiegestuetzStand;
|
||||
/** Setzt `gemacht` fest. Korrektur. */
|
||||
gemachtSetzen: (n: number) => LiegestuetzStand;
|
||||
/** Neuer Stream: `gemacht` auf 0, `offen` bleibt. */
|
||||
neuerStream: () => LiegestuetzStand;
|
||||
};
|
||||
|
||||
export function createLiegestuetze(db: Db, name = 'liegestuetze'): Liegestuetze {
|
||||
@@ -24,13 +37,27 @@ export function createLiegestuetze(db: Db, name = 'liegestuetze'): Liegestuetze
|
||||
INSERT INTO zaehler (name, wert, geaendert) VALUES (?, ?, ?)
|
||||
ON CONFLICT(name) DO UPDATE SET wert = excluded.wert, geaendert = excluded.geaendert
|
||||
`);
|
||||
const OFFEN = name;
|
||||
const GEMACHT = `${name}-gemacht`;
|
||||
|
||||
const stand = (): number => (lesen.get(name) as { wert: number } | undefined)?.wert ?? 0;
|
||||
const setzen = (n: number): number => {
|
||||
const wert = Math.min(HOECHSTENS, Math.max(0, Math.trunc(n)));
|
||||
schreiben.run(name, wert, new Date().toISOString());
|
||||
return wert;
|
||||
const wert = (n: string): number => (lesen.get(n) as { wert: number } | undefined)?.wert ?? 0;
|
||||
const schreibe = (n: string, w: number): number => {
|
||||
const sauber = Math.min(HOECHSTENS, Math.max(0, Math.trunc(w)));
|
||||
schreiben.run(n, sauber, new Date().toISOString());
|
||||
return sauber;
|
||||
};
|
||||
const stand = (): LiegestuetzStand => ({ offen: wert(OFFEN), gemacht: wert(GEMACHT) });
|
||||
|
||||
return { stand, setzen, aendern: (n) => setzen(stand() + n) };
|
||||
return {
|
||||
stand,
|
||||
aendern(n) {
|
||||
const vorher = wert(OFFEN);
|
||||
const nachher = schreibe(OFFEN, vorher + n);
|
||||
if (nachher < vorher) schreibe(GEMACHT, wert(GEMACHT) + (vorher - nachher));
|
||||
return stand();
|
||||
},
|
||||
setzen(n) { schreibe(OFFEN, n); return stand(); },
|
||||
gemachtSetzen(n) { schreibe(GEMACHT, n); return stand(); },
|
||||
neuerStream() { schreibe(GEMACHT, 0); return stand(); },
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user