136й слой. Общая схема изменений слоя:
updateMessageReactions peer:Peer msg_id:int reactions:MessageReactions = Update;
reactionCount flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
messageReactions flags:# min:flags.0?true results:Vector<ReactionCount> recent_reactons:flags.1?Vector<MessageUserReaction> = MessageReactions;
messageUserReaction user_id:long reaction:string = MessageUserReaction;
messages.messageReactionsList flags:# count:int reactions:Vector<MessageUserReaction> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
message 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 id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long 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 = Message;
availableReaction reaction:string title:string static_icon:Document select_animation:Document activate_animation:Document effect_animation:Document = AvailableReaction;
messages.availableReactionsNotModified = messages.AvailableReactions;
messages.availableReactions hash:int reactions:Vector<AvailableReaction> = messages.AvailableReactions;
---functions---
messages.sendReaction flags:# peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
messages.getMessagesReactions peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.getAvailableReactions hash:int = messages.AvailableReactions;
messages.availableReactionsNotModified = messages.AvailableReactions;
messages.availableReactions hash:int reactions:Vector<AvailableReaction> = messages.AvailableReactions;
---functions---
messages.getAvailableReactions hash:int = messages.AvailableReactions;
Метод messages.getAvailableReactions
позволяет получить все возможные реакции, они же являются реакциями для личных сообщений.
Для каждой реакции возвращаются следующие поля конструктора availableReaction
:reaction
- строка с эмодзи реакции, например ,
.
title
- строка с текстовым описанием реакции, например Thumbs Up
, Red Heart
static_icon
- статичная иконка в формате WebP для отображения прямо внутри блока сообщенияselect_animation
- короткая анимация подрагивания в формате TGS, которая воспроизводится в списке реакцийactivate_animation
- анимация самого действия эмодзи в формате TGS (примеры: t.me/addstickers/animatedemojies)effect_animation
- анимация эффектов вокруг эмодзи в формате TGS (примеры: t.me/addstickers/EmojiAnimations).
Поле hash
конструктора messages.availableReactions
позволяет не выкачивать повторно весь ответ на этот метод при отсутствии изменений. Если повторно вызвать messages.getAvailableReactions
с указанием этого поля, то метод может вернуть messages.availableReactionsNotModified
, если содержимое не изменилось. При отсутствии на клиенте локального кеша можно передавать hash = 0
.
Предлагается вызывать метод не реже одного раза в 3600 секунд для постоянного поддержания списка возможных реакций.
channelFull flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.20?true can_set_location:flags.16?true has_scheduled:flags.19?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?Vector<string> = ChatFull;
chatFull flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?Vector<string> = ChatFull;
Новое поле available_reactions
в конструкторах channelFull
и chatFull
задаёт список доступных в конкретной группе или канале реакций. Строки внутри вектора соответствуют значениям availableReaction.reaction
разрешённых реакций.
Если поля нету по флагу, значит реакции были выключены администратором.
messages.setChatAvailableReactions peer:InputPeer available_reactions:Vector<string> = Updates;
Метод messages.setChatAvailableReactions
позволяет изменить набор включенных в канале или группе реакций.
Параметр peer
задаёт канал или группу, в которой происходит редактирование. Метод доступен только участникам группы или админам канала с правом change_info
, остальным вернётся ошибка 400 CHAT_ADMIN_REQUIRED
.
Параметр available_reactions
должен содержать список включенных реакций (а именно строки reaction
, полученные ранее с помощью messages.getAvailableReactions
). Для выключения всех реакций необходимо передать пустой вектор.
Обновления на тему изменения доступных в чате реакций пока не предусмотрены, поэтому при изменении клиент сам обновляет поле available_reactions
в channelFull
/ chatFull
локального кеша. Остальные участники получат новый список доступных реакций вместе с очередным вызовом messages.getFullChat
/ channels.getFullChannel
(сейчас он почти всегда вызывается при очередном входе в чат).
Если были переданы эмодзи, которые не встречаются в актуальном ответе на messages.getAvailableReactions
, то вернётся ошибка 400 REACTION_INVALID
. Для её обработки можно вызвать метод messages.getAvailableReactions
для актуализации возможных реакций.
reactionCount flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
messageReactions flags:# min:flags.0?true results:Vector<ReactionCount> recent_reactons:flags.1?Vector<MessageUserReaction> = MessageReactions;
messageUserReaction user_id:long reaction:string = MessageUserReaction;
message 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 id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long 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 = Message;
В конструкторе message
появляется новое опциональное поле reactions:flags.20?MessageReactions
. Оно содержит информацию, которая требуется для отображения секции реакций прямо в блоке сообщения в интерфейсе.
Если присутствует флаг min
, значит конструктор содержит информацию о реакциях без учёта текущего пользователя. В таком случае не стоит перезаписывать существующее локальное значение выбранной пользователем реакции (флаг chosen
). Обработка флага должна быть похожа на обработку таких флагов в опросах (pollResults.min
и pollAnswerVoters.chosen
)
Поле results
содержит список установленных реакций со счётчиками, а также информацию о том, какая реакция была выбрана текущим пользователем (флаг reactionCount.chosen
при отсутствии флага min
).
,
Поле recent_reactons
может содержать до трех последних оставленных реакций в обратном хронологическом порядке вместе с их авторами. Поле может отсутствовать по флагу, в случае если список пользователей недоступен, например в каналах.
Поле user_id
содержит идентификатор пользователя, который оставил эту реакцию. Стоит ожидать, что соответствующий пользователь вернётся в списке users:Vector<User>
c флагом min.
Поле reaction
содержит строку с эмодзи реакции.
---functions---
messages.sendReaction flags:# peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
Метод messages.sendReaction
позволяет добавить/изменить/удалить свою реакцию к определённому сообщению.
Параметр peer
задаёт чат, в который происходит отправка реакции. Вызов метода доступен всем участникам, которые могут просматривать историю чата.
Параметр msg_id
задаёт идентификатор сообщения, к которому необходимо добавить реакцию. Реакции к messageService
добавлять нельзя. Также нельзя добавлять реакции к отложенным и спонсорским сообщениям.
Параметр reaction
задаёт реакцию, которую необходимо добавить. Значение должно совпадать с одним из элементов available_reactions
в группах/каналах или availableReaction.reaction
в случае личных сообщений. При установке реакции необходимо также установить нулевой бит поля flags. Для удаления реакции необходимо не передавать нулевой бит поля flags и соответствующее поле reaction
.
При добавлении / изменении реакции в группах и каналах можно также передавать reaction
, который совпадает с одной из уже оставленных реакций под этим сообщением. То есть ограничение available_reactions
накладывается только на добавление первой реакции с соответствующим эмодзи.
В случае успеха вернётся Updates, который будет содержать новое обновление updateMessageReactions
, которое будет описано ниже.
При передаче недопустимого чата в peer
вернётся ошибка 400 PEER_ID_INVALID
.
При передаче недопустимого msg_id
(например, сообщение было удалено или является сервисным) вернётся ошибка 400 MESSAGE_ID_INVALID
.
При передаче недопустимой реакции вернётся ошибка 400 REACTION_INVALID
.
updateMessageReactions peer:Peer msg_id:int reactions:MessageReactions = Update;
---functions---
messages.getMessagesReactions peer:InputPeer id:Vector<int> = Updates;
Новое обновление updateMessageReactions
соответствует изменению счетчиков реакций на сообщение. Поля peer
и msg_id
содержат чат и идентификатор сообщения, соответственно, в котором изменились счётчики. Поле reactions
содержит новое значение в соответствующем объекте message
. При получении такого обновления необходимо обновить локальный кеш соответствующего сообщения в БД и памяти, а также обновить состояние UI.
Также при добавлении первой реакции на сообщение будет приходить обновление о редактировании сообщения updateEditMessage
/ updateEditChannelMessage
. При наличии поля reactions
в новом объекте сообщения необходимо применять его аналогично, как если бы он пришёл из обновления updateMessageReactions
.
Метод messages.getMessagesReactions
позволяет обновить счетчики реакций у сообщения. Следует использовать этот метод для получения актуального состояния сообщений, отображаемых на экране, но не чаще, чем раз в 15 секунд для каждого из сообщений. Не следует запрашивать информацию о сообщениях, у которых нету поля reactions:flags.20?MessageReactions
по флагу.
В ответе метода будут содержаться обновления updateMessageReactions
, которые необходимо применить.
С помощью такого периодического обновления состояния отображаемых сообщений реализуется механизм шорт-поллинга. При реализации имеет смысл оглядываться на уже существующую реализацию messages.getPollResults
+ updateMessagePoll
.
messages.messageReactionsList flags:# count:int reactions:Vector<MessageUserReaction> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
---functions---
messages.getMessageReactionsList flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = messages.MessageReactionsList;
Метод messages.getMessageReactionsList
позволяет получить список реакций на то или иное сообщение в обратном хронологическом порядке.
Параметры peer
и id
задают чат и идентификатор сообщения, список реакций к которому необходимо получить.
Параметр reaction
позволяет опционально отфильтровать список по определённой реакции. Для этого необходимо установить соответствующий бит в flags
, а также передать одну из возможных реакций в значение поля.
Параметр limit
позволяет ограничить число получаемых элементов списка. Допустимые значения 1-100. Рекомендуется использовать значение 100
для подгрузки общего списка без ограничения по reaction
и 50
в случае фильтра по конкретным реакциям.
Параметр offset
позволяет продолжить подгрузку списка с предыдущей позиции. Необходимо передавать значение next_offset
из последнего результата.
В ответ на метод вернётся конструктор messages.messageReactionsList
со следующими полями:
Поле count
содержит общее число элементов в списке.
Поле reactions
содержит вектор самих реакций: пользователь + соответствующий emoji. В случае переданного параметра reaction
его значение должно совпадать со всеми messageUserReaction.reaction
в ответе.
Поле users
содержит вектор пользователей, упомянутых в reactions
.
Поле next_offset
содержит значение параметра offset
для подгрузки следующей порции списка. В случае отсутствия поля по флагу можно считать, что подгрузка всего списка завершена.
Обратите внимание, что при подгрузке сплошного списка реакций (без фильтра reaction
) можно предзаполнять остальные вкладки, вручную отфильтровав полученный список по эмодзи. Далее при открытии новой вкладки с фильтром по реакции можно сразу показать отфильтрованный общий список. Если список ещё не был загружен до конца, то необходимо отобразить недостающие мерцающие заглушки и выполнить запрос с reaction
и без offset
, после чего полученным результатом перезаписать весь список этой вкладки. При этом перестановок не ожидается, поскольку порядок во всех случаях обратный хронологический.
Похожая подгрузка списков уже была реализована в приложении в опросах, при просмотре списка проголосовавших, имеет смысл переиспользовать эту логику.