Saved messages

The Saved Messages chat allows users to bookmark messages and media: it's a personal cloud storage for any messages or media you may want to send or forward there.

Internally, the Saved Messages chat is simply the private chat with ourselves (i.e. the chat with inputPeerSelf): the only difference between the Saved Messages chat and a chat with any other user, is that additional features are available to better organize messages and media sent to it.

Schema:

message#76bec211 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 id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?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;
messageFwdHeader#4e4df4bb flags:# imported:flags.7?true saved_out:flags.11?true from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int saved_from_id:flags.8?Peer saved_from_name:flags.9?string saved_date:flags.10?int psa_type:flags.6?string = MessageFwdHeader;

savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;

updateSavedDialogPinned#aeaf9e74 flags:# pinned:flags.0?true peer:DialogPeer = Update;
updatePinnedSavedDialogs#686c85a6 flags:# order:flags.0?Vector<DialogPeer> = Update;

messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
messages.savedDialogsNotModified#c01f6fe8 count:int = messages.SavedDialogs;

savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;

---functions---

messages.getSavedDialogs#5381d21a flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs;
messages.getSavedHistory#3d9a414d peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.deleteSavedHistory#6e98102b flags:# peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory;
messages.getPinnedSavedDialogs#d63d94e0 = messages.SavedDialogs;
messages.toggleSavedDialogPin#ac81bbde flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.reorderPinnedSavedDialogs#8b716587 flags:# force:flags.0?true order:Vector<InputDialogPeer> = Bool;


messages.search#a7b4e929 flags:# peer:InputPeer q:string from_id:flags.0?InputPeer saved_peer_id:flags.2?InputPeer top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.getSearchCounters#1bbcf300 flags:# peer:InputPeer saved_peer_id:flags.2?InputPeer top_msg_id:flags.0?int filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
messages.getSearchResultsCalendar#6aa3f6bd flags:# peer:InputPeer saved_peer_id:flags.2?InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar;
messages.getSearchResultsPositions#9c7f2f10 flags:# peer:InputPeer saved_peer_id:flags.2?InputPeer filter:MessagesFilter offset_id:int limit:int = messages.SearchResultsPositions;

Messages sent and forwarded from various users (including ourselves) to Saved Messages are automatically categorized by their original dialog into a saved dialog list, quite similar to the normal dialog list used to normally interact with chats.

To add new dialogs to the normal dialog list a user has to write to them (or join a channel/chat, etc.).
To add new dialogs to the saved dialog list, simply forward messages from any normal dialog to inputPeerSelf (the current user): the forwarded messages (including outgoing ones) will be added to a saved dialog with the same ID of the original dialog.

This includes outgoing messages, for example assume the following:

  • Our user id is equal to 11111111
  • We send a message A with ID 10 to a supergroup with id=-100122222222 (bot API format, equivalent to a peerChannel with ID 122222222)
  • Another user with id=133333333 replies B to our previous message, message ID 11
  • We forward both messages A and B with IDs 10 and 11 to inputPeerSelf, which will:
    • Create a new savedDialog with peer=-100122222222 (if it doesn't exist already because we already forwarded messages from that supergroup)
    • Generate two messages:
      • Message A:
        • id: an unrelated message ID, the next one in the common ID sequence, for example 1234
        • peer_id: 11111111
        • saved_peer_id: -100122222222
        • fwd_from.from_id: 11111111
        • fwd_from.saved_from_peer: -100122222222
        • fwd_from.saved_from_msg_id: 10
      • Message B:
        • id: an unrelated message ID, the next one in the common ID sequence, for example 1235
        • reply_to.reply_to_msg_id: 1234
        • peer_id: 11111111
        • saved_peer_id: -100122222222
        • fwd_from.from_id: 133333333
        • fwd_from.saved_from_peer: -100122222222
        • fwd_from.saved_from_msg_id: 11

Saving messages from private chats with users with forward privacy enabled will add them to a saved dialog entry of a special anonymous user with id=2666000.

Clients may use the following pseudocode to manually populate the saved_peer_id of old layer < 170 messages stored in the local database.

// user_id is the ID of the current user.

if (message.peer_id == user_id) {
  if (message.fwd_from.saved_from_peer) {
    message.saved_peer_id = message.fwd_from.saved_from_peer
  } elseif (message.fwd_from.from_id) {
    message.saved_peer_id = user_id;
  } elseif (message.fwd_from.from_name) {
    message.saved_peer_id = 2666000;
  } else {
    message.saved_peer_id = user_id;
  }
}

Sending (not forwarding from another dialog) new messages directly to ourselves will add them to a saved dialog entry with ourselves.

A set of methods can then be used to obtain this dialog list, pin/unpin dialogs inside of it, view and remove messages from saved dialogs: messages.getSavedDialogs, messages.getSavedHistory, messages.deleteSavedHistory, messages.getPinnedSavedDialogs, messages.toggleSavedDialogPin, messages.reorderPinnedSavedDialogs work just like their counterparts messages.getDialogs, messages.getHistory, messages.deleteHistory, messages.getPinnedDialogs, messages.toggleDialogPin, messages.reorderPinnedDialogs, with the sole difference that they affect the saved dialog list, instead of the main dialog list.

To search for messages within a saved dialog, use the usual messages.search, messages.getSearchCounters, messages.getSearchResultsCalendar, messages.getSearchResultsPositions methods with peer=inputPeerSelf and saved_peer_id=id of the saved dialog.