Skip to content

Latest commit

Β 

History

47 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation


What this is

A live, paying-users Telegram bot that turns OpenAI's Responses API into a complete assistant for a market where most AI products simply aren't localized: Uzbekistan.

It is not a wrapper around one API call. It streams answers token by token into Telegram's native "thinking" UI, decides on its own when to search the web, write and run Python, draw an image, set a reminder or remember a fact about you β€” and it stays inside a hard cost budget while doing it.

Everything the user sees is in Uzbek β€” including error messages, limit notices and paywalls. So is every comment in the source: this codebase is maintained by Uzbek speakers, for Uzbek speakers.


Features

πŸ’¬ Conversation

Streaming replies rendered through Telegram's Rich Message API, with animated status, premium custom emoji and a live elapsed-time counter. Falls back gracefully three levels deep so an answer is never lost.

🌐 Live web search

Multi-query DuckDuckGo search with full-page fetching, source comparison and a mandatory citation format. Fires automatically when a question needs today's data.

πŸ–Ό Vision

Send a photo with or without a caption and get a real analysis β€” receipts, screenshots, homework, documents, medicine labels.

πŸŽ™ Voice in, voice out

Speech-to-text and a natural spoken reply. Language is auto-detected per message, so an Uzbek question gets an Uzbek voice, a Russian one gets a Russian voice.

πŸ“„ Documents

PDF, Word, Excel, PowerPoint and plain text are parsed and analysed. Send a file with no caption and the bot waits for your follow-up instruction instead of guessing.

πŸ›  File generation

The model writes Python, the bot runs it in a sandbox and sends back the result: presentations, reports, spreadsheets, charts, format conversions β€” with a design guide that keeps the output looking professional.

🧠 Long-term memory

The model chooses what is worth keeping β€” name, profession, city, preferences β€” and it survives /new, forever. Category-prefixed and validated before it ever reaches the database.

⏰ Reminders & digests

A Telegram-native advantage: the bot can start the conversation. One-off or recurring reminders, plus a daily digest on topics you pick.


Architecture

flowchart TB
    TG["Telegram Bot API"] --> DP["aiogram Dispatcher<br/>ordered handler chain"]

    DP --> PAY["Payments<br/>registered first"]
    DP --> FSM["FSM flows<br/>gift Β· promo Β· digest"]
    DP --> GATE["Maintenance gate"]
    DP --> BUSY["Spam guard<br/>GeneratingState"]
    DP --> H["Content handlers<br/>text Β· photo Β· doc Β· voice"]

    H --> Q{"Quota check"}
    Q -->|"denied"| UP["Localized limit notice<br/>+ upgrade button"]
    Q -->|"allowed"| AI["services/ai.py<br/>streaming tool loop"]

    AI <--> OAI["OpenAI Responses API"]
    AI --> T1["internet_search"]
    AI --> T2["run_python_sandbox"]
    AI --> T3["generate_image"]
    AI --> T4["update_memory"]
    AI --> T5["manage_reminder"]

    T2 --> SB["Isolated subprocess<br/>scrubbed env Β· rlimits Β· timeout"]

    AI --> ST["Rich streaming UI"]
    ST --> TG

    AI <--> PG[("PostgreSQL<br/>history Β· memory Β· quotas Β· payments")]

    WATCH["Background watchers<br/>reminders Β· digests Β· expiry Β· win-back"] --> TG
    WATCH <--> PG
Loading

How one message is answered

sequenceDiagram
    autonumber
    participant U as User
    participant B as Bot
    participant M as OpenAI
    participant T as Tools

    U->>B: "Make me a slide deck on the EV market"
    B->>B: Charge points Β· pick reasoning effort
    B-->>U: Animated status starts

    B->>M: Stream request with active tools
    M-->>B: internet_search(...)
    B->>T: Multi-query search + page fetch
    T-->>B: Sources
    B-->>U: Status switches to "searching"

    B->>M: Feed results back
    M-->>B: run_python_sandbox(python code)
    B->>T: Execute in sandbox
    T-->>B: output/deck.pptx
    B-->>U: Status switches to "building file"

    B->>M: Feed result back
    M-->>B: Final answer, streamed
    B-->>U: Rich message + attached file
    B->>B: Save history Β· refund unused quota
Loading

Each tool has its own round budget. When a budget runs out that tool is dropped from the request, which forces the model to answer instead of looping. A separate total-round ceiling guards against everything else.


Engineering decisions worth reading

πŸ’Έ Two independent quota systems β€” because one was a support nightmare

Points cover ordinary messages and scale with reasoning effort β€” "salom" is cheap, an integral is not. But file generation, image drawing and deep research are billed on separate daily counters.

The reason is concrete: when file generation came out of the points budget, three presentations drained a user's entire day and they experienced it as "the bot broke". Now the expensive operations have their own ceilings and ordinary chat keeps working after they run out.

Each expensive operation is charged once per user request, no matter how many times the model calls the tool β€” and refunded automatically if nothing was produced.

πŸ”’ The model's output is an untrusted boundary

Anything the model writes into a tool call reaches the database, so validation lives in the data layer β€” never in the tool description. An instruction is not a guarantee.

  • Memory entries are stripped of newlines, so a multi-line "fact" cannot render as fake instructions in a later prompt.
  • Card, passport and account number patterns are rejected outright.
  • Every UPDATE/DELETE driven by a model-supplied index carries AND user_id = $N, and the index is bounds-checked against the list actually shown to the model before the database is touched.
  • Reminder times are rejected if they are in the past, unparseable, or beyond the maximum horizon.
πŸ§ͺ Running model-written Python without losing the host

The sandbox runs each snippet in a fresh temp directory as a subprocess with a scrubbed environment β€” no bot token, no API key, no database URL β€” plus CPU, memory, file-size and process-count rlimits, a hard timeout and process-group kill.

The honest caveat, documented in the source rather than hidden: network is not blocked, because the host offers no container isolation. The mitigation is that there is nothing to steal in that environment and the timeout caps abuse.

⚑ Prompt caching shapes where text is allowed to live

The system prompt is built to day precision so the prefix is byte-identical all day and prompt caching actually hits. Anything per-user β€” long-term memory, the user's name, the current time β€” goes into the message list as a developer message, never into the system prompt.

Putting one user-specific string in the wrong place silently destroys the cache for every user, and nothing warns you.

🎯 Handler registration order is a safety constraint

Payment handlers are registered directly on the dispatcher, before every router. The spam guard that answers "please wait, generating…" has no content filter β€” if a payment confirmation arrived while a reply was streaming, that guard would swallow it: money taken, subscription not granted.

By the same logic the maintenance gate sits before the AI handlers but after /start, while /pro and /promo sit after it β€” selling a subscription for a disabled bot is a refund waiting to happen.

🩺 Failure modes are designed, not discovered
  • Model unavailable or rate-limited β†’ automatic fallback down a model list.
  • Rich Message rejected β†’ edit an existing message β†’ plain message without formatting. Long answers are split at paragraph boundaries with code fences reopened across parts.
  • Transcription fails β†’ second engine.
  • Voice synthesis fails β†’ multilingual fallback voice.
  • Payment cannot be granted β†’ the user is told their money is safe and the admin gets everything needed to fix it by hand.
  • User blocked the bot β†’ marked inactive instead of retried forever.

Tech stack

Layer Choice Why
Bot framework aiogram 3.29 Native async, FSM, and Guest Mode support
AI OpenAI Responses API Streaming + parallel tool calling in one loop
Database PostgreSQL via asyncpg Connection pooling, FOR UPDATE row locks for quota races
Sandbox subprocess + rlimits No Docker-in-Docker available on the host
Docs python-pptx Β· reportlab Β· openpyxl Β· python-docx Model writes the code, these do the work
Speech OpenAI audio models, with a free-tier fallback path Quality where it is paid for, availability everywhere
Payments Telegram Stars (XTR) No payment provider, no KYC, works instantly
Hosting Railway Postgres included, deploys by commit SHA

Project structure

β”œβ”€β”€ main.py                  # entrypoint β€” handler order is documented and load-bearing
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ config.py            # model, prompts, plans, limits, costs β€” one source of truth
β”‚   β”œβ”€β”€ loader.py            # bot, dispatcher, OpenAI client singletons
β”‚   └── memory.py            # in-RAM buffers with TTL cleanup
β”œβ”€β”€ handlers/
β”‚   β”œβ”€β”€ messages.py          # text Β· photo Β· document Β· voice + streaming UI
β”‚   β”œβ”€β”€ pro.py               # payments, gifts, promo codes, referrals
β”‚   β”œβ”€β”€ admin.py             # broadcasts, statistics, user management, maintenance
β”‚   β”œβ”€β”€ guest.py             # Guest Mode β€” one identity in groups and DMs
β”‚   β”œβ”€β”€ digest.py            # daily digest scheduling
β”‚   └── helpers.py           # background watchers: reminders, expiry, win-back
β”œβ”€β”€ services/
β”‚   β”œβ”€β”€ ai.py                # the tool loop, vision, speech, search
β”‚   β”œβ”€β”€ sandbox.py           # isolated execution of model-written Python
β”‚   └── file_task_quota.py   # charge once, refund if nothing was produced
β”œβ”€β”€ db/
β”‚   β”œβ”€β”€ database.py          # quotas, plans, payments, memory, reminders
β”‚   └── history.py           # conversation history in Postgres + RAM cache
└── tests/                   # 30 standalone assert-based suites

Testing

No framework, no fixtures, no mocking library. Every suite is a standalone script that fails loudly with a message explaining why the check exists.

python tests/test_pro_security.py     # payment attack scenarios
python tests/test_tool_status.py      # tool loop status and prompt routing
python tests/test_long_reply.py       # long answers must never disappear
python tests/test_plan_limits.py      # quota and refund invariants

They exist to lock down behaviour that is invisible until it breaks in production β€” a tool call silently routed to web search, a refund handed to a user who was never charged, a status animation lying about what the bot is doing.


πŸš€ Running it yourself
git clone https://github.com/JumayevOU/ChatGPT-AI.git
cd ChatGPT-AI
pip install -r requirements.txt

Create a .env file:

BOT_TOKEN=...          # @BotFather
OPENAI_API_KEY=...     # platform.openai.com
DATABASE_URL=...       # postgres://user:pass@host:port/db
python main.py

Tables are created on first run. ffmpeg on PATH is optional and only used by the free-tier voice path.



Built by Og'abek Jumayev

Telegram GitHub

The bot is live and in daily use β€” talk to it.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages