approck-aiogram-utils
Small helpers for aiogram bots that use Approck Messaging: sending Message models to Telegram, safe callbacks, and optional FastStream integration (Redis streams).
Requirements
- Python 3.10+
- uv (recommended)
Install
From PyPI:
uv add approck-aiogram-utils
Or with pip:
pip install approck-aiogram-utils
Optional extras
| Extra | Purpose |
|---|---|
events |
Approck Events SDK integration |
sentry |
Sentry reporting helpers |
transport |
approck-messaging with transport (e.g. FastStream) |
Other optional dependency groups (e.g. for the stream consumer factory and caption sanitization) are defined in pyproject.toml under [project.optional-dependencies] — install them alongside transport when you use those code paths.
Example for messaging + transport:
uv add "approck-aiogram-utils[transport]"
Usage
Send a text or image from your handler
The smallest path is send_simple_message: plain text, optional cover image URL, optional keyboard.
from aiogram import Bot
from approck_aiogram_utils.message import send_simple_message
async def notify_user(bot: Bot, telegram_id: int) -> None:
await send_simple_message(
bot,
chat_id=telegram_id,
caption="Hello from Approck Messaging",
cover="https://example.com/preview.png", # optional
)
Send a full Message model
Use send_message when you already have an approck_messaging Message (text, media, inline buttons, video note, etc.).
from aiogram import Bot
from approck_messaging.models.message import Message, MessageMedia, MessageType
from approck_aiogram_utils.message import send_message
async def deliver(bot: Bot, chat_id: int) -> None:
msg = Message(
type=MessageType.GENERIC,
caption="Rich payload",
media=[
MessageMedia(
id="1",
type="image/jpeg",
name="photo.jpg",
url="https://example.com/photo.jpg",
status="finished",
)
],
)
await send_message(bot, chat_id=chat_id, message=msg)
Supported MessageType values are wired in send_message (see approck_aiogram_utils.message); unknown types raise NotImplementedError.
Optional lifecycle callbacks
Define async functions that match CallbackType (same signature as in approck_aiogram_utils.callback). Use callback_call if you need the same “log and never raise” behavior around your own code.
from approck_aiogram_utils.callback import callback_call
async def my_callback(
message,
message_channel=None,
exc=None,
extra=None,
) -> None:
...
await callback_call(
message=message,
callback=my_callback,
message_channel="my-channel",
extra={"source": "worker"},
)
When you use the integrated factory (create_app) or approck_messaging.subscriber.Subscriber yourself, pass before_send_callback, on_success_callback, on_forbidden_callback, and on_failure_callback — they are forwarded into send_message_handler and run around delivery (see the FastStream section below).
Bundled callback presets (install the matching extras):
events— module next tohandlers.py: Approck Events for success / forbidden paths.sentry— module next tohandlers.py: Sentry for failures.
FastStream: Redis streams → Telegram
Idea in one line: a producer writes TransportMessage JSON to a Redis stream; FastStream runs send_message_handler; the handler uses aiogram.Bot from FastStream context and sends the message to Telegram.
Producer --XADD--> Redis stream --consumer group--> send_message_handler --API--> Telegram
^ |
| v
(channel name) context.set_global("bot", …), callbacks
Data flow (one message)
- A producer
XADDs (or equivalent) to a Redis stream. The message body must deserialize intoTransportMessage(see example at the end of this section). send_message_handler(subscriber function) runs inside FastStream. It readsTransportMessage, optionalvalid_until, optionally sanitizescaptionif the optional dependencies used by that handler are installed, then calls the internal send pipeline (same callbacks as in aiogram-only usage).TelegramBadRequestis swallowed so the broken message is acked and skipped; other exceptions becomeNackMessageso the broker can retry (see FastStream docs).
How the bot and callbacks get into the handler
send_message_handler takes bot and the four callbacks from FastStream Context(), not from your function arguments. In practice that means either:
Subscriber.from_uri(...)(used insidecreate_app) passes yourBotand callbacks when building the subscriber — you do not set globals yourself, or- In tests / a custom broker, you set the same keys with
context.set_global("<name>", value)before publishing (names match the handler parameters:bot,before_send_callback,on_success_callback,on_forbidden_callback,on_failure_callback).
context.set_global key |
Value |
|---|---|
bot |
aiogram.Bot used to call Telegram |
before_send_callback |
optional CallbackType |
on_success_callback |
optional CallbackType |
on_forbidden_callback |
optional CallbackType |
on_failure_callback |
optional CallbackType |
More detail: FastStream context.
Two ways to wire it
A — Integrated aiogram app (create_app)
create_app (in the integration package’s app.py, next to handlers.py) builds a TelegramDispatcher, optional Redis FSM storage, and — if you pass message_channels — an approck_messaging.subscriber.Subscriber: for each channel name it registers StreamSub(...) with consumer group telegram-bot and wraps send_message_handler. On dispatcher startup it starts the FastStream broker; on shutdown it closes the broker. You still pass your Router, optional BotCommand list, and the four callbacks above.
Requires transport plus the other optional dependencies from pyproject.toml that app.py imports.
B — Your own FastStream app (manual)
You own the RedisBroker / FastStream app: attach send_message_handler to the same channel names your producers use, call context.set_global for bot (and any callbacks), then start the broker. The tests in this repo (tests/annotation.py, tests/test_callbacks.py) follow this pattern with TestRedisBroker.
Minimal test-style wiring
from aiogram import Bot
from approck_aiogram_utils.integration.uprock.handlers import send_message_handler
from approck_messaging.models.message import MessageType, TransportMessage, TransportMessageRecipient
from faststream import FastStream, context
from faststream.redis import RedisBroker, TestRedisBroker
broker = RedisBroker("redis://localhost:6379")
FastStream(broker)
broker.subscriber("test-channel")(send_message_handler)
context.set_global("bot", Bot(token="123456:ABC-DEF"))
context.set_global("on_success_callback", None) # or your CallbackType
async def run_once():
async with TestRedisBroker(broker) as br:
await br.publish(
TransportMessage(
recipient=TransportMessageRecipient(telegram_id=123456789),
type=MessageType.GENERIC,
caption="Hello",
).model_dump(),
channel="test-channel",
)
In production, replace TestRedisBroker with a real RedisBroker URL and the same context.set_global setup before the broker serves traffic. The same pattern is exercised under tests/ in this repository. The string passed to broker.subscriber(...) must match the channel= you use when publishing.
Payload shape (TransportMessage)
from approck_messaging.models.message import MessageType, TransportMessage, TransportMessageRecipient
TransportMessage(
recipient=TransportMessageRecipient(telegram_id=123456789),
type=MessageType.GENERIC,
caption="Hello",
)
Publish model_dump() (or equivalent JSON) so the subscriber validates into TransportMessage. The message_channel passed into your callbacks is taken from the raw Redis message metadata (see send_message_handler and raw_message.raw_message["channel"]).
Development
uv sync # project + dev tools (pytest, ruff, mypy)
uv run pytest
uv run ruff check .
uv run ruff format .
uv build # sdist and wheel under dist/
License
MIT — see LICENSE.
Metadata
Release files for approck-aiogram-utils 0.2.24
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| approck_aiogram_utils-0.2.24.tar.gz | 9.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| approck_aiogram_utils-0.2.24-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.6 kB
Release files / approck_aiogram_utils-0.2.24.tar.gz
| Download URL | approck_aiogram_utils-0.2.24.tar.gz |
|---|---|
| Size | 9.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7751e6a86f3851a3ea0188077ada517a5edf4d80ec8a674230f343228fc30d5a
|
|
BLAKE2b-256 checksum How to use checksums |
f1f34b801834a8ebb8891f36234d878f8089561873997aad409940fb50cf9037
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / approck_aiogram_utils-0.2.24-py3-none-any.whl
| Download URL | approck_aiogram_utils-0.2.24-py3-none-any.whl |
|---|---|
| Size | 12.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
19744d035c5be5e8c4365f4cb042fde2765152ec6be191b992632a10b54776e3
|
|
BLAKE2b-256 checksum How to use checksums |
138f9b66f65337b440deaa0887a1f57c89abc1b97b968cfb9f4a8690441282ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for approck-aiogram-utils 0.2.24
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.