Types¶
The hand-written wrappers a handler actually receives. Each keeps the TL object it was
built from on .raw, so nothing is lost by taking the friendly one.
sunnygram.types.message.Message
dataclass
¶
One message, and what can be done about it.
payment
property
¶
The payment this service message reports, if it reports one.
A successful payment arrives as a service message instead of as an update of its own, so this is where a bot finds out it was paid. Only the bot that sold the thing gets the reading carrying the charge id, which a refund needs.
chat_id
property
¶
Which chat this is in, by id, even when nothing named the chat.
A message that arrives alongside an answer comes with the users and chats that answer carried, and chat is the friendly form of that. A message the server made of something we just sent often arrives with none of them, so there is an id inside it and nothing to build a Chat out of. Everything that only needs the id works either way.
buttons
property
¶
The rows of inline buttons under this message, if it has any.
file_ref
property
¶
This message's file, as one string that can be written down.
The portable form: put it in a database, hand it to another process, and sending the same file later costs one call with no upload and no download. It names where the message came from as well, so the token inside it can be renewed when it goes stale.
album_id
property
¶
Which album this message is part of, if it is part of one.
An album is not one message carrying several files. It is several messages that share this id, which the clients then draw as one block, so a photo in an album arrives here on its own like any other.
react
async
¶
React to this message, or clear this account's reactions on it.
copy_to
async
¶
Send this message on without saying where it came from.
The difference from forward_to is what the other side sees: a forward carries the original author's name, and this does not.
reply
async
¶
Answer this message in its own chat, as a reply to it.
respond
async
¶
Say something in the same chat, without replying to anything.
reply_file
async
¶
Answer this message with a file, as a reply to it.
The kind is worked out from the name, so a .mp4 arrives playable and a .jpg as a photo. Pass kind= to overrule that.
reply_album
async
¶
Answer this message with several files as one group.
reply_media
async
¶
Answer with a file Telegram already holds, uploading nothing.
reply_file is for bytes that have to go up. This is for a file that is already there: what another message carries, or what a program wrote down after sending it once. Passing one of those to reply_file would try to upload the reference itself.
edit
async
¶
Rewrite this message, which only works on one of ours.
edit_media
async
¶
Replace the file this message carries with another one.
edit_markup
async
¶
Change the buttons under this message, or take them away.
get_reply
async
¶
The message this one answers, fetched only if it has to be.
Nearly always it does not: the message being replied to came past this client a moment ago and is already on reply_to_message, and the quoted kind of reply carries an outline of it in the update itself. Both are answered from here without a call. What is left is an old message someone scrolled up to, and that costs one.
An outline is not enough for this, so a quote is exchanged here for the message it was taken from.
download
async
¶
Fetch whatever file this message carries.
The message goes down instead of the media off it, so that a file reference which has gone stale can be renewed: the message is what renews it, and the media on its own does not say which message that was.
from_raw
classmethod
¶
from_raw(message: Any, *, users: dict[int, Any] | None = None, chats: dict[int, Any] | None = None, replies: dict[int, Any] | None = None, client: Any = None) -> Message | None
Wrap a message off the wire, with whatever came alongside it.
The users and chats are the ones the same answer carried. They are what turns the ids inside a message into people and places, which is why every call that returns messages returns them too.
replies is the same idea one step further: the other messages in the same answer, so that a reply to one of them is tied to it here rather than fetched later. A page of history usually contains both halves of a conversation, and pairing them up costs a dict lookup.
sunnygram.types.callback.CallbackQuery
dataclass
¶
One press of an inline button.
text
property
¶
The payload as text, which is how nearly every bot writes one.
Payloads are bytes on the wire and a program almost always puts a short string in them, so this is the form handlers and filters read. A payload that is not text at all reads as empty instead of raising, since a filter asking about it is not the place to find that out.
chat_id
property
¶
Which chat the press came from, even when nothing named the chat.
The same shape as Message.chat_id and for the same reason: chat is built out of the users and chats an update carried, and an update that carried none of them still has the peer inside it. A press on an inline message has no chat at all and answers None, which is the honest answer instead of a zero that would compare equal to something.
is_inline
property
¶
Whether the message this was pressed on came from an inline query.
answer
async
¶
Stop the spinner, and optionally say something while doing it.
Nothing at all is a valid answer and is what a bot sends when the real reply is an edit to the message. text puts a notice along the top of the screen; alert makes it a box they have to dismiss instead.
cache_time lets the client answer the same press itself for that many seconds without asking again, which is worth setting for a button whose answer cannot change.
edit
async
¶
Rewrite the message this button is under.
The usual way to answer a press: the message becomes the new state and the buttons change with it. An inline message answers with nothing, because Telegram says only whether the edit went through.
edit_markup
async
¶
Change the buttons and leave the text alone, or take them away.
Passing nothing removes the keyboard, which a bot does with a one-shot menu once it has been used.
get_message
async
¶
Fetch the message this button is under.
Costs a call. An inline message cannot be fetched at all: it has no chat to fetch it from, which is why editing one takes the opaque id rather than a message.
reply
async
¶
Say something new in the chat, as a reply to the message pressed.
from_raw
classmethod
¶
from_raw(update: Any, *, users: dict[int, Any] | None = None, chats: dict[int, Any] | None = None, client: Any = None) -> CallbackQuery | None
Wrap a press off the wire, with whatever came alongside it.
sunnygram.types.user.User
dataclass
¶
Someone with an account.
marked_id
property
¶
The same id, for code that stores peers without knowing the kind.
A person's id is already unambiguous, so this is the id itself. It is here so that anything holding either a Chat or a User can write down one number without asking which it is holding.
from_raw
classmethod
¶
Wrap a user off the wire, or answer None for one with nothing in it.
sunnygram.types.chat.Chat
dataclass
¶
A conversation, whoever or whatever is on the other side of it.
marked_id
property
¶
This chat's id in the spelling that says what it is on its own.
id is the id the protocol uses, which is the right one for a raw call and the wrong one to write down on its own: the number does not say which of the three id spaces it came from, so a stored 3003 could be a person or a small group. This pairs it back with the kind, which is what anything keeping a chat id in a database or a config wants.
of_peer
classmethod
¶
The chat a Peer names, out of what came alongside it.
A Peer is an id and which of the three id spaces it is in, and nothing else, so it only becomes a chat with the users and chats the same answer carried.
sunnygram.types.dialog.Dialog
dataclass
¶
A conversation as it appears in the list of them.
from_raw
classmethod
¶
from_raw(dialog: Any, *, users: dict[int, Any] | None = None, chats: dict[int, Any] | None = None, messages: dict[int, Any] | None = None, client: Any = None) -> Dialog | None
Wrap a dialog, with everything the same page carried.
The last message is looked up by id among the messages that came with it instead of fetched, which is why this takes them: the answer that carries the dialogs carries their last messages too, and going back for one would be a round trip per row.
sunnygram.types.topic.Topic
dataclass
¶
A thread in a forum, as someone reading it sees it.
from_raw
classmethod
¶
from_raw(topic: Any, *, chat_id: int = 0, users: dict[int, Any] | None = None, chats: dict[int, Any] | None = None, messages: dict[int, Any] | None = None, client: Any = None) -> Topic | None
Wrap a topic, with the messages that came on the same page.
A deleted topic arrives as a different constructor carrying only an id, and there is nothing to wrap in that, so it comes back as nothing.
Inline mode¶
Both halves of it: the query that arrived, the result that goes back, and what was picked. See Inline mode.
sunnygram.types.inline.InlineQuery
dataclass
¶
What someone has typed after the bot's name, so far.
answer
async
¶
answer(results: list[InlineResult | Any], *, cache_time: int = CACHE_TIME, gallery: bool = False, private: bool = False, next_offset: str = '', switch_pm: str = '', start_parameter: str = '', parse_mode: str | None = '') -> bool
Offer these results, which stops the client loading.
Answering is not optional. Telegram holds the query open until the bot says something about it, and until then the person sees a panel that never finishes. An empty list is a complete answer and is what a bot sends when it has nothing for that query.
gallery draws the results as a grid of pictures rather than as a list of rows. private means the answer was built for this one person and must not be cached for anybody else, which matters the moment a result depends on who asked. next_offset is the cursor the next query in the same session arrives with, and is how a long list is paged: hand back where this page ended and the client asks for the rest by scrolling.
switch_pm puts a button above the results that takes the person into the bot's own chat, carrying start_parameter with them. That is how a bot that needs setting up first says so, instead of answering with an apology it has no way to act on.
from_raw
classmethod
¶
from_raw(update: Any, *, users: dict[int, Any] | None = None, client: Any = None) -> InlineQuery | None
Wrap a query off the wire, with whoever came alongside it.
sunnygram.types.inline.InlineResult
dataclass
¶
One thing a bot offers in answer to an inline query.
Built by the factories below instead of by hand: each of them knows which of Telegram's four result constructors it needs and what has to travel with it. What they have in common is the message, which gets sent if this result is the one picked, and which is not built until the answer goes out because the parse mode belongs to the client rather than to the result.
article
classmethod
¶
article(title: str, text: str, *, description: str = '', url: str = '', thumb: str = '', id: str = '', no_webpage: bool = False, reply_markup: Any = None) -> InlineResult
A row of text, which is what most bots answer with.
The title and description are what the person reads in the list; text is the message they send by picking it, and the two have no reason to be the same. A url is shown as where the article came from and is not opened by picking it.
photo
classmethod
¶
photo(photo: Any, *, caption: str = '', thumb: str = '', id: str = '', reply_markup: Any = None) -> InlineResult
A photo, either one Telegram holds or one on the web.
Anything send_media takes is a photo it already holds: a Photo off a message, an InputPhoto, a portable file reference, a foreign file id. A http link is the other case, and Telegram fetches it itself when the result is picked, so the link has to still work then.
A photo Telegram already holds carries no title or description, because its own constructor has nowhere to put them.
animation
classmethod
¶
animation(animation: Any, *, caption: str = '', title: str = '', thumb: str = '', id: str = '', reply_markup: Any = None) -> InlineResult
A soundless looping video, which is what a gif is here.
Telegram tells the two web forms apart by what is being served: a real gif is a gif, and an mp4 with no sound is what every client actually wants, so a link ending in mp4 is offered as that.
video
classmethod
¶
video(video: Any, *, caption: str = '', title: str = '', description: str = '', thumb: str = '', id: str = '', reply_markup: Any = None) -> InlineResult
A video, held or on the web.
audio
classmethod
¶
audio(audio: Any, *, caption: str = '', title: str = '', description: str = '', thumb: str = '', id: str = '', reply_markup: Any = None) -> InlineResult
A song or any other audio file.
voice
classmethod
¶
voice(voice: Any, *, caption: str = '', title: str = '', id: str = '', reply_markup: Any = None) -> InlineResult
A voice note, which is an audio file drawn as a waveform.
document
classmethod
¶
document(document: Any, *, caption: str = '', title: str = '', description: str = '', thumb: str = '', mime: str = 'application/octet-stream', id: str = '', reply_markup: Any = None) -> InlineResult
Any file at all, which is what everything that is not a photo is.
The mime type only matters for the web form, where Telegram has nothing but the link to go on. Telegram allows pdf and zip there and nothing else, which is its rule instead of this one.
sticker
classmethod
¶
A sticker, which has to be one Telegram already holds.
There is no web form for this one: a sticker is a document in a set and a link to an image is not one.
location
classmethod
¶
location(latitude: float, longitude: float, title: str, *, live_period: int = 0, heading: int = 0, thumb: str = '', id: str = '', reply_markup: Any = None) -> InlineResult
A point on the map.
live_period turns it into a location that keeps updating for that many seconds, which is what sharing your position looks like.
venue
classmethod
¶
venue(latitude: float, longitude: float, title: str, address: str, *, provider: str = '', venue_id: str = '', venue_type: str = '', thumb: str = '', id: str = '', reply_markup: Any = None) -> InlineResult
A place, which is a point on the map with a name and a street.
contact
classmethod
¶
contact(phone: str, first_name: str, *, last_name: str = '', vcard: str = '', thumb: str = '', id: str = '', reply_markup: Any = None) -> InlineResult
Someone's phone number, as a contact card.
game
classmethod
¶
One of the bot's games, by the short name it was registered with.
to_raw ¶
This result as the protocol spells it.
Four constructors, and which one is right follows from what the result carries: a game is its own, a photo Telegram holds is its own, any other file it holds is the document one, and everything else, web forms included, is the general one.
sunnygram.types.inline.ChosenResult
dataclass
¶
Which result someone picked, and what they had typed to find it.
Only bots that asked for this are told, and asking is a setting on the bot instead of a call: BotFather calls it inline feedback. Telegram samples it for busy bots, so this is a statistic, not a receipt, and a program that has to know something happened should learn it from the message.
editable
property
¶
Whether the message that was sent can still be rewritten.
Only if the result carried an inline keyboard. Telegram issues an id for that message so the buttons under it can be answered, and with no buttons there is nothing to answer and no id, so the message is gone from the bot's reach the moment it is sent.
edit_markup
async
¶
Change the buttons under the message this result sent.
from_raw
classmethod
¶
from_raw(update: Any, *, users: dict[int, Any] | None = None, client: Any = None) -> ChosenResult | None
Wrap a chosen result off the wire.
Members¶
sunnygram.types.member.Member
dataclass
¶
Someone's standing in a chat, whichever kind of chat it is.
is_admin
property
¶
Whether they may act on the chat, the creator included.
This is the question most callers are really asking, and asking it directly avoids the mistake of testing for admin and forgetting that the creator is not one.
from_raw
classmethod
¶
Wrap whichever of the nine constructors arrived.
sunnygram.types.member.MemberStatus ¶
Bases: StrEnum
Where someone stands, in the six ways Telegram distinguishes.
sunnygram.types.member.MemberUpdate
dataclass
¶
Someone's standing in a chat changing, from what to what.
Both kinds of chat produce this and they are the same thing here. What Telegram gives is a pair, and the pair is what answers every question worth asking: someone with no standing before and a standing now has joined; someone who was an ordinary member and is now an admin has been promoted; someone present before and absent now has left, or has been thrown out, and which of those it was is whether they did it to themselves.
Either side of the pair can be missing, and the missing one is the information: no standing before means they were not here, and no standing after means they are not here now.
chat_id is marked the Bot API way, which is the spelling resolve takes back, so a greeter can answer straight into the chat. The members inside carry no chat id of their own, deliberately: one number for the chat, in one place, instead of the same chat spelled two ways in one object.
what
property
¶
One word for what happened, which a log line wants.
The words are the questions below, tried in the order that makes the most specific one win: being banned is also leaving, and being promoted while joining is still joining.
restricted
property
¶
Whether they are present and newly limited in what they may do.
by_self
property
¶
Whether they did this to themselves.
The difference between joining and being added, and between leaving and being removed. Telegram says both with the same pair and only the actor tells them apart.
invite_link
property
¶
The link they came in through, if they came in through one.
Worth reading on a join: a chat with several links knows which campaign someone arrived from, and this is where that is said.
status
property
¶
Where they stand now, or nothing if they are no longer anywhere.
from_raw
classmethod
¶
Wrap either of the two updates that say this.
sunnygram.types.join.JoinRequest
dataclass
¶
One person waiting to be let into one chat.
invite_link
property
¶
The link they used, when it was a link they could be told apart by.
A chat with several links can tell where someone came from, which is the whole reason to have several. A request that came in some other way has nothing here instead of a made up link.
decline
async
¶
Turn them down.
They are told, and they may ask again: this is a refusal, not a ban. Banning is what stops someone asking forever.
from_raw
classmethod
¶
from_raw(update: Any, *, users: dict[int, Any] | None = None, client: Any = None) -> JoinRequest | None
Wrap a request off the wire, with whoever came alongside it.
Reactions and polls¶
sunnygram.types.reaction.ReactionUpdate
dataclass
¶
Reactions on one message changing, in whichever way we were told.
by_person
property
¶
Whether this reading is one person's reactions, not totals.
The one question worth asking before reading anything else. A bot gets this reading and a user account gets the other, and which one arrived decides whether actor_id and the before and after pair mean anything.
added
property
¶
What this person just put on the message.
Empty on the totals reading, because it cannot be worked out from totals: two people swapping reactions leaves every number the same.
get_message
async
¶
Fetch the message these reactions are on. Costs a call.
from_raw
classmethod
¶
Wrap whichever of the three updates that say this arrived.
sunnygram.types.poll.Poll
dataclass
¶
A poll as it stands, as far as this update says.
known
property
¶
Whether this update carried the poll itself or only its results.
Telegram sends the question and the answers when the poll changed and the results alone the rest of the time, so a program that wants to draw the whole poll from an update either keeps the first one it saw or asks for it with get_poll.
located
property
¶
Whether this update said which message the poll is in.
It usually does not. The poll id is what Telegram considers the name of a poll, and the message is only mentioned when the update happens to have it, so anything acting on the message has to be told where it is.
winner
property
¶
The answer with the most votes, or nothing if the poll is tied.
Nothing instead of an arbitrary one: a tie is a real outcome and picking a side of it silently is the kind of thing that is found out much later.
correct
property
¶
The right answer, for a quiz that has said which it is.
vote
async
¶
Answer the poll by position, or with nothing to take a vote back.
from_raw
classmethod
¶
Wrap a poll update, with or without the poll itself in it.
sunnygram.types.poll.PollAnswer
dataclass
¶
One of the things a poll offers, and how it is doing.
sunnygram.types.poll.PollVote
dataclass
¶
One person's vote in one poll.
Only a public poll produces this. The answers are positions, which is how they are counted from when the poll is sent, so a program reads options[0] as an index into the answers it wrote.
The small events¶
Records with nothing to do about them, which is why they share a file.
sunnygram.types.events.DeletedMessages
dataclass
¶
Messages that are gone, and where they were if that was said.
located
property
¶
Whether Telegram said which chat these were deleted from.
Only a channel says so. Everywhere else the ids belong to the account's own numbering across all of its private chats and small groups, and no chat is named, so this is False and chat_id is zero, not a plausible wrong answer.
from_raw
classmethod
¶
Wrap either of the two deletion updates.
sunnygram.types.events.Status
dataclass
¶
sunnygram.types.events.Typing
dataclass
¶
Someone doing something in a chat that is worth showing.
Typing is one of eighteen of these and the rest are just as real: recording a voice note, uploading a video, picking a sticker. They are all one event with a word for which, since a program either shows what someone is doing or ignores all of it.
from_raw
classmethod
¶
Wrap any of the three updates that say someone is doing something.
sunnygram.types.events.Blocked
dataclass
¶
Someone blocking this account, or unblocking it.
sunnygram.types.events.Stopped
dataclass
¶
Someone stopping a bot, or starting it again after having stopped it.
The bot side of being blocked, and the one number every bot should watch: it is the difference between a quiet audience and one that has left.
from_raw
classmethod
¶
Wrap a bot being stopped off the wire.
Buttons¶
A keyboard is a shape with no call attached, which is why it is here, not among the methods. See Buttons.
sunnygram.types.buttons.Button ¶
Every kind of button, one call each.
Nothing here talks to the network and nothing holds state, so a button can be built once and reused for as long as the program runs.
callback
staticmethod
¶
callback(text: str, data: str | bytes | None = None, *, password: bool = False) -> types.KeyboardButtonCallback
A button that calls the bot back with a payload.
The payload defaults to the label, which a program wants often enough to be worth not writing twice. It is capped at sixty four bytes by Telegram, so it is a key into what the program knows, not the thing itself, and something too long is refused here instead of on the wire.
password asks the person to confirm with their account password before the press goes through, which is Telegram's own safeguard for a button that does something irreversible.
url
staticmethod
¶
A button that opens a link. Nothing reaches the program.
login
staticmethod
¶
login(text: str, url: str, *, button_id: int = 0, forward_text: str = '') -> types.KeyboardButtonUrlAuth
A button that logs the person into a website as themselves.
The site is told who pressed it, once they have agreed to that, which is what makes this different from an ordinary link.
switch_inline
staticmethod
¶
switch_inline(text: str, query: str = '', *, same_chat: bool = False) -> types.KeyboardButtonSwitchInline
A button that starts an inline query somewhere.
Pressing it opens the chat picker with the bot's name and the query already typed. same_chat keeps it in the chat the button is in, which is the form a bot uses to hand someone its own results.
web_app
staticmethod
¶
A button that opens a mini app.
The ordinary form belongs under a message and tells the app who opened it. simple is the other one, which sits above the text field and does not.
game
staticmethod
¶
The button that starts the game a message carries.
pay
staticmethod
¶
The button that pays for the invoice a message carries.
copy
staticmethod
¶
A button that puts something on the clipboard, and calls no one.
profile
staticmethod
¶
A button that opens someone's profile.
text
staticmethod
¶
A plain suggestion above the text field, which sends its own label.
Nothing comes back but an ordinary message, so a handler for one of these is a text filter instead of a callback handler.
request_phone
staticmethod
¶
A suggestion that asks for the person's phone number.
They are asked to confirm, and what arrives is an ordinary message carrying a contact.
request_location
staticmethod
¶
A suggestion that asks where the person is, with the same confirmation.
request_poll
staticmethod
¶
A suggestion that opens the poll composer.
Saying nothing about quiz lets them choose; saying True or False fixes it to one kind.
sunnygram.types.buttons.keyboard ¶
keyboard(rows: Any, *, columns: int = 0, resize: bool = True, one_time: bool = False, persistent: bool = False, selective: bool = False, placeholder: str | None = None) -> base.ReplyMarkup
Build a keyboard out of buttons, in whichever kind they belong to.
The rows are a list of lists, or a flat list for a single row, or one button on its own. A string counts as a plain label, so a reply keyboard can be written as the words on it.
columns lays a flat list out for you, which is the case that otherwise turns three buttons into a nested list no one enjoys reading.
The remaining arguments only mean anything to a reply keyboard: resize shrinks it to the buttons rather than taking a third of the screen, and is on here because the other way round looks like a mistake in every client that draws it; one_time folds it away after a press; persistent keeps it up instead of the ordinary text field; selective shows it only to the people a message names or replies to; and placeholder is the grey text in the field behind it. An inline keyboard has nowhere to put any of them and ignores them.
sunnygram.types.buttons.force_reply ¶
force_reply(*, placeholder: str | None = None, one_time: bool = True, selective: bool = False) -> types.ReplyKeyboardForceReply
Open the other side's keyboard with this message already being replied to.
The way to ask a question and be sure the answer comes back attached to it, which is worth more in a group than in a private chat: without it an answer is an ordinary message and matching it to the question is guesswork.
sunnygram.types.buttons.remove_keyboard ¶
Take away the reply keyboard a previous message put up.
Only reply keyboards. An inline keyboard belongs to its message and is removed by editing that message's markup away.
sunnygram.types.buttons.buttons_of ¶
The rows of buttons under a message, or nothing if it has none.
Only the inline kind. The other keyboard is a suggestion of things to type and pressing one sends its text, which arrives as an ordinary message instead of as anything to do with the message that put the keyboard up.
Rights¶
Both of these say what someone may do, in the readable direction: True is allowed. See
Running a chat for why that is worth stating.
sunnygram.types.rights.AdminRights
dataclass
¶
What an administrator is allowed to do.
Everything is off by default, so promoting someone without saying what they may do gives them a title and no powers, which is the safe way round. Use the presets for the ordinary cases.
granted
property
¶
The names of everything that is on, for showing someone.
everything
classmethod
¶
Every power there is, short of anonymity.
Not the same as being the owner: transferring ownership is its own call and is deliberately not reachable from here.
moderator
classmethod
¶
The usual set for someone keeping order: remove people and posts.
with_ ¶
The same rights with a few changed, since these do not mutate.
to_raw ¶
The TL form, which shares this convention and needs no flipping.
from_raw
classmethod
¶
Read a set back off a chat or a participant.
sunnygram.types.rights.Permissions
dataclass
¶
What an ordinary member is allowed to do.
True is allowed. Restricting someone is passing the set with the things they may no longer do turned off, and the same shape sets the default for everybody in a chat.
Note what Telegram does with the group of them: turning off send_messages turns off everything that is a way of sending a message, whatever this says about the rest, because the server treats it as covering them. That is the server's rule and it is not undone here.
denied
property
¶
The names of everything that is off, which is what a restriction is.
everything
classmethod
¶
No restriction at all, which is also what lifting one looks like.
read_only
classmethod
¶
The usual mute: silenced but still present, able to read and no more.
none
classmethod
¶
Nothing at all, not even seeing the chat, which is what a ban is.
with_ ¶
The same permissions with a few changed, since these do not mutate.
to_raw ¶
The TL form, with every flag flipped to the banned convention.
until is when the restriction lifts, as a unix time. Zero is forever, which is Telegram's own spelling and not a missing value.
from_raw
classmethod
¶
Read a set back off a chat or a participant, flipping as it comes.
Updates¶
sunnygram.updates.manager.Event
dataclass
¶
One update, with the users and chats it talks about.
Updates name people and chats by id and leave the client to know the rest, so whatever came in the same container is carried along here. The peer cache has them too by the time this arrives, but it keeps only what is needed to reach someone; the full objects are here, for as long as the event is.
sunnygram.updates.manager.UpdateManager ¶
The single source of truth for how far through the updates we are.
events
property
¶
Updates in order, once each, for whoever wants to act on them.
dropped_events
property
¶
How many events were dropped because no one was draining them.
These are gone, and unlike every other loss in this layer they are not made up for later. By the time an event reaches the queue its counter has already been applied, so as far as the stream is concerned it was delivered, and no difference will ever mention it again. That is the deliberate half of rule P6: a program that stops reading loses the newest rather than stalling the session. A number above zero here means the program was handed something it never looked at, and the remedy is to drain faster or to build the manager with a bigger events_queue, because nothing this layer does afterwards can recover it.
The other kind of loss, further down, is recoverable and is counted by resyncs instead.
failures
property
¶
How many times acting on an update did not go through.
Not fatal on its own: whatever failed leaves a gap, and the next update to notice it asks again. A number that keeps climbing is the signal.
resyncs
property
¶
How many times the stream had to be rebuilt from a difference.
Counts the recoveries that were not the ordinary kind: a session the server started for itself, and updates the connection underneath threw away before they reached here. Both are survivable, because in both cases the counters had not moved yet and a difference can still fetch what went missing. Neither should be routine, so a number that keeps climbing says the program is either reconnecting or falling behind.
Not to be confused with dropped_events, which counts what was thrown away on the way out of this layer instead of on the way in, and which no difference can bring back.
idle_catch_up
property
¶
How long the stream may stay silent before we go and ask anyway.
Zero turns the watchdog off, which is the right answer only for a program that would rather miss the news than make a call it did not ask for.
start
async
¶
Learn where the stream is, then follow it.
catch_up decides what happens to whatever was missed while the program was not running. Fetching it is the right default for anything that acts on messages; skipping it suits a program that only cares about what happens from now on, and is much cheaper after a long absence.
feed
async
¶
Take one thing the server sent and get every update out of it.
Public because a call that changes something answers with updates of its own, and those count exactly as much as the ones that arrive on their own. Sending a message and then being told about it twice is the bug this prevents.
sunnygram.recent.RecentMessages ¶
A bounded, least-recently-used record of messages, by chat and id.
remember ¶
Hold onto a message, evicting the least recently used if full.
A message that does not say which chat it is in is skipped instead of stored under a guess: without one there is no key that a later lookup could use. The id is enough, so a message that arrived with no chat object alongside it is still held.
forget ¶
Drop one, for a message that is known to be gone.
Text¶
sunnygram.parser.parse ¶
Read marked-up text, and answer with the text and its entities.
sunnygram.parser.unparse ¶
Write text and its entities back out as marked-up text.