feat(privacy): reconnect the message export

The exporter has worked since v1.4.8. The form that drives it went out
with the old settings window in May, which left PRIVACY.md promising an
access request the plugin had no way to answer.

New section in the data and privacy tab: time range, sender substring,
channel groups, format, and a save dialog. Form state lives in the tab,
not the config -- a filter describes one action, and a stale "last 7
days, sender Mira" reappearing weeks later is a worse start than an
empty form.

StreamForExport now takes a caller-owned connection. The reader stays
open for as long as the file is written, seconds to minutes on a large
history, and chat keeps arriving throughout -- so the primary connection
would be read here and written by UpsertMessage at once, and
SqliteConnection is not thread-safe. Holding the read lock instead would
trade that for freezing the game.

ChannelGroups lifts the eight groups out of the deleted tab and finishes
them: 37 of 89 channels belonged to no group and were therefore
unreachable in the UI. Game Master channels follow ChatTypeExt.Parent(),
so GmTell sits with the other tells rather than under system traffic --
an access request that quietly drops part of what it promises is the
dangerous kind of gap.

Also here:

- OpenSecondaryConnection disposes on a failing pragma. Open can succeed
  and journal_mode=WAL still time out, and with Pooling=false the
  connection then survives until a finalizer reaches it. Affects the
  full-text rebuild worker too.
- StreamForExport builds its logger before the reader, so a throwing
  CreateLogger cannot leave a reader nobody owns.
- The export thread takes the gate itself instead of the caller taking
  it first. Acquiring before Start would strand the gate for the session
  if thread creation failed, and the gate also holds back the sweep.
- Notifications are skipped once teardown has started. The thread has no
  cancellation path and finishing the file is right, but reporting it to
  a plugin that is gone is not.
- Transient widget rows that return their value instead of saving it.
  Writing the config file on every keystroke of a sender filter would be
  both pointless and slow.
- Five translated keys for "another database operation is running", in
  all 25 languages. Two of the four operation names have no trigger yet;
  they arrive with the cleanup and maintenance sections.
This commit is contained in:
2026-08-18 21:25:10 +02:00
parent 90bf986f76
commit d59ee62223
33 changed files with 1056 additions and 71 deletions
+16 -1
View File
@@ -1156,4 +1156,19 @@
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Palauta tämä välilehti pääikkunaan</value>
</data>
</root>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Toinen tietokantatoiminto on käynnissä: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>säilytysajo</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>vienti</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>siivous</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>historian tyhjennys</value>
</data>
</root>