Models

The objects the library returns are frozen dataclasses: you can read them, compare them and use them as dictionary keys, but not change them. Python names are in snake_case; the JSON names of the API are in camelCase (from becomes from_user).

Every model keeps the JSON it was made from in raw, so a field that a newer server adds and this version does not know is still there (message.raw.get("someNewField")). Each model has a from_json() class method to build it from the API’s JSON; a missing required field raises PayloadError. Dates are datetime objects in UTC.

Enums

The enums are str enums: a member equals its value (ChatKind.GROUP == "group"). A value that this version does not know does not raise: it becomes a pseudo-member that still equals its string.

class ChatKind(*values)

The kind of a chat: private, group or channel.

A str enum, so chat.kind == "group" and chat.kind == ChatKind.GROUP are the same test. A kind that a newer server adds does not raise: it becomes a member of its own that equals its string and matches none of the names below.

PRIVATE

"private", a chat between the bot and one person.

GROUP

"group", a group chat.

CHANNEL

"channel", a channel. A post there may speak in the channel’s name, and then it has no sender.

Examples

>>> ChatKind.GROUP == "group"
True
>>> ChatKind("supergroup")  # a kind this version does not know
<ChatKind.supergroup: 'supergroup'>
class ChatMode(*values)

Whether a chat is readable by the server or end-to-end encrypted.

The server decides, and the mode says which body a message must carry. A private chat follows the bot: it is encrypted once the bot has published a key. A group or channel follows its history setting: readable when new members can see the history, encrypted otherwise. Bot finds out the mode before it sends, and encrypts when it must.

PROTECTED

"protected", end-to-end encrypted. Messages travel as armored OpenPGP messages, which the server cannot read.

OPEN

"open", readable. Messages travel as text, which the server reads and passes on.

class BotStatus(*values)

Whether the bot is running or paused by its owner: BotInfo.status.

ACTIVE

"active", the bot works normally.

PAUSED

"paused", the owner paused the bot. While it is paused, every API call raises BotPausedError, except GET /me, GET /webhook, DELETE /webhook and POST /token/revoke.

class SeatStatus(*values)

Where somebody stands in a chat: the status of a Seat.

In a private chat the bot’s own seat is member, or kicked while the person blocks it.

ADMIN

"admin", an administrator. Seat.can says what they may do.

MEMBER

"member", an ordinary member.

RESTRICTED

"restricted", a member who may do less than the others, until Seat.restricted_until when that is set.

LEFT

"left", not in the chat, for example after leaving it.

KICKED

"kicked", removed from the chat by somebody else. In a private chat, the person blocked the bot.

class UpdateKind(*values)

What an Update is about, and so which of its fields is set.

A kind that a newer server adds becomes a member of its own. Such an update has none of those fields set, and its JSON is still in raw.

MESSAGE

"message", a new message for the bot, in a private chat or a group. It is in the update’s message.

EDITED_MESSAGE

"edited_message", somebody edited a message the bot had received. The new version is in message.

DELETED_MESSAGES

"deleted_messages", messages were deleted for everyone. They are listed in deleted.

CALLBACK_QUERY

"callback_query", somebody pressed a button with callback data under one of the bot’s messages. The press is in callback_query.

MY_CHAT_MEMBER

"my_chat_member", the bot’s own seat in a chat changed: added, promoted, restricted, removed or left; in a private chat, blocked or unblocked. The change is in chat_member.

class HandleKind(*values)

What a handle turned out to belong to: a person, a group or a channel.

A str enum, so found.kind == "user" and found.kind == HandleKind.USER are the same test. A kind a newer server adds becomes a member of its own rather than raising.

USER

"user", somebody’s account - a bot’s included.

GROUP

"group", a public group.

CHANNEL

"channel", a public channel.

Updates

class Update(
*,
raw=<factory>,
update_id,
kind,
message=None,
callback_query=None,
deleted=None,
chat_member=None,
)

One thing that happened, as the server delivers it.

kind says what happened, and so which of the other fields is set: message for a new or an edited message, callback_query for a button press, deleted for messages deleted for everyone, chat_member for a change of the bot’s own seat. The others are None.

Handlers get the event, not the update: a Message, a CallbackQuery, a DeletedMessages or a ChatMemberUpdate. A handler registered with Router.on_update, and any handler with an update parameter, gets the Update itself. Its events are data only: a MessageBase or a CallbackQueryBase, without the methods. A kind that a newer server adds still parses: such an update has no event, and its JSON is in raw.

update_id

The update’s ID. It grows by one with each update the bot gets.

Type:

int

kind

What happened.

Type:

UpdateKind

message

For a message update, the new message; for an edited_message update, the message as it is after the edit.

Type:

MessageBase | None

callback_query

For a callback_query update, the button press.

Type:

CallbackQueryBase | None

deleted

For a deleted_messages update, the messages that were deleted.

Type:

DeletedMessages | None

chat_member

For a my_chat_member update, the change of the bot’s seat.

Type:

ChatMemberUpdate | None

Example

>>> update = Update.from_json({
...     "updateId": 412,
...     "kind": "deleted_messages",
...     "chat": {"id": 7781, "kind": "private"},
...     "messageIds": [1834, 1835],
... })
>>> update.kind
<UpdateKind.DELETED_MESSAGES: 'deleted_messages'>
>>> update.deleted.message_ids, update.chat.id, update.message
((1834, 1835), 7781, None)
property chat: ChatRef | None

The chat the update is about, whatever its kind.

None only for a kind this version does not know.

property from_user: User | None

Who caused the update, when it says.

The sender of a message, the person who pressed a button, or who changed the bot’s seat. None for deleted messages, for a channel post in the channel’s name, for a seat change that does not say who, and for a kind this version does not know.

property event: Any

The event the update carries, whichever field holds it.

A MessageBase, a CallbackQueryBase, a DeletedMessages or a ChatMemberUpdate, as kind says. None for a kind this version does not know.

classmethod from_json(data)

Builds an update, its event included, from the API’s JSON.

The bot does this for every update it fetches or is sent. The event is built as a plain MessageBase, CallbackQueryBase, DeletedMessages or ChatMemberUpdate: attached to no bot, and not decrypted.

Parameters:

data (Any) – One update, as GET /updates lists them or a webhook posts it.

Returns:

The update, with data kept in raw. A missing kind is read as "unknown", and an update of a kind this version does not know has no event.

Raises:

PayloadError – data is not an object; its updateId is missing or not an integer; or the event its kind calls for is missing or not valid (see MessageBase.from_json, CallbackQueryBase.from_json, ChatRef.from_json and Seat.from_json).

Return type:

Update

class MessageBase(
*,
raw=<factory>,
id,
chat,
from_user,
date,
reply_to=None,
kind='user',
text=None,
media=(),
signature=None,
armored=None,
plaintext=None,
verification=None,
_bot=None,
)

A message a person wrote, as the bot receives it: the data, without methods.

Handlers get it as a Message (a SyncMessage on a SyncBot), which adds Message.reply and the other methods. The bot’s own messages never arrive as updates: sends and edits return them as SentMessage objects.

In an end-to-end encrypted chat, the bot decrypts the message before any handler sees it. text and media then come from inside it, armored keeps the original, plaintext the exact decrypted body, and verification says what the signature check found. A message the bot cannot decrypt goes to the error handlers instead. In a readable chat the server reads the message itself: armored, plaintext and verification are None.

id

The message’s ID, unique inside its chat.

Type:

int

chat

The chat it was written in.

Type:

ChatRef

from_user

Who wrote it. None for a channel post in the channel’s name: the bot, like any reader, is not told who wrote it.

Type:

User | None

date

When it was sent, in UTC.

Type:

datetime

reply_to

The ID of the message it answers, or None.

Type:

int | None

kind

The kind of message, as the server says: "user" for anything a person typed or attached.

Type:

str

text

What the person wrote, or the caption of a picture. None when there are no words, as for a sticker, a call notice or a payment request. In a readable chat the server hands it over trimmed, with runs of empty lines folded into one, and cut at 4000 characters. In an encrypted chat it is the decrypted text as the person wrote it; only CR LF line ends become LF.

Type:

str | None

media

The pictures, clips and files that came with it, one Media each. Empty when there are none.

Type:

tuple[Media, …]

signature

For a channel post in the channel’s name, the name a signed post shows its readers. None when the channel does not sign its posts, and for any other message.

Type:

str | None

armored

In an encrypted chat, the OpenPGP message as it arrived, ASCII-armored. None in a readable chat.

Type:

str | None

plaintext

In an encrypted chat, the decrypted body exactly as the sender’s app wrote it, in the app’s internal format: a picture or a sticker is a marker followed by JSON. The bot reads text and media out of it. None in a readable chat.

Type:

str | None

verification

In an encrypted chat, what the check of the sender’s signature found. None in a readable chat.

Type:

Verification | None

Example

>>> message = MessageBase.from_json({
...     "id": 1834,
...     "chat": {"id": 7781, "kind": "private"},
...     "from": {"id": 55, "username": "anna"},
...     "date": 1767225600000,
...     "text": "/weather rome",
... })
>>> message.chat_id, message.from_user.mention, message.is_command
(7781, '@anna', True)
>>> message.date.isoformat()
'2026-01-01T00:00:00+00:00'

Note

In a readable chat, the server marks a message armored whenever its text holds an OpenPGP armor line, as when somebody pastes a PGP block. If the bot cannot decrypt it, the handlers get it as words anyone in the chat can read: text holds the block, and armored is None.

property encrypted: bool

True when the message came end-to-end encrypted, so armored is set.

property chat_id: int

The ID of the chat, the same as message.chat.id.

property is_command: bool

True when text starts with /.

It looks at the first character only. parse_command tells a real command from a text such as "/ hi", and reads its name and arguments.

classmethod from_json(data)

Builds a message from the API’s JSON, as an update carries it.

Nothing is decrypted here: from an encrypted chat, text stays None and armored holds the message. The bot decrypts messages before its handlers get them.

Parameters:

data (Any) – The message object of an update.

Returns:

The message, with data kept in raw. A missing kind is "user". Called on Message or SyncMessage, it builds one of those, attached to no bot (see Message.bind).

Raises:

PayloadError – data is not an object; its id or date is missing or not an integer; its chat is missing or not valid; or its from or one of its media items is not valid.

Return type:

MessageBase

class Message(
*,
raw=<factory>,
id,
chat,
from_user,
date,
reply_to=None,
kind='user',
text=None,
media=(),
signature=None,
armored=None,
plaintext=None,
verification=None,
_bot=None,
)

Bases: MessageBase

A received message whose methods use the Bot that got it.

classmethod bind(message, bot)

Copies a plain MessageBase into one that acts through bot.

The bot does this to every message before a handler sees it. You need it yourself only for a message you built or read back from somewhere, in a test for example.

Parameters:
  • message (MessageBase) – The message as the API gave it.

  • bot (Bot) – The bot its methods will go through.

Returns:

The same fields, as a Message bound to bot.

Return type:

Message

async reply(
text,
*,
markup=None,
quote=True,
silent=False,
encrypt=None,
send_id=None,
)

Answers in the same chat, quoting this message unless quote=False.

A send like any other: Bot.send_message does the work, so the answer is sealed and signed when the chat is end-to-end encrypted, and the same errors come out of it.

Parameters:
  • text (str) – The answer, with the rules of Bot.send_message.

  • markup (InlineKeyboard | None) – Buttons under the answer. Never encrypted.

  • quote (bool) – False writes into the chat without quoting this message.

  • silent (bool) – True posts without a notification. It only matters in channels.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

  • send_id (str | None) – A UUID that makes this send safe to repeat. One is made for you.

Returns:

The SentMessage as the server stored it, which can be edited or deleted.

Raises:

RuntimeError – The message was not handed out by a Bot, so it has nothing to answer through.

Return type:

SentMessage

async answer(text, *, markup=None, silent=False, encrypt=None, send_id=None)

Writes into the same chat without quoting: reply with quote=False.

Parameters:
  • text (str) – The message, with the rules of Bot.send_message.

  • markup (InlineKeyboard | None) – Buttons under it. Never encrypted.

  • silent (bool) – True posts without a notification. It only matters in channels.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

  • send_id (str | None) – A UUID that makes this send safe to repeat. One is made for you.

Returns:

The SentMessage as the server stored it.

Raises:

RuntimeError – The message was not handed out by a Bot.

Return type:

SentMessage

async delete()

Deletes this message for everyone. Needs the delete messages right in a group.

Returns:

True once the message is gone.

Raises:
Return type:

bool

async typing(typing=True)

Shows “typing…” in this chat for a few seconds.

Parameters:

typing (bool) – False clears the indicator early.

Returns:

True if the indicator was shown, False if one went out to this chat less than 3 seconds ago.

Raises:

RuntimeError – The message was not handed out by a Bot.

Return type:

bool

class SyncMessage(
*,
raw=<factory>,
id,
chat,
from_user,
date,
reply_to=None,
kind='user',
text=None,
media=(),
signature=None,
armored=None,
plaintext=None,
verification=None,
_bot=None,
)

Bases: MessageBase

A received message whose methods use the SyncBot that got it.

classmethod bind(message, bot)

Copies a plain MessageBase into one that acts through bot.

The bot does this to every message before a handler sees it. You need it yourself only for a message you built or read back from somewhere, in a test for example.

Parameters:
  • message (MessageBase) – The message as the API gave it.

  • bot (SyncBot) – The bot its methods will go through.

Returns:

The same fields, as a SyncMessage bound to bot.

Return type:

SyncMessage

reply(
text,
*,
markup=None,
quote=True,
silent=False,
encrypt=None,
send_id=None,
)

Answers in the same chat, quoting this message unless quote=False.

A send like any other: SyncBot.send_message does the work, so the answer is sealed and signed when the chat is end-to-end encrypted, and the same errors come out of it.

Parameters:
  • text (str) – The answer, with the rules of SyncBot.send_message.

  • markup (InlineKeyboard | None) – Buttons under the answer. Never encrypted.

  • quote (bool) – False writes into the chat without quoting this message.

  • silent (bool) – True posts without a notification. It only matters in channels.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

  • send_id (str | None) – A UUID that makes this send safe to repeat. One is made for you.

Returns:

The SyncSentMessage as the server stored it, which can be edited or deleted.

Raises:

RuntimeError – The message was not handed out by a SyncBot, so it has nothing to answer through.

Return type:

SyncSentMessage

answer(text, *, markup=None, silent=False, encrypt=None, send_id=None)

Writes into the same chat without quoting: reply with quote=False.

Parameters:
  • text (str) – The message, with the rules of SyncBot.send_message.

  • markup (InlineKeyboard | None) – Buttons under it. Never encrypted.

  • silent (bool) – True posts without a notification. It only matters in channels.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

  • send_id (str | None) – A UUID that makes this send safe to repeat. One is made for you.

Returns:

The SyncSentMessage as the server stored it.

Raises:

RuntimeError – The message was not handed out by a SyncBot.

Return type:

SyncSentMessage

delete()

Deletes this message for everyone. Needs the delete messages right in a group.

Returns:

True once the message is gone.

Raises:
Return type:

bool

typing(typing=True)

Shows “typing…” in this chat for a few seconds.

Parameters:

typing (bool) – False clears the indicator early.

Returns:

True if the indicator was shown, False if one went out to this chat less than 3 seconds ago.

Raises:

RuntimeError – The message was not handed out by a SyncBot.

Return type:

bool

class Media(
*,
raw=<factory>,
id,
kind,
mime=None,
url=None,
width=None,
height=None,
duration_ms=None,
name=None,
size=None,
encrypted=False,
)

A picture, clip or file attached to a message: one item of message.media.

In a readable chat the server lists only the pictures and videos that were sent in the clear. In an encrypted chat the list comes from inside the encrypted message, so it describes every kind of attachment, and the files on the server are normally encrypted too. Bot.download_media downloads a file and decrypts it. That always works in a private chat. In an encrypted group the sender’s phone seals the file only to the members’ keys, so it works only when the bot is among them (its privacy mode is off); otherwise the download raises NotARecipientError.

id

The file’s ID, a UUID.

Type:

str

kind

What it is: "image", "video", "audio" or "file".

Type:

str

mime

The MIME type, such as "image/jpeg", or None.

Type:

str | None

url

Where the file is, /media/<id> on the API server. Bot.download_media uses it.

Type:

str | None

width

The width in pixels, or None.

Type:

int | None

height

The height in pixels, or None.

Type:

int | None

duration_ms

The length of a clip, in milliseconds, or None. The API calls it duration.

Type:

int | None

name

The file name, when the sender’s app gives one, or None. Only in encrypted chats.

Type:

str | None

size

The size in bytes, when the sender’s app gives it, or None. Only in encrypted chats.

Type:

int | None

encrypted

True when the file on the server is encrypted with OpenPGP. Bot.download_media then decrypts it.

Type:

bool

classmethod from_json(data)

Builds an attachment from an item of the media of a message in a readable chat.

The server sends no name, size or encrypted there, so they keep their defaults. Attachments of encrypted chats are read from inside the decrypted message instead.

Parameters:

data (Any) – The media object (id, kind, mime, url, width, height, duration).

Returns:

The attachment, with data kept in raw. A missing kind is "file".

Raises:

PayloadError – data is not an object, or its id is missing.

Return type:

Media

class CallbackQueryBase(
*,
raw=<factory>,
id,
from_user,
chat,
message_id,
data,
expires_at=None,
_bot=None,
)

A press on a button with callback data: the data, without methods.

Handlers get it as a CallbackQuery (a SyncCallbackQuery on a SyncBot), which adds CallbackQuery.answer and the methods that change the message. The pressed button spins on the person’s phone until the bot answers, which it can do once, within 60 seconds of the press. A press that nothing answered gets an empty answer from the bot once the update is handled, so the button stops spinning (unless auto_answer_callbacks=False).

id

The ID of the press, to answer it with.

Type:

str

from_user

Who pressed the button.

Type:

User

chat

The chat of the message the button is under.

Type:

ChatRef

message_id

The ID of the message the button is under, one of the bot’s own.

Type:

int

data

The callback_data of the button that was pressed.

Type:

str

expires_at

The deadline to answer, 60 seconds after the press, in UTC. None when the server does not say.

Type:

datetime | None

Warning

The data of a button travels in clear, even in an encrypted chat: the server has to know which button was pressed. Put in it an ID that means something only to your bot (order:8812), not personal data.

property expired: bool

True once expires_at has passed, by this computer’s clock.

After that the server refuses an answer with CallbackExpiredError. False when expires_at is None.

classmethod from_json(data)

Builds a press from the callback object of a callback_query update.

Parameters:

data (Any) – The callback object.

Returns:

The press, with data kept in raw. When the object has no data string, the data field is "". Called on CallbackQuery or SyncCallbackQuery, it builds one of those, attached to no bot (see CallbackQuery.bind).

Raises:

PayloadError – data is not an object; its id is missing or not a string; its messageId is missing or not an integer; or its from or chat is missing or not valid.

Return type:

CallbackQueryBase

class CallbackQuery(
*,
raw=<factory>,
id,
from_user,
chat,
message_id,
data,
expires_at=None,
_bot=None,
)

Bases: CallbackQueryBase

A button press, with answer() and helpers to change the message.

classmethod bind(query, bot)

Copies a plain CallbackQueryBase into one that acts through bot.

The bot does this to every press before a handler sees it.

Parameters:
  • query (CallbackQueryBase) – The press as the API gave it.

  • bot (Bot) – The bot its methods will go through.

Returns:

The same fields, as a CallbackQuery bound to bot.

Return type:

CallbackQuery

async answer(text=None, *, alert=False, url=None)

Stops the spinner. text flashes a line, alert=True shows a box, url offers a page.

Once per press, within 60 seconds of it. The bot keeps no spinner turning: a press no handler answered gets an empty answer when the update is done.

Parameters:
  • text (str | None) – Up to 200 characters on one line. None shows nothing.

  • alert (bool) – True shows text in a box the person has to close.

  • url (str | None) – An http or https address the phone offers to open.

Returns:

True. Answering twice raises instead.

Raises:
Return type:

bool

Warning

Answers are not encrypted, even in an encrypted chat.

async edit_message(text=None, *, markup=KEEP, encrypt=None)

Edits the message the pressed button sits under.

Parameters:
  • text (str | None) – The new text, with the rules of Bot.send_message. None keeps it.

  • markup (InlineKeyboard | None) – The buttons. KEEP keeps them, None removes them, a keyboard replaces them. Never encrypted.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

Returns:

The edited message, as the server stored it.

Raises:
  • ForbiddenError – The buttons are under somebody else’s message, so the bot may not edit it.

  • RuntimeError – The press was not handed out by a Bot.

Return type:

SentMessage

async delete_message()

Deletes the message the pressed button sits under.

Returns:

True once the message is gone.

Raises:
  • ForbiddenError – The message is not the bot’s own and it has no right to delete it.

  • RuntimeError – The press was not handed out by a Bot.

Return type:

bool

async send(text, *, markup=None, encrypt=None)

Sends a new message into the chat the button was pressed in.

Parameters:
  • text (str) – The message, with the rules of Bot.send_message.

  • markup (InlineKeyboard | None) – Buttons under it. Never encrypted.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

Returns:

The SentMessage as the server stored it.

Raises:

RuntimeError – The press was not handed out by a Bot.

Return type:

SentMessage

class SyncCallbackQuery(
*,
raw=<factory>,
id,
from_user,
chat,
message_id,
data,
expires_at=None,
_bot=None,
)

Bases: CallbackQueryBase

A button press, with answer() and helpers to change the message (sync).

classmethod bind(query, bot)

Copies a plain CallbackQueryBase into one that acts through bot.

The bot does this to every press before a handler sees it.

Parameters:
Returns:

The same fields, as a SyncCallbackQuery bound to bot.

Return type:

SyncCallbackQuery

answer(text=None, *, alert=False, url=None)

Stops the spinner. text flashes a line, alert=True shows a box, url offers a page.

Once per press, within 60 seconds of it. The bot keeps no spinner turning: a press no handler answered gets an empty answer when the update is done.

Parameters:
  • text (str | None) – Up to 200 characters on one line. None shows nothing.

  • alert (bool) – True shows text in a box the person has to close.

  • url (str | None) – An http or https address the phone offers to open.

Returns:

True. Answering twice raises instead.

Raises:
Return type:

bool

Warning

Answers are not encrypted, even in an encrypted chat.

edit_message(text=None, *, markup=KEEP, encrypt=None)

Edits the message the pressed button sits under.

Parameters:
  • text (str | None) – The new text, with the rules of SyncBot.send_message. None keeps it.

  • markup (InlineKeyboard | None) – The buttons. KEEP keeps them, None removes them, a keyboard replaces them. Never encrypted.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

Returns:

The edited message, as the server stored it.

Raises:
Return type:

SyncSentMessage

delete_message()

Deletes the message the pressed button sits under.

Returns:

True once the message is gone.

Raises:
Return type:

bool

send(text, *, markup=None, encrypt=None)

Sends a new message into the chat the button was pressed in.

Parameters:
  • text (str) – The message, with the rules of SyncBot.send_message.

  • markup (InlineKeyboard | None) – Buttons under it. Never encrypted.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

Returns:

The SyncSentMessage as the server stored it.

Raises:

RuntimeError – The press was not handed out by a SyncBot.

Return type:

SyncSentMessage

class DeletedMessages(*, raw=<factory>, chat, message_ids)

Messages deleted for everyone in one chat: what a deleted_messages update carries.

Handlers registered with Router.on_deleted_messages get it. In a group it lists the messages the bot had received. In a private chat it lists any message of the chat, the bot’s own included: also the ones the bot deleted itself, and a whole history the person cleared. It does not carry who deleted them. Its raw is the JSON of the whole update.

chat

The chat the messages were deleted from.

Type:

ChatRef

message_ids

The IDs of the deleted messages.

Type:

tuple[int, …]

Example

Forget what the bot kept about them:

@bot.on_deleted_messages()
async def forget(deleted):
    for message_id in deleted.message_ids:
        await db.forget(deleted.chat.id, message_id)
class ChatMemberUpdate(*, raw=<factory>, chat, from_user, date, old, new)

A change of the bot’s own seat in a chat: what a my_chat_member update carries.

Handlers registered with Router.on_chat_member get it. The bot was added, promoted, restricted, removed, or it left; in a private chat, the person blocked it (the seat becomes kicked) or unblocked it (member again). joined, left, blocked and unblocked name the usual cases; compare old and new for the others, such as a promotion (change.new.is_admin). Its raw is the JSON of the whole update.

chat

The chat where the bot’s seat changed.

Type:

ChatRef

from_user

Who made the change, or None when the update does not say.

Type:

User | None

date

When it changed, in UTC, or None when the update does not say.

Type:

datetime | None

old

The bot’s seat before the change.

Type:

Seat

new

The bot’s seat after it.

Type:

Seat

Example

Say hello in a new group, and log who blocks the bot:

@bot.on_chat_member()
async def seat_changed(change):
    if change.joined and change.chat.is_group:
        await bot.send_message(change.chat, "Hello. Type /help to see what I do.")
    elif change.blocked:
        log.info("blocked in chat %s", change.chat.id)
property joined: bool

True when the bot is in the chat now and was not before.

It was added to the chat, or unblocked in a private chat. In the chat means an admin, member or restricted seat (Seat.is_inside).

property left: bool

True when the bot was in the chat and is not any more.

It was removed or it left, or the person blocked it in a private chat.

property blocked: bool

True when the person in a private chat blocked the bot.

Until they unblock it, a send to them raises BlockedByUserError.

property unblocked: bool

True when the person in a private chat unblocked the bot.

Messages the bot sent

class SentMessageBase(
*,
raw=<factory>,
id,
chat,
date,
reply_to=None,
markup=None,
text=None,
armored=None,
_bot=None,
)

A message of the bot’s own, as a send or an edit returns it: the data, without methods.

Bot.send_message, Bot.edit_message and the methods that send, such as Message.reply, return it as a SentMessage (a SyncSentMessage on a SyncBot), which adds SentMessage.edit and SentMessage.delete. The fields are what the server stored.

id

The message’s ID, unique inside its chat.

Type:

int

chat

The chat it was sent to.

Type:

ChatRef

date

When it was sent, in UTC.

Type:

datetime

reply_to

The ID of the message it quotes, or None.

Type:

int | None

markup

Its buttons, or None when it has none.

Type:

InlineKeyboard | None

text

Its text, in a readable chat. None in an encrypted chat, where the server cannot read it.

Type:

str | None

armored

In an encrypted chat, the OpenPGP message that was sent, sealed and signed, ASCII-armored. None in a readable chat.

Type:

str | None

property encrypted: bool

True when the message went out end-to-end encrypted, so armored is set.

classmethod from_json(data)

Builds the message from the answer of POST /messages or PATCH /messages/{id}.

Parameters:

data (Any) – The answer.

Returns:

The message, with data kept in raw. Called on SentMessage or SyncSentMessage, it builds one of those, attached to no bot (see SentMessage.bind).

Raises:

PayloadError – data is not an object; its id or date is missing or not an integer; or its chat is missing or not valid.

Return type:

SentMessageBase

class SentMessage(
*,
raw=<factory>,
id,
chat,
date,
reply_to=None,
markup=None,
text=None,
armored=None,
_bot=None,
)

Bases: SentMessageBase

One of the bot’s messages, with edit() and delete().

classmethod bind(message, bot)

Copies a plain SentMessageBase into one that acts through bot.

The methods that send do this to what they return, so you rarely call it yourself.

Parameters:
  • message (SentMessageBase) – The message as the API gave it back.

  • bot (Bot) – The bot its methods will go through.

Returns:

The same fields, as a SentMessage bound to bot.

Return type:

SentMessage

async edit(text=None, *, markup=KEEP, encrypt=None)

New words, new buttons, or both: Bot.edit_message on this message.

Parameters:
  • text (str | None) – The new text, with the rules of Bot.send_message. None keeps it.

  • markup (InlineKeyboard | None) – The buttons. KEEP, the default, keeps them; None removes them; a keyboard replaces them. Never encrypted.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

Returns:

The edited message, as the server stored it.

Raises:
Return type:

SentMessage

async delete()

Deletes this message for everyone. A bot may always delete its own.

Returns:

True once the message is gone.

Raises:
Return type:

bool

class SyncSentMessage(
*,
raw=<factory>,
id,
chat,
date,
reply_to=None,
markup=None,
text=None,
armored=None,
_bot=None,
)

Bases: SentMessageBase

One of the bot’s messages, with edit() and delete() (sync).

classmethod bind(message, bot)

Copies a plain SentMessageBase into one that acts through bot.

The methods that send do this to what they return, so you rarely call it yourself.

Parameters:
  • message (SentMessageBase) – The message as the API gave it back.

  • bot (SyncBot) – The bot its methods will go through.

Returns:

The same fields, as a SyncSentMessage bound to bot.

Return type:

SyncSentMessage

edit(text=None, *, markup=KEEP, encrypt=None)

New words, new buttons, or both: SyncBot.edit_message on this message.

Parameters:
  • text (str | None) – The new text, with the rules of SyncBot.send_message. None keeps it.

  • markup (InlineKeyboard | None) – The buttons. KEEP, the default, keeps them; None removes them; a keyboard replaces them. Never encrypted.

  • encrypt (bool | None) – None follows the chat; True refuses to write in clear, False refuses to write sealed.

Returns:

The edited message, as the server stored it.

Raises:
Return type:

SyncSentMessage

delete()

Deletes this message for everyone. A bot may always delete its own.

Returns:

True once the message is gone.

Raises:
Return type:

bool

KEEP

The default of markup in every edit, meaning “leave the buttons as they are”.

markup=None removes the buttons, and a keyboard replaces them. You rarely write KEEP yourself: Bot.edit_message, SentMessage.edit and CallbackQuery.edit_message use it when markup is left out. It is false in a boolean test, so check for it with markup is KEEP.

Chats and people

class ChatRef(*, raw=<factory>, id, kind, title=None, username=None)

A chat as updates name it: its ID and kind, and the title of a group or channel.

Every Bot method that takes a chat takes a ChatRef too, so message.chat can be passed as it is.

id

The chat’s ID.

Type:

int

kind

Private chat, group or channel.

Type:

ChatKind

title

The name of a group or channel. None in a private chat.

Type:

str | None

username

The public handle of a group or channel, without @. None in a private chat, and for a group or channel that has none.

Type:

str | None

property is_private: bool

True for a private chat between the bot and one person.

property is_group: bool

True for a group.

property is_channel: bool

True for a channel.

classmethod from_json(data)

Builds a chat from the API’s JSON (id, kind, title, username).

Parameters:

data (Any) – The chat object, such as the chat of a message.

Returns:

The chat, with data kept in raw. A missing kind is read as "private".

Raises:

PayloadError – data is not an object, or its id is missing or not an integer.

Return type:

ChatRef

class Chat(
*,
raw=<factory>,
id,
kind,
title=None,
username=None,
mode,
user=None,
blocked=None,
description=None,
member_count=None,
history_visible=None,
slow_mode_seconds=None,
me=None,
)

Bases: ChatRef

A chat the bot is in, with its mode: what Bot.get_chat returns (GET /chats/{id}).

It has every field of ChatRef, and more. A private chat fills user and blocked; a group or channel fills the others, including the bot’s own seat in me.

mode

Readable or end-to-end encrypted, and so which body a message must carry. If a server ever left it out, protected: the safe guess, since a sealed message sent to a readable chat is refused without giving anything away.

Type:

ChatMode

user

In a private chat, the person on the other side.

Type:

ChatPeer | None

blocked

In a private chat, True when the person blocked the bot.

Type:

bool | None

description

The description of a group or channel, or None.

Type:

str | None

member_count

How many members a group or channel has.

Type:

int | None

history_visible

In a group or channel, True when new members can read the history from before they joined. Such a chat is readable; any other is encrypted.

Type:

bool | None

slow_mode_seconds

In a group, the slow mode delay in seconds, 0 when it is off. Sending faster than that raises SlowModeError.

Type:

int | None

me

In a group or channel, the bot’s own seat.

Type:

Seat | None

property protected: bool

True when the chat is end-to-end encrypted (mode is protected).

classmethod from_json(data)

Builds a chat from the answer of GET /chats/{id}.

Parameters:

data (Any) – The chat object.

Returns:

The chat, with data kept in raw. A missing kind is read as "private", a missing mode as "protected".

Raises:

PayloadError – data is not an object, or it or its user has no integer id.

Return type:

Chat

class User(*, raw=<factory>, id, username=None, first_name=None, last_name=None, is_bot=False)

Somebody as a bot may know them: an ID, a handle and a name. Never a phone number.

id

The user’s ID.

Type:

int

username

Their handle, without @, or None if they have none.

Type:

str | None

first_name

Their first name, or None.

Type:

str | None

last_name

Their last name, or None.

Type:

str | None

is_bot

True for a bot.

Type:

bool

Example

>>> anna = User.from_json({"id": 55, "username": "anna", "firstName": "Anna"})
>>> anna.full_name, anna.mention
('Anna', '@anna')
property full_name: str

The first and last name, joined by a space.

Without a name, the handle with @; without either, "user <id>", as in "user 55".

property mention: str

The @handle when the user has one, full_name otherwise.

classmethod from_json(data)

Builds a user from the API’s JSON (id, username, firstName, …).

Parameters:

data (Any) – The user object, such as the from of a message.

Returns:

The user, with data kept in raw.

Raises:

PayloadError – data is not an object, or its id is missing or not an integer.

Return type:

User

class ChatPeer(
*,
raw=<factory>,
id,
username=None,
first_name=None,
last_name=None,
is_bot=False,
deleted=False,
has_key=False,
)

Bases: User

Somebody with a key: Chat.user, the other side of a private chat, and Resolved.user.

It has every field of User, and two more. isBot is sent for a lookup but not for the person in a private chat, where is_bot stays False; deleted is the other way round, since a lookup never finds a closed account to begin with.

deleted

True when the person has deleted their account.

Type:

bool

has_key

True when the person has published an OpenPGP key. Without one, the bot cannot send them anything in an encrypted chat.

Type:

bool

classmethod from_json(data, where='chat.user')

Builds the person from a user object the server sent.

Parameters:
  • data (Any) – The user object: of a private chat in GET /chats/{id}, or of GET /resolve/{username}.

  • where (str) – The name this object goes by in an error message.

Returns:

The person, with data kept in raw.

Raises:

PayloadError – data is not an object, or its id is missing or not an integer.

Return type:

ChatPeer

class Seat(
*,
raw=<factory>,
status,
admin_rights=None,
can=None,
custom_title=None,
rights=None,
restricted_until=None,
)

A place in a chat: the bot’s own (Chat.me), or somebody’s (Member).

Which fields are set depends on status. An admin seat carries admin_rights, can and maybe custom_title; a member or restricted seat carries rights and maybe restricted_until; left and kicked carry nothing else.

status

Where the seat stands: admin, member, restricted, left or kicked.

Type:

SeatStatus

admin_rights

An administrator’s rights, as a bit mask. can has the same rights by name.

Type:

int | None

can

An administrator’s rights, by name.

Type:

AdminRights | None

custom_title

An administrator’s title, or None.

Type:

str | None

rights

What a member or a restricted member may do, as a bit mask.

Type:

int | None

restricted_until

When a restriction ends. None when it has no end, and for a seat that is not restricted.

Type:

datetime | None

property is_admin: bool

True for an administrator’s seat.

property is_inside: bool

True when the seat is in the chat (admin, member or restricted).

classmethod from_json(data)

Builds a seat from the API’s JSON (status, adminRights, can, …).

Parameters:

data (Any) – The seat object, such as the me of a chat or the old and new of a my_chat_member update.

Returns:

The seat, with data kept in raw. A missing status is read as "left".

Raises:

PayloadError – data is not an object.

Return type:

Seat

class Member(
*,
raw=<factory>,
status,
admin_rights=None,
can=None,
custom_title=None,
rights=None,
restricted_until=None,
user,
)

Bases: Seat

Somebody’s seat in a group or channel, with who they are.

Bot.get_chat_member returns one, and ChatMembers.members lists them. It has every field of Seat, and one more.

user

Who holds the seat.

Type:

User

classmethod from_json(data)

Builds a member from the API’s JSON: a seat with a user in it.

Parameters:

data (Any) – The member object.

Returns:

The member, with data kept in raw.

Raises:

PayloadError – data is not an object, or its user is missing or has no integer id.

Return type:

Member

class AdminRights(
*,
raw=<factory>,
delete_messages=False,
ban_users=False,
add_members=False,
pin_messages=False,
change_info=False,
manage_admins=False,
invite_links=False,
post_messages=False,
edit_messages=False,
manage_comments=False,
)

What an administrator may do, one flag per right: Seat.can.

A right that this version does not know is still in raw, under its JSON name.

delete_messages

May delete other people’s messages.

Type:

bool

ban_users

May ban users.

Type:

bool

add_members

May add members, bots included.

Type:

bool

pin_messages

May pin messages.

Type:

bool

change_info

May change the chat’s info.

Type:

bool

manage_admins

May manage the administrators.

Type:

bool

May manage invite links.

Type:

bool

post_messages

May post messages.

Type:

bool

edit_messages

May edit messages.

Type:

bool

manage_comments

May manage comments.

Type:

bool

classmethod from_json(data)

Builds the rights from the can object of an administrator’s seat.

Parameters:

data (Any) – The can object, with one boolean per right (deleteMessages, banUsers, …).

Returns:

The rights, with data kept in raw. A right that is missing is False.

Raises:

PayloadError – data is not an object.

Return type:

AdminRights

class ChatMembers(*, raw=<factory>, members, member_count)

The members of a group or channel, as the bot may see them: Bot.get_chat_members.

The answer of GET /chats/{id}/members. The bot sees what a member in its seat would see: where the member list is hidden, a bot that is not an admin sees the admins only.

members

Each member, with their seat.

Type:

tuple[Member, …]

member_count

How many members are in members. It is the length of the list the bot may see, not the size of the chat: Chat.member_count counts everyone.

Type:

int

classmethod from_json(data)

Builds the list from the answer of GET /chats/{id}/members.

Parameters:

data (Any) – The answer, with members and memberCount.

Returns:

The members, with data kept in raw. Without memberCount, member_count is the length of members.

Raises:

PayloadError – data is not an object, or one of its members is not a valid member object (see Member.from_json).

Return type:

ChatMembers

class ChatKeys(*, raw=<factory>, keys, member_count=None)

The keys a message into an encrypted chat must be sealed to: Bot.get_chat_keys.

The answer of GET /chats/{id}/keys. Bot fetches these keys by itself before it sends into an encrypted chat. The bot’s own key is never in the list: add it yourself when you seal by hand (BotKey.seal does). Bots in privacy mode are left out, as the app does.

keys

One UserKey per member who has a key. In a private chat, the person’s only.

Type:

tuple[UserKey, …]

member_count

In a group or channel, how many members it has. Always more than the number of keys: the bot itself is not in keys, nor is a member without a key. None in a private chat.

Type:

int | None

classmethod from_json(data)

Builds the list from the answer of GET /chats/{id}/keys.

Parameters:

data (Any) – The answer, with keys and, in a group or channel, memberCount.

Returns:

The keys, with data kept in raw.

Raises:

PayloadError – data is not an object, or one of its keys is not a valid key object (see UserKey.from_json).

Return type:

ChatKeys

class UserKey(*, raw=<factory>, user_id, fingerprint, public_key)

Somebody’s OpenPGP public key, as the server hands it out.

Bot.get_user_key returns one, and ChatKeys.keys lists them. The fingerprint is what the server says: PublicKey.from_user_key reads the key and checks that it has that fingerprint before anything is sealed to it.

user_id

Whose key it is.

Type:

int

fingerprint

The key’s fingerprint as the server gives it, or None.

Type:

str | None

public_key

The key, as ASCII-armored text.

Type:

str

Warning

The keys come from the server. A server that lies could hand out a key of its own in place of a person’s. Bot(pin_keys=True) notices when a person’s key changes.

classmethod from_json(data)

Builds a key from the API’s JSON (userId, fingerprint, publicKey).

Parameters:

data (Any) – The key object, such as the answer of GET /users/{userId}/key.

Returns:

The key, with data kept in raw.

Raises:

PayloadError – data is not an object, its publicKey is missing, or its userId is missing or not an integer.

Return type:

UserKey

Handles

class Resolved(*, raw=<factory>, kind, id, username=None, user=None, chat=None)

What a handle turned out to be: the answer of Bot.resolve.

id is filled in whichever it was, so a caller that only wants the number never has to look at kind. Exactly one of user and chat carries the rest.

kind

Whether the handle belongs to a person, a group or a channel.

Type:

HandleKind

id

The user ID, or the chat ID.

Type:

int

username

The handle it actually goes by, which is not always the one that was asked with: somebody who answers to several returns their main one.

Type:

str | None

user

The person, when kind is user. None otherwise.

Type:

ChatPeer | None

chat

The group or channel, when kind is group or channel. None otherwise.

Type:

ChatCard | None

Example

>>> found = Resolved.from_json({"kind": "user", "id": 55, "username": "anna",
...                             "user": {"id": 55, "username": "anna"}})
>>> found.id, found.is_user, found.chat
(55, True, None)
property is_user: bool

True when the handle belongs to a person or a bot.

property is_chat: bool

True when the handle belongs to a group or a channel.

property mention: str

The handle with an @ in front, or "id <n>" for something that has none.

classmethod from_json(data)

Builds the answer of GET /resolve/{username}.

Parameters:

data (Any) – The result object.

Returns:

What the handle was, with data kept in raw. A missing kind is read as "user", which is what a bare user object means.

Raises:

PayloadError – data is not an object, or its id is missing or not an integer.

Return type:

Resolved

class ChatCard(
*,
raw=<factory>,
id,
kind,
title=None,
username=None,
description=None,
member_count=None,
history_visible=None,
)

Bases: ChatRef

A public group or channel seen from outside: Resolved.chat.

What its short-link page shows and no more, since the bot is not in it: no mode to pick a body with and no seat. Pass it straight to Bot.get_chat once an administrator has added the bot.

description

The description, or None.

Type:

str | None

member_count

How many members it has.

Type:

int | None

history_visible

True when somebody walking in can read what was said before.

Type:

bool | None

classmethod from_json(data)

Builds the card from the chat of GET /resolve/{username}.

Parameters:

data (Any) – The chat object of the answer.

Returns:

The card, with data kept in raw.

Raises:

PayloadError – data is not an object, or its id is missing or not an integer.

Return type:

ChatCard

The bot

class BotInfo(
*,
raw=<factory>,
id,
username,
name,
about=None,
description=None,
mode=ChatMode.OPEN,
fingerprint=None,
privacy_mode=True,
allow_groups=True,
status=BotStatus.ACTIVE,
webhook=<factory>,
queued=0,
limits=<factory>,
)

The bot as the server holds it, and the limits the server enforces (GET /me).

Bot.get_me returns it, and Bot keeps the answer it got at start-up in Bot.me. It works while the bot is paused, so it is the way to find out why nothing else does.

id

The bot’s user ID.

Type:

int

username

Its handle, without @. It ends in bot.

Type:

str

name

Its display name.

Type:

str

about

The one line under the name on its profile, or None.

Type:

str | None

description

What the bot does, shown before the first message, or None.

Type:

str | None

mode

The mode of the bot’s private chats: protected once it has published a key, open before.

Type:

ChatMode

fingerprint

The fingerprint of the published key, or None.

Type:

str | None

privacy_mode

True when, in groups, the bot gets only what is addressed to it: commands, mentions of its handle and replies to its messages. False when it gets every message of a readable group. The owner sets it in the app.

Type:

bool

allow_groups

True when people can add the bot to groups.

Type:

bool

status

active, or paused when the owner paused the bot.

Type:

BotStatus

webhook

The webhook in brief.

Type:

WebhookSummary

queued

How many updates are waiting to be fetched or delivered.

Type:

int

limits

The numbers this server enforces.

Type:

Limits

Example

Read a limit instead of hard-coding it:

me = await bot.get_me()
if len(report) > me.limits.text_max:
    report = report[: me.limits.text_max - 3] + "..."
property protected: bool

True when mode is protected, so the bot’s private chats are encrypted.

property paused: bool

True when the owner paused the bot.

While it is paused, most API calls raise BotPausedError (see BotStatus.PAUSED).

classmethod from_json(data)

Builds the bot from the answer of GET /me.

Parameters:

data (Any) – The answer.

Returns:

The bot, with data kept in raw. A missing mode is read as "protected" when fingerprint is set, "open" otherwise.

Raises:

PayloadError – data is not an object, or its id is missing or not an integer.

Return type:

BotInfo

class Limits(
*,
raw=<factory>,
text_max=4096,
armored_max=131072,
markup_bytes=8192,
keyboard_rows=20,
buttons_per_row=8,
buttons_total=100,
button_text_max=64,
callback_data_bytes=64,
url_max=1024,
callback_answer_max=200,
callback_window_ms=60000,
poll_timeout_max_s=50,
poll_limit_max=100,
webhook_timeout_ms=10000,
sends_per_second=30,
sends_per_group_per_minute=20,
sends_per_private_per_second=1,
api_calls_per_minute=600,
commands_per_scope=100,
command_description_max=256,
)

The numbers the server you are on enforces: BotInfo.limits.

Read them rather than hard-coding them. Bot uses some by itself: text_max to check a text before sending it, and the four rate limits to pace its own RateLimiter.

Each field is a JSON name of limits in Python style: text_max is textMax, poll_timeout_max_s is pollTimeoutMaxS. The defaults are the documented numbers. They apply only when a server leaves a name out, or sends something that is not a positive whole number. A limit this version has no field for is still in raw: read it with get.

text_max

The longest text, in UTF-16 units, as JavaScript counts them: most emoji count 2. Default 4096.

Type:

int

armored_max

The longest armored body, in characters. Default 131072.

Type:

int

markup_bytes

The largest keyboard, in bytes of JSON. Default 8192.

Type:

int

keyboard_rows

The most rows of buttons under one message. Default 20.

Type:

int

buttons_per_row

The most buttons in one row. Default 8.

Type:

int

buttons_total

The most buttons under one message. Default 100.

Type:

int

button_text_max

The longest button text, in UTF-16 units. Default 64.

Type:

int

callback_data_bytes

The longest callback_data of a button, in bytes of UTF-8. Default 64.

Type:

int

url_max

The longest URL of a link button, a callback answer or a webhook, in characters. Default 1024.

Type:

int

callback_answer_max

The longest text of a callback answer, in characters. Default 200.

Type:

int

callback_window_ms

How long a button press can still be answered, in milliseconds. Default 60000.

Type:

int

poll_timeout_max_s

The longest wait of a long poll, in seconds. Default 50.

Type:

int

poll_limit_max

The most updates one poll hands over. Default 100.

Type:

int

webhook_timeout_ms

How long the server waits for the webhook to answer, in milliseconds. Default 10000.

Type:

int

sends_per_second

Messages per second, over all chats. Default 30.

Type:

int

sends_per_group_per_minute

Messages per minute into one group or channel. Default 20.

Type:

int

sends_per_private_per_second

Messages per second into one private chat. Default 1.

Type:

int

api_calls_per_minute

API calls per minute, of any kind. Default 600.

Type:

int

commands_per_scope

The most commands in one menu. Default 100.

Type:

int

command_description_max

The longest command description, in characters. Default 256.

Type:

int

get(name, default=None)

Reads any limit by its JSON name, including the ones this version has no field for.

It reads what the server sent, in raw. A name the server left out gives default, even when a field has a documented default for it.

Parameters:
  • name (str) – The JSON name, such as "botsPerChat".

  • default (int | None) – What to return when the server did not send that limit as an integer.

Returns:

The server’s number, or default.

Return type:

int | None

Example

>>> limits = Limits.from_json({"textMax": 4096, "botsPerChat": 20})
>>> limits.get("botsPerChat")
20
>>> limits.get("pollLimitMax") is None
True
>>> limits.poll_limit_max
100
classmethod from_json(data)

Builds the limits from the limits object of GET /me.

Parameters:

data (Any) – The limits object. Anything else, None included, gives the defaults.

Returns:

The limits. The object itself is kept in raw.

Return type:

Limits

class WebhookInfo(
*,
raw=<factory>,
url=None,
has_secret=False,
pending=0,
failures=0,
last_error=None,
last_ok_at=None,
next_attempt_at=None,
)

The webhook, if any, and how deliveries are going: Bot.get_webhook.

GET /webhook fills every field. POST /webhook and DELETE /webhook (Bot.set_webhook and Bot.delete_webhook) answer with url, has_secret and pending only, and the other fields keep their defaults.

url

The address updates are posted to, or None when the bot uses long polling.

Type:

str | None

has_secret

True when a secret is set. The secret itself is never shown.

Type:

bool

pending

How many updates are still waiting.

Type:

int

failures

Failed deliveries in a row. After ten, the owner is told in the app.

Type:

int

last_error

What went wrong last, or None.

Type:

str | None

last_ok_at

When a delivery last worked, or None.

Type:

datetime | None

next_attempt_at

While the server backs off after a failure, when it tries again; otherwise None.

Type:

datetime | None

Example

See how the webhook is doing:

info = await bot.get_webhook()
if info.failures:
    print(f"{info.failures} failures in a row, last: {info.last_error}")
classmethod from_json(data)

Builds the webhook’s state from the answer of GET /webhook.

POST /webhook and DELETE /webhook answer with the same object, with fewer fields.

Parameters:

data (Any) – The answer.

Returns:

The webhook’s state, with data kept in raw.

Raises:

PayloadError – data is not an object.

Return type:

WebhookInfo

class WebhookSummary(
*,
raw=<factory>,
url=None,
has_secret=False,
failures=0,
last_error=None,
)

The webhook in brief, as part of BotInfo.

Bot.get_webhook gives the whole picture, as a WebhookInfo.

url

The address updates are posted to, or None when the bot uses long polling.

Type:

str | None

has_secret

True when a secret is set. The secret itself is never shown.

Type:

bool

failures

Failed deliveries in a row. After ten, the owner is told in the app.

Type:

int

last_error

What went wrong last, or None.

Type:

str | None

classmethod from_json(data)

Builds the summary from the webhook object of GET /me.

Parameters:

data (Any) – The webhook object. Anything else, None included, gives the defaults.

Returns:

The summary. The object itself is kept in raw.

Return type:

WebhookSummary

class PublishedKey(*, raw=<factory>, fingerprint, mode)

What the server answers when the bot publishes its key (PUT /key).

Bot.publish_key and Bot.rotate_key return it. From then on the bot is protected: its private chats are end-to-end encrypted, and only its owner can turn that off, from the app.

fingerprint

The fingerprint of the key now published.

Type:

str

mode

The bot’s mode now, protected.

Type:

ChatMode

classmethod from_json(data)

Builds the answer of PUT /key from its JSON.

Parameters:

data (Any) – The answer, with fingerprint and mode.

Returns:

The published key, with data kept in raw. A missing fingerprint is "", a missing mode is "protected".

Raises:

PayloadError – data is not an object.

Return type:

PublishedKey

Commands

class Command(*, raw=<factory>, command, description)

One entry of the menu the app shows when somebody types /.

Bot.get_commands, given a scope, returns a list of them. You can also make one to pass to Bot.set_commands, which takes (name, description) pairs as well.

command

The name, without the /: 1 to 32 lowercase letters, digits and _.

Type:

str

description

What the command does, 1 to 256 characters.

Type:

str

to_json()

Turns the command into the API’s JSON form.

Returns:

A dict with command and description, as PUT /commands takes it.

Return type:

dict[str, str]

Example

>>> Command(command="start", description="Say hello").to_json()
{'command': 'start', 'description': 'Say hello'}
classmethod from_json(data)

Builds a command from the API’s JSON (command, description).

Parameters:

data (Any) – One command object, as GET /commands lists them.

Returns:

The command, with data kept in raw. A missing field is "".

Raises:

PayloadError – data is not an object.

Return type:

Command

class CommandScope(*, raw=<factory>, scope, commands)

One command menu, and where the app shows it.

Bot.get_commands, without a scope, lists one per scope; Bot.set_commands and Bot.delete_commands answer with the one they changed. The app shows the narrowest scope that has commands: in a private chat private, then default; in a group chat:<id>, then groupAdmins (for admins), then groups, then default.

scope

"default", "private", "groups", "groupAdmins", or "chat:<id>" for one chat.

Type:

str

commands

The commands of the menu.

Type:

tuple[Command, …]

classmethod from_json(data)

Builds a menu from the API’s JSON (scope, commands).

Parameters:

data (Any) – One scope object, as GET /commands lists them and PUT /commands answers.

Returns:

The menu, with data kept in raw. A missing scope is "default".

Raises:

PayloadError – data is not an object, or one of its commands is not an object.

Return type:

CommandScope