Skip to main content

revolt_database/models/emojis/
model.rs

1use std::collections::HashSet;
2use std::str::FromStr;
3
4use once_cell::sync::Lazy;
5use revolt_models::v0;
6use revolt_result::Result;
7use ulid::Ulid;
8
9use crate::events::client::EventV1;
10use crate::Database;
11
12static PERMISSIBLE_EMOJIS: Lazy<HashSet<String>> = Lazy::new(|| {
13    include_str!("unicode_emoji.txt")
14        .split('\n')
15        .map(|x| x.replace('\u{FE0F}', ""))
16        .collect()
17});
18
19auto_derived_partial!(
20    /// Emoji
21    pub struct Emoji {
22        /// Unique Id
23        #[serde(rename = "_id")]
24        pub id: String,
25        /// What owns this emoji
26        pub parent: EmojiParent,
27        /// Uploader user id
28        pub creator_id: String,
29        /// Emoji name
30        pub name: String,
31        /// Whether the emoji is animated
32        #[serde(skip_serializing_if = "crate::if_false", default)]
33        pub animated: bool,
34        /// Whether the emoji is marked as nsfw
35        #[serde(skip_serializing_if = "crate::if_false", default)]
36        pub nsfw: bool,
37    },
38    "PartialEmoji"
39);
40
41auto_derived!(
42    /// Parent Id of the emoji
43    #[serde(tag = "type")]
44    pub enum EmojiParent {
45        Server { id: String },
46        Detached,
47    }
48);
49
50#[allow(clippy::disallowed_methods)]
51impl Emoji {
52    /// Get parent id
53    fn parent(&self) -> &str {
54        match &self.parent {
55            EmojiParent::Server { id } => id,
56            EmojiParent::Detached => "",
57        }
58    }
59
60    /// Create an emoji
61    pub async fn create(&self, db: &Database) -> Result<()> {
62        db.insert_emoji(self).await?;
63
64        EventV1::EmojiCreate(self.clone().into())
65            .p(self.parent().to_string())
66            .await;
67
68        Ok(())
69    }
70
71    /// Delete an emoji
72    pub async fn delete(&self, db: &Database) -> Result<()> {
73        EventV1::EmojiDelete {
74            id: self.id.to_string(),
75        }
76        .p(self.parent().to_string())
77        .await;
78
79        db.detach_emoji(self).await
80    }
81
82    /// Update an emoji
83    pub async fn update(&mut self, db: &Database, partial: PartialEmoji) -> Result<()> {
84        if let Some(name) = partial.name.clone() {
85            self.name = name;
86        }
87
88        db.update_emoji(&self.id, &partial).await?;
89
90        EventV1::EmojiUpdate {
91            id: self.id.clone(),
92            data: v0::PartialEmoji {
93                name: partial.name.clone(),
94            },
95        }
96        .p(self.parent().to_string())
97        .await;
98
99        Ok(())
100    }
101
102    /// Check whether we can use a given emoji
103    pub async fn can_use(db: &Database, emoji: &str) -> Result<bool> {
104        if Ulid::from_str(emoji).is_ok() {
105            db.fetch_emoji(emoji).await?;
106            Ok(true)
107        } else {
108            let sanitized_emoji = emoji.replace('\u{FE0F}', "");
109            Ok(PERMISSIBLE_EMOJIS.contains(&sanitized_emoji))
110        }
111    }
112
113    /// Generates a PartialEmoji containing the data which has changed in an update
114    pub fn generate_diff(&self, partial: &PartialEmoji) -> PartialEmoji {
115        let mut before = PartialEmoji::default();
116
117        generate_diff!(
118            self, before, partial, remove,
119            (
120                name,
121            )
122        );
123
124        before
125    }
126}