django-aiogram¶
Run aiogram in a container next to Django, write handlers as ordinary Django app code, and send Telegram messages from anywhere in the project.
from django_aiogram import bot, bots
bot.send(chat_id=CHAT_ID, text='hello')
bots['support'].send(chat_id=CHAT_ID, text='and from the second one')
One bot or twenty: bot is the one a single-bot project has, bots[alias] is the rest, and
they share a connection wherever their settings agree. Multiple bots.
Only the bot container runs the polling loop. Elsewhere send() queues the message and
returns — where the process is enabled; ENABLED=0 makes it a no-op that still names the
message. send_raw() skips the queue and talks to Telegram from the calling process.
Four transports, one setting¶
BROKER names the transport, and your code does not change with it: the same bot.send(),
the same handlers, the same event log, the same manage.py start_tgbot. What differs is what
becomes of a message whose worker was killed mid-send, and what recovery is — a command, a
clock, or the broker's own doing.
-
The default.
RPUSHand a blocking pop, with an in-flight list per worker. At-least-once whereverBLMOVEis available, and a worker name that has to survive its container.pip install 'django-aiogram[redis]' -
The same server, a consumer group, and no identity to keep: any consumer recovers a dead one's work. Wants Redis 7.0 and refuses below it.
pip install 'django-aiogram[redis]' -
The broker tracks its own consumers, so an unacknowledged message comes back without being asked. One thread per connection, which shapes the rest.
pip install 'django-aiogram[rabbitmq,redis]' -
Offsets settle a contiguous prefix, ordering is per partition, and a refusal rewinds everything after it. The model that differs most from the other three.
pip install 'django-aiogram[kafka,redis]'
redis is in the last two lines for the FSM store rather than the queue: FSM_STORAGE
defaults to aiogram's Redis one, and FSM_STORAGE: 'memory' is what drops it.
Delivery compares the four on what each asks of an operator.
Start here¶
-
Install, configure, run — and the two floors each transport adds, driver and server.
-
Every key under
TELEGRAM_BOT_DEFAULTSandTELEGRAM_BOTS, with its default and the check id that guards it. -
Aliases and identities, what a section may say, and what twenty bots actually cost.
-
Bots that arrive while it runs: providers, reconciliation, quarantine, intents, leases.
-
tg_router.pyin any installed app: routers, filters, FSM, and the ORM from async code. -
send,asend,send_many,enqueue,send_raw— keyboards, files, errors, Celery. -
Your suite without a broker:
fakeredis, asserting what was queued, draining by hand. -
The shared instance, its internals, and what stays public across releases.
Running it¶
-
How a queued message reaches Telegram, what each transport guarantees, and which consumer mode to pick.
-
A compose recipe per transport, healthchecks, the shutdown budget, per-process opt-out.
-
Updates over HTTP instead of polling for them, in four steps.
-
Where polling stops, why a webhook costs nothing per bot, and what grows with the bots.
-
Staying inside Telegram's published limits, and what the pacer does when you do not.
-
Where a bot's credential is kept, encrypting the column, and rotating the key with nothing down.
-
Configuring bots, profiles and queues from the admin, and the permission the token is behind.
-
The
django_aiogramlogger, its structured fields, and which lines deserve an alert. -
An optional table recording what each bot did, and a signal to count it without one.
Reference¶
-
What can be queued, the pickle threat model, and the way off it.
-
Symptom to cause, with every line the healthcheck and the webhook can print.
-
What each major release changed and what you have to do, newest first.
-
The brief to hand a coding agent, and what they get wrong without it.
Requires Python 3.10–3.14, Django 5.2+ and aiogram 3.30+. No transport driver is a
dependency of the package since 4.0: the one you name brings its own, and asks for its own
server — Redis Streams wants 7.0 and refuses below it, and the list runs on anything, giving
at-least-once wherever BLMOVE is available: a capability, not a version number.
Installation has the pair for each of the four.
Source ·
PyPI ·
Changelog