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 using PyCypherLib (PyPI); CredentialManager uses the encLines / decLines API (from PyCypher import Cy).

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: no DEBUG output (TgrEzLi, ezlog, stdlib)
        "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 uses ezlog (ezlog-py). When debug is False (default), DEBUG lines are not emitted from TgrEzLi, from libraries logging via the stdlib, or from ezlog’s debug level. You can also use the preconfigured log instance:

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

Mutate after creation:

  • bot.set_debug(flag) – enable/disable DEBUG log emission (same as the debug flag under logging in the config dict).
  • 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):

Uses the same lines API as in the PyCypherLib README: Cy().encLines(file).Lines([token, "name:chat_id", ...]).P(password) and decrypt with Cy().decLines(file).P(password) (returns a string or list of strings; TgrEzLi normalizes both). Password: at least 8 characters (PyCypher requirement). Default file: tgrdata.cy in the current working directory (CredentialManager).

# First time: save token and chats with a password (then connects)
bot.signup(TOKEN, CHAT_DICT, "your_password")  # password >= 8 chars

# Later: load and connect
bot.login("your_password")
bot.send_msg("Hi from login")
bot.run()

Default file: tgrdata.cy in the working directory. TEL.signup / TEL.login use that path. To use another path without TEL, instantiate CredentialManager("path/to/file.cy") from TgrEzLi.crypto and call signup / login on it.


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: PyCypherLib encLines / decLines (signup, login; default file tgrdata.cy).

Logging uses ezlog on the console. DEBUG output is emitted only when logging.debug is True in config or bot.set_debug(True); otherwise DEBUG is suppressed for TgrEzLi, stdlib loggers, and ezlog. If save_log_file is True, lines are also 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 with PyCypherLib lines encryption (tgrdata.cy by default); passwords must be at least 8 characters. Wrong password or corrupted data raises ValueError (wrapped by CredentialManager on decrypt). 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 © 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.4.tar.gz (15.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.4-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tgrezli-1.0.4.tar.gz
  • Upload date:
  • Size: 15.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.4.tar.gz
Algorithm Hash digest
SHA256 e7b90a45cf6664dacd38c9832460065a5062114721c705e416f63f432970ee06
MD5 fc5e3d6b9e7856128e19a150f49b4ac6
BLAKE2b-256 10c9b0e754d6b43ed51a260c463fd863d04b87fa549796d3baa6e7891936ffbd

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tgrezli-1.0.4-py3-none-any.whl
  • Upload date:
  • Size: 20.4 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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 3ae7cf428f737abffb2030ce4666b484bff1287f5a04c34ad5acaf1bee79a7b6
MD5 e36d7e1e8e7b740f37d0ef89a284404e
BLAKE2b-256 9d6eedc31b58b8c4da4357d44b3df1aa004f6c843ea8a1405990e86e695f8141

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