twilight_cache_inmemory/event/
interaction.rs

1use crate::{CacheableModels, InMemoryCache, UpdateCache, config::ResourceType};
2use std::borrow::Cow;
3use twilight_model::{
4    application::interaction::InteractionData, gateway::payload::incoming::InteractionCreate,
5};
6
7impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for InteractionCreate {
8    fn update(&self, cache: &InMemoryCache<CacheModels>) {
9        // Cache interaction member
10        if cache.wants(ResourceType::MEMBER)
11            && let (Some(member), Some(guild_id)) = (&self.member, self.guild_id)
12            && let Some(user) = &member.user
13        {
14            cache.cache_user(Cow::Borrowed(user), self.guild_id);
15
16            cache.cache_borrowed_partial_member(guild_id, member, user.id);
17        }
18
19        // Cache interaction user
20        if cache.wants(ResourceType::USER)
21            && let Some(user) = &self.user
22        {
23            cache.cache_user(Cow::Borrowed(user), None);
24        }
25
26        // Cache resolved interaction data
27        if let Some(InteractionData::ApplicationCommand(data)) = &self.data
28            && let Some(resolved) = &data.resolved
29        {
30            // Cache resolved users and members
31            for u in resolved.users.values() {
32                if cache.wants(ResourceType::USER) {
33                    cache.cache_user(Cow::Borrowed(u), self.guild_id);
34                }
35
36                if !cache.wants(ResourceType::MEMBER) || self.guild_id.is_none() {
37                    continue;
38                }
39
40                // This should always match, because resolved members
41                // are guaranteed to have a matching resolved user
42                if let Some(member) = &resolved.members.get(&u.id)
43                    && let Some(guild_id) = self.guild_id
44                {
45                    cache.cache_borrowed_interaction_member(guild_id, member, u.id);
46                }
47            }
48
49            // Cache resolved roles
50            if cache.wants(ResourceType::ROLE)
51                && let Some(guild_id) = self.guild_id
52            {
53                cache.cache_roles(guild_id, resolved.roles.values().cloned());
54            }
55        }
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use crate::DefaultInMemoryCache;
62    use std::collections::HashMap;
63    use twilight_model::guild::RoleColors;
64    use twilight_model::{
65        application::{
66            command::CommandType,
67            interaction::{
68                Interaction, InteractionData, InteractionDataResolved, InteractionMember,
69                InteractionType, application_command::CommandData,
70            },
71        },
72        channel::{
73            Channel, ChannelType, Message,
74            message::{
75                MessageFlags, MessageType,
76                sticker::{MessageSticker, StickerFormatType},
77            },
78        },
79        gateway::payload::incoming::InteractionCreate,
80        guild::{MemberFlags, PartialMember, Permissions, Role, RoleFlags},
81        id::Id,
82        oauth::ApplicationIntegrationMap,
83        user::User,
84        util::{ImageHash, Timestamp, image_hash::ImageHashParseError},
85    };
86
87    #[allow(clippy::too_many_lines, deprecated)]
88    #[test]
89    fn interaction_create() -> Result<(), ImageHashParseError> {
90        let timestamp = Timestamp::from_secs(1_632_072_645).expect("non zero");
91        // let avatar1 = ImageHash::parse(b"1ef6bca4fddaa303a9cd32dd70fb395d")?;
92        let avatar2 = ImageHash::parse(b"3a43231a99f4dfcf0fd94d1d8defd301")?;
93        let avatar3 = ImageHash::parse(b"5e23c298295ad37936cfe24ad314774f")?;
94        let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
95
96        let cache = DefaultInMemoryCache::new();
97
98        cache.update(&InteractionCreate(Interaction {
99            app_permissions: Some(Permissions::SEND_MESSAGES),
100            application_id: Id::new(1),
101            authorizing_integration_owners: ApplicationIntegrationMap {
102                guild: None,
103                user: None,
104            },
105            channel: Some(Channel {
106                bitrate: None,
107                guild_id: None,
108                id: Id::new(400),
109                kind: ChannelType::GuildText,
110                last_message_id: None,
111                last_pin_timestamp: None,
112                name: None,
113                nsfw: None,
114                owner_id: None,
115                parent_id: None,
116                permission_overwrites: None,
117                position: None,
118                rate_limit_per_user: None,
119                recipients: None,
120                rtc_region: None,
121                topic: None,
122                user_limit: None,
123                application_id: None,
124                applied_tags: None,
125                available_tags: None,
126                default_auto_archive_duration: None,
127                default_forum_layout: None,
128                default_reaction_emoji: None,
129                default_sort_order: None,
130                default_thread_rate_limit_per_user: None,
131                flags: None,
132                icon: None,
133                invitable: None,
134                managed: None,
135                member: None,
136                member_count: None,
137                message_count: None,
138                newly_created: None,
139                thread_metadata: None,
140                video_quality_mode: None,
141            }),
142            channel_id: Some(Id::new(2)),
143            context: None,
144            data: Some(InteractionData::ApplicationCommand(Box::new(CommandData {
145                guild_id: None,
146                id: Id::new(5),
147                name: "command name".into(),
148                kind: CommandType::ChatInput, // This isn't actually a valid command, so just mark it as a slash command.
149                options: Vec::new(),
150                resolved: Some(InteractionDataResolved {
151                    attachments: HashMap::new(),
152                    channels: HashMap::new(),
153                    members: HashMap::from([(
154                        Id::new(7),
155                        InteractionMember {
156                            avatar: None,
157                            avatar_decoration_data: None,
158                            banner: None,
159                            communication_disabled_until: None,
160                            flags,
161                            joined_at: Some(timestamp),
162                            nick: None,
163                            pending: false,
164                            permissions: Permissions::empty(),
165                            premium_since: None,
166                            roles: vec![Id::new(8)],
167                        },
168                    )]),
169                    messages: HashMap::from([(
170                        Id::new(4),
171                        Message {
172                            activity: None,
173                            application: None,
174                            application_id: None,
175                            attachments: Vec::new(),
176                            author: User {
177                                accent_color: None,
178                                avatar: Some(avatar3),
179                                avatar_decoration: None,
180                                avatar_decoration_data: None,
181                                banner: None,
182                                bot: false,
183                                discriminator: 1,
184                                email: None,
185                                flags: None,
186                                global_name: Some("test".to_owned()),
187                                id: Id::new(3),
188                                locale: None,
189                                mfa_enabled: None,
190                                name: "test".to_owned(),
191                                premium_type: None,
192                                primary_guild: None,
193                                public_flags: None,
194                                system: None,
195                                verified: None,
196                            },
197                            call: None,
198                            channel_id: Id::new(2),
199                            components: Vec::new(),
200                            content: "ping".to_owned(),
201                            edited_timestamp: None,
202                            embeds: Vec::new(),
203                            flags: Some(MessageFlags::empty()),
204                            guild_id: Some(Id::new(1)),
205                            id: Id::new(4),
206                            interaction: None,
207                            interaction_metadata: None,
208                            kind: MessageType::Regular,
209                            member: Some(PartialMember {
210                                avatar: None,
211                                avatar_decoration_data: None,
212                                banner: None,
213                                communication_disabled_until: None,
214                                deaf: false,
215                                flags,
216                                joined_at: Some(timestamp),
217                                mute: false,
218                                nick: Some("member nick".to_owned()),
219                                permissions: None,
220                                premium_since: None,
221                                roles: Vec::new(),
222                                user: None,
223                            }),
224                            mention_channels: Vec::new(),
225                            mention_everyone: false,
226                            mention_roles: Vec::new(),
227                            mentions: Vec::new(),
228                            message_snapshots: Vec::new(),
229                            pinned: false,
230                            poll: None,
231                            reactions: Vec::new(),
232                            reference: None,
233                            referenced_message: None,
234                            role_subscription_data: None,
235                            sticker_items: vec![MessageSticker {
236                                format_type: StickerFormatType::Png,
237                                id: Id::new(1),
238                                name: "sticker name".to_owned(),
239                            }],
240                            timestamp,
241                            thread: None,
242                            tts: false,
243                            webhook_id: None,
244                        },
245                    )]),
246                    roles: HashMap::from([(
247                        Id::new(8),
248                        Role {
249                            color: 0u32,
250                            colors: RoleColors {
251                                primary_color: 0,
252                                secondary_color: None,
253                                tertiary_color: None,
254                            },
255                            hoist: false,
256                            icon: None,
257                            id: Id::new(8),
258                            managed: false,
259                            mentionable: true,
260                            name: "role name".into(),
261                            permissions: Permissions::empty(),
262                            position: 2i64,
263                            flags: RoleFlags::empty(),
264                            tags: None,
265                            unicode_emoji: None,
266                        },
267                    )]),
268                    users: HashMap::from([(
269                        Id::new(7),
270                        User {
271                            accent_color: None,
272                            avatar: Some(avatar2),
273                            avatar_decoration: None,
274                            avatar_decoration_data: None,
275                            banner: None,
276                            bot: false,
277                            discriminator: 5678,
278                            email: None,
279                            flags: None,
280                            global_name: Some("different name".to_owned()),
281                            id: Id::new(7),
282                            locale: None,
283                            mfa_enabled: None,
284                            name: "different name".into(),
285                            premium_type: None,
286                            primary_guild: None,
287                            public_flags: None,
288                            system: None,
289                            verified: None,
290                        },
291                    )]),
292                }),
293                target_id: None,
294            }))),
295            entitlements: Vec::new(),
296            guild: None,
297            guild_id: Some(Id::new(3)),
298            guild_locale: None,
299            id: Id::new(4),
300            kind: InteractionType::ApplicationCommand,
301            locale: Some("en-GB".to_owned()),
302            member: Some(PartialMember {
303                avatar: None,
304                avatar_decoration_data: None,
305                banner: None,
306                communication_disabled_until: None,
307                deaf: false,
308                flags,
309                joined_at: Some(timestamp),
310                mute: false,
311                nick: None,
312                permissions: Some(Permissions::empty()),
313                premium_since: None,
314                roles: Vec::new(),
315                user: Some(User {
316                    accent_color: None,
317                    avatar: Some(avatar3),
318                    avatar_decoration: None,
319                    avatar_decoration_data: None,
320                    banner: None,
321                    bot: false,
322                    discriminator: 1234,
323                    email: None,
324                    flags: None,
325                    global_name: Some("test".to_owned()),
326                    id: Id::new(6),
327                    locale: None,
328                    mfa_enabled: None,
329                    name: "username".into(),
330                    premium_type: None,
331                    primary_guild: None,
332                    public_flags: None,
333                    system: None,
334                    verified: None,
335                }),
336            }),
337            message: None,
338            token: "token".into(),
339            user: None,
340        }));
341
342        {
343            let guild_members = cache.guild_members(Id::new(3)).unwrap();
344            assert_eq!(guild_members.len(), 2);
345        }
346
347        {
348            let member = cache.member(Id::new(3), Id::new(6)).unwrap();
349            let user = cache.user(member.user_id).unwrap();
350            assert_eq!(user.avatar.as_ref().unwrap(), &avatar3);
351        }
352
353        {
354            let member = cache.member(Id::new(3), Id::new(7)).unwrap();
355            let user = cache.user(member.user_id).unwrap();
356            assert_eq!(user.avatar.as_ref().unwrap(), &avatar2);
357        }
358
359        {
360            let guild_roles = cache.guild_roles(Id::new(3)).unwrap();
361            assert_eq!(guild_roles.len(), 1);
362        }
363
364        Ok(())
365    }
366}