Feature #104
closedFrappe Telegram AI Chatbot - Prototype
Description
AI Telegram Chatbot for Frappe / ERPNext¶
Build a secure, read-only AI assistant that allows users to ask natural-language questions about their Frappe / ERPNext data directly from Telegram while preserving the user's existing Frappe roles, DocType permissions, User Permissions and field-level restrictions.
1. Executive Summary¶
This story covers the implementation of the Frappe Telegram AI chatbot.
The application provides a Telegram interface over a Frappe / ERPNext system. Each Telegram user authenticates with their own Frappe account. The chatbot stores only the resulting Frappe session identifier in encrypted form and performs every Frappe lookup using that user's own session.
The AI does not receive the user's Frappe password or session ID. It receives only the Frappe data returned by tools that execute under the authenticated user's permissions.
The core workflow is:
Telegram User
|
| Frappe username + password
v
Telegram Bot
|
| POST /api/method/login
v
Frappe / ERPNext
|
| authenticated session (sid)
v
Encrypted Session Store
|
v
Natural-language Question
|
v
AI Agent
|
| tool/function calls
v
Read-only Frappe Tools
|
| user's own Frappe sid
v
Frappe REST API
|
v
AI-generated Answer
|
v
Telegram
The chatbot is intentionally read-only. It can search, count, aggregate and open Frappe records, but it cannot create, modify, submit or delete records.
2. Jira Story Definition¶
User Story¶
As a Frappe / ERPNext user,
I want to authenticate with my Frappe account and ask natural-language questions about the Frappe data that I am permitted to access through Telegram,
so that I can quickly retrieve business information without opening the Frappe web interface and manually searching records.
Story Type¶
Feature
Primary Deliverable¶
A production-ready Telegram chatbot that:
- authenticates users against Frappe;
- preserves Frappe's existing access control;
- translates natural-language questions into validated Frappe read operations;
- uses an OpenAI-compatible AI model with tool/function calling;
- returns concise answers in Telegram;
- maintains limited conversation context for follow-up questions;
- protects Frappe sessions and passwords;
- prevents write operations.
3. Business Objective¶
The objective is to provide a conversational interface over existing Frappe / ERPNext data without creating a second permission system.
Instead of:
Open Frappe
|
v
Find DocType
|
v
Build filters
|
v
Search records
|
v
Calculate totals
|
v
Interpret result
the user can ask:
"Show my last 5 sales orders"
"How many open leads do we have?"
"Total unpaid sales invoices this month by customer"
"Show the details of SO-00045"
The AI determines which read-only Frappe tools are required, executes them through the user's Frappe session, and converts the returned data into a concise Telegram response.
4. Scope¶
4.1 In Scope¶
- Telegram bot using aiogram.
- Private-chat authentication flow.
- Frappe username/password authentication.
- Frappe session management.
- Encrypted persistent session storage.
- Automatic session revalidation.
- Frappe role and readable DocType discovery.
- Natural-language question handling.
- OpenAI-compatible AI provider integration.
- AI tool/function calling.
- Dynamic Frappe DocType discovery.
- DocType metadata inspection.
- Field validation.
- Read-only record searching.
- Record counting.
- Aggregation.
- Full-record retrieval.
- Filters and sorting.
- Grouped aggregate queries.
- Conversation memory.
- Telegram message splitting.
- Rate limiting.
- Failed-login throttling.
- Password message deletion.
- Private-chat restriction.
- Logout/session cleanup.
- Configuration validation.
- Service health checks.
- Troubleshooting and operational diagnostics.
4.2 Out of Scope¶
The chatbot does not:
- create Frappe records;
- update Frappe records;
- delete records;
- submit documents;
- cancel documents;
- execute raw SQL;
- bypass Frappe permissions;
- maintain a separate application-level role/permission system;
- send Frappe passwords to the AI provider;
- send Frappe session IDs to the AI provider;
- provide Telegram group-based business-data access;
- support Frappe two-factor-authenticated accounts through this login flow.
5. Functional Requirements¶
| # | Requirement | Description |
|---|---|---|
| 1 | Telegram interface | Users interact with the application through Telegram |
| 2 | Frappe authentication | Authenticate each user using their own Frappe account |
| 3 | Session persistence | Persist the Frappe session securely for subsequent conversations |
| 4 | Permission inheritance | Use Frappe as the authority for all data access |
| 5 | AI question understanding | Interpret natural-language business questions |
| 6 | Tool selection | Allow the AI to select appropriate read-only Frappe tools |
| 7 | Dynamic DocType discovery | Find readable DocTypes when the AI is unsure of the exact name |
| 8 | Metadata discovery | Inspect actual DocType fields before constructing queries |
| 9 | Record search | Search/list records with filters, sorting and limits |
| 10 | Counting | Count records matching user criteria |
| 11 | Aggregation | Calculate sum, average, minimum, maximum or count |
| 12 | Record detail | Retrieve a complete record by DocType and name |
| 13 | Query validation | Reject invalid fields, filters, operators and aggregate arguments |
| 14 | Conversation context | Maintain limited user-specific conversation memory |
| 15 | Session validation | Re-check stored sessions periodically |
| 16 | Security controls | Encrypt stored sessions and throttle authentication/requests |
| 17 | Telegram formatting | Split long responses into Telegram-safe message sizes |
| 18 | Operational checks | Validate configuration and external service connectivity |
| 19 | Read-only enforcement | Prevent AI from performing write operations |
| 20 | Logout | Close the Frappe session and remove local session data |
6. High-Level Architecture¶
flowchart TB
U["Telegram User"]
subgraph TG["Telegram"]
BOT["Telegram Bot"]
end
subgraph APP["Frappe Telegram AI Application"]
H["Bot Handlers"]
AUTH["Authentication Service"]
AGENT["AI Agent"]
TOOLS["Read-only Frappe Tools"]
SCHEMA["DocType Metadata + Query Validation"]
AI["AI Provider Client"]
STORE["Encrypted SQLite Session Store"]
SEC["Security Controls"]
CONFIG["Configuration / Runtime"]
end
subgraph FR["Frappe / ERPNext"]
LOGIN["Frappe Login API"]
META["DocType Metadata"]
DATA["Frappe Data APIs"]
PERM["Roles / User Permissions / Field Permissions"]
end
U --> BOT
BOT --> H
H --> AUTH
H --> AGENT
AUTH --> LOGIN
AUTH --> STORE
AUTH --> SEC
AGENT --> AI
AGENT --> TOOLS
TOOLS --> SCHEMA
TOOLS --> DATA
DATA --> PERM
META --> SCHEMA
AI --> AGENT
AGENT --> H
H --> BOT
BOT --> U
CONFIG --> H
CONFIG --> AUTH
CONFIG --> AGENT
CONFIG --> SEC
7. Authentication and Security Architecture¶
The security model deliberately avoids duplicating Frappe's permission system.
sequenceDiagram
participant U as Telegram User
participant B as Bot
participant A as AuthService
participant F as Frappe
participant DB as SQLite Session Store
U->>B: /login
B->>U: Frappe username/email
U->>B: Username
B->>U: Frappe password
U->>B: Password
B->>B: Delete password message
B->>A: username + password
A->>F: POST /api/method/login
F-->>A: sid + full name
A->>F: get_logged_user(sid)
F-->>A: authenticated Frappe user
A->>F: session_boot(sid)
F-->>A: roles + readable DocTypes
A->>DB: Store encrypted sid
A-->>B: Principal
B-->>U: Logged in + roles
Security Rules¶
- Passwords are never stored.
- Password messages are deleted after reading when configured.
- Frappe session IDs are encrypted before persistent storage.
- The encryption key is supplied through configuration.
- Each Telegram user is mapped to their own Frappe session.
- Frappe calls are made using the requesting user's session.
- The HTTP client intentionally does not share a cookie jar between users.
- AI requests do not contain the password or Frappe session ID.
- Login attempts are throttled after repeated failures.
- Questions are rate-limited per Telegram user.
- Stored Frappe sessions are periodically revalidated.
-
/logoutcloses the Frappe session and removes stored session data.
8. Permission Model¶
Principle¶
Frappe is the only authority for data access.
The chatbot does not define independent application roles such as:
Telegram Admin
Telegram Sales User
Telegram HR User
Instead:
Telegram User
|
v
Frappe Login
|
v
Frappe User
|
+--> Roles
+--> DocType permissions
+--> User Permissions
+--> Owner rules
+--> Field permission levels
+--> Custom permission logic
|
v
Allowed Frappe Data
A Sales User therefore receives the data Frappe allows that user to access, while an HR user receives the data allowed to that HR user.
Profile Information¶
At login/revalidation, the application reads:
- authenticated Frappe username;
- full name;
- Frappe roles;
- readable DocTypes where available.
These values are used to guide the AI and provide early rejection when Frappe has already reported that a DocType is not readable.
The actual Frappe API call is still performed with the user's own session, so Frappe remains the enforcement layer.
9. AI Agent Workflow¶
flowchart TD
Q["User Question"] --> S["Build AI Context"]
S --> P["System Prompt"]
P --> AI["AI Provider"]
AI --> D{"Tool Calls Required?"}
D -->|No| A["Final Answer"]
D -->|Yes| T["Read-only Frappe Tool"]
T --> V["Validate Arguments"]
V --> M["Load / Check DocType Metadata"]
M --> F["Frappe API using User sid"]
F --> R["Tool Result"]
R --> AI
AI --> D
A --> O["Telegram Response"]
The AI agent runs a bounded tool loop.
For each question:
- Build the system prompt.
- Include limited conversation history.
- Send the question and tool definitions to the AI provider.
- If the AI requests a tool, validate and execute it.
- Return the tool result to the AI.
- Continue until the AI produces an answer or the tool-round limit is reached.
- Return the final answer to Telegram.
- Save the question and answer in bounded conversation memory.
10. AI System Rules¶
The system prompt enforces the following behaviour:
- Retrieve factual information through tools.
- Never invent records, numbers, names or statuses.
- Respect the user's Frappe permissions.
- Do not work around permission errors.
- Discover the DocType when the exact name is uncertain.
- Inspect DocType fields before querying uncertain fields.
- Prefer count/aggregate operations for count and total questions.
- Treat submitted documents appropriately when business totals normally require submitted records.
- Remain read-only.
- Ask one short clarification when a question is ambiguous.
- Answer in the same language as the user's question.
- Keep Telegram responses concise.
- Include document IDs so users can find the records in Frappe.
11. AI Provider Integration¶
The application uses an OpenAI-compatible chat-completions interface with tool/function calling.
Supported provider configurations in the project:
| Provider | Supported |
|---|---|
| OpenRouter | Yes |
| OpenAI | Yes |
| Groq | Yes |
| Google Gemini through OpenAI-compatible endpoint | Yes |
| Ollama | Yes |
| Custom OpenAI-compatible server | Yes |
The provider configuration contains:
ai:
provider: "openrouter"
api_key: ""
model: "openai/gpt-4o-mini"
base_url: ""
temperature: 0.1
max_tokens: 1200
timeout_seconds: 90
max_tool_rounds: 6
For Ollama, an API key is not required.
The selected model must support tool/function calling because the AI needs to request Frappe data through the defined tools.
12. Read-Only Frappe Toolset¶
The chatbot exposes five core AI tools.
12.1 list_doctypes
¶
Purpose:
Find readable Frappe DocTypes when the AI is unsure of the exact DocType name.
Example:
Question:
"Show me open customer complaints"
Possible flow:
list_doctypes("complaint")
|
v
Identify matching DocType
The tool uses the readable DocType information reported by Frappe.
12.2 describe_doctype
¶
Purpose:
Inspect the actual structure of a DocType before querying it.
Returns information such as:
- DocType name;
- title field;
- submittable status;
- single/non-single status;
- default fields;
- field names;
- labels;
- field types;
- Link/Select options;
- child tables.
This prevents the AI from blindly inventing field names.
12.3 search_records
¶
Purpose:
Search/list records with:
- fields;
- filters;
- sorting;
- result limit.
Example:
search_records(
doctype="Sales Order",
fields=["name", "customer", "grand_total"],
filters=[["status", "=", "To Deliver and Bill"]],
order_by="modified desc",
limit=5
)
The actual query is sent to Frappe using the user's session.
12.4 count_records
¶
Purpose:
Return the number of records matching filters.
Useful for questions such as:
How many open leads do we have?
The AI can use:
count_records("Lead", ...)
instead of retrieving every lead.
12.5 aggregate_records
¶
Purpose:
Perform:
-
sum; -
avg; -
min; -
max; -
count.
It can optionally group results by another field.
Example:
Total purchase this year by supplier
can be translated into an aggregate operation grouped by supplier.
Only valid numeric fields can be used for numeric aggregation.
12.6 get_record
¶
Purpose:
Retrieve one complete Frappe record by DocType and name.
Example:
Show the details of SO-00045
becomes:
get_record(
doctype="Sales Order",
name="SO-00045"
)
Child tables are included where available, while internal/noise fields are cleaned before being passed back to the AI.
13. Query Validation Layer¶
The AI is not allowed to send arbitrary field or filter definitions directly to Frappe.
The validation layer checks the request against the actual DocType metadata.
flowchart LR
A["AI Tool Arguments"] --> B["JSON Validation"]
B --> C["DocType Validation"]
C --> D["Field Validation"]
D --> E["Filter Validation"]
E --> F["Operator Validation"]
F --> G["Sort Validation"]
G --> H["Limit Validation"]
H --> I["Frappe API"]
Field Validation¶
The application verifies that requested fields actually exist.
Unknown fields produce a query error rather than being sent to Frappe.
Filter Validation¶
Supported operators include:
=
!=
>
<
>=
<=
like
not like
in
not in
between
is
timespan
Time-span filters include values such as:
today
yesterday
tomorrow
this week
last week
next week
this month
last month
next month
this quarter
last quarter
next quarter
this year
last year
next year
last 6 months
next 6 months
Sort Validation¶
Sorting must use a valid field and:
<fieldname> asc
or:
<fieldname> desc
Aggregate Validation¶
Supported aggregate functions:
sum
avg
min
max
count
Numeric aggregation is limited to supported numeric Frappe field types such as:
Currency
Float
Int
Percent
Duration
Protected Field Types¶
The metadata layer excludes fields that should not be queried as ordinary values, including:
Section Break
Column Break
Tab Break
HTML
Button
Fold
Heading
Table
Table MultiSelect
Password
Image
Password fields are therefore not exposed to the AI as queryable fields.
14. Query Result Protection¶
The application limits the amount of data passed into the AI context.
Configuration includes:
limits:
default_records: 20
max_records: 100
max_tool_result_chars: 12000
conversation_memory: 10
requests_per_minute: 20
Record Limit¶
A query has:
- a default record limit;
- a hard maximum record limit.
This prevents a natural-language request from unintentionally loading an excessive number of records.
Tool Result Size¶
If the serialized tool response exceeds the configured character limit:
- record lists are reduced;
- grouped results may be reduced;
- large child tables are truncated;
- the result is marked as truncated.
This keeps the AI context bounded.
15. Telegram User Experience¶
Private Chat Only¶
The chatbot intentionally operates in private Telegram chats.
The application does not process normal business questions from group chats.
Private Chat
|
+--> Login
+--> Questions
+--> Commands
Group usage receives the private-chat instruction instead.
16. Login Workflow¶
stateDiagram-v2
[*] --> LoggedOut
LoggedOut --> Username: /start or /login
Username --> Password: username entered
Password --> Authenticating: password entered
Authenticating --> LoggedIn: Frappe accepts credentials
Authenticating --> Username: Invalid login
Authenticating --> LoggedOut: 2FA unsupported / unavailable
LoggedIn --> LoggedOut: /logout
LoggedIn --> Username: Session expires
Username --> LoggedOut: /cancel
Password --> LoggedOut: /cancel
Login Sequence¶
- User sends
/startor/login. - Bot asks for Frappe username/email.
- User enters username.
- Bot asks for password.
- User enters password.
- Password message is deleted when enabled.
- Frappe validates credentials.
- Frappe returns a session ID.
- The bot resolves the authenticated Frappe user.
- User roles/readable DocTypes are loaded.
- Session ID is encrypted and stored.
- User is informed that login succeeded.
17. Session Management¶
Persistent sessions are stored in:
data/sessions.db
The database contains a mapping between:
Telegram User ID
|
v
Frappe User
|
v
Encrypted Frappe sid
The password is never stored.
The session store records:
- Telegram user ID;
- Telegram username;
- Frappe user;
- full name;
- encrypted session ID;
- creation timestamp;
- last checked timestamp.
Session Revalidation¶
The configured default is:
security:
session_check_minutes: 5
The application re-checks the session with Frappe periodically.
If the session has ended because:
- the Frappe session expired;
- the user logged out;
- the user was disabled;
- the session became invalid;
the local session is removed and the user is asked to log in again.
18. Logout Workflow¶
When the user sends:
/logout
the application:
- Clears conversation memory.
- Removes the local session.
- Attempts to close the Frappe session.
- Confirms that the user is logged out.
No Frappe password remains in storage.
19. Conversation Memory¶
The agent maintains limited per-user conversation memory.
Configured default:
conversation_memory: 10
Memory is maintained per Telegram user and is reset by:
/new
It is also cleared when:
/logout
or when the application restarts.
This allows follow-up questions while preventing unlimited conversation history from growing the AI context.
Example:
User:
Show my last 5 sales orders
Bot:
SO-001...
SO-002...
User:
Which customer is the largest one?
Bot:
...
The second question can use the bounded prior conversation context.
20. Telegram Response Handling¶
Telegram allows messages up to approximately 4096 characters.
The implementation uses a safer internal limit of:
4000 characters
Long AI answers are split at:
- line breaks where possible;
- spaces where necessary;
- hard boundaries only as a final fallback.
This avoids failed Telegram sends caused by oversized messages.
The system prompt also instructs the AI to keep responses short and Telegram-friendly.
21. Commands¶
The chatbot exposes:
| Command | Purpose |
|---|---|
/start |
Start the bot / begin login |
/login |
Log in or switch Frappe account |
/logout |
Close the Frappe session and log out |
/me |
Show Frappe user, name, roles and readable DocType information |
/new |
Start a new conversation and clear previous question/answer memory |
/cancel |
Cancel the current login flow |
/help |
Show usage instructions |
22. Error Handling¶
The application defines explicit error categories.
| Error | Behaviour |
|---|---|
| Invalid credentials | User is asked to retry |
| Too many failed logins | Temporary login lock |
| 2FA account | Bot reports that this login flow does not support it |
| Frappe unavailable | User receives a service-unavailable message |
| Session expired | Local session is cleared and login is requested |
| Permission denied | User is informed that their Frappe role does not allow the requested access |
| DocType not found | AI receives a controlled error and can correct its approach |
| Invalid query | AI receives validation feedback |
| AI timeout | User receives a concise AI-service error |
| AI API key rejected | Configuration/API-key error is reported |
| AI rate limit | User is asked to retry later |
| Telegram conflict | Another polling instance must be stopped |
| Non-text message | Bot asks the user to send a text question |
23. Login Security Controls¶
Failed Login Throttling¶
The application tracks failed Frappe logins per Telegram user.
Default:
max_login_attempts: 5
lockout_minutes: 15
Once the configured threshold is reached, further login attempts are temporarily blocked.
This is an additional application-level protection; Frappe can also enforce its own account-security policies.
24. Request Rate Limiting¶
Questions are limited per Telegram user.
Default:
requests_per_minute: 20
The limiter uses a sliding one-minute window.
If the user exceeds the limit:
You are sending questions too quickly.
Please wait a moment.
No AI/Frappe query is performed for the rejected request.
25. Frappe REST Integration¶
The Frappe client supports the following operations.
| Frappe Operation | Purpose |
|---|---|
/api/method/login |
Authenticate user |
/api/method/logout |
End user session |
/api/method/frappe.auth.get_logged_user |
Verify session owner |
/api/method/frappe.sessions.get |
Retrieve roles/readable DocTypes |
/api/method/frappe.desk.form.load.getdoctype |
Retrieve DocType metadata |
/api/method/frappe.client.get_list |
Search/list records |
/api/method/frappe.client.get_count |
Count records |
/api/resource/{doctype}/{name} |
Retrieve a specific record |
/api/method/ping |
Service health check |
The Frappe client uses explicit session headers:
Cookie: sid=<user session>
The HTTP client does not retain a shared cookie jar.
This prevents sessions from different Telegram users from being accidentally mixed.
26. Frappe Error Mapping¶
The application maps Frappe responses into controlled application errors.
Examples:
401 / SessionExpired
-> SessionInvalid
403 / PermissionError
-> PermissionDenied
404 / DoesNotExistError
-> NotFound
400 / 409 / 417 / 422
-> InvalidRequest
5xx
-> FrappeUnavailable
This allows the Telegram layer and AI agent to distinguish:
- authentication problems;
- permission problems;
- invalid queries;
- missing records;
- server/network problems.
27. Runtime Architecture¶
flowchart TB
MAIN["main.py"]
CHECK["python main.py check"]
RUN["python main.py"]
KEY["python main.py genkey"]
MAIN --> CHECK
MAIN --> RUN
MAIN --> KEY
RUN --> SETTINGS["Settings"]
SETTINGS --> APP["Application.build()"]
APP --> FR["FrappeClient"]
APP --> AI["AIProvider"]
APP --> STORE["SessionStore"]
APP --> AUTH["AuthService"]
APP --> TOOLS["FrappeTools"]
APP --> AGENT["Agent"]
APP --> HANDLERS["BotHandlers"]
HANDLERS --> AGENT
HANDLERS --> AUTH
AGENT --> TOOLS
AGENT --> AI
AUTH --> FR
TOOLS --> FR
STORE --> DB["SQLite"]
The runtime builds the application dependencies and starts the aiogram polling dispatcher.
28. Configuration¶
The project uses:
config/config.yaml
A clean template is supplied as:
config/config.example.yaml
Main configuration areas:
app:
telegram:
frappe:
ai:
security:
limits:
Application¶
app:
name: "Frappe AI Assistant"
log_level: "INFO"
data_dir: "data"
Telegram¶
telegram:
bot_token: ""
The token is created through Telegram's BotFather.
Frappe¶
frappe:
url: ""
verify_ssl: true
timeout_seconds: 30
Default API paths target Frappe v14/v15.
AI¶
ai:
provider: "openrouter"
api_key: ""
model: "openai/gpt-4o-mini"
base_url: ""
temperature: 0.1
max_tokens: 1200
timeout_seconds: 90
max_tool_rounds: 6
Security¶
security:
encryption_key: ""
session_check_minutes: 5
max_login_attempts: 5
lockout_minutes: 15
delete_password_messages: true
Limits¶
limits:
default_records: 20
max_records: 100
max_tool_result_chars: 12000
conversation_memory: 10
requests_per_minute: 20
29. Configuration Validation¶
The project provides:
python main.py check
The check validates required settings and then tests:
Configuration
|
+--> Frappe
|
+--> Telegram token
|
+--> AI service
Successful validation results in:
Ready to start: python main.py
If a configuration item is invalid, the check identifies the relevant setting.
30. Encryption Key Generation¶
A Fernet-compatible key is required for encrypted session storage.
Generate it with:
python main.py genkey
The generated value is placed in:
security:
encryption_key: "..."
Changing this key invalidates the application's ability to decrypt existing stored sessions, causing users to log in again.
31. Installation¶
The project requires:
Python 3.10+
Create a virtual environment:
python -m venv .venv
Windows:
.\.venv\Scripts\Activate.ps1
Linux/macOS:
source .venv/bin/activate
Install dependencies:
pip install -r requirements.txt
Dependencies include:
aiogram
httpx
pydantic
PyYAML
cryptography
32. Startup Flow¶
flowchart TD
A["Clone / copy project"] --> B["Create virtual environment"]
B --> C["Activate .venv"]
C --> D["pip install -r requirements.txt"]
D --> E["Create config/config.yaml"]
E --> F["Generate encryption key"]
F --> G["python main.py check"]
G --> H{"All checks pass?"}
H -->|No| I["Fix configuration / connectivity"]
I --> G
H -->|Yes| J["python main.py"]
J --> K["Telegram long polling"]
The bot uses Telegram long polling, so the supplied implementation does not require a public webhook URL.
33. Production Deployment¶
The project recommends running the bot under a process manager such as:
systemd
Supervisor
Docker
Production requirements include:
- protect
config/config.yaml; - keep API keys outside source control;
- restrict file permissions on configuration;
- protect the encryption key;
- use HTTPS for the Frappe site where applicable;
- ensure only one polling instance uses the Telegram bot token;
- monitor application logs;
- ensure the bot process can reach Frappe and the selected AI provider.
The application is designed to run as a long-lived bot process.
34. Project Structure¶
frappe_telegram_ai/
│
├── main.py
│
├── config/
│ ├── config.yaml
│ └── config.example.yaml
│
├── app/
│ ├── core/
│ │ ├── config.py
│ │ └── logging.py
│ │
│ ├── frappe/
│ │ ├── client.py
│ │ └── errors.py
│ │
│ ├── auth/
│ │ ├── principal.py
│ │ └── service.py
│ │
│ ├── storage/
│ │ └── sessions.py
│ │
│ ├── security/
│ │ ├── crypto.py
│ │ └── limits.py
│ │
│ ├── ai/
│ │ └── provider.py
│ │
│ ├── agent/
│ │ ├── agent.py
│ │ ├── schema.py
│ │ └── tools.py
│ │
│ ├── bot/
│ │ ├── handlers.py
│ │ └── texts.py
│ │
│ └── runtime.py
│
└── docs/
├── ACCESS_CONTROL.md
└── TROUBLESHOOTING.md
35. Module Responsibilities¶
| Module | Responsibility |
|---|---|
main.py |
Start, configuration check and encryption-key generation |
core/config.py |
YAML configuration loading and validation |
core/logging.py |
Application logging |
frappe/client.py |
Frappe REST communication |
frappe/errors.py |
Frappe-specific error types |
auth/service.py |
Login, logout, session restoration and revalidation |
auth/principal.py |
Authenticated Frappe user context |
storage/sessions.py |
Persistent encrypted session mapping |
security/crypto.py |
Fernet encryption/decryption |
security/limits.py |
Login throttling and request rate limiting |
ai/provider.py |
OpenAI-compatible AI client and tool-call parsing |
agent/agent.py |
AI system prompt, tool loop and conversation memory |
agent/schema.py |
DocType metadata and query validation |
agent/tools.py |
Read-only Frappe tool implementation |
bot/handlers.py |
Telegram commands, login flow and question handling |
bot/texts.py |
User-facing Telegram messages |
runtime.py |
Dependency wiring and bot startup |
36. Detailed Data Flow¶
Question → Answer¶
┌───────────────────────┐
│ Telegram User │
│ "How many open leads?"│
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ Bot Handler │
│ - session check │
│ - rate limit │
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ Agent │
│ - system prompt │
│ - conversation memory │
│ - user context │
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ AI Provider │
│ decides tool call │
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ count_records │
│ doctype = Lead │
│ filters = ... │
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ Query Validation │
│ valid DocType/fields │
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ Frappe REST API │
│ user's sid │
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ Count Result │
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ AI Final Answer │
└───────────┬───────────┘
│
v
┌───────────────────────┐
│ Telegram │
│ concise response │
└───────────────────────┘
37. Example Query Workflows¶
Example A — Recent Sales Orders¶
User:
Show my last 5 sales orders
Expected agent reasoning path:
Question
|
v
Sales Order DocType
|
v
search_records
|
+--> order by modified desc
+--> limit 5
|
v
Frappe
|
v
Records
|
v
AI concise answer
Example B — Count Open Leads¶
User:
How many open leads do we have?
Preferred operation:
count_records
rather than loading every Lead record.
Example C — Total Unpaid Sales Invoices¶
User:
Total unpaid sales invoices this month
The agent can:
- identify the Sales Invoice DocType;
- inspect its fields if required;
- construct validated filters;
- aggregate the appropriate numeric field;
- return the total.
Example D — Grouped Total¶
User:
Total purchase this year by supplier
The agent can use:
aggregate_records
function = sum
field = appropriate numeric field
group_by = supplier
filters = current-year criteria
The query is validated against actual DocType metadata before reaching Frappe.
Example E — Record Detail¶
User:
Show the details of SO-00045
The agent can use:
get_record
doctype = Sales Order
name = SO-00045
The resulting document and relevant child-table information are cleaned before being returned to the AI.
38. Read-Only Enforcement¶
The system has multiple layers supporting read-only behaviour.
Layer 1 — AI Instruction¶
The system prompt explicitly states that the assistant can only read data.
Layer 2 — Tool Surface¶
Only read-oriented tools are exposed:
list_doctypes
describe_doctype
search_records
count_records
aggregate_records
get_record
There are no create/update/delete tools.
Layer 3 — Frappe Client¶
The data methods implemented by the client are read-only.
Result¶
A request such as:
Create a new customer
should receive a response explaining that the assistant is read-only rather than executing a write operation.
39. Data Privacy Boundary¶
flowchart LR
P["Frappe Password"] --> F["Frappe Login"]
F --> S["Frappe sid"]
S --> E["Encrypted Local Storage"]
S -. never sent .-> AI["AI Provider"]
P -. never sent .-> AI
FDATA["Frappe Data Allowed to User"] --> AI
AI --> ANS["Answer"]
The AI provider receives:
- the conversation context;
- the system instructions;
- tool results returned by the user's permitted Frappe queries.
The AI provider does not receive:
- the Frappe password;
- the Frappe session ID.
40. Access-Control Decision Flow¶
flowchart TD
A["User asks about DocType"] --> B{"Logged in?"}
B -->|No| C["Request Frappe login"]
B -->|Yes| D{"DocType known?"}
D -->|No| E["list_doctypes"]
E --> F["Identify valid DocType"]
D -->|Yes| F
F --> G{"Frappe says user can read it?"}
G -->|No| H["Return permission denied"]
G -->|Yes| I["Load DocType metadata"]
I --> J["Validate fields / filters"]
J --> K{"Valid query?"}
K -->|No| L["Return controlled query error to AI"]
K -->|Yes| M["Call Frappe with user's sid"]
M --> N["Permission enforcement by Frappe"]
N --> O["Tool result"]
O --> P["AI answer"]
41. Edge Cases¶
Invalid Login¶
The user is asked to retry and the failure contributes to the login throttle.
Too Many Login Attempts¶
The Telegram user is temporarily locked from attempting another login.
Two-Factor Authentication¶
The current implementation reports that the Frappe account uses two-factor authentication and cannot use this login flow.
Expired Session¶
The stored session is removed and the user is asked to authenticate again.
Changed Frappe Roles¶
Roles and readable DocTypes are refreshed during periodic session checks.
Permission Denied¶
The agent does not attempt to bypass the permission. It reports the restriction.
Unknown DocType¶
The AI can use list_doctypes.
Unknown Field¶
The AI can use describe_doctype.
Invalid Field¶
The query validator rejects it before the request is sent.
Large Result¶
The result is reduced to the configured context size and marked as truncated where applicable.
Long Telegram Answer¶
The answer is split into Telegram-sized messages.
Too Many Questions¶
The per-user rate limiter rejects the request temporarily.
Frappe Unavailable¶
The bot returns a controlled Frappe connectivity message.
AI Unavailable¶
The bot returns a controlled AI-service message.
Bot Running Twice¶
Telegram polling can conflict when two instances use the same bot token. Only one polling instance should run.
42. Logging¶
The project supports:
DEBUG
INFO
WARNING
ERROR
The logging system is designed not to write:
- Frappe passwords;
- Frappe session IDs.
Operational failures are logged with controlled information useful for diagnosis.
For troubleshooting:
app:
log_level: "DEBUG"
can be used when more detail is required.
43. Health / Readiness Check¶
The command:
python main.py check
performs three external checks after configuration validation:
1. Frappe
|
+--> /api/method/ping
2. Telegram
|
+--> bot.get_me()
3. AI
|
+--> chat completion test
This provides a single pre-start verification path.
44. Acceptance Criteria¶
| # | Acceptance Criteria |
|---|---|
| AC-01 | A user can start the bot in a private Telegram chat |
| AC-02 | The bot requests Frappe username/email |
| AC-03 | The bot requests Frappe password |
| AC-04 | The password message is deleted when configured |
| AC-05 | Invalid Frappe credentials do not create a stored session |
| AC-06 | Successful login creates an encrypted session mapping |
| AC-07 | The user can use natural-language questions after login |
| AC-08 | The AI can select read-only Frappe tools |
| AC-09 | Tool arguments are validated against actual DocType metadata |
| AC-10 | Unknown fields are rejected before querying Frappe |
| AC-11 | The user's own Frappe session is used for every data lookup |
| AC-12 | Frappe permissions determine the accessible data |
| AC-13 | The AI cannot create, update, submit or delete records |
| AC-14 | Count questions can use count_records
|
| AC-15 | Total/grouping questions can use aggregate_records
|
| AC-16 | Individual documents can be retrieved using get_record
|
| AC-17 | Session validity is periodically rechecked |
| AC-18 | Expired sessions require the user to log in again |
| AC-19 |
/logout closes the Frappe session and clears local session state |
| AC-20 |
/new clears conversation memory |
| AC-21 | Excessive requests are rate limited |
| AC-22 | Repeated failed logins are throttled |
| AC-23 | Long responses are split safely for Telegram |
| AC-24 |
python main.py check validates configuration and services |
| AC-25 | The application can start using python main.py
|
| AC-26 | Group-chat business questions are rejected in favour of private chat |
| AC-27 | AI provider failures are returned as controlled user-facing errors |
| AC-28 | Frappe connectivity failures are returned as controlled user-facing errors |
45. Testing Checklist¶
Installation¶
- Python 3.10+ available.
- Virtual environment created.
- Dependencies installed.
-
config/config.yamlcreated. - Encryption key generated.
Configuration¶
- Telegram bot token configured.
- Frappe URL configured.
- AI provider configured.
- AI model supports tool calling.
- AI API key configured where required.
- Encryption key configured.
- Limits reviewed.
Connectivity¶
-
python main.py checksucceeds. - Frappe ping succeeds.
- Telegram token is valid.
- AI service returns a test response.
Authentication¶
-
/startworks. - Username prompt works.
- Password prompt works.
- Password message deletion works.
- Valid login works.
- Invalid login is rejected.
- Login throttling works.
-
/logoutworks. -
/meshows the correct user and roles. - Session expiry causes re-login.
AI Queries¶
- Simple list query.
- Filtered query.
- Count query.
- Aggregate query.
- Grouped aggregate.
- Record detail query.
- Follow-up question using memory.
- Unknown DocType handling.
- Unknown field handling.
- Invalid filter handling.
- Permission-denied handling.
- Read-only write request rejection.
Telegram¶
- Long answer splitting.
- Text-only behaviour.
- Private-chat restriction.
- Rate limiting.
-
/help. -
/new. -
/cancel.
46. Troubleshooting Matrix¶
| Problem | Resolution |
|---|---|
config/config.yaml needs attention |
Fill the settings identified by python main.py check
|
| Cannot reach Frappe | Verify frappe.url and network access |
| Self-signed local Frappe certificate | Use frappe.verify_ssl: false only where appropriate |
| Invalid Frappe login | Verify username/password and Frappe account status |
| Too many failed logins | Wait for configured lockout period |
| 2FA message | Use an account supported by the current login flow |
| Bot repeatedly requests login | Check Frappe session expiry, account status and encryption key |
| Permission denied | Correct the Frappe user's role/permission in Frappe |
| AI rejects API key | Check ai.api_key and provider |
| AI answers without querying | Use a model/provider supporting tool/function calling |
| Telegram conflict | Stop the other bot instance using the same token |
| Bot does not answer in group | Use a private Telegram chat |
47. Security Responsibilities¶
Frappe¶
Responsible for:
- authentication;
- role permissions;
- DocType permissions;
- User Permissions;
- ownership rules;
- field-level permissions;
- custom permission logic;
- session validity.
Application¶
Responsible for:
- encrypted session storage;
- login throttling;
- request rate limiting;
- password-message deletion;
- tool argument validation;
- result-size limits;
- conversation-memory limits;
- read-only tool exposure;
- private-chat restriction.
AI Provider¶
Responsible only for:
- understanding the natural-language request;
- selecting tools;
- interpreting returned data;
- generating the final natural-language answer.
The AI provider is not an authorization layer.
48. Important Design Decisions¶
48.1 Frappe Remains the Source of Truth¶
No duplicate permission configuration is maintained in YAML.
Frappe Roles
|
v
Frappe Permissions
|
v
Frappe API
|
v
Telegram AI
48.2 Per-User Sessions¶
Each Telegram user receives their own Frappe session.
The client does not maintain a shared cookie jar.
48.3 Read-Only by Design¶
The chatbot intentionally exposes only read-oriented operations.
This reduces the impact of incorrect AI interpretation and keeps the first version focused on information retrieval.
48.4 Metadata Before Query¶
When field information is uncertain:
DocType
|
v
describe_doctype
|
v
validate field
|
v
query
This is preferable to allowing the AI to invent Frappe field names.
48.5 Bounded AI Tool Loop¶
The number of tool-call rounds is configurable.
Default:
6 rounds
If the budget is exhausted, the agent requests a final response using the information already gathered.
48.6 Bounded Context¶
Both tool-result size and conversation memory are limited.
This controls:
- AI context size;
- response latency;
- unnecessary data transfer;
- token consumption.
49. Current Implementation Status¶
Implementation status: Implemented in the supplied frappe_telegram_ai project.
The supplied project contains the complete application structure for:
- Telegram bot handling;
- Frappe authentication;
- encrypted sessions;
- role-aware access;
- AI tool calling;
- read-only Frappe querying;
- DocType metadata validation;
- conversation memory;
- security limits;
- configuration validation;
- startup and health checks;
- troubleshooting documentation.
This story describes the implementation present in the supplied project. It does not claim that every acceptance-test checkbox has been freshly executed unless a separate test report records that execution.
50. Known Limitations¶
- The current bot supports a read-only Frappe workflow.
- Frappe two-factor-authenticated accounts are not supported by the current Telegram login flow.
- Business-data interaction is intentionally restricted to private Telegram chats.
- The selected AI model must support tool/function calling.
- AI-provider availability and rate limits remain external dependencies.
- The bot uses Telegram long polling, so only one active polling instance should use the same bot token.
- Changing
security.encryption_keymakes existing encrypted sessions unreadable and requires users to log in again. - Conversation memory is intentionally bounded and is cleared by
/new,/logout, or application restart. - Large tool results are intentionally truncated to stay within configured AI context limits.
- The bot does not provide write operations such as creating or modifying Frappe documents.
51. Future Extension Possibilities¶
The current architecture provides a controlled foundation for additional capabilities, subject to explicit security and permission requirements.
Potential future areas include:
Current
|
+--> Read-only Frappe AI
|
+--> More read-only business tools
+--> Better domain-specific query guidance
+--> Additional reporting capabilities
+--> More Frappe/ERPNext DocType intelligence
Any future write capability would require a separate security design and should not be assumed from the current read-only implementation.
52. Final End-to-End Flow¶
┌───────────────────────────────┐
│ Telegram User │
└───────────────┬───────────────┘
│
│ /start
v
┌───────────────────────────────┐
│ Telegram Bot Handler │
│ │
│ Username → Password │
│ Password message deleted │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ Authentication Service │
└───────────────┬───────────────┘
│
│ Frappe Login
v
┌───────────────────────────────┐
│ Frappe / ERPNext │
│ │
│ Credentials + Roles + Access │
└───────────────┬───────────────┘
│
│ sid
v
┌───────────────────────────────┐
│ Encrypted Session Storage │
└───────────────┬───────────────┘
│
│ User Question
v
┌───────────────────────────────┐
│ AI Agent │
│ │
│ Understand → Select Tool │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ Query Validation / Metadata │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ Read-only Frappe Tools │
│ │
│ Search / Count / Aggregate │
│ Get Record / Describe / List │
└───────────────┬───────────────┘
│
│ User's own sid
v
┌───────────────────────────────┐
│ Frappe REST API │
│ │
│ Frappe permissions enforced │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ Tool Result │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ AI Agent │
│ Natural-language answer │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ Telegram Response │
└───────────────────────────────┘
53. Jira Definition of Done¶
The story is considered complete when:
- Telegram bot starts successfully.
- Configuration validation passes.
- Frappe connectivity is verified.
- Telegram token is verified.
- AI provider connectivity is verified.
- User can authenticate using Frappe credentials.
- Passwords are not persisted.
- Frappe sessions are encrypted at rest.
- Frappe sessions are periodically revalidated.
- Frappe roles/permissions control accessible data.
- Natural-language questions can invoke the read-only toolset.
- Tool arguments are validated against actual Frappe metadata.
- Search/count/aggregate/get-record workflows operate correctly.
- AI cannot perform write operations.
- Conversation memory works within configured limits.
- Rate limiting is active.
- Login throttling is active.
- Long Telegram responses are split safely.
- Logout clears the application session.
- Private-chat restriction works.
- Error handling provides controlled user-facing messages.
- Production configuration is protected from source control.
- Only one Telegram polling instance is deployed per bot token.
54. Reference Commands¶
Install¶
python -m venv .venv
Activate — Windows¶
.\.venv\Scripts\Activate.ps1
Activate — Linux/macOS¶
source .venv/bin/activate
Install Dependencies¶
pip install -r requirements.txt
Generate Encryption Key¶
python main.py genkey
Validate Configuration and Services¶
python main.py check
Start Bot¶
python main.py
55. Source Project Reference¶
Project:
frappe_telegram_ai
Main entry point:
main.py
Configuration:
config/config.yaml
config/config.example.yaml
Runtime:
app/runtime.py
Telegram:
app/bot/
AI:
app/ai/
app/agent/
Frappe:
app/frappe/
Authentication:
app/auth/
Security:
app/security/
Persistent sessions:
app/storage/
Documentation:
docs/ACCESS_CONTROL.md
docs/TROUBLESHOOTING.md
The story is based on the implementation and documentation supplied in the frappe_telegram_ai project. It intentionally distinguishes implemented behaviour from future possibilities and does not introduce write capabilities that are not present in the supplied code.
Files