Skip to main content

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 / TgArgs in 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 TReq client.
  • 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, log

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

You can pass a dict config (TgrEzLiConfig) to TEL(); all keys are optional. If connection contains token and chats, connect() is called automatically in __init__ (you can still call connect() yourself later for REPL or password-based login).

from TgrEzLi import TEL, log

# Optional: connect on init
bot = TEL({
    "connection": {
        "token": "123456789:ABCDEF...",
        "chats": {"chat1": "123456789", "chat2": "789456123"},
    },
    "logging": {
        "debug": False,           # default: extra debug logs
        "save_log_file": False,   # default: do not append to file
        "log_file_path": "TgrEzLi.log",
    },
    "api_server": {
        "host": "localhost",
        "port": 9999,
    },
})
# If connection was provided, bot is already connected; send_msg() works.

Logging: save_log_file (default False) controls writing log lines to a file; log_file_path is the path. The bot’s console output always uses ezlog. You can also use the preconfigured log instance:

from TgrEzLi import log
log.info("Hello")
log.success("Done")

Mutate after creation:

  • bot.set_save_log(flag) – enable/disable writing log lines to the file (save_log_file).
  • bot.set_host(host) / bot.set_port(port) – API server host/port.

Legacy: You can still pass a TgrezliConfig (dataclass) for backward compatibility; it is converted to the new structure internally.


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 calling run()).
  • run()Optional. Blocks the main thread until stop() (e.g. Ctrl+C). Use in scripts so the process does not exit. If API routes are registered, starts the API server when you call run().
  • stop() – Stops polling and API server; if run() 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 package
    • TEL – bot class (core)
    • TgMsg, TgCmd, TgCb, TgArgs – handler context proxies (from TgrEzLi.models)
    • TReq, TgrezliRequestError – HTTP client for the embedded API
    • TgrezliConfig – config dataclass (from TgrEzLi.types)
  • TgrEzLi.cryptoCredentialManager (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

tgrezli-1.0.2.tar.gz (14.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tgrezli-1.0.2-py3-none-any.whl (19.3 kB view details)

Uploaded Python 3

File details

Details for the file tgrezli-1.0.2.tar.gz.

File metadata

  • Download URL: tgrezli-1.0.2.tar.gz
  • Upload date:
  • Size: 14.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","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

Hashes for tgrezli-1.0.2.tar.gz
Algorithm Hash digest
SHA256 a06fcb672d62b58ce94ab5d30305ed4f9ea37da8240d18838ab19b85183fa908
MD5 2ecff497108ab522168c5322a6e6dd81
BLAKE2b-256 01fdd3186ce8ba591ef7a2ab1deb189affb6f0b6c7a51e7828bc3c712570ab68

See more details on using hashes here.

File details

Details for the file tgrezli-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: tgrezli-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 19.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","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

Hashes for tgrezli-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 0d2065344f3389e7717bbd12459bd0a68a53945f25ce4073ab3eabc37fa99027
MD5 4c2127a346b9896efc13a90537c9e8fe
BLAKE2b-256 695dcd316acb12008a28cd65782d88509c6b0656ecd94271bb7811ecd5e7dce0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page