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'); 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', () => { test('Wortfilter: normaler Text geht durch', () => {
assert.equal(wortfilterTreffer('Grüße an Abdu, geiler Stream!', LISTE), undefined); 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 * Lange Eintraege (ab 5 Buchstaben) werden im zusammengezogenen Text
* gesucht, damit Punkte und Leerzeichen dazwischen nicht helfen. Kurze nur * gesucht, damit Punkte und Leerzeichen dazwischen nicht helfen. Kurze nur
* am Wortanfang: zusammengezogen stuende "nazi" sonst auch in "kann azubi". * 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 { export function wortfilterTreffer(text: string, blockliste: string[]): string | undefined {
const dicht = fuerFilter(text, false); const dicht = fuerFilter(text, false);
@@ -155,6 +157,7 @@ export function wortfilterTreffer(text: string, blockliste: string[]): string |
return blockliste.find((eintrag) => { return blockliste.find((eintrag) => {
const e = fuerFilter(eintrag, false); const e = fuerFilter(eintrag, false);
if (!e) return 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)); 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 # Gesprochen wird im Hub mit Piper, nicht im Browser - dort haengt die
# Stimme am Rechner, der streamt. Name einer Datei unter models/piper/ # Stimme am Rechner, der streamt. Name einer Datei unter models/piper/
# (ohne .onnx) oder ein absoluter Pfad. Einrichten: scripts/install-piper.sh # (ohne .onnx) oder ein absoluter Pfad. Einrichten: scripts/install-piper.sh
stimme: de_DE-mls-medium stimme: de_DE-thorsten-medium
# mls hat 236 Sprecher in einem Modell; hier steht die Nummer (0-235). # Nur bei Stimmen mit mehreren Sprechern, z. B. de_DE-mls-medium (0-235).
# Bei Stimmen mit nur einem Sprecher weglassen. # Die klang im Stream allerdings verwaschen.
sprecher: 0 # sprecher: 0
# Overlay-Server. Zugleich die einzige Tuer nach aussen, wenn der Dienst # Overlay-Server. Zugleich die einzige Tuer nach aussen, wenn der Dienst
# zentral laeuft: Uebersicht, Einrichtung und OAuth-Rueckruf haengen daran. # zentral laeuft: Uebersicht, Einrichtung und OAuth-Rueckruf haengen daran.
+22 -6
View File
@@ -1,6 +1,6 @@
# Übergabe # Ü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 liest zuerst diese Seite, dann [`07-mvp-plan.md`](07-mvp-plan.md). Für Abdu
selbst gibt es das [Handbuch](handbuch/handbuch.pdf). 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 | | | S5 Onboarding + Sichtung | offen | |
| S6 Erstchatter-Begrüßung | 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). [`offene-punkte.md`](offene-punkte.md).
## Neu: der Dienst läuft zentral ## 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 | | Belohnung | „Nachricht vorlesen", 1000 Punkte, Texteingabe Pflicht |
| Freigabe | keine, wird direkt vorgelesen | | Freigabe | keine, wird direkt vorgelesen |
| Schutz | Wortfilter (29 Einträge), 200 Zeichen, 3 je Person und Stunde, „Sofort still" auf der Übersichtsseite | | 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 **Die Stimme entsteht im Hub, nicht im Browser.** Zuerst lief es über
`speechSynthesis` in der Browser-Quelle. Das hat in OBS gesprochen, aber `speechSynthesis` in der Browser-Quelle. Das hat in OBS gesprochen, aber
amerikanisch: OBS' Browser sah keine deutsche Stimme und die Seite nahm die 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 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 in 0,7 s), das Stimmenmodell unter `models/piper/`. Beides überlebt jedes
Ausrollen; eingerichtet mit `scripts/install-piper.sh`. Proben aller 236 Ausrollen; eingerichtet mit `scripts/install-piper.sh`.
mls-Sprecher lassen sich mit `--speaker N` erzeugen.
**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`). **Die Belohnung muss über die API angelegt sein** (`abdulive rewards:anlegen`).
Nur die App, die eine Belohnung angelegt hat, darf deren Einlösungen 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 „nazi" in „kann azubi". Die Liste steht nur in der Hub-Konfiguration, nicht
im Repo. 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 **Der YAML-Leser konnte keine Listen aus Maps.** `- titel: X` zerfiel in
Zeichenketten, jede Belohnung war ohne Meldung unwirksam - auch das Beispiel Zeichenketten, jede Belohnung war ohne Meldung unwirksam - auch das Beispiel
in `config.example.yaml`. Behoben und getestet; die bestehenden Dateien 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` **`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 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 (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 **Danach S5.** Nicht wegen des Codes, sondern weil das Werkzeug ohne
Sichtungsseite für Abdu nicht bedienbar ist. Die Kette funktioniert, aber sie Sichtungsseite für Abdu nicht bedienbar ist. Die Kette funktioniert, aber sie
+1 -1
View File
@@ -146,7 +146,7 @@ const DEFAULTS = {
maxZeichen: 200, maxZeichen: 200,
blockliste: [] as string[], blockliste: [] as string[],
proPersonProStunde: 3, proPersonProStunde: 3,
stimme: 'de_DE-mls-medium', stimme: 'de_DE-thorsten-medium',
}, },
}, },
overlay: { port: 4791, enabled: true }, overlay: { port: 4791, enabled: true },
+2 -2
View File
@@ -13,12 +13,12 @@
# Umgebung (mit Vorgaben): # Umgebung (mit Vorgaben):
# PVE_HOST=192.168.2.20 Proxmox-Host # PVE_HOST=192.168.2.20 Proxmox-Host
# CT=141 Container-ID # 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 set -euo pipefail
PVE_HOST="${PVE_HOST:-192.168.2.20}" PVE_HOST="${PVE_HOST:-192.168.2.20}"
CT="${CT:-141}" CT="${CT:-141}"
STIMME="${STIMME:-de_DE-mls-medium}" STIMME="${STIMME:-de_DE-thorsten-medium}"
# de_DE-thorsten-medium -> de/de_DE/thorsten/medium # de_DE-thorsten-medium -> de/de_DE/thorsten/medium
IFS=- read -r LOCALE SPRECHER QUALI <<< "$STIMME" IFS=- read -r LOCALE SPRECHER QUALI <<< "$STIMME"