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.
|
- Allow to add notes to generated invite codes.
|
||||||
- Added `registration.default_role` configuration option.
|
- Added `registration.default_role` configuration option.
|
||||||
|
- Save emojis attached to actor objects.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,13 @@ use mitra_config::Instance;
|
||||||
use crate::activitypub::{
|
use crate::activitypub::{
|
||||||
actors::types::Actor,
|
actors::types::Actor,
|
||||||
fetcher::fetchers::fetch_file,
|
fetcher::fetchers::fetch_file,
|
||||||
|
handlers::create::handle_emoji,
|
||||||
receiver::HandlerError,
|
receiver::HandlerError,
|
||||||
|
vocabulary::{EMOJI, HASHTAG},
|
||||||
};
|
};
|
||||||
use crate::database::DatabaseClient;
|
use crate::database::DatabaseClient;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
|
posts::validators::EMOJIS_MAX_NUM,
|
||||||
profiles::queries::{create_profile, update_profile},
|
profiles::queries::{create_profile, update_profile},
|
||||||
profiles::types::{
|
profiles::types::{
|
||||||
DbActorProfile,
|
DbActorProfile,
|
||||||
|
@ -78,10 +81,38 @@ async fn fetch_actor_images(
|
||||||
(maybe_avatar, maybe_banner)
|
(maybe_avatar, maybe_banner)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tags(actor: &Actor) -> () {
|
async fn parse_tags(
|
||||||
for tag_value in &actor.tag {
|
db_client: &impl DatabaseClient,
|
||||||
log::debug!("found actor tag: {}", tag_value);
|
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(
|
pub async fn create_remote_profile(
|
||||||
|
@ -103,7 +134,12 @@ pub async fn create_remote_profile(
|
||||||
).await;
|
).await;
|
||||||
let (identity_proofs, payment_options, extra_fields) =
|
let (identity_proofs, payment_options, extra_fields) =
|
||||||
actor.parse_attachments();
|
actor.parse_attachments();
|
||||||
parse_tags(&actor);
|
parse_tags(
|
||||||
|
db_client,
|
||||||
|
instance,
|
||||||
|
media_dir,
|
||||||
|
&actor,
|
||||||
|
).await?;
|
||||||
let mut profile_data = ProfileCreateData {
|
let mut profile_data = ProfileCreateData {
|
||||||
username: actor.preferred_username.clone(),
|
username: actor.preferred_username.clone(),
|
||||||
hostname: Some(actor_address.hostname),
|
hostname: Some(actor_address.hostname),
|
||||||
|
@ -153,7 +189,12 @@ pub async fn update_remote_profile(
|
||||||
).await;
|
).await;
|
||||||
let (identity_proofs, payment_options, extra_fields) =
|
let (identity_proofs, payment_options, extra_fields) =
|
||||||
actor.parse_attachments();
|
actor.parse_attachments();
|
||||||
parse_tags(&actor);
|
parse_tags(
|
||||||
|
db_client,
|
||||||
|
instance,
|
||||||
|
media_dir,
|
||||||
|
&actor,
|
||||||
|
).await?;
|
||||||
let mut profile_data = ProfileUpdateData {
|
let mut profile_data = ProfileUpdateData {
|
||||||
display_name: actor.name.clone(),
|
display_name: actor.name.clone(),
|
||||||
bio: actor.summary.clone(),
|
bio: actor.summary.clone(),
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde_json::{Value as JsonValue};
|
use serde_json::{Value as JsonValue};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use mitra_config::Config;
|
use mitra_config::{Config, Instance};
|
||||||
use mitra_utils::{
|
use mitra_utils::{
|
||||||
html::clean_html,
|
html::clean_html,
|
||||||
urls::get_hostname,
|
urls::get_hostname,
|
||||||
|
@ -32,7 +33,7 @@ use crate::models::{
|
||||||
get_emoji_by_remote_object_id,
|
get_emoji_by_remote_object_id,
|
||||||
update_emoji,
|
update_emoji,
|
||||||
},
|
},
|
||||||
emojis::types::EmojiImage,
|
emojis::types::{DbEmoji, EmojiImage},
|
||||||
emojis::validators::{
|
emojis::validators::{
|
||||||
validate_emoji_name,
|
validate_emoji_name,
|
||||||
EMOJI_MAX_SIZE,
|
EMOJI_MAX_SIZE,
|
||||||
|
@ -230,6 +231,91 @@ pub fn get_object_links(
|
||||||
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(
|
pub async fn get_object_tags(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
db_client: &impl DatabaseClient,
|
db_client: &impl DatabaseClient,
|
||||||
|
@ -361,97 +447,25 @@ pub async fn get_object_tags(
|
||||||
links.push(linked.id);
|
links.push(linked.id);
|
||||||
};
|
};
|
||||||
} else if tag_type == EMOJI {
|
} 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 {
|
if emojis.len() >= EMOJIS_MAX_NUM {
|
||||||
log::warn!("too many emojis");
|
log::warn!("too many emojis");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let tag_name = tag.name.trim_matches(':');
|
match handle_emoji(
|
||||||
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(
|
|
||||||
db_client,
|
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,
|
&instance,
|
||||||
&tag.icon.url,
|
|
||||||
tag.icon.media_type.as_deref(),
|
|
||||||
EMOJI_MAX_SIZE,
|
|
||||||
&media_dir,
|
&media_dir,
|
||||||
).await {
|
tag_value,
|
||||||
Ok(file) => file,
|
).await? {
|
||||||
Err(error) => {
|
Some(emoji) => {
|
||||||
log::warn!("failed to fetch emoji: {}", error);
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
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) {
|
if !emojis.contains(&emoji.id) {
|
||||||
emojis.push(emoji.id);
|
emojis.push(emoji.id);
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
log::warn!(
|
log::warn!("skipping tag of type {}", tag_type);
|
||||||
"skipping tag of type {}",
|
|
||||||
tag_type,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if let Some(ref object_id) = object.quote_url {
|
if let Some(ref object_id) = object.quote_url {
|
||||||
|
|
Loading…
Reference in a new issue