Vorlesen: Wortfilter trifft nicht mehr jedes w, Stimme Thorsten

"www" in der Blockliste wurde beim Zusammenziehen gezogener Buchstaben zu
"w" und traf als kurzer Eintrag jedes Wort, das mit w anfaengt. Im Stream
flog damit fast jede Nachricht raus ("wie", "weg"), die Punkte gingen
zurueck. Was nach dem Normalisieren unter 3 Buchstaben hat, trifft jetzt
nur noch als ganzes Wort.

de_DE-mls-medium, Sprecher 0, klang verwaschen. Die Phoneme stimmten, es
lag am Modell. Vorgabe ist jetzt de_DE-thorsten-medium; im Hub schon so
eingestellt.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZeasyAlexandClaude Opus 5.5 committed 2026-09-25 00:17:17 +02:00
1 parent 58e1d98351
commit 5622499877
6 files changed
+41 -13

No files matched your search

+9
View File
@@ -25,6 +25,15 @@ test('Wortfilter: kurze Eintraege nur am Wortanfang, sonst trifft es Harmloses',
assert.equal(wortfilterTreffer('N4Z1', LISTE), 'nazi');
});
test('Wortfilter: "www" wird zu "w" und darf nur als ganzes Wort treffen', () => {
// So im Stream passiert: jede Nachricht mit "wie" oder "weg" flog raus.
const liste = ['www', 'http'];
assert.equal(wortfilterTreffer('wie kann es sein, dass ich nicht mod bin?!', liste), undefined);
assert.equal(wortfilterTreffer('Ladiesman hat einen weg', liste), undefined);
assert.equal(wortfilterTreffer('geh auf www.example.de', liste), 'www');
assert.equal(wortfilterTreffer('https://example.de', liste), 'http');
});
test('Wortfilter: normaler Text geht durch', () => {
assert.equal(wortfilterTreffer('Grüße an Abdu, geiler Stream!', LISTE), undefined);
});
+3
View File
@@ -148,6 +148,8 @@ export function fuerFilter(text: string, mitLuecken: boolean): string {
* Lange Eintraege (ab 5 Buchstaben) werden im zusammengezogenen Text
* gesucht, damit Punkte und Leerzeichen dazwischen nicht helfen. Kurze nur
* am Wortanfang: zusammengezogen stuende "nazi" sonst auch in "kann azubi".
* Was danach kuerzer als 3 Buchstaben ist, nur als ganzes Wort: aus "www"
* wird "w", und das stuende am Anfang von "wie" und "weg".
*/
export function wortfilterTreffer(text: string, blockliste: string[]): string | undefined {
const dicht = fuerFilter(text, false);
@@ -155,6 +157,7 @@ export function wortfilterTreffer(text: string, blockliste: string[]): string |
return blockliste.find((eintrag) => {
const e = fuerFilter(eintrag, false);
if (!e) return false;
if (e.length < 3) return woerter.includes(e);
return e.length >= 5 ? dicht.includes(e) : woerter.some((w) => w.startsWith(e));
});
}
+4 -4
View File
@@ -155,10 +155,10 @@ rewards:
# Gesprochen wird im Hub mit Piper, nicht im Browser - dort haengt die
# Stimme am Rechner, der streamt. Name einer Datei unter models/piper/
# (ohne .onnx) oder ein absoluter Pfad. Einrichten: scripts/install-piper.sh
stimme: de_DE-mls-medium
# mls hat 236 Sprecher in einem Modell; hier steht die Nummer (0-235).
# Bei Stimmen mit nur einem Sprecher weglassen.
sprecher: 0
stimme: de_DE-thorsten-medium
# Nur bei Stimmen mit mehreren Sprechern, z. B. de_DE-mls-medium (0-235).
# Die klang im Stream allerdings verwaschen.
# sprecher: 0
# Overlay-Server. Zugleich die einzige Tuer nach aussen, wenn der Dienst
# zentral laeuft: Uebersicht, Einrichtung und OAuth-Rueckruf haengen daran.
+22 -6
View File
@@ -1,6 +1,6 @@
# Übergabe
Stand **2026-09-23**, letztes gebautes Paket 0.5.4. Wer hier weitermacht,
Stand **2026-09-25**, letztes gebautes Paket 0.5.4. Wer hier weitermacht,
liest zuerst diese Seite, dann [`07-mvp-plan.md`](07-mvp-plan.md). Für Abdu
selbst gibt es das [Handbuch](handbuch/handbuch.pdf).
@@ -20,7 +20,7 @@ Auslöser bis zum fertigen Short steht im Code vollständig. Geprüft ist sie
| S5 Onboarding + Sichtung | offen | |
| S6 Erstchatter-Begrüßung | offen | |
280 Tests, alle grün. Was noch fehlt, steht vollständig in
281 Tests, alle grün. Was noch fehlt, steht vollständig in
[`offene-punkte.md`](offene-punkte.md).
## Neu: der Dienst läuft zentral
@@ -120,15 +120,20 @@ prüft (`apps/hub/src/rewards.ts`), Piper spricht, `/tts` spielt ab.
| Belohnung | „Nachricht vorlesen", 1000 Punkte, Texteingabe Pflicht |
| Freigabe | keine, wird direkt vorgelesen |
| Schutz | Wortfilter (29 Einträge), 200 Zeichen, 3 je Person und Stunde, „Sofort still" auf der Übersichtsseite |
| Stimme | Piper, `de_DE-mls-medium`, Sprecher 0 |
| Stimme | Piper, `de_DE-thorsten-medium` (mls klang verwaschen) |
**Die Stimme entsteht im Hub, nicht im Browser.** Zuerst lief es über
`speechSynthesis` in der Browser-Quelle. Das hat in OBS gesprochen, aber
amerikanisch: OBS' Browser sah keine deutsche Stimme und die Seite nahm die
erste beliebige. Piper liegt unter `/opt/piper` im LXC (2 Kerne: 200 Zeichen
in 0,7 s), das Stimmenmodell unter `models/piper/`. Beides überlebt jedes
Ausrollen; eingerichtet mit `scripts/install-piper.sh`. Proben aller 236
mls-Sprecher lassen sich mit `--speaker N` erzeugen.
Ausrollen; eingerichtet mit `scripts/install-piper.sh`.
**Die erste Stimme war `de_DE-mls-medium`, Sprecher 0**, und klang im Stream
„wie jemand mit Schlaganfall". Die Phoneme stimmten (`piper --debug`), es lag
am Modell: Hörbuch-Aufnahmen von 236 Laien, Sprecher 0 ist ein beliebiger.
Seit 2026-09-24 läuft `de_DE-thorsten-medium`, ein Sprecher, darum kein
`sprecher` mehr in der Konfiguration. Das mls-Modell liegt noch daneben.
**Die Belohnung muss über die API angelegt sein** (`abdulive rewards:anlegen`).
Nur die App, die eine Belohnung angelegt hat, darf deren Einlösungen
@@ -142,6 +147,15 @@ zusammengezogenen Text gesucht, kürzere nur am Wortanfang, sonst stünde
„nazi" in „kann azubi". Die Liste steht nur in der Hub-Konfiguration, nicht
im Repo.
**Der Filter hat eine Stunde lang fast alles abgelehnt.** `www` in der Liste
wurde zusammengezogen zu `w` und traf damit jedes Wort, das mit w anfängt
(„wie", „weg"). Die Punkte gingen zurück, im Log stand nur `Wortfilter`.
Behoben am 2026-09-24: was nach dem Zusammenziehen unter 3 Buchstaben hat,
trifft nur noch als ganzes Wort. **Noch offen:** lange Einträge treffen über
Wortgrenzen hinweg, „Spaß tut gut" fliegt wegen `spast` raus, „eine Gerade"
wegen `neger`. Wer eine Ablehnung nachvollziehen will: der Text steht im
Ereignis `points.redeemed` in der Datenbank.
**Der YAML-Leser konnte keine Listen aus Maps.** `- titel: X` zerfiel in
Zeichenketten, jede Belohnung war ohne Meldung unwirksam - auch das Beispiel
in `config.example.yaml`. Behoben und getestet; die bestehenden Dateien
@@ -408,7 +422,9 @@ Wirkung.
**`apps/recap` bauen.** Angefangen, aber nicht committet: `apps/recap/src/recap.ts`
und Ergänzungen in `packages/db/src/repos.ts` liegen nur im Arbeitsstand
(die Typprüfung meckert dort noch über `url: undefined`). Der Webhook liegt
bereit, der Hub hat die Daten.
bereit, der Hub hat die Daten. Achtung: `deploy-hub.sh` schiebt den ganzen
Arbeitsstand, der halbe Recap liegt also schon im LXC. Er wird nur von nichts
aufgerufen.
**Danach S5.** Nicht wegen des Codes, sondern weil das Werkzeug ohne
Sichtungsseite für Abdu nicht bedienbar ist. Die Kette funktioniert, aber sie
+1 -1
View File
@@ -146,7 +146,7 @@ const DEFAULTS = {
maxZeichen: 200,
blockliste: [] as string[],
proPersonProStunde: 3,
stimme: 'de_DE-mls-medium',
stimme: 'de_DE-thorsten-medium',
},
},
overlay: { port: 4791, enabled: true },
+2 -2
View File
@@ -13,12 +13,12 @@
# Umgebung (mit Vorgaben):
# PVE_HOST=192.168.2.20 Proxmox-Host
# CT=141 Container-ID
# STIMME=de_DE-mls-medium Name aus huggingface.co/rhasspy/piper-voices
# STIMME=de_DE-thorsten-medium Name aus huggingface.co/rhasspy/piper-voices
set -euo pipefail
PVE_HOST="${PVE_HOST:-192.168.2.20}"
CT="${CT:-141}"
STIMME="${STIMME:-de_DE-mls-medium}"
STIMME="${STIMME:-de_DE-thorsten-medium}"
# de_DE-thorsten-medium -> de/de_DE/thorsten/medium
IFS=- read -r LOCALE SPRECHER QUALI <<< "$STIMME"