Utilities for aiogram bots: Approck messaging helpers, callbacks, and optional FastStream/Uprock integration.
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file approck_aiogram_utils-0.2.24.tar.gz.
File metadata
- Download URL: approck_aiogram_utils-0.2.24.tar.gz
- Upload date:
- Size: 9.3 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7751e6a86f3851a3ea0188077ada517a5edf4d80ec8a674230f343228fc30d5a
|
|
| MD5 |
b6889faaf969582f90c077e25336817f
|
|
| BLAKE2b-256 |
f1f34b801834a8ebb8891f36234d878f8089561873997aad409940fb50cf9037
|
File details
Details for the file approck_aiogram_utils-0.2.24-py3-none-any.whl.
File metadata
- Download URL: approck_aiogram_utils-0.2.24-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19744d035c5be5e8c4365f4cb042fde2765152ec6be191b992632a10b54776e3
|
|
| MD5 |
c2e6571cacfe5b55a291202053a4ab44
|
|
| BLAKE2b-256 |
138f9b66f65337b440deaa0887a1f57c89abc1b97b968cfb9f4a8690441282ae
|