Adressdaten aus dem Swyx-Client; core-Modul und Firefox-Plugin ausgelagert

SwyxTray liest jetzt die Telefonbuecher (global und persoenlich) des
SwyxIt!-Clients in einen Adress-Cache, bietet Kontaktsuche ueber
FulltextSearchInContactsEx und Nummernaufloesung ueber DispResolveNumber;
alles ueber den WebSocket-Zugang abrufbar.

Das Spring-Boot-Modul core/ und das SwyxFFPlugin werden als eigene
Projekte gepflegt und aus diesem Repository entfernt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 15:10:13 +02:00
co-authored by Claude Fable 5
parent cd355c4e21
commit fcd0d62d78
48 changed files with 427 additions and 17014 deletions
+48 -22
View File
@@ -26,18 +26,8 @@ laufenden SwyxIt!-Client verbindet.
Browser laufende Seite wählen, annehmen und auflegen kann und im Gegenzug
jede Zustandsänderung zugestellt bekommt (siehe unten).
- Kontextmenü (Rechtsklick): Status, belegte Leitungen, *WebSocket-Zugang*,
*Lese Tabs*, *Tab öffnen*, *Tab schliessen*, *Neu verbinden*,
*Protokoll öffnen*, *Beenden*. Doppelklick zeigt den Status als Sprechblase.
Die drei Tab-Punkte laufen über den Tab-Verwaltungskanal zum Firefox-Plugin
(siehe unten); ohne verbundenes Plugin (oder mit `PluginPort: 0`) bleiben
sie ohne Ergebnis bzw. grau. *Lese Tabs* holt die Titel der offenen Tabs
und zeigt sie als Sprechblase — die vollständige Liste samt Adressen landet
im Protokoll. *Tab öffnen* öffnet einen Tab mit `https://www.assecutor.de`,
*Tab schliessen* schließt alle Tabs, deren Titel „Assecutor Data Service
GmbH" enthält — als Komposition aus `list` und `close` je Treffer, wie beim
`focus`-Kommando (beide Werte sind Konstanten in
[TrayApplicationContext.cs](src/SwyxTray/TrayApplicationContext.cs)).
*Neu verbinden*, *Protokoll öffnen*, *Beenden*. Doppelklick zeigt den
Status als Sprechblase.
## Verwendetes SDK
@@ -59,8 +49,16 @@ Genutzte Schnittstellen:
- `IClientLineMgrDisp``DispInit`, `DispRegisterUser`, `DispNumberOfLines`,
`DispGetLine`, `DispSelectedLineNumber`, `DispIsServerUp`,
`DispGetCurrentUser`, `DispGetCurrentServer`, `DispReleaseUser`
`DispGetCurrentUser`, `DispGetCurrentServer`, `DispReleaseUser`,
`FulltextSearchInContactsEx`, `DispResolveNumber`, `DispClientConfig`
- `IClientLineDisp``DispState`, `DispPeerNumber`, `DispPeerName`
- `IClientConfig``PbxPhoneBookEnumerator`, `UserPhoneBookEnumerator`
(globales und persönliches Telefonbuch für den Adress-Cache)
- `INameNumberSearchResult(Collection)` — Ergebnistyp der Kontaktsuche.
Die Telefonbuch-Sammlungen und ihre Einträge kommen dagegen **nicht** als
`IDispCollection`/`IPbxPhoneBookEntryDisp` an, sondern als reine
IDispatch-Objekte — sie werden per `IEnumerable` und Late-Binding gelesen
(siehe „Stand der Prüfung")
- `IClientLineMgrEventsPub_Event``PubOnLineMgrNotification(msg, param)`
- `CLMgrLineStates` — die Leitungszustände als typisierte Aufzählung
@@ -83,7 +81,6 @@ Genutzte Schnittstellen:
| [Web/Protocol.cs](src/SwyxTray/Web/Protocol.cs) | die JSON-Nachrichten |
| [Web/WebSocketConfig.cs](src/SwyxTray/Web/WebSocketConfig.cs) | Port, Token, erlaubte Origins |
| [examples/browser-client.html](examples/browser-client.html) | Beispielseite zum Erproben des Zugangs |
| [src/SwyxFFPlugin/](src/SwyxFFPlugin/) | die Firefox-Erweiterung: [manifest.json](src/SwyxFFPlugin/manifest.json) und [background.js](src/SwyxFFPlugin/background.js) |
| [Log.cs](src/SwyxTray/Log.cs) | Dateiprotokoll unter `%LOCALAPPDATA%\SwyxTray\swyxtray.log` |
### Zwei Entwurfsentscheidungen
@@ -207,13 +204,12 @@ Hauptports (`focus`, `tabs`, `opentab`, `closetab`) enden dann mit
SwyxTray einen vorhandenen Tab als Komposition nach vorn: `list`, passenden
Tab per `close` schließen und die URL per `open` neu öffnen.
Das Plugin selbst liegt in [src/SwyxFFPlugin/](src/SwyxFFPlugin/) —
[background.js](src/SwyxFFPlugin/background.js) setzt genau diese drei
Das Plugin selbst (SwyxFFPlugin) wird als **eigenes Projekt** gepflegt und ist
nicht Teil dieses Repositories. Sein `background.js` setzt genau diese drei
Aufträge um und verbindet sich selbsttätig neu (1 s Abstand, je Fehlversuch
verdoppelt bis 30 s), falls SwyxTray noch nicht oder nicht mehr läuft.
Installation zum Erproben: `about:debugging#/runtime/this-firefox`
*Temporäres Add-on laden…* → die `manifest.json` des Ordners wählen (Näheres
im [README des Plugins](src/SwyxFFPlugin/README.md)).
*Temporäres Add-on laden…* → die `manifest.json` des Plugin-Projekts wählen.
Sind die Prüfungen wieder eingeschaltet, braucht die Adresse zusätzlich das
Token (`ws://127.0.0.1:17655/?token=…`), und in `AllowedOrigins` muss der
@@ -233,7 +229,10 @@ wobei die UUID je Installation zufällig vergeben wird — sie steht in
| `tabs` | — | liest die offenen Firefox-Tabs über das Plugin aus |
| `opentab` | `url` | öffnet über das Plugin einen neuen Firefox-Tab |
| `closetab` | `tabId` | schließt über das Plugin den Tab mit dieser Id |
| `status` | — | fordert den vollen Zustand an |
| `addresses` | — | liefert die zwischengespeicherten Adressdaten (Telefonbücher), Antwort im Feld `addresses` |
| `contacts` | `query` | durchsucht die Adressdaten des Swyx-Clients (Telefonbücher und Kontakt-Plugins), Antwort im Feld `contacts` |
| `resolve` | `number` | löst eine Rufnummer über die Adressdaten in einen Namen auf, Antwort im Feld `name` (leer = unbekannt) |
| `status` | — | fordert den vollen Zustand samt der zwischengespeicherten Adressdaten an |
| `reconnect` | — | verwirft die CLMgr-Verbindung und baut sie neu auf |
| `ping` | — | Lebenszeichen |
@@ -292,9 +291,10 @@ Tab-Liste von selbst — wer sie aktuell braucht, fragt mit `tabs` nach:
### Meldungen (App → Seite)
Direkt nach dem Verbinden kommen `hello` und ein `snapshot`, danach ein
`snapshot` bei **jeder** Zustandsänderung. Die Seite muss also nie pollen und
nie selbst Zustand mitführen:
Direkt nach dem Verbinden kommen `hello`, ein `snapshot` und die
zwischengespeicherten Adressdaten als `addresses`, danach ein `snapshot` bei
**jeder** Zustandsänderung. Die Seite muss also nie pollen und nie selbst
Zustand mitführen:
```json
{"type":"snapshot","connected":true,"serverUp":true,"overall":"Ringing",
@@ -306,6 +306,22 @@ nie selbst Zustand mitführen:
`overall` ist derselbe Gesamtzustand, den auch die Farbe des Tray-Symbols zeigt.
**Adressdaten** (seit Protokollversion 8): Die App liest beim Verbindungsaufbau
zu SwyxIt! — also beim Start und bei jeder Neuverbindung — sowie danach **alle
60 Minuten** das globale und das persönliche Telefonbuch aus dem Swyx-Client
und hält sie im Speicher vor. Jede verbundene Seite bekommt den Stand als
`addresses`-Nachricht zugestellt: direkt nach dem `snapshot` beim Verbinden,
auf ein `status`-Kommando und unaufgefordert **nach jedem Neueinlesen**. Wer
den Cache selbst abfragen will, sendet das Kommando `addresses` und erhält
die Einträge im gleichnamigen Feld der Antwort. Bei einem Lesefehler behält
der Cache den letzten erfolgreichen Stand.
```json
{"type":"addresses","addresses":[
{"name":"Muster GmbH","number":"+493012345","description":"Globales Telefonbuch"},
{"name":"Schmidt, Anna","number":"123","description":"Persoenliches Telefonbuch"}]}
```
Zusätzlich zum `snapshot` meldet die App **Anruf-Ereignisse** als eigene
Nachricht vom Typ `call` — damit muss eine Seite keine zwei Zustände
vergleichen, um einen eingehenden Ruf zu erkennen:
@@ -536,6 +552,16 @@ der Ereignissenke, Auslesen aller vier Leitungen samt Zustand und ausgewählter
Leitung, Ableitung des Gesamtzustands, Symbolerzeugung, Änderungserkennung
(keine Doppelmeldungen über mehrere Zyklen) und das Dateiprotokoll.
**Adressdaten gegen den laufenden Client verifiziert:** Das globale Telefonbuch
wird mit allen **404 Einträgen** gelesen (`Adressdaten gelesen: 404 Einträge`,
knapp eine Sekunde nach dem Verbindungsaufbau). Dabei zeigte sich, dass CLMgr
die Telefonbücher als reine IDispatch-Objekte liefert: weder die Sammlung noch
ihre Einträge implementieren die typisierten Interop-Schnittstellen
(`IDispCollection`, `IPbxPhoneBookEntryDisp`) — ein Cast darauf scheitert still
und ergibt 0 Einträge. Gelesen wird deshalb per `IEnumerable` und Late-Binding
(`dynamic`), siehe `AppendPhoneBook` in
[Swyx/SwyxClient.cs](src/SwyxTray/Swyx/SwyxClient.cs).
**WebSocket-Zugang gegen die laufende App geprüft** (mit einem
`ClientWebSocket` als Gegenstelle):