imessage_database/message_types/
variants.rs

1/*!
2 Variants represent the different types of iMessages that exist in the `messages` table.
3*/
4
5use std::fmt::Display;
6
7use plist::Value;
8
9use crate::{
10    error::plist::PlistParseError,
11    message_types::{
12        app_store::AppStoreMessage, collaboration::CollaborationMessage, music::MusicMessage,
13        placemark::PlacemarkMessage, url::URLMessage,
14    },
15    tables::messages::models::GroupAction,
16};
17
18/// # Tapbacks
19///
20/// [Tapbacks](https://support.apple.com/guide/messages/react-with-tapbacks-icht504f698a/mac) look like normal messages in the database. Only the latest tapback
21/// is stored. For example:
22///
23/// - user receives message -> user likes message
24///   - This will create a message and a like message
25/// - user receives message -> user likes message -> user unlikes message
26///   - This will create a message and a like message
27///   - but that like message will get dropped when the unlike message arrives
28///   - When messages drop the ROWIDs become non-sequential: the ID of the dropped message row is not reused
29///   - This means unliking an old message will make it look like the tapback was applied/removed at the
30///     time of latest change; the history of tapback statuses is not kept
31///
32/// ## Technical detail
33///
34/// The index specified by the prefix maps to the index of the body part given by [`Message::body()`](crate::tables::messages::Message#method.body).
35///
36/// - `bp:` GUID prefix for bubble message tapbacks (url previews, apps, etc).
37/// - `p:0/` GUID prefix for normal messages (body text, attachments).
38///
39/// If a message has 3 attachments followed by some text:
40/// - 0 is the first image
41/// - 1 is the second image
42/// - 2 is the third image
43/// - 3 is the text of the message
44///
45/// In this example, a Like on `p:2/` is a like on the third image.
46#[derive(Debug)]
47pub enum Tapback<'a> {
48    /// Heart
49    Loved,
50    /// Thumbs up
51    Liked,
52    /// Thumbs down
53    Disliked,
54    /// Laughing face
55    Laughed,
56    /// Exclamation points
57    Emphasized,
58    /// Question marks
59    Questioned,
60    /// Custom emoji tapbacks
61    Emoji(Option<&'a str>),
62    /// Custom sticker tapbacks
63    Sticker,
64}
65
66impl Display for Tapback<'_> {
67    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        match self {
69            Tapback::Emoji(emoji) => match emoji {
70                Some(e) => write!(fmt, "{e}"),
71                None => write!(fmt, "Unknown emoji!"),
72            },
73            _ => write!(fmt, "{self:?}"),
74        }
75    }
76}
77
78/// Application Messages
79///
80/// Messages sent via an app's iMessage integration will send in a special balloon instead of a normal
81/// text balloon. This represents the different variants of message balloon.
82#[derive(Debug)]
83pub enum CustomBalloon<'a> {
84    /// Generic third party [applications](crate::message_types::app)
85    Application(&'a str),
86    /// [URL](crate::message_types::url) previews
87    URL,
88    /// Handwritten animated messages
89    Handwriting,
90    /// Digital Touch message
91    DigitalTouch,
92    /// Apple Pay (one of Sent, Requested, Received)
93    ApplePay,
94    /// Fitness.app messages
95    Fitness,
96    /// Photos.app slideshow messages
97    Slideshow,
98    /// [Check In](https://support.apple.com/guide/iphone/use-check-in-iphc143bb7e9/ios) messages
99    CheckIn,
100    /// Find My messages
101    FindMy,
102    /// Poll messages
103    Polls,
104}
105
106/// URL Message Types
107///
108/// Apple sometimes overloads `com.apple.messages.URLBalloonProvider` with
109/// other types of messages; this enum represents those variants.
110#[derive(Debug)]
111pub enum URLOverride<'a> {
112    /// [`URL`](crate::message_types::url) previews
113    Normal(URLMessage<'a>),
114    /// [`Apple Music`](crate::message_types::music) messages
115    AppleMusic(MusicMessage<'a>),
116    /// [`App Store`](crate::message_types::app_store) messages
117    AppStore(AppStoreMessage<'a>),
118    /// [`Collaboration`](crate::message_types::collaboration) messages
119    Collaboration(CollaborationMessage<'a>),
120    /// [`Placemark`](crate::message_types::placemark) messages
121    SharedPlacemark(PlacemarkMessage<'a>),
122}
123
124/// Announcement Message Types
125///
126/// Announcements are messages sent to a thread for actions that are not balloons, i.e.
127/// updating the name of the group or changing the group photo
128#[derive(Debug)]
129pub enum Announcement<'a> {
130    /// All parts of the message were unsent
131    FullyUnsent,
132    /// A group action
133    GroupAction(GroupAction<'a>),
134    /// A user kept an audio message
135    AudioMessageKept,
136    /// Types that may occur in the future
137    Unknown(&'a i32),
138}
139
140/// Tapback Action Container
141///
142/// Tapbacks can either be added or removed; this enum represents those states
143#[derive(Debug)]
144pub enum TapbackAction {
145    /// Tapback was added to the message
146    Added,
147    /// Tapback was removed from the message
148    Removed,
149}
150
151/// Message variant container
152///
153/// Messages can exist as one of many different variants, this encapsulates
154/// all of the possibilities.
155#[derive(Debug)]
156pub enum Variant<'a> {
157    /// An iMessage with a standard text body that may include attachments
158    Normal,
159    /// A message that has been [edited](crate::message_types::edited::EditStatus::Edited) or [unsent](crate::message_types::edited::EditStatus::Unsent)
160    Edited,
161    /// A [tapback](https://support.apple.com/guide/messages/react-with-tapbacks-icht504f698a/mac)
162    ///
163    /// The `usize` indicates the index of the message's [`body()`](crate::tables::messages::Message#method.body) the tapback is applied to.
164    Tapback(usize, TapbackAction, Tapback<'a>),
165    /// An [iMessage app](https://support.apple.com/en-us/HT206906) generated message
166    App(CustomBalloon<'a>),
167    /// A `SharePlay` message
168    SharePlay,
169    /// A vote cast on a poll
170    Vote,
171    /// A new option sent to a poll
172    PollUpdate,
173    /// Container for new or unknown messages
174    Unknown(i32),
175}
176
177/// Defines behavior for different types of messages that have custom balloons
178pub trait BalloonProvider<'a> {
179    /// Creates the object from a `HashMap` of item attributes
180    fn from_map(payload: &'a Value) -> Result<Self, PlistParseError>
181    where
182        Self: Sized;
183}