Per-user token quotas in YAML, and a container clock set to Shanghai
A Telegram bot with a silky smooth AI experience. UI enabled.
At a glance
- What is it?
- ChatGPT-Telegram-Bot is a self-hosted Telegram bridge to OpenAI or Azure OpenAI with streaming replies and DALL·E 3 images, where quotas are three YAML dictionaries keyed by user level, MySQL comes from a second compose file, the redis package is installed but never served, and the image hardcodes the timezone to Asia/Shanghai.
- Who is it for?
- It fits someone who already runs a container and wants a private Telegram bridge to their own OpenAI or Azure OpenAI deployment, with quotas enforced per user level rather than globally. Check four things before you start.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 35 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Quotas are three dictionaries keyed by user level
The access control is data, not code. Three required parameters in config.yaml are described the same way: the key is the user level, and the value is a number. RATE_LIMIT sets how many chats can be made inside the window, CONTEXT_COUNT sets how many contexts are included in each chat, and MAX_TOKEN sets the maximum tokens returned by the AI per chat.
A fourth parameter, TIME_SPAN, is the window those counts are measured in, in minutes. Read together, they mean a level is a bundle of three numbers plus a shared clock: how often you may talk, how much history you carry, and how long a reply may be.
This is the mechanism behind the feature described as user level division with different single session token numbers, context numbers and session frequencies. Nothing in the code has to change to add a tier; you add a key. The cost is that the whole policy lives in one file, so a mistake in a YAML indentation is a policy mistake, and there is no documented place to record why a given level got the numbers it has.
OpenAI or Azure, decided by one switch
The backend is a single optional parameter, AI.TYPE, with two values, openai and azure, defaulting to openai. Everything else follows from that choice.
For native OpenAI you set AI.MODEL to the model name. For Azure OpenAI, AI.MODEL takes the deployment name instead, which is a different thing from a model name and is the single most common source of confusion when moving a configuration across. Three more parameters become relevant only on the Azure path: AI.BASE, AI.VERSION and AI.MODEL, where the base value can be found either in the Keys and Endpoints section of the Azure portal or in Azure OpenAI Studio under Playground and Code View, and the version is the Azure OpenAI version number.
So the optional block is optional in the literal sense, and mandatory in the Azure case. That asymmetry is fine but undocumented as a rule, and it is the reason a config that works against native OpenAI will fail silently in the sense that the same key now means something else.
The project supports both because corporate Azure deployments are common, and the cost is a config surface roughly twice as large than a single-provider bot would need.
The example config is named differently in the docs and the tree
The configuration instructions say all required settings live in config.yaml and that config.yaml.example should be referred to for the file format and content. The repository root does contain an example file, under the name config.example.yaml.
The two names are transposed. So a first-time setup that follows the documentation literally will look for a file that is not there, and a reader who starts from the tree will find a file the documentation never mentions.
It is a small thing, and it is the kind of thing that costs an hour the first time. The deeper point is that everything else in the deployment path is unambiguous: the required parameters are named, the bot token comes from @botFather, the developer chat ID comes from @get_id_bot, and the MySQL block covers the connection. The example file is the one place where the documentation and the repository disagree, and it is the one place a beginner must read.
Copy config.example.yaml to config.yaml rather than creating a file named after the documentation.
The container clock is pinned to Asia/Shanghai
The Dockerfile is short enough to read in full, and two lines in it will surprise anyone outside that timezone. It installs cryptography separately before the requirements file, then links the zoneinfo entry for Asia/Shanghai to the local time and writes the same zone into /etc/timezone.
So the rate-limit window, which config.yaml expresses in minutes, is measured against a clock that the image sets for you. For an operator in another timezone this does not change the arithmetic, but it does make log lines, message timestamps and any date the bot prints disagree with the wall clock on the host.
The rest of the image is conventional. It starts from python:3.11-slim, copies the whole build context into /app with ADD, installs requirements and cleans apt, and sets the entrypoint to python main.py. The compose file is thinner still: compose version 3.9, one service named bot, the image from GitHub Container Registry, and a single volume mounting ./config.yaml to /app/config.yaml.
That means every deployment decision outside the config file is already made for you, which is convenient, and it also means changing any of them means forking the compose file rather than editing a setting.
MySQL lives in a second compose file, redis has no service
The database is not in the main compose file. A separate command brings it up from its own file:
docker-compose up -d -f db/docker-compose.yamlThen the schema has to be imported by hand, with mysql pointed at db/database.sql. Two steps, in that order, and the second one is not scripted, so an operator who skips it gets a bot that starts and then fails on its first query.
Now look at the requirements. redis 4.5.5 is pinned there, while the compose file defines one service, the bot, with one volume and no Redis anywhere. PyMySQL and DBUtils are also pinned, which matches the MySQL setup, but Redis has no counterpart in either compose file.
What that means in practice is that whatever the Redis import is for, it is not provisioned by the documented path. Either it is optional, or it is a session store that a deployment has to add. Either way it is worth knowing before you deploy, since an import that fails on a missing client is a confusing way to learn that the requirements file and the infrastructure are describing different systems.
tests/ is in the tree, but the Makefile cannot run it
The repository contains a tests/ directory and the Makefile has no test target. The available targets are install, run, image, preflight and help, and nothing else.
install creates a virtualenv directory called venv and installs requirements into it. run executes main.py with that interpreter, so the two targets share the same environment and running the bot is one word. help prints the target list, and its descriptions are bilingual, each line mixing English labels with Chinese explanations.
The two image targets are the interesting ones. Both build with buildx for two platforms, linux/amd64 and linux/arm64, and both push, so a plain make image publishes straight to the registry. image pushes the latest tag and preflight pushes the same build under a preflight tag, which gives you a way to exercise a push without moving the published pointer.
There is no lint, no format and no test target, so a change that breaks the test suite is only caught if you remember to run the suite yourself.
Fifteen identities and image generation, both from 2024
The feature set is a list of fifteen preset bot identities that can be switched quickly, plus support for custom ones, real-time streaming responses, picture generation with the DALL·E 3 model, a one-click clear of the chat contents with the ability to restart the conversation at any time, native Telegram buttons, an English and Chinese interface, and containerisation.
The TODO list has two items and both are ticked: letting users bring their own OpenAI key for more freedom, and improving the error handler. The error path is tied to the DEVELOPER_CHAT_ID parameter, which is where the bot sends messages when it hits an error, so an operator who leaves that unset loses failures silently.
The release history explains where the features came from. v2.0.1 in April 2024 updated the OpenAI client to 1.19.0, which is the version still pinned in requirements.txt. v2.3.0 followed in November 2024. v3.0.1, dated 29 December 2024, added creating images from text with DALL·E.
Since then the branch has moved: the last push is dated 7 September 2026, so there is work on main that no tag covers. The licence is MIT and a hosted instance is linked for trying the interface before you deploy anything.
Editorial conclusion
It fits someone who already runs a container and wants a private Telegram bridge to their own OpenAI or Azure OpenAI deployment, with quotas enforced per user level rather than globally. Check four things before you start. The documentation names the example config as config.yaml.example while the repository ships config.example.yaml, so copy the right file. MySQL comes from a separate compose file under db/ and needs a schema import, and redis is in the requirements without a service anywhere in the compose files. The image pins the timezone to Asia/Shanghai, which will be wrong for you. And the newest tagged release is v3.0.1 from 29 December 2024 while the branch was pushed on 7 September 2026, so install from a commit you choose rather than from the latest tag.
Frequently asked questions
What is V-know/ChatGPT-Telegram-Bot?
It is a self-hosted Telegram bot that proxies conversations to OpenAI or Azure OpenAI with streaming replies, and it can generate images with the DALL·E 3 model. It adds fifteen preset bot identities plus custom ones, native Telegram buttons, an English and Chinese interface, and per-user-level limits. The licence is MIT.
How do I set per-user limits in ChatGPT-Telegram-Bot?
Three required keys in config.yaml are dictionaries where the key is the user level and the value is a number: RATE_LIMIT for chats allowed inside the window, CONTEXT_COUNT for contexts kept per chat, and MAX_TOKEN for the maximum tokens returned per chat. TIME_SPAN sets that window in minutes.
How do I switch ChatGPT-Telegram-Bot to Azure OpenAI?
Set AI.TYPE to azure instead of the default openai, then supply AI.BASE, which is found in the Keys and Endpoints section of the Azure portal or in Azure OpenAI Studio under Playground and Code View, AI.VERSION, and AI.MODEL, which for Azure is the deployment name rather than a model name.
What database does ChatGPT-Telegram-Bot need?
MySQL, started from a separate compose file with docker-compose up -d -f db/docker-compose.yaml, and its schema imported with mysql -uusername -p -e "source db/database.sql". The main docker-compose.yaml defines only the bot service and mounts ./config.yaml, so the database is not part of it. Note that redis 4.5.5 is in requirements.txt with no matching service.
How do I run ChatGPT-Telegram-Bot?
Install dependencies with pip install -r requirements.txt and start it with python main.py, which pipes output to debug.log. There is also a container path, docker run --rm --name chatgpt-telegram-bot -v ./config.yaml:/app/config.yaml ghcr.io/v-know/chatgpt-telegram-bot:latest, or docker-compose up -d. The Makefile offers install, run, image, preflight and help targets but no test target, even though the repository contains a tests directory.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/v-know-chatgpt-telegram-bot)