{"id":49537484,"url":"https://github.com/arunrajiah/odoopilot","last_synced_at":"2026-05-02T12:06:37.032Z","repository":{"id":352927041,"uuid":"1217228523","full_name":"arunrajiah/odoopilot","owner":"arunrajiah","description":"AI messaging bridge for Odoo Community — query and act on live ERP data from Telegram \u0026 WhatsApp using Claude, GPT-4, Groq, or Ollama","archived":false,"fork":false,"pushed_at":"2026-04-30T07:22:54.000Z","size":902,"stargazers_count":3,"open_issues_count":1,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-30T09:16:29.756Z","etag":null,"topics":["ai-agent","anthropic","erp","llm","odoo","odoo-module","open-source","python","telegram-bot","whatsapp"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arunrajiah.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["arunrajiah"]}},"created_at":"2026-04-21T17:13:23.000Z","updated_at":"2026-04-30T07:22:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/arunrajiah/odoopilot","commit_stats":null,"previous_names":["arunrajiah/odoopilot"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/arunrajiah/odoopilot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fodoopilot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fodoopilot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fodoopilot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fodoopilot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arunrajiah","download_url":"https://codeload.github.com/arunrajiah/odoopilot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fodoopilot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32533381,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T11:28:32.350Z","status":"ssl_error","status_checked_at":"2026-05-02T11:27:30.140Z","response_time":132,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","anthropic","erp","llm","odoo","odoo-module","open-source","python","telegram-bot","whatsapp"],"created_at":"2026-05-02T12:06:31.341Z","updated_at":"2026-05-02T12:06:33.529Z","avatar_url":"https://github.com/arunrajiah.png","language":"Python","funding_links":["https://github.com/sponsors/arunrajiah"],"categories":[],"sub_categories":[],"readme":"# OdooPilot\n\n![OdooPilot — Your team uses Odoo, without logging in to Odoo](odoopilot/static/description/banner.png)\n\n**Your team uses Odoo — without logging in to Odoo.**\n\nOdooPilot gives every employee an AI assistant on Telegram or WhatsApp that connects to the\nsame Odoo instance, scoped to the same permissions they already have. They apply for leave,\napprove requests, check tasks, update the CRM pipeline, and validate stock moves — by chatting\nwith a bot in their own language. No Odoo login, no app to install, no training.\n\n\u003e **For your internal team.** Not for your customers. Each linked chat user is an Odoo user,\n\u003e sees only the data they're authorised to see, and every write is recorded in the audit trail.\n\n```\nMira (WhatsApp):    \"I need 3 days off next month — Mar 14–16.\"\nOdooPilot:          \"Filed leave request for 3 days (Mar 14–16). Carlos has been notified.\"\n\nCarlos (Telegram):  [inline button: ✅ Approve   ❌ Refuse ]\nCarlos:             taps Approve.\nOdooPilot:          \"✅ Leave approved. Mira has been notified.\"\n```\n\nThe Odoo adoption problem solved: data is no longer stale because the people who generate it\n(field sales, warehouse staff, anyone who occasionally needs HR or Project) finally have a way\nto reach Odoo that fits their day. Same data, same permissions, same audit trail — just lower\nfriction.\n\nNo external service to host. No per-seat SaaS fees. Everything runs inside your Odoo instance.  \nPowered by **Claude AI**, **ChatGPT / GPT-4**, **Groq** (free tier), or **Ollama** (100% local).  \nWorks on **Telegram** and **WhatsApp**. Supports **15 languages**. LGPL-3 open-source.\n\n---\n\n## What it does\n\n- **Conversational queries on live Odoo data** — Tasks, CRM, Sales, Invoices, Inventory, Purchase, HR, Leaves\n- **Write actions with a confirmation gate** — Yes/No button required before any record changes\n- **Two channels, full parity** — Telegram bot and WhatsApp Cloud API\n- **Choice of LLM** — Anthropic Claude, OpenAI GPT-4o, Groq (free tier), or Ollama (100% local)\n- **15 UI languages** — per-user `/language` command\n- **Proactive notifications** — daily task digest and overdue-invoice alerts\n- **Self-hosted** — pure Odoo addon, runs entirely inside your instance, no separate service\n- **Auditable** — immutable log of every AI action (timestamp, user, tool, args, result)\n- **Open source** — LGPL-3, free to install, fork, and extend\n\n---\n\n## Architecture\n\nEverything runs inside the Odoo addon — no separate Python service, no Docker container, no cloud deployment.\n\n```\n Telegram                       WhatsApp Cloud API\n     │                                  │\n     │  HTTPS POST                      │  HTTPS POST\n     │  X-Telegram-Bot-                 │  X-Hub-Signature-256\n     │  Api-Secret-Token                │  (HMAC-SHA256 of body)\n     ▼                                  ▼\n┌──────────────────────────────────────────────────────────────────┐\n│  OdooPilot Odoo Addon                                            │\n│                                                                  │\n│  ┌────────────────────────────────────────────────────────┐     │\n│  │  HTTP Controllers  (controllers/main.py)               │     │\n│  │  • Verify webhook signature in constant time           │     │\n│  │  • Per-(channel, chat_id) sliding-window rate limit    │     │\n│  │  • Idempotency dedup on update_id / messages[].id      │     │\n│  │  • Hand off to bounded worker pool                     │     │\n│  └─────────────────────────┬──────────────────────────────┘     │\n│                            │                                     │\n│  ┌─────────────────────────▼──────────────────────────────┐     │\n│  │  Agent  (services/agent.py)                            │     │\n│  │  • Load session · build messages · run LLM tool loop   │     │\n│  │  • Read tools execute immediately                      │     │\n│  │  • Write tools → preflight → resolve target → stage    │     │\n│  │    pending_args + per-write nonce → ask Yes/No         │     │\n│  │  • On confirmed Yes → execute under linked-user env    │     │\n│  └────┬─────────────────────────────────┬─────────────────┘     │\n│       │                                 │                        │\n│  ┌────▼──────────┐         ┌────────────▼────────────────┐      │\n│  │  LLM Client   │         │  ORM Tools (services/tools) │      │\n│  │  Anthropic    │         │  project / sales / crm      │      │\n│  │  OpenAI       │         │  invoices / inventory       │      │\n│  │  Groq         │         │  purchase / hr / leaves     │      │\n│  │  Ollama       │         │  + 5 write tools w/ confirm │      │\n│  └───────────────┘         └─────────────────────────────┘      │\n│                                                                  │\n│  Models                                                          │\n│  ────────                                                        │\n│  odoopilot.session         conversation history + pending nonce  │\n│  odoopilot.identity        chat_id → Odoo user mapping           │\n│  odoopilot.audit           immutable log of every tool call      │\n│  odoopilot.link.token      SHA-256 hashed magic-link tokens      │\n│  odoopilot.delivery.seen   webhook idempotency table             │\n└──────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## Quickstart\n\n### Prerequisites\n\n- Odoo **17.0 Community** (self-hosted or Odoo.sh)\n- An LLM API key — [Anthropic](https://console.anthropic.com), [OpenAI](https://platform.openai.com), [Groq](https://console.groq.com) (free tier, no card), or a local [Ollama](https://ollama.com) endpoint\n- One of:\n  - A **Telegram bot token** from [@BotFather](https://t.me/BotFather), and/or\n  - A **WhatsApp Business** account with the [Meta Cloud API](https://developers.facebook.com/docs/whatsapp/cloud-api) enabled (phone number ID, access token, app secret)\n- Odoo must be reachable from the internet (for webhook delivery)\n\n### 1. Install the addon\n\nCopy the `odoopilot/` directory into your Odoo addons path, then:\n\n```bash\n# Restart Odoo and update the module list\n./odoo-bin -c odoo.conf -u odoopilot\n```\n\nOr install from the [Odoo App Store](https://apps.odoo.com/apps/modules/17.0/odoopilot).\n\n### 2. Configure in Odoo Settings\n\nGo to **Settings → OdooPilot** and fill in the channels you want to enable.\n\n#### Telegram\n\n| Field | Value |\n|-------|-------|\n| Telegram Bot Token | Paste the token from @BotFather |\n\nThen click **Register Webhook**. The action calls Telegram's `setWebhook` API and **auto-generates a 32-byte secret**, which Telegram echoes back on every delivery as `X-Telegram-Bot-Api-Secret-Token`. The endpoint rejects any request whose header doesn't match.\n\n#### WhatsApp\n\n| Field | Value |\n|-------|-------|\n| WhatsApp Phone Number ID | From Meta App Dashboard → WhatsApp → API setup |\n| WhatsApp Access Token | Permanent token from Meta App Dashboard |\n| WhatsApp Verify Token | Any random string — paste the same value into Meta's webhook config |\n| WhatsApp App Secret | App Secret from Meta App Dashboard → Settings → Basic |\n\nThen in Meta's webhook config, set the callback URL to `https://YOUR_ODOO/odoopilot/webhook/whatsapp` and the verify token to whatever you pasted above.\n\n\u003e The App Secret is **mandatory**. Without it the WhatsApp webhook refuses all traffic. Meta signs every POST with `X-Hub-Signature-256` (HMAC-SHA256 of the raw body keyed with the App Secret); OdooPilot verifies this in constant time before any business logic runs.\n\n#### LLM provider\n\n| Field | Value |\n|-------|-------|\n| LLM Provider | `anthropic`, `openai`, `groq`, or `ollama` |\n| LLM API Key | Your provider key (not used for `ollama`) |\n| LLM Model (optional) | Override the default — see table below |\n\nDefault models if you leave the override blank:\n\n| Provider | Default model | Notes |\n|----------|---------------|-------|\n| `anthropic` | `claude-3-5-haiku-20241022` | Best reasoning per dollar |\n| `openai` | `gpt-4o-mini` | Widest ecosystem |\n| `groq` | `llama-3.3-70b-versatile` | Free tier, very fast |\n| `ollama` | (set in override) | 100% local, e.g. `llama3.2` |\n\n#### Optional throttling knobs\n\nThese are read once at first use from `ir.config_parameter`. Defaults are fine for most installs; raise them if your team is large, lower them if you suspect abuse.\n\n| Parameter | Default | What it controls |\n|-----------|---------|------------------|\n| `odoopilot.rate_limit_per_hour` | `30` | Max messages per (channel, chat_id) per window |\n| `odoopilot.rate_limit_window_seconds` | `3600` | Sliding-window length |\n| `odoopilot.worker_pool_size` | `8` | Bounded thread pool for webhook dispatch |\n\n### 3. Link employee accounts\n\nEach employee sends `/link` to the bot. The bot replies with a one-time URL.\nThe employee opens the URL while logged into Odoo, sees a confirmation page,\nclicks **Confirm and link**, and they're done.\n\nThe flow uses a **two-step CSRF-protected handshake**: GET previews, POST consumes. A logged-in admin who renders an `\u003cimg src=\"…/odoopilot/link/start?token=…\"\u003e` from a malicious record won't get silently linked — the consume only happens on a POST with Odoo's session-bound CSRF token.\n\n### 4. Start chatting\n\nEach linked user can send:\n\n- Any natural-language question — *\"What invoices are overdue?\"*, *\"Show my open tasks\"*, *\"Approve John's leave\"*\n- `/start` — short hello\n- `/link` — re-issue a linking URL (existing identity is replaced)\n- `/language \u003ccode\u003e` — set their preferred reply language (15 supported); `/language auto` to revert to auto-detect\n\nRead tools execute immediately. Write tools show an inline **Yes / No** button — the prompt names the resolved record (not the LLM's argument string), and the click carries a per-write nonce so it can't be swapped out from under you.\n\n---\n\n## Supported domains\n\n| Domain | Read | Write (with confirmation) |\n|--------|------|--------------------------|\n| Project \u0026 Tasks | ✅ list, filter, deadlines | ✅ mark task done |\n| Sales \u0026 CRM | ✅ pipeline, orders, revenue | ✅ confirm sale order · update CRM stage · create lead |\n| Invoices \u0026 Accounting | ✅ overdue, balances, bills | — |\n| Inventory | ✅ stock levels, locations | — |\n| HR \u0026 Leaves | ✅ leave balances, pending requests, employees | ✅ approve leave |\n| Purchase | ✅ purchase orders, RFQs | — |\n\nWrite tools always show an inline Yes/No confirmation before touching data.\n\n---\n\n## LLM providers\n\nOdooPilot calls each provider's HTTP API directly via `requests` — no extra Python dependencies beyond what Odoo already ships, and you can swap providers in **Settings → OdooPilot** without restarting. See the [Quickstart table](#llm-provider) above for the four supported providers and their default models.\n\nTo run **100% local** with no third-party API calls, pick `ollama` and point the provider at your local Ollama endpoint via `odoopilot.ollama_base_url` (default `http://localhost:11434`). Your business data and prompts never leave your server.\n\n---\n\n## Security\n\nOdooPilot has been through a public audit (April 2026, [u/jeconti on r/Odoo](https://github.com/arunrajiah/odoopilot/blob/main/CHANGELOG.md#17070--2026-04-26--security-release)) and three follow-up internal reviews. The current model:\n\n### Webhook authentication\n- **Telegram** verifies the `X-Telegram-Bot-Api-Secret-Token` header on every POST. The secret is **mandatory** and auto-generated by the *Register webhook* action; missing or mismatched secret returns 403.\n- **WhatsApp** verifies Meta's `X-Hub-Signature-256` HMAC-SHA256 in constant time. The Meta App Secret is **mandatory**; without it the endpoint returns 403.\n- Both compares use `hmac.compare_digest`.\n\n### Per-write confirmation that survives prompt injection\n- Every write tool runs `preflight_write` to **resolve the target record before staging** — the staged args carry a real `res_id`, not the LLM's argument string. Wildcard-only or overly-short names are rejected outright.\n- The confirmation prompt names the **resolved record's `display_name`**, so a user clicking *Yes* sees what they're actually about to mutate (not what the LLM claimed it was).\n- Every staged write generates a fresh `secrets.token_urlsafe(12)` nonce embedded in the Yes/No button payload. A prompt injection that tries to swap the staged tool between staging and the click rotates the nonce and the click is rejected.\n\n### Magic-link account binding\n- Tokens are stored as SHA-256 digests (the raw token never persists), single-use, and expire after one hour.\n- The link flow is **two-step CSRF-protected**: GET shows a preview page; POST with Odoo's session-bound CSRF token does the actual link. Cross-site `\u003cimg src=\u003e` attacks cannot silently link an admin's account.\n- Identity hijack defence: a logged-in user with a valid token cannot overwrite an existing `(channel, chat_id)` mapping owned by a different user — the attempt is refused at both preview and commit.\n\n### User scoping\n- Each chat is resolved to an `odoopilot.identity` row. The agent then runs under that Odoo user (`sudo_env(user=identity.user_id.id)`) — every read and write is filtered by the user's existing record-rule access. The bot **cannot do more than the user could do interactively**.\n- Webhook dispatch helpers receive `sudo_env` (named explicitly) only for the unavoidable bootstrap lookups (config, identity, session, link token). All business-data access uses the user-scoped env.\n\n### Cost \u0026 resource bounds\n- Per-(channel, chat_id) sliding-window rate limit prevents an authenticated user (or a flood of forged messages) from driving unbounded paid-LLM spend.\n- Bounded thread pool replaces the previous unbounded daemon-thread spawn — saturation drops gracefully with HTTP 200 so the platform doesn't retry-storm.\n\n### Idempotency\n- Telegram retries on 5xx and timeouts; WhatsApp likewise. The `odoopilot.delivery.seen` table dedups on Telegram `update_id` and WhatsApp `messages[].id` with a SQL UNIQUE constraint. A redelivered confirmation click cannot re-execute the staged write.\n\n### Operational hygiene\n- Audit log writes an immutable `odoopilot.audit` row for every tool call (timestamp, user, tool, args, result, success).\n- Telegram bot tokens are scrubbed from any logged exception string.\n- Static security scanning (`bandit` + `semgrep`) runs in CI on every push.\n\n### Reporting a vulnerability\n\nPlease don't disclose publicly. Use [GitHub Security Advisories](https://github.com/arunrajiah/odoopilot/security/advisories/new) — see [SECURITY.md](SECURITY.md) for the full disclosure policy, supported versions, and threat model.\n\n---\n\n## Status \u0026 roadmap\n\nCurrent release: **17.0.11.0.0** (CHANGELOG: [full history](CHANGELOG.md))\n\nRecent line-up (all on the `17.0` branch and the Odoo App Store):\n\n| Version | Date | Theme |\n|---------|------|-------|\n| **17.0.11.0.0** | 2026-05-02 | Polish pass — banner, CI security scanning (bandit/semgrep), listing renderable check |\n| **17.0.10.0.0** | 2026-04-28 | Repositioning + community panel + listing fix |\n| **17.0.9.0.0** | 2026-04-27 | Defence-in-depth — token scrub, sudo_env rename, hygiene |\n| **17.0.8.0.0** | 2026-04-27 | 5 fixes from internal post-release audit (CSRF, hijack, wildcard, rate limit, idempotency) |\n| **17.0.7.0.0** | 2026-04-26 | Public audit fixes (HMAC, mandatory secret, per-write nonce, hashed tokens) |\n\nComing next:\n\n- 📋 **Odoo 18 port** — adapt to the 18 ORM, dual-branch CI\n- 📋 **Operator-friendly admin views** — group audit log by user, recent-activity dashboard\n- 📋 **OCA submission** once the 18 port lands\n\n---\n\n## Contributing\n\nPull requests welcome. The fastest path to a merged PR:\n\n1. Pick an unimplemented tool or domain from the table above\n2. Add it to `odoopilot/services/tools.py` following the existing pattern\n3. Register the tool schema in `odoopilot/services/agent.py`\n4. Open a PR — CI must be green (ruff format + lint + XML check)\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for full details.\n\n---\n\n## Sponsor \u0026 feedback\n\nOdooPilot is free, open-source, and solo-maintained. After install, **Settings → OdooPilot** ends with quick links for all of these — or use the URLs directly:\n\n- **♥ Sponsor on GitHub** → https://github.com/sponsors/arunrajiah\n- **💬 Feedback \u0026 ideas** → https://github.com/arunrajiah/odoopilot/discussions/new?category=ideas\n- **🛠 Report a bug** → https://github.com/arunrajiah/odoopilot/issues/new/choose\n- **🔒 Report a security issue (private)** → https://github.com/arunrajiah/odoopilot/security/advisories/new\n\n---\n\n## License\n\n[LGPL-3.0-or-later](LICENSE) — same as Odoo Community and OCA modules.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farunrajiah%2Fodoopilot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farunrajiah%2Fodoopilot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farunrajiah%2Fodoopilot/lists"}