68106d7a5a
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.
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.py→backend/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 viaPOST /api/fundraising/log-communication.matrix_io.py— message splitting, thread-root detection, threaded-reply sender.settings.py— Matrix + CRM-API config (namedsettings, notconfig, to avoid shadowingingest/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.