Files
ten31-database/backend/matrix_intake
Keysat 68106d7a5a Add Matrix NL-query Q&A surface (W2 step 5)
Read-only natural-language query over the curated nl_query endpoint, answered
in-thread. Two entry points (room-per-purpose model): a dedicated Q&A room
(MATRIX_QUERY_ROOM) where every top-level message is a question, plus the
?/@bot trigger in the intake room as a cross-room convenience. Both routes hit
the same handle_query -> crm_client.nl_query -> POST /api/query/nl; translation
runs on the box's local model, nothing leaves the box, and there is no write
path so no approval gate applies.

Pure logic (trigger parsing, answer rendering) in query.py with offline tests;
async room wiring in bot.py (live-smoke only, per the bot's convention).

Bot-side only, ships on the Spark via git pull + restart. Depends on the
box-side /api/query/nl endpoint, which lands with the v93 s9pk (reminders + W2):
until v93 is installed the Q&A surface 404s, so the bot deploy is staged to
follow that install.
2026-06-18 19:46:54 -05:00
..

Matrix intake bot

Turns a typed message in a dedicated Matrix room into a proposed fundraising-grid add/edit, gated on in-thread human approval before any write. Runs as its own process (on the Spark), separate from the CRM. Full design + rules: docs/guides/matrix-intake.md.

Run

# 1. Install the one third-party dep (isolated to this component — NOT the CRM runtime)
python3 -m pip install -r requirements.txt          # matrix-nio

# 2. Fill the MATRIX_* and CRM_BOT_* vars in the repo .env (see ../../.env.example),
#    and create a dedicated CRM user for CRM_BOT_USERNAME/PASSWORD (admin → invite user).

# 3. Start the listener
python3 bot.py

It primes the Matrix sync past history (no backlog replay), then listens. Post a message in the intake room; it replies in a thread with the parsed proposal. Reply yes to commit, edit field=value to change a field, or no to discard.

Layout

  • bot.py — entrypoint: connect, prime-then-listen, dispatch (lifts matrix-bridge's plumbing).
  • parse.py — message → structured proposal via local Qwen (spark.pybackend/ingest/llm.py).
  • proposals.py — in-memory pending-proposal store + the yes/edit/no state machine.
  • crm_client.py — login + GET /api/intake/match + write via POST /api/fundraising/log-communication.
  • matrix_io.py — message splitting, thread-root detection, threaded-reply sender.
  • settings.py — Matrix + CRM-API config (named settings, not config, to avoid shadowing ingest/config).

Test (offline)

python3 test_parse.py && python3 test_proposals.py && python3 test_crm_client.py
# endpoint + create→match contract (boots the real server against a temp DB):
cd ../ && python3 test_intake_endpoints.py

Live Matrix behavior needs creds + matrix-nio and can only be smoke-tested on the Spark.