Skip to content
10 changes: 8 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,28 @@ MINIO_HOST_BIND=127.0.0.1
MINIO_API_PORT=9000
MINIO_CONSOLE_PORT=9001

# API (optional defaults; WEBHOOK_SHARED_SECRET required to accept ingest calls)
# API (optional defaults; API_SHARED_SECRET required to accept ingest calls)
WEBHOOK_INGEST_HOST=0.0.0.0
WEBHOOK_INGEST_PORT=8090
# Required: ingest requests are rejected when unset
WEBHOOK_SHARED_SECRET=
API_SHARED_SECRET=

# Worker / consumer (optional defaults)
WORKER_NAME=integrations-worker
WORKER_QUEUE_NAMES=jobs.default
WORKER_BURST=false
CRM_LINKEDIN_FIELD=cLinkedInUrl
MAX_ATTACHMENTS_PER_CONTACT=3
MAX_FILE_SIZE_MB=10
ALLOWED_FILE_TYPES=pdf,doc,docx,txt
RESUME_KEYWORDS=resume,cv,curriculum
OPENAI_API_KEY=
# For OpenRouter, set OPENAI_BASE_URL=https://openrouter.ai/api/v1
OPENAI_BASE_URL=
OPENAI_MODEL=gpt-4o-mini
# Resume model name without provider prefix; OpenRouter is auto-prefixed to openai/<model>
RESUME_AI_MODEL=gpt-4o-mini
RESUME_EXTRACTOR_VERSION=v1
CRM_SYNC_ENABLED=true
CRM_SYNC_INTERVAL_SECONDS=900
CRM_SYNC_PAGE_SIZE=200
Expand All @@ -62,6 +67,7 @@ DISCORD_BOT_TOKEN=your_bot_token_here
HEALTHCHECK_PORT=3000
DISCORD_SENDMSG_CHARACTER_LIMIT=2000
CHECK_EMAIL_WAIT=2
WORKER_API_BASE_URL=http://worker-api:8090
AUDIT_API_BASE_URL=
AUDIT_API_TIMEOUT_SECONDS=2.0
# Required for Discord bot commands that post to channels
Expand Down
17 changes: 5 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,11 @@ repos:
# Run the formatter on changed files only
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.0
- repo: local
hooks:
- id: mypy
args: [--strict]
name: mypy
entry: ./scripts/mypy.sh
language: system
pass_filenames: false
files: ^(apps/discord_bot/src/five08/|apps/worker/src/five08/|packages/shared/src/five08/)
additional_dependencies:
- aiohttp>=3.13.1
- discord.py~=2.6.0
- pydantic~=2.10
- pydantic-settings~=2.8
- redis>=6.4.0
- requests~=2.31
- rq>=2.6.0
- types-requests>=2.32.4.20250913
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ async def setup(bot: commands.Bot) -> None:
- Add shared env/config in `packages/shared/src/five08/settings.py`.
- Add service-specific settings in local service `config.py` by subclassing shared settings.
- Keep secrets in env vars, not code.
- For Discord CRM audit writes, use `AUDIT_API_BASE_URL` and shared `WEBHOOK_SHARED_SECRET`.
- For Discord CRM audit writes, use `AUDIT_API_BASE_URL` and shared `API_SHARED_SECRET`.

## Agent Guidelines

Expand Down
5 changes: 3 additions & 2 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,10 @@ Use `.env.example` as source of truth. Key categories:

- Shared queue/runtime: `REDIS_URL`, `REDIS_QUEUE_NAME`, `POSTGRES_URL`, `JOB_MAX_ATTEMPTS`, `JOB_RETRY_BASE_SECONDS`, `JOB_RETRY_MAX_SECONDS`, `LOG_LEVEL`, webhook settings
- Bot credentials/integrations: Discord, email, Espo, Kimai
- Discord CRM audit writer: `AUDIT_API_BASE_URL`, `AUDIT_API_TIMEOUT_SECONDS` (plus shared `WEBHOOK_SHARED_SECRET`)
- Discord CRM audit writer: `AUDIT_API_BASE_URL`, `AUDIT_API_TIMEOUT_SECONDS` (plus shared `API_SHARED_SECRET`)
- Worker controls: `WORKER_NAME`, `WORKER_QUEUE_NAMES`, `WORKER_BURST`
- Worker CRM processing: `MAX_ATTACHMENTS_PER_CONTACT`, `MAX_FILE_SIZE_MB`, `ALLOWED_FILE_TYPES`, `RESUME_KEYWORDS`, `OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_MODEL`
- Worker CRM processing: `MAX_ATTACHMENTS_PER_CONTACT`, `MAX_FILE_SIZE_MB`, `ALLOWED_FILE_TYPES`, `RESUME_KEYWORDS`, `OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_MODEL`, `RESUME_EXTRACTOR_VERSION`
- Resume upload UX wiring: `WORKER_API_BASE_URL` on bot, `CRM_LINKEDIN_FIELD` on worker.

## CI Notes

Expand Down
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ Migrations:
### Worker API Endpoints

- `GET /health`: Redis/Postgres/worker health check.
- `GET /jobs/{job_id}`: Fetch queued job status/result payload.
- `POST /jobs/resume-extract`: Enqueue resume profile extraction.
- `POST /jobs/resume-apply`: Enqueue confirmed CRM field apply.
- `POST /webhooks/{source}`: Generic webhook enqueue endpoint.
- `POST /webhooks/espocrm`: EspoCRM webhook endpoint (expects array payload).
- `POST /webhooks/espocrm/people-sync`: EspoCRM contact-change webhook for people cache sync.
Expand Down Expand Up @@ -134,7 +137,7 @@ Use `.env.example` as the source of truth for defaults.

### Worker API Ingest

- `Required` for protected endpoints: `WEBHOOK_SHARED_SECRET` (ingest requests are rejected when unset)
- `Required` for protected endpoints: `API_SHARED_SECRET` (ingest requests are rejected when unset)
- `Optional`: `WEBHOOK_INGEST_HOST` (default: `0.0.0.0`)
- `Optional`: `WEBHOOK_INGEST_PORT` (default: `8090`)

Expand All @@ -149,18 +152,22 @@ Use `.env.example` as the source of truth for defaults.
- `Optional`: `CRM_SYNC_ENABLED` (default: `true`)
- `Optional`: `CRM_SYNC_INTERVAL_SECONDS` (default: `900`)
- `Optional`: `CRM_SYNC_PAGE_SIZE` (default: `200`)
- `Optional`: `CRM_LINKEDIN_FIELD` (default: `cLinkedInUrl`)
- `Optional`: `MAX_ATTACHMENTS_PER_CONTACT` (default: `3`)
- `Optional`: `MAX_FILE_SIZE_MB` (default: `10`)
- `Optional`: `ALLOWED_FILE_TYPES` (default: `pdf,doc,docx,txt`)
- `Optional`: `RESUME_KEYWORDS` (default: `resume,cv,curriculum`)
- `Optional`: `OPENAI_API_KEY` (if unset, heuristic extraction is used)
- `Optional`: `OPENAI_BASE_URL`
- `Optional`: `OPENAI_MODEL` (default: `gpt-4o-mini`)
- `Optional`: `OPENAI_BASE_URL` (set `https://openrouter.ai/api/v1` for OpenRouter)
- `Optional`: `RESUME_AI_MODEL` (default: `gpt-4o-mini`; use plain names like `gpt-4o-mini`, OpenRouter gets auto-prefixed to `openai/<model>`)
- `Optional`: `OPENAI_MODEL` (default: `gpt-4o-mini`; fallback/legacy model setting)
- `Optional`: `RESUME_EXTRACTOR_VERSION` (default: `v1`; used in resume processing idempotency/ledger keys)

### Discord Bot Core

- `Required`: `DISCORD_BOT_TOKEN`
- `Required`: `CHANNEL_ID`
- `Optional`: `WORKER_API_BASE_URL` (default: `http://worker-api:8090`)
- `Optional`: `HEALTHCHECK_PORT` (default: `3000`)
- `Optional`: `DISCORD_SENDMSG_CHARACTER_LIMIT` (default: `2000`)
- `Optional`: `CHECK_EMAIL_WAIT` (default: `2`)
Expand All @@ -174,7 +181,7 @@ Use `.env.example` as the source of truth for defaults.

### Discord CRM Audit Logging (Best Effort)

- `Optional`: `AUDIT_API_BASE_URL` (when set with `WEBHOOK_SHARED_SECRET`, CRM commands emit best-effort audit events)
- `Optional`: `AUDIT_API_BASE_URL` (when set with `API_SHARED_SECRET`, CRM commands emit best-effort audit events)
- `Optional`: `AUDIT_API_TIMEOUT_SECONDS` (default: `2.0`)

### Kimai (Legacy/Deprecating)
Expand Down
Loading