Feature request
Add a filters.IS_BOT (and its negation) so handlers can filter on whether the
message author is a bot, without hand-rolling a MessageFilter subclass.
Rationale
User.is_bot has been part of the User model for as long as the Bot API has
had it, and it is the natural discriminator for multi-bot group chats. But
filters.py never exposes it:
$ python -c "
import re
src = open('telegram/ext/filters.py').read()
print('is_bot occurrences in filters.py:', src.count('is_bot'))
m = re.search(r'__all__\s*=\s*\((.*?)\)', src, re.S)
print('total filters exported:', len([x for x in m.group(1).split(',') if x.strip()]))
"
is_bot occurrences in filters.py: 0
total filters exported: 66
66 exported filters, including several in exactly this neighbourhood —
ViaBot, SenderChat, ChatType, IS_AUTOMATIC_FORWARD, IS_TOPIC_MESSAGE,
IS_FROM_OFFLINE — and none of them answers "did a bot write this?".
The closest existing filters are not substitutes:
ViaBot matches inline messages sent via a bot (message.via_bot).
A bot posting normally in a group has via_bot is None, so ViaBot misses it.
SenderChat covers channel/anonymous-admin senders, not bot users.
IS_AUTOMATIC_FORWARD is about channel-to-discussion-group forwards.
So today the only way is a custom filter:
class _IsBot(filters.MessageFilter):
def filter(self, message):
return bool(message.from_user and message.from_user.is_bot)
IS_BOT = _IsBot(name="filters.IS_BOT")
That is ~5 lines, but it is 5 lines every project rewrites, it is easy to get
subtly wrong (from_user is None for channel posts — a naive
message.from_user.is_bot raises AttributeError), and it is undiscoverable:
a user scanning the filters list reasonably concludes PTB cannot do it.
Use case
I run a fleet of machines (2x DGX Spark, 2x Jetson Thor, AGX Orin, Orin Nano)
each hosting an agent that joins one shared Telegram group. Agents need to
treat bot-authored messages differently from human ones — typically: read them
for context, but only act when a human addressed them, so two bots cannot
ping-pong into a loop.
That is a routing decision on is_bot, made at handler-registration time,
which is precisely what filters is for:
# react to humans
app.add_handler(MessageHandler(filters.TEXT & ~filters.IS_BOT, on_human))
# observe peers without replying
app.add_handler(MessageHandler(filters.TEXT & filters.IS_BOT, on_peer))
versus today, where the bot/human split has to happen inside the callback,
after the handler already matched.
Multi-bot groups are increasingly common (agent fleets, bridges, relays,
notification bots coexisting), and every one of them needs this discriminator.
Proposed change
Add to telegram/ext/filters.py, following the existing IS_AUTOMATIC_FORWARD
/ IS_TOPIC_MESSAGE pattern:
class _IsBot(MessageFilter):
__slots__ = ()
def filter(self, message: Message) -> bool:
return bool(message.from_user and message.from_user.is_bot)
IS_BOT = _IsBot(name="filters.IS_BOT")
"""Messages whose sender is a bot (:attr:`telegram.User.is_bot`).
Note:
Channel posts and anonymous-admin messages have no :attr:`Message.from_user`
and therefore do **not** match; see :attr:`filters.SenderChat` for those.
"""
Then export IS_BOT in __all__. Negation already works via ~filters.IS_BOT,
so no separate IS_NOT_BOT is needed.
The from_user is None guard is the part worth having in the library rather
than in every downstream copy.
Caveat worth documenting either way
Independent of this filter: Telegram's privacy mode is ON by default, so a
bot in a group only receives messages that mention it or reply to it — including
messages from other bots. Bot.can_read_all_group_messages reports the current
setting.
Whether or not IS_BOT lands, a sentence in the group-chat docs noting that
can_read_all_group_messages must be True (BotFather → /setprivacy →
Disable) for a bot to observe other bots would save people a long debugging
session — the failure is completely silent: correct code, no updates delivered,
no error.
I'm happy to open a PR with the filter, tests, and that docs note if the
direction is acceptable.
Environment
python-telegram-bot 22.6, Python 3.11.
Feature request
Add a
filters.IS_BOT(and its negation) so handlers can filter on whether themessage author is a bot, without hand-rolling a
MessageFiltersubclass.Rationale
User.is_bothas been part of theUsermodel for as long as the Bot API hashad it, and it is the natural discriminator for multi-bot group chats. But
filters.pynever exposes it:66 exported filters, including several in exactly this neighbourhood —
ViaBot,SenderChat,ChatType,IS_AUTOMATIC_FORWARD,IS_TOPIC_MESSAGE,IS_FROM_OFFLINE— and none of them answers "did a bot write this?".The closest existing filters are not substitutes:
ViaBotmatches inline messages sent via a bot (message.via_bot).A bot posting normally in a group has
via_bot is None, soViaBotmisses it.SenderChatcovers channel/anonymous-admin senders, not bot users.IS_AUTOMATIC_FORWARDis about channel-to-discussion-group forwards.So today the only way is a custom filter:
That is ~5 lines, but it is 5 lines every project rewrites, it is easy to get
subtly wrong (
from_userisNonefor channel posts — a naivemessage.from_user.is_botraisesAttributeError), and it is undiscoverable:a user scanning the filters list reasonably concludes PTB cannot do it.
Use case
I run a fleet of machines (2x DGX Spark, 2x Jetson Thor, AGX Orin, Orin Nano)
each hosting an agent that joins one shared Telegram group. Agents need to
treat bot-authored messages differently from human ones — typically: read them
for context, but only act when a human addressed them, so two bots cannot
ping-pong into a loop.
That is a routing decision on
is_bot, made at handler-registration time,which is precisely what
filtersis for:versus today, where the bot/human split has to happen inside the callback,
after the handler already matched.
Multi-bot groups are increasingly common (agent fleets, bridges, relays,
notification bots coexisting), and every one of them needs this discriminator.
Proposed change
Add to
telegram/ext/filters.py, following the existingIS_AUTOMATIC_FORWARD/
IS_TOPIC_MESSAGEpattern:Then export
IS_BOTin__all__. Negation already works via~filters.IS_BOT,so no separate
IS_NOT_BOTis needed.The
from_user is Noneguard is the part worth having in the library ratherthan in every downstream copy.
Caveat worth documenting either way
Independent of this filter: Telegram's privacy mode is ON by default, so a
bot in a group only receives messages that mention it or reply to it — including
messages from other bots.
Bot.can_read_all_group_messagesreports the currentsetting.
Whether or not
IS_BOTlands, a sentence in the group-chat docs noting thatcan_read_all_group_messagesmust beTrue(BotFather →/setprivacy→Disable) for a bot to observe other bots would save people a long debugging
session — the failure is completely silent: correct code, no updates delivered,
no error.
I'm happy to open a PR with the filter, tests, and that docs note if the
direction is acceptable.
Environment
python-telegram-bot 22.6, Python 3.11.