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

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 first on_api_req is 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 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.0.tar.gz (13.2 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.0-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

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

Hashes for tgrezli-1.0.0.tar.gz
Algorithm Hash digest
SHA256 075f8cf42ba68cdf1d2631e8be51d0c882c6f8c809a2380a7802d373cd526a25
MD5 f31fe558adab1d48e565b59557d365d6
BLAKE2b-256 bc4c1fbaedaf5096b53c011ddc2beebcd6b83c24db9d5c338acf23fc53896e1f

See more details on using hashes here.

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

Hashes for tgrezli-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 19dbeeae76e13665c1cda4354515070f27c58a990d9e74865261116d3382cf36
MD5 561107ad6d41f4c282ac90a3a17eac63
BLAKE2b-256 725ae711e1cf0dadc56528d9bff859add5d7aee43fb5962c7d19d246594e1401

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