Clients should report read state, message views and music listens using a specific set of methods.
---functions---
messages.readHistory#0e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
messages.readSavedHistory#ba4a3b5b parent_peer:InputPeer peer:InputPeer max_id:int = Bool;
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool;
Use one of the following methods, according to the type of the dialog where the messages were marked as read:
Batch calls to these methods as follows:
These methods only update the server-side unread state: they do not increment channel post view counters and do not send viewport metrics.
message#95ef6f2b flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post_stars:flags2.8?true paid_suggested_post_ton:flags2.9?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int from_rank:flags2.12?string peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long guestchat_via_from:flags2.19?Peer reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost schedule_repeat_period:flags2.10?int summary_from_language:flags2.11?string = Message;
decryptedMessageActionReadMessages#0c4f40be random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageService#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage;
---functions---
messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
The readHistory methods ยป should still always be invoked in batched mode when reading incoming messages: however, the following methods should also be used in addition to readHistory, to mark certain types of individual media messages as read, mark live locations as read, and start deletion timers for expiring media files:
Unlike the readHistory methods, these methods take a list of specific message IDs rather than a max_id range.
To avoid re-fetching the updated message constructors when marking non-secret chat messages as read, clients should locally clear the following unread state after successfully sending the readMessageContents request:
Invoke these methods only for incoming messages, in the cases described below.
For an incoming non-secret chat message with an unread media mention, invoke messages.readMessageContents or channels.readMessageContents when the message becomes visible in the chat.
Condition: message.mentioned && message.media_unread && !message.isVoice() && !message.isRoundVideo()
An unread media mention is represented by the message.mentioned and message.media_unread flags both being set.
Do not use message.media_unread alone as a visibility trigger. Without message.mentioned, unread playable media is marked as read only when playback starts or, for expiring media, when the media is opened.
Voice messages have a messageMediaDocument with a documentAttributeAudio with the voice flag set.
Round videos have a messageMediaDocument with a documentAttributeVideo with the round_message flag set.
Voice messages and round videos are excluded from this condition because they are marked as read when played.
Secret chat messages are excluded from this condition because decryptedMessage does not contain mentioned or media_unread flags.
For an incoming non-secret chat message with unread playable media, invoke messages.readMessageContents or channels.readMessageContents when playback starts.
Condition: message.media_unread is set, and the user starts playback of the media.
For secret chat voice messages and round videos, decryptedMessage does not contain a media_unread flag. Clients should keep a local content-unread flag, initialized to true when receiving a voice message or round video, and clear it when the media is played.
Condition for secret chats: local_content_unread && (decryptedMessage.isVoice() || decryptedMessage.isRoundVideo()) when playback starts.
For secret chats, invoke messages.sendEncryptedService with a decryptedMessageActionReadMessages containing the decryptedMessage.random_id.
For incoming non-secret chat messages with expiring media, invoke messages.readMessageContents or channels.readMessageContents and start the deletion timer when the user opens the media or starts playback.
Expiring media has a non-zero messageMediaPhoto.ttl_seconds or messageMediaDocument.ttl_seconds.
Condition: 0 < media.ttl_seconds < 0x7FFFFFFF && local_destroy_timer_not_started && user_opens_or_starts_playback.
For incoming non-secret chat media with media.ttl_seconds == 0x7FFFFFFF, invoke messages.readMessageContents or channels.readMessageContents when the user opens the media or starts playback.
Condition: media.ttl_seconds == 0x7FFFFFFF && local_destroy_timer_not_started && user_opens_or_starts_playback.
Do not start a time-based deletion timer. Locally delete the media after the media viewer or voice message player is closed.
Secret chat messages use decryptedMessage.ttl.
For 0 < ttl < 0x7FFFFFFF, invoke messages.sendEncryptedService with a decryptedMessageActionReadMessages containing the decryptedMessage.random_id when the user opens the media or starts playback, and start the local deletion timer at the same time.
Condition: 0 < decryptedMessage.ttl < 0x7FFFFFFF && local_destroy_timer_not_started && user_opens_or_starts_playback.
For secret chat media with decryptedMessage.ttl == 0x7FFFFFFF, do not send decryptedMessageActionReadMessages. Treat the media as view-once: show it only after an explicit open action, and locally delete the media after it is closed.
For a non-secret chat message containing a live location, invoke messages.readMessageContents or channels.readMessageContents when the live location map is open.
Messages containing live locations contain a messageMediaGeoLive.
Condition: message.media instanceof messageMediaGeoLive
Re-invoke the method on all known live location messages in the chat, usually the ones returned by messages.getRecentLocations, every 60 seconds until the live location map is closed.
message#95ef6f2b flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post_stars:flags2.8?true paid_suggested_post_ton:flags2.9?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int from_rank:flags2.12?string peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long guestchat_via_from:flags2.19?Peer reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost schedule_repeat_period:flags2.10?int summary_from_language:flags2.11?string = Message;
messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:flags.2?MessageReplies = MessageViews;
messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> users:Vector<User> = messages.MessageViews;
---functions---
messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector<int> increment:Bool = messages.MessageViews;
Messages sent to and forwarded from channels have a view counter in the message.views field: to increment the view counter and fetch the updated view counter, forward counter and thread information, invoke the messages.getMessagesViews method with increment set to boolTrue.
Calls to messages.getMessagesViews should be batched as follows:
views field that are visible in the chat viewport to a local per-peer queue.increment set to boolTrue, one request per peer, with at most 100 message IDs per request.For unread messages, queue the view counter when the bottom edge of the message becomes visible.
For already read messages with a views field, queue it when the vertical center of the message becomes visible.
inputMessageReadMetric#402b4495 msg_id:int view_id:long time_in_view_ms:int active_time_in_view_ms:int height_to_viewport_ratio_permille:int seen_range_ratio_permille:int = InputMessageReadMetric;
---functions---
messages.reportReadMetrics#4067c5e6 peer:InputPeer metrics:Vector<InputMessageReadMetric> = Bool;
Use messages.reportReadMetrics to report how long visible messages stayed in the chat viewport.
The viewport is the visible rectangle of the chat history list after excluding in-chat UI that covers messages.
Track one exposure while a message remains in the viewport:
view_id for every accepted exposure.Each inputMessageReadMetric is computed as follows:
msg_id is the server message ID in peer.view_id is a random non-zero 64-bit ID generated for this exposure.time_in_view_ms is the total exposure time in milliseconds of at least one pixel of the message after the 300 millisecond entry grace period.active_time_in_view_ms is the part of time_in_view_ms accumulated while the user is active.height_to_viewport_ratio_permille is round(max_message_height * 1000 / max_viewport_height). It can be greater than 1000 when the message is taller than the viewport.seen_range_ratio_permille is round(max_seen_vertical_range * 1000 / max_message_height), clamped to the 0...1000 range.If a grouped media album is rendered as one visual block, use the album block rectangle consistently when computing height_to_viewport_ratio_permille and seen_range_ratio_permille for the messages in that block.
---functions---
messages.reportMusicListen#ddbcd819 id:InputDocument listened_duration:int = Bool;
Use messages.reportMusicListen to report listening time for a document that represents a song, i.e. an audio document described by documentAttributeAudio without the voice flag.
Accumulate only the time during which the song is playing. Do not include paused, pausing or stopped time.
Invoke messages.reportMusicListen when playback stops, when another track starts, when the player is closed, or after a pause lasts at least 60 seconds.
Invoke messages.reportMusicListen only if the accumulated duration is at least 3 seconds:
id is the InputDocument of the listened song.listened_duration is the accumulated playing time in whole seconds.