Save emojis attached to actor objects
This commit is contained in:
parent
4204350375
commit
bd53e147ca
3 changed files with 144 additions and 88 deletions
|
@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
- Allow to add notes to generated invite codes.
|
||||
- Added `registration.default_role` configuration option.
|
||||
- Save emojis attached to actor objects.
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
|
|
@ -5,10 +5,13 @@ use mitra_config::Instance;
|
|||
use crate::activitypub::{
|
||||
actors::types::Actor,
|
||||
fetcher::fetchers::fetch_file,
|
||||
handlers::create::handle_emoji,
|
||||
receiver::HandlerError,
|
||||
vocabulary::{EMOJI, HASHTAG},
|
||||
};
|
||||
use crate::database::DatabaseClient;
|
||||
use crate::models::{
|
||||
posts::validators::EMOJIS_MAX_NUM,
|
||||
profiles::queries::{create_profile, update_profile},
|
||||
profiles::types::{
|
||||
DbActorProfile,
|
||||
|
@ -78,10 +81,38 @@ async fn fetch_actor_images(
|
|||
(maybe_avatar, maybe_banner)
|
||||
}
|
||||
|
||||
fn parse_tags(actor: &Actor) -> () {
|
||||
for tag_value in &actor.tag {
|
||||
log::debug!("found actor tag: {}", tag_value);
|
||||
async fn parse_tags(
|
||||
db_client: &impl DatabaseClient,
|
||||
instance: &Instance,
|
||||
media_dir: &Path,
|
||||
actor: &Actor,
|
||||
) -> Result<(), HandlerError> {
|
||||
let mut emojis = vec![];
|
||||
for tag_value in actor.tag.clone() {
|
||||
let tag_type = tag_value["type"].as_str().unwrap_or(HASHTAG);
|
||||
if tag_type == EMOJI {
|
||||
if emojis.len() >= EMOJIS_MAX_NUM {
|
||||
log::warn!("too many emojis");
|
||||
continue;
|
||||
};
|
||||
match handle_emoji(
|
||||
db_client,
|
||||
instance,
|
||||
media_dir,
|
||||
tag_value,
|
||||
).await? {
|
||||
Some(emoji) => {
|
||||
if !emojis.contains(&emoji.id) {
|
||||
emojis.push(emoji.id);
|
||||
};
|
||||
},
|
||||
None => continue,
|
||||
};
|
||||
} else {
|
||||
log::warn!("skipping actor tag of type {}", tag_type);
|
||||
};
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn create_remote_profile(
|
||||
|
@ -103,7 +134,12 @@ pub async fn create_remote_profile(
|
|||
).await;
|
||||
let (identity_proofs, payment_options, extra_fields) =
|
||||
actor.parse_attachments();
|
||||
parse_tags(&actor);
|
||||
parse_tags(
|
||||
db_client,
|
||||
instance,
|
||||
media_dir,
|
||||
&actor,
|
||||
).await?;
|
||||
let mut profile_data = ProfileCreateData {
|
||||
username: actor.preferred_username.clone(),
|
||||
hostname: Some(actor_address.hostname),
|
||||
|
@ -153,7 +189,12 @@ pub async fn update_remote_profile(
|
|||
).await;
|
||||
let (identity_proofs, payment_options, extra_fields) =
|
||||
actor.parse_attachments();
|
||||
parse_tags(&actor);
|
||||
parse_tags(
|
||||
db_client,
|
||||
instance,
|
||||
media_dir,
|
||||
&actor,
|
||||
).await?;
|
||||
let mut profile_data = ProfileUpdateData {
|
||||
display_name: actor.name.clone(),
|
||||
bio: actor.summary.clone(),
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use chrono::Utc;
|
||||
use serde_json::{Value as JsonValue};
|
||||
use uuid::Uuid;
|
||||
|
||||
use mitra_config::Config;
|
||||
use mitra_config::{Config, Instance};
|
||||
use mitra_utils::{
|
||||
html::clean_html,
|
||||
urls::get_hostname,
|
||||
|
@ -32,7 +33,7 @@ use crate::models::{
|
|||
get_emoji_by_remote_object_id,
|
||||
update_emoji,
|
||||
},
|
||||
emojis::types::EmojiImage,
|
||||
emojis::types::{DbEmoji, EmojiImage},
|
||||
emojis::validators::{
|
||||
validate_emoji_name,
|
||||
EMOJI_MAX_SIZE,
|
||||
|
@ -230,6 +231,91 @@ pub fn get_object_links(
|
|||
links
|
||||
}
|
||||
|
||||
pub async fn handle_emoji(
|
||||
db_client: &impl DatabaseClient,
|
||||
instance: &Instance,
|
||||
media_dir: &Path,
|
||||
tag_value: JsonValue,
|
||||
) -> Result<Option<DbEmoji>, HandlerError> {
|
||||
let tag: EmojiTag = match serde_json::from_value(tag_value) {
|
||||
Ok(tag) => tag,
|
||||
Err(error) => {
|
||||
log::warn!("invalid emoji tag: {}", error);
|
||||
return Ok(None);
|
||||
},
|
||||
};
|
||||
let tag_name = tag.name.trim_matches(':');
|
||||
if validate_emoji_name(tag_name).is_err() {
|
||||
log::warn!("invalid emoji name");
|
||||
return Ok(None);
|
||||
};
|
||||
let maybe_emoji_id = match get_emoji_by_remote_object_id(
|
||||
db_client,
|
||||
&tag.id,
|
||||
).await {
|
||||
Ok(emoji) => {
|
||||
if emoji.updated_at >= tag.updated {
|
||||
// Emoji already exists and is up to date
|
||||
return Ok(Some(emoji));
|
||||
};
|
||||
if emoji.emoji_name != tag_name {
|
||||
log::warn!("emoji name can't be changed");
|
||||
return Ok(None);
|
||||
};
|
||||
Some(emoji.id)
|
||||
},
|
||||
Err(DatabaseError::NotFound("emoji")) => None,
|
||||
Err(other_error) => return Err(other_error.into()),
|
||||
};
|
||||
let (file_name, file_size, maybe_media_type) = match fetch_file(
|
||||
instance,
|
||||
&tag.icon.url,
|
||||
tag.icon.media_type.as_deref(),
|
||||
EMOJI_MAX_SIZE,
|
||||
media_dir,
|
||||
).await {
|
||||
Ok(file) => file,
|
||||
Err(error) => {
|
||||
log::warn!("failed to fetch emoji: {}", error);
|
||||
return Ok(None);
|
||||
},
|
||||
};
|
||||
let media_type = match maybe_media_type {
|
||||
Some(media_type) if EMOJI_MEDIA_TYPES.contains(&media_type.as_str()) => {
|
||||
media_type
|
||||
},
|
||||
_ => {
|
||||
log::warn!(
|
||||
"unexpected emoji media type: {:?}",
|
||||
maybe_media_type,
|
||||
);
|
||||
return Ok(None);
|
||||
},
|
||||
};
|
||||
log::info!("downloaded emoji {}", tag.icon.url);
|
||||
let image = EmojiImage { file_name, file_size, media_type };
|
||||
let emoji = if let Some(emoji_id) = maybe_emoji_id {
|
||||
update_emoji(
|
||||
db_client,
|
||||
&emoji_id,
|
||||
image,
|
||||
&tag.updated,
|
||||
).await?
|
||||
} else {
|
||||
let hostname = get_hostname(&tag.id)
|
||||
.map_err(|_| ValidationError("invalid emoji ID"))?;
|
||||
create_emoji(
|
||||
db_client,
|
||||
tag_name,
|
||||
Some(&hostname),
|
||||
image,
|
||||
Some(&tag.id),
|
||||
&tag.updated,
|
||||
).await?
|
||||
};
|
||||
Ok(Some(emoji))
|
||||
}
|
||||
|
||||
pub async fn get_object_tags(
|
||||
config: &Config,
|
||||
db_client: &impl DatabaseClient,
|
||||
|
@ -361,97 +447,25 @@ pub async fn get_object_tags(
|
|||
links.push(linked.id);
|
||||
};
|
||||
} else if tag_type == EMOJI {
|
||||
let tag: EmojiTag = match serde_json::from_value(tag_value) {
|
||||
Ok(tag) => tag,
|
||||
Err(error) => {
|
||||
log::warn!("invalid emoji tag: {}", error);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
if emojis.len() >= EMOJIS_MAX_NUM {
|
||||
log::warn!("too many emojis");
|
||||
continue;
|
||||
};
|
||||
let tag_name = tag.name.trim_matches(':');
|
||||
if validate_emoji_name(tag_name).is_err() {
|
||||
log::warn!("invalid emoji name");
|
||||
continue;
|
||||
};
|
||||
let maybe_emoji_id = match get_emoji_by_remote_object_id(
|
||||
match handle_emoji(
|
||||
db_client,
|
||||
&tag.id,
|
||||
).await {
|
||||
Ok(emoji) => {
|
||||
if emoji.updated_at >= tag.updated {
|
||||
// Emoji already exists and is up to date
|
||||
if !emojis.contains(&emoji.id) {
|
||||
emojis.push(emoji.id);
|
||||
};
|
||||
continue;
|
||||
};
|
||||
if emoji.emoji_name != tag_name {
|
||||
log::warn!("emoji name can't be changed");
|
||||
continue;
|
||||
};
|
||||
Some(emoji.id)
|
||||
},
|
||||
Err(DatabaseError::NotFound("emoji")) => None,
|
||||
Err(other_error) => return Err(other_error.into()),
|
||||
};
|
||||
let (file_name, file_size, maybe_media_type) = match fetch_file(
|
||||
&instance,
|
||||
&tag.icon.url,
|
||||
tag.icon.media_type.as_deref(),
|
||||
EMOJI_MAX_SIZE,
|
||||
&media_dir,
|
||||
).await {
|
||||
Ok(file) => file,
|
||||
Err(error) => {
|
||||
log::warn!("failed to fetch emoji: {}", error);
|
||||
continue;
|
||||
tag_value,
|
||||
).await? {
|
||||
Some(emoji) => {
|
||||
if !emojis.contains(&emoji.id) {
|
||||
emojis.push(emoji.id);
|
||||
};
|
||||
},
|
||||
};
|
||||
let media_type = match maybe_media_type {
|
||||
Some(media_type) if EMOJI_MEDIA_TYPES.contains(&media_type.as_str()) => {
|
||||
media_type
|
||||
},
|
||||
_ => {
|
||||
log::warn!(
|
||||
"unexpected emoji media type: {:?}",
|
||||
maybe_media_type,
|
||||
);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
log::info!("downloaded emoji {}", tag.icon.url);
|
||||
let image = EmojiImage { file_name, file_size, media_type };
|
||||
let emoji = if let Some(emoji_id) = maybe_emoji_id {
|
||||
update_emoji(
|
||||
db_client,
|
||||
&emoji_id,
|
||||
image,
|
||||
&tag.updated,
|
||||
).await?
|
||||
} else {
|
||||
let hostname = get_hostname(&tag.id)
|
||||
.map_err(|_| ValidationError("invalid emoji ID"))?;
|
||||
create_emoji(
|
||||
db_client,
|
||||
tag_name,
|
||||
Some(&hostname),
|
||||
image,
|
||||
Some(&tag.id),
|
||||
&tag.updated,
|
||||
).await?
|
||||
};
|
||||
if !emojis.contains(&emoji.id) {
|
||||
emojis.push(emoji.id);
|
||||
None => continue,
|
||||
};
|
||||
} else {
|
||||
log::warn!(
|
||||
"skipping tag of type {}",
|
||||
tag_type,
|
||||
);
|
||||
log::warn!("skipping tag of type {}", tag_type);
|
||||
};
|
||||
};
|
||||
if let Some(ref object_id) = object.quote_url {
|
||||
|
|
Loading…
Reference in a new issue