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.1.tar.gz (14.6 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.1-py3-none-any.whl (19.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tgrezli-1.0.1.tar.gz
  • Upload date:
  • Size: 14.6 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

Hashes for tgrezli-1.0.1.tar.gz
Algorithm Hash digest
SHA256 b41bc574be1e0b8a58a161a2d737549bdcb5ad717eaef02e6557f322bc58148c
MD5 b8a302908321371d1b9b3304132cc56e
BLAKE2b-256 b8846709cec54cc9be95f6784bc8e6d49152fa6245a95932c84520242b05b8cc

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tgrezli-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 19.3 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

Hashes for tgrezli-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6b6b1dd4edf8aea63a8236c84ea99afd4d3323636528ae008debfb96fbf0fdbc
MD5 ff132a24dfc8fd9b8306eefa9f288781
BLAKE2b-256 8b038c4c53f163b296ddd0af0a7c377b430521d884cacc9ce30c4d726557e41e

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