Telegram bot library with sync-style API, handlers for messages/commands/callbacks, and optional embedded API server.
Project description
TgrEzLi
Telegram bot library with a sync-style API, handlers for messages, commands, and callbacks, and an optional embedded HTTP server for custom POST endpoints. Built on python-telegram-bot and ezlog for logging.
- Sync-style usage: register handlers with decorators, use
TgMsg/TgCmd/TgCb/TgArgsin handlers (thread-local context). - Multiple chats: connect with a
{chat_name: chat_id}dict; restrict handlers per chat. - Optional API server: expose custom POST routes; call them with the
TReqclient. - Credential encryption: store token and chats in an encrypted file with PyCypherLib (included by default).
Install
pip install TgrEzLi
Dependencies: ezlog-py, python-telegram-bot, PyCypherLib, requests.
Quick start
After connect() the bot starts polling in a background thread, so send_msg() and handlers work immediately (no need to call run()). Use run() only to block the main thread until you press Ctrl+C or call stop() (e.g. in a script so the process does not exit).
from TgrEzLi import TEL, TgMsg, TgCmd, TgCb, TgArgs, TReq
bot = TEL()
# 1) Connect: starts polling in background. send_msg() works right away.
TOKEN = "123456789:ABCDEFGHIJKLMNO"
CHAT_DICT = {"chat1": "123456789", "chat2": "789456123"}
bot.connect(TOKEN, CHAT_DICT)
# You can send without calling run() (e.g. from REPL)
bot.send_msg("Ciao a tutti")
# 2) Register handlers (before or after connect)
@bot.on_message()
def on_message():
bot.send_msg(f"Hi {TgMsg.userName}! You wrote: {TgMsg.text}")
@bot.on_command("start")
def on_start():
bot.send_msg(f"Welcome {TgCmd.userName}!")
if TgCmd.args:
bot.send_msg(f"Args: {TgCmd.args}")
# 3) Optional: block main thread until Ctrl+C (so the script does not exit)
bot.run()
From the REPL: after connect() you can call bot.send_msg("...") and it is sent; handlers are active. If you redefine a handler (same function name), the new one replaces the previous one (no duplicate execution).
Configuration
Create the bot with optional config:
from TgrEzLi import TEL
from TgrEzLi.types import TgrezliConfig
config = TgrezliConfig(
save_log=True,
log_file="TgrEzLi.log",
api_host="localhost",
api_port=9999,
)
bot = TEL(config)
Or mutate after creation:
bot.set_save_log(flag)– enable/disable writing log lines to the file (console always uses ezlog).bot.set_host(host)– API server host (used when the firston_api_reqis registered).bot.set_port(port)– API server port.
Defaults: save_log=True, log_file="TgrEzLi.log", api_host="localhost", api_port=9999.
Connecting and running
connect(token, chat_dict)– Prepares the bot and starts polling in a background thread.send_msg()and handlers work immediately (e.g. from REPL you can send without callingrun()).run()– Optional. Blocks the main thread untilstop()(e.g. Ctrl+C). Use in scripts so the process does not exit. If API routes are registered, starts the API server when you callrun().stop()– Stops polling and API server; ifrun()was waiting, it returns. Safe to call from a signal handler (Ctrl+C) or from another thread.
Direct:
bot.connect(TOKEN, {"chat1": "123456789", "chat2": "789456123"})
bot.send_msg("Hello!") # works immediately
# ... register handlers ...
bot.run() # optional: block until Ctrl+C
Encrypted storage (PyCypherLib):
# First time: save token and chats with a password (then connects)
bot.signup(TOKEN, CHAT_DICT, "your_password")
bot.run()
# Later: load and connect
bot.login("your_password")
bot.send_msg("Hi from login")
bot.run()
Data is stored in tgrdata.cy by default (PyCypherLib).
Sending content
All send methods accept an optional chat: None (default/first chat), a string (one chat name), or a list/set of names.
| Method | Description |
|---|---|
send_msg(text, chat=None) |
Text message |
reply_to_msg(text, msg_id, chat=None) |
Reply to a message |
send_img(photo_path, caption=None, chat=None) |
Photo |
send_file(file_path, caption=None, chat=None) |
Document |
send_position(latitude, longitude, chat=None) |
Location |
send_buttons(text, buttons, chat=None) |
Message with inline keyboard (see below) |
send_log(limit=None, chat=None) |
Last limit lines of log file (or full) |
send_info(chat=None) |
Bot info (chats, handlers, API server) |
send_registered_handlers(chat=None) |
List of registered handlers (debug) |
Inline buttons: buttons is a list of rows; each row is a list of {"text": "...", "value": "..."} (value = callback_data).
bot.send_buttons("Choose:", [
[{"text": "Red", "value": "red"}, {"text": "Blue", "value": "blue"}],
[{"text": "Cancel", "value": "cancel"}],
])
CamelCase aliases exist: sendMsg, replyToMsg, sendImg, sendFile, sendPosition, sendButtons, sendLog, sendInfo, sendRegisteredHandlers.
Handlers
Replace-by-name: If you register a handler whose function has the same name as an existing one (same type: message, command, or callback), the new handler replaces the old one. So in the REPL you can redefine handle_message and only the latest version will run.
Handlers run in a background thread; inside them use the global proxies TgMsg, TgCmd, TgCb, TgArgs to access the current payload. chat filters which chats trigger the handler: None = default chat, string = one chat, list/set = multiple chats.
Message handler
@bot.on_message() # default chat
@bot.on_message("chat1") # only chat1
@bot.on_message(["chat1", "chat2"])
def on_message():
# TgMsg: .text, .msgId, .chatId, .userId, .userName, .timestamp, .raw_update
bot.send_msg(f"Echo: {TgMsg.text}")
Command handler
@bot.on_command("start")
@bot.on_command("help", "chat1")
def on_start():
# TgCmd: .command, .args, .msgId, .chatId, .userId, .userName, .timestamp, .raw_update
bot.send_msg(f"Welcome {TgCmd.userName}!")
if TgCmd.args:
bot.send_msg(f"You said: {TgCmd.args}")
Callback handler (inline buttons)
@bot.on_callback("chat1")
def on_callback():
# TgCb: .text, .value, .msgId, .chatId, .userId, .userName, .timestamp, .raw_update
if TgCb.value == "red":
bot.send_msg(f"You pressed {TgCb.text} -> {TgCb.value}")
API request handler
Register a POST route; the embedded server starts automatically when the first route is registered.
@bot.on_api_req("/action", args=["chat", "msg"])
def action():
# TgArgs.get(key, default)
chat_id = TgArgs.get("chat")
msg = TgArgs.get("msg")
bot.send_msg(msg, chat=chat_id)
TReq – client to call these endpoints:
from TgrEzLi import TReq
TReq("/action").host("127.0.0.1").port(9999).arg("chat", "chat1").arg("msg", "Hello!").send()
# or
TReq("/action").body({"chat": "chat1", "msg": "Hello!"}).send()
.host(), .port(), .timeout(seconds) are optional (defaults: localhost, 9999, 30s). .send() returns a requests.Response; on failure raises TgrezliRequestError.
CamelCase decorator aliases: onMessage, onCommand, onCallback, onApiReq.
Data interfaces (handler context)
Use only inside the corresponding handler; otherwise TgMsg / TgCmd / TgCb / TgArgs raise if accessed.
| Proxy | Handler | Main fields |
|---|---|---|
| TgMsg | on_message |
text, msgId, chatId, userId, userName, timestamp, raw_update |
| TgCmd | on_command |
command, args, msgId, chatId, userId, userName, timestamp, raw_update |
| TgCb | on_callback |
text, value, msgId, chatId, userId, userName, timestamp, raw_update |
| TgArgs | on_api_req |
TgArgs.get(key, default) for POST body |
All IDs/names are available as both snake_case (e.g. msg_id) and camelCase (e.g. msgId) on the data objects.
Stopping
stop() stops polling and the API server; if run() was blocking, it returns. Call stop() via Ctrl+C (SIGINT is handled) or from another thread. After that, to use the bot again you must call connect() (or login()) again.
Project layout
TgrEzLi– main packageTEL– bot class (core)TgMsg,TgCmd,TgCb,TgArgs– handler context proxies (fromTgrEzLi.models)TReq,TgrezliRequestError– HTTP client for the embedded APITgrezliConfig– config dataclass (fromTgrEzLi.types)
TgrEzLi.crypto–CredentialManager(uses PyCypherLib):signup(token, chat_dict, password),login(password).
Logging is done with ezlog on the console; if save_log is true, the same messages are appended to the configured log file.
Security and robustness
- API server: POST body size limited (default 1 MiB); invalid JSON returns 400; unknown route 404. No built-in authentication – protect the server (e.g. firewall, reverse proxy, or add your own auth in routes).
- Credentials: Stored in an encrypted file when using
signup/login(PyCypherLib); decryption only with the correct password. Keep the file and password secure. - Handlers: User code runs in daemon threads; exceptions are logged and do not crash the bot.
License
MIT License. Copyright (c) eaannist.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tgrezli-1.0.0.tar.gz.
File metadata
- Download URL: tgrezli-1.0.0.tar.gz
- Upload date:
- Size: 13.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
075f8cf42ba68cdf1d2631e8be51d0c882c6f8c809a2380a7802d373cd526a25
|
|
| MD5 |
f31fe558adab1d48e565b59557d365d6
|
|
| BLAKE2b-256 |
bc4c1fbaedaf5096b53c011ddc2beebcd6b83c24db9d5c338acf23fc53896e1f
|
File details
Details for the file tgrezli-1.0.0-py3-none-any.whl.
File metadata
- Download URL: tgrezli-1.0.0-py3-none-any.whl
- Upload date:
- Size: 17.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19dbeeae76e13665c1cda4354515070f27c58a990d9e74865261116d3382cf36
|
|
| MD5 |
561107ad6d41f4c282ac90a3a17eac63
|
|
| BLAKE2b-256 |
725ae711e1cf0dadc56528d9bff859add5d7aee43fb5962c7d19d246594e1401
|