1use serde::{Deserialize, Serialize};
2use twilight_model::{
3 application::interaction::InteractionType,
4 channel::{
5 Attachment, ChannelMention, Message,
6 message::{
7 Component, Embed, MessageActivity, MessageApplication, MessageCall, MessageFlags,
8 MessageInteraction, MessageReference, MessageSnapshot, MessageSticker, MessageType,
9 Reaction, RoleSubscriptionData,
10 },
11 },
12 guild::PartialMember,
13 id::{
14 Id,
15 marker::{
16 ApplicationMarker, ChannelMarker, GuildMarker, InteractionMarker, MessageMarker,
17 RoleMarker, UserMarker, WebhookMarker,
18 },
19 },
20 poll::Poll,
21 util::Timestamp,
22};
23
24#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
26pub struct CachedMessageInteraction {
27 pub id: Id<InteractionMarker>,
28 #[serde(rename = "type")]
29 pub kind: InteractionType,
30 pub name: String,
31 pub user_id: Id<UserMarker>,
32}
33
34impl From<MessageInteraction> for CachedMessageInteraction {
35 fn from(message_interaction: MessageInteraction) -> Self {
36 #[expect(
40 clippy::unneeded_field_pattern,
41 reason = "clearer that we're explicitly skipping those fields"
42 )]
43 let MessageInteraction {
44 id,
45 kind,
46 member: _,
47 name,
48 user,
49 } = message_interaction;
50
51 Self {
52 id,
53 kind,
54 name,
55 user_id: user.id,
56 }
57 }
58}
59
60impl PartialEq<MessageInteraction> for CachedMessageInteraction {
61 fn eq(&self, other: &MessageInteraction) -> bool {
62 self.id == other.id
63 && self.kind == other.kind
64 && self.name == other.name
65 && self.user_id == other.user.id
66 }
67}
68
69#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
70pub struct CachedMessage {
71 pub activity: Option<MessageActivity>,
72 pub application: Option<MessageApplication>,
73 pub application_id: Option<Id<ApplicationMarker>>,
74 pub attachments: Vec<Attachment>,
75 pub author: Id<UserMarker>,
76 pub call: Option<MessageCall>,
77 pub channel_id: Id<ChannelMarker>,
78 pub components: Vec<Component>,
79 pub content: String,
80 pub edited_timestamp: Option<Timestamp>,
81 pub embeds: Vec<Embed>,
82 pub flags: Option<MessageFlags>,
83 pub guild_id: Option<Id<GuildMarker>>,
84 pub id: Id<MessageMarker>,
85 pub interaction: Option<CachedMessageInteraction>,
86 pub kind: MessageType,
87 pub member: Option<PartialMember>,
88 pub mention_channels: Vec<ChannelMention>,
89 pub mention_everyone: bool,
90 pub mention_roles: Vec<Id<RoleMarker>>,
91 pub mentions: Vec<Id<UserMarker>>,
92 pub message_snapshots: Vec<MessageSnapshot>,
93 pub pinned: bool,
94 pub poll: Option<Poll>,
95 pub reactions: Vec<Reaction>,
96 pub reference: Option<MessageReference>,
97 pub role_subscription_data: Option<RoleSubscriptionData>,
98 pub sticker_items: Vec<MessageSticker>,
99 pub thread_id: Option<Id<ChannelMarker>>,
100 pub timestamp: Timestamp,
101 pub tts: bool,
102 pub webhook_id: Option<Id<WebhookMarker>>,
103}
104
105impl From<Message> for CachedMessage {
106 #[expect(deprecated)]
107 fn from(message: Message) -> Self {
108 #[expect(
109 clippy::unneeded_field_pattern,
110 reason = "clearer that we're explicitly skipping those fields"
111 )]
112 let Message {
113 activity,
114 application,
115 application_id,
116 attachments,
117 author,
118 call,
119 channel_id,
120 components,
121 content,
122 edited_timestamp,
123 embeds,
124 flags,
125 guild_id,
126 id,
127 interaction,
128 interaction_metadata: _,
129 kind,
130 member,
131 mention_channels,
132 mention_everyone,
133 mention_roles,
134 mentions,
135 message_snapshots,
136 pinned,
137 poll,
138 reactions,
139 reference,
140 referenced_message: _,
141 role_subscription_data,
142 sticker_items,
143 timestamp,
144 thread,
145 tts,
146 webhook_id,
147 } = message;
148
149 Self {
150 id,
151 activity,
152 application,
153 application_id,
154 attachments,
155 author: author.id,
156 call,
157 channel_id,
158 components,
159 content,
160 edited_timestamp,
161 embeds,
162 flags,
163 guild_id,
164 interaction: interaction.map(CachedMessageInteraction::from),
165 kind,
166 member,
167 mention_channels,
168 mention_everyone,
169 mention_roles,
170 mentions: mentions.into_iter().map(|mention| mention.id).collect(),
171 message_snapshots,
172 pinned,
173 poll,
174 reactions,
175 reference,
176 role_subscription_data,
177 sticker_items,
178 thread_id: thread.map(|thread| thread.id),
179 timestamp,
180 tts,
181 webhook_id,
182 }
183 }
184}
185
186impl PartialEq<Message> for CachedMessage {
187 #[expect(deprecated)]
188 fn eq(&self, other: &Message) -> bool {
189 self.id == other.id
190 && self.activity == other.activity
191 && self.application == other.application
192 && self.application_id == other.application_id
193 && self.attachments == other.attachments
194 && self.author == other.author.id
195 && self.call == other.call
196 && self.channel_id == other.channel_id
197 && self.components == other.components
198 && self.content == other.content
199 && self.edited_timestamp == other.edited_timestamp
200 && self.embeds == other.embeds
201 && self.flags == other.flags
202 && self.guild_id == other.guild_id
203 && self.interaction.as_ref().map_or_else(
204 || other.interaction.is_none(),
205 |interaction| {
206 other
207 .interaction
208 .as_ref()
209 .is_some_and(|other_interaction| interaction == other_interaction)
210 },
211 )
212 && self.kind == other.kind
213 && self.member == other.member
214 && self.mention_channels == other.mention_channels
215 && self.mention_everyone == other.mention_everyone
216 && self.mention_roles == other.mention_roles
217 && self.mentions.len() == other.mentions.len()
218 && self
219 .mentions
220 .iter()
221 .zip(other.mentions.iter())
222 .all(|(user_id, mention)| user_id == &mention.id)
223 && self.pinned == other.pinned
224 && self.reactions == other.reactions
225 && self.reference == other.reference
226 && self.role_subscription_data == other.role_subscription_data
227 && self.sticker_items == other.sticker_items
228 && self.thread_id == other.thread.as_ref().map(|thread| thread.id)
229 && self.timestamp == other.timestamp
230 && self.tts == other.tts
231 && self.webhook_id == other.webhook_id
232 }
233}