diff --git a/CHANGELOG.md b/CHANGELOG.md index 63e9133..d7a2f30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Save sizes of media attachments and other files to database. +- Added `import-emoji` command. ### Security diff --git a/docs/mitractl.md b/docs/mitractl.md index cec76ed..f210be4 100644 --- a/docs/mitractl.md +++ b/docs/mitractl.md @@ -74,6 +74,12 @@ Delete empty remote profiles: mitractl delete-empty-profiles 100 ``` +Import custom emoji from another instance: + +```shell +mitractl import-emoji emojiname example.org +``` + Generate ethereum address: ```shell diff --git a/src/atom/feeds.rs b/src/atom/feeds.rs index 1112d8e..5d0521c 100644 --- a/src/atom/feeds.rs +++ b/src/atom/feeds.rs @@ -1,20 +1,16 @@ use ammonia::clean_text; -use chrono::{DateTime, NaiveDateTime, Utc}; use crate::activitypub::identifiers::{local_actor_id, local_object_id}; use crate::config::Instance; use crate::models::posts::types::Post; use crate::models::profiles::types::DbActorProfile; -use crate::utils::html::clean_html_all; +use crate::utils::{ + datetime::get_min_datetime, + html::clean_html_all, +}; const ENTRY_TITLE_MAX_LENGTH: usize = 75; -fn get_min_datetime() -> DateTime { - let native = NaiveDateTime::from_timestamp_opt(0, 0) - .expect("0 should be a valid argument"); - DateTime::from_utc(native, Utc) -} - fn make_entry( instance_url: &str, post: &Post, diff --git a/src/bin/mitractl.rs b/src/bin/mitractl.rs index c2f06ca..d3fe81d 100644 --- a/src/bin/mitractl.rs +++ b/src/bin/mitractl.rs @@ -34,6 +34,7 @@ async fn main() { SubCommand::DeleteUnusedAttachments(cmd) => cmd.execute(&config, db_client).await.unwrap(), SubCommand::DeleteOrphanedFiles(cmd) => cmd.execute(&config, db_client).await.unwrap(), SubCommand::DeleteEmptyProfiles(cmd) => cmd.execute(&config, db_client).await.unwrap(), + SubCommand::ImportEmoji(cmd) => cmd.execute(&config, db_client).await.unwrap(), SubCommand::UpdateCurrentBlock(cmd) => cmd.execute(&config, db_client).await.unwrap(), SubCommand::ResetSubscriptions(cmd) => cmd.execute(&config, db_client).await.unwrap(), SubCommand::CreateMoneroWallet(cmd) => cmd.execute(&config).await.unwrap(), diff --git a/src/cli.rs b/src/cli.rs index 29b8058..2e5d86d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -19,7 +19,12 @@ use crate::ethereum::{ use crate::models::{ attachments::queries::delete_unused_attachments, cleanup::find_orphaned_files, - emojis::queries::delete_emoji, + emojis::queries::{ + create_emoji, + delete_emoji, + get_emoji_by_name_and_hostname, + }, + emojis::validators::EMOJI_LOCAL_MAX_SIZE, oauth::queries::delete_oauth_tokens, posts::queries::{delete_post, find_extraneous_posts, get_post_by_id}, profiles::queries::{ @@ -45,6 +50,7 @@ use crate::utils::{ generate_rsa_key, serialize_private_key, }, + datetime::get_min_datetime, files::remove_files, passwords::hash_password, }; @@ -72,6 +78,7 @@ pub enum SubCommand { DeleteUnusedAttachments(DeleteUnusedAttachments), DeleteOrphanedFiles(DeleteOrphanedFiles), DeleteEmptyProfiles(DeleteEmptyProfiles), + ImportEmoji(ImportEmoji), UpdateCurrentBlock(UpdateCurrentBlock), ResetSubscriptions(ResetSubscriptions), CreateMoneroWallet(CreateMoneroWallet), @@ -373,6 +380,41 @@ impl DeleteEmptyProfiles { } } +/// Import custom emoji from another instance +#[derive(Parser)] +pub struct ImportEmoji { + emoji_name: String, + hostname: String, +} + +impl ImportEmoji { + pub async fn execute( + &self, + _config: &Config, + db_client: &impl DatabaseClient, + ) -> Result<(), Error> { + let emoji = get_emoji_by_name_and_hostname( + db_client, + &self.emoji_name, + &self.hostname, + ).await?; + if emoji.image.file_size > EMOJI_LOCAL_MAX_SIZE { + println!("emoji is too big"); + return Ok(()); + }; + create_emoji( + db_client, + &emoji.emoji_name, + None, + emoji.image, + None, + &get_min_datetime(), + ).await?; + println!("added emoji to local collection"); + Ok(()) + } +} + /// Update blockchain synchronization starting block #[derive(Parser)] pub struct UpdateCurrentBlock { diff --git a/src/models/emojis/queries.rs b/src/models/emojis/queries.rs index b16266b..d539b2c 100644 --- a/src/models/emojis/queries.rs +++ b/src/models/emojis/queries.rs @@ -76,6 +76,23 @@ pub async fn update_emoji( Ok(emoji) } +pub async fn get_emoji_by_name_and_hostname( + db_client: &impl DatabaseClient, + emoji_name: &str, + hostname: &str, +) -> Result { + let maybe_row = db_client.query_opt( + " + SELECT emoji + FROM emoji WHERE emoji_name = $1 AND hostname = $2 + ", + &[&emoji_name, &hostname], + ).await?; + let row = maybe_row.ok_or(DatabaseError::NotFound("emoji"))?; + let emoji = row.try_get("emoji")?; + Ok(emoji) +} + pub async fn get_emoji_by_remote_object_id( db_client: &impl DatabaseClient, object_id: &str, diff --git a/src/models/emojis/validators.rs b/src/models/emojis/validators.rs index 621b48c..219a3b9 100644 --- a/src/models/emojis/validators.rs +++ b/src/models/emojis/validators.rs @@ -4,6 +4,7 @@ use crate::errors::ValidationError; const EMOJI_NAME_RE: &str = r"^[\w.]+$"; pub const EMOJI_MAX_SIZE: usize = 250 * 1000; // 250 kB +pub const EMOJI_LOCAL_MAX_SIZE: usize = 50 * 1000; // 50 kB pub const EMOJI_MEDIA_TYPES: [&str; 2] = [ "image/gif", "image/png", diff --git a/src/utils/datetime.rs b/src/utils/datetime.rs new file mode 100644 index 0000000..bc5d4ad --- /dev/null +++ b/src/utils/datetime.rs @@ -0,0 +1,7 @@ +use chrono::{DateTime, NaiveDateTime, Utc}; + +pub fn get_min_datetime() -> DateTime { + let native = NaiveDateTime::from_timestamp_opt(0, 0) + .expect("0 should be a valid argument"); + DateTime::from_utc(native, Utc) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 5032a20..0cfd55f 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,6 +2,7 @@ pub mod caip2; pub mod canonicalization; pub mod crypto_rsa; pub mod currencies; +pub mod datetime; pub mod files; pub mod html; pub mod id;