magnetar/src/model/processing/emoji.rs

172 lines
5.6 KiB
Rust

use crate::model::processing::{PackError, PackResult};
use crate::model::{PackType, PackingContext};
use either::Either;
use futures_util::TryFutureExt;
use itertools::Itertools;
use magnetar_calckey_model::ck;
use magnetar_calckey_model::emoji::EmojiTag;
use magnetar_common::util::{parse_reaction, RawReaction};
use magnetar_sdk::types::emoji::{EmojiBase, PackEmojiBase};
use magnetar_sdk::types::note::{Reaction, ReactionShortcode, ReactionUnicode, ReactionUnknown};
use magnetar_sdk::types::Id;
use magnetar_sdk::{Packed, Required};
use std::sync::Arc;
pub fn parse_emoji_or_raw(tag: &str) -> Either<String, RawReaction> {
parse_reaction(tag).map_or_else(|| Either::Left(tag.to_string()), Either::Right)
}
pub fn shortcode_tag_or_none(value: &RawReaction) -> Option<EmojiTag<'_>> {
match value {
RawReaction::Shortcode { shortcode, host } => Some(EmojiTag {
name: shortcode.as_str(),
host: host.as_deref(),
}),
_ => None,
}
}
pub fn resolve_reaction<'a>(
value: RawReaction,
code_lookup: &impl Fn(&str, Option<&str>) -> Option<&'a ck::emoji::Model>,
) -> Reaction {
match value {
RawReaction::Unicode(text) => Reaction::Unicode(ReactionUnicode(text)),
RawReaction::Shortcode { shortcode, host } => code_lookup(&shortcode, host.as_deref())
.map_or_else(
|| {
Reaction::Unknown(ReactionUnknown {
raw: format!(
":{shortcode}{}:",
host.as_deref().map(|h| format!("@{h}")).unwrap_or_default()
),
})
},
|e| {
Reaction::Shortcode(ReactionShortcode {
name: shortcode.clone(),
host: host.clone(),
url: e.public_url.clone(),
})
},
),
}
}
pub struct EmojiModel;
impl EmojiModel {
pub fn pack_existing(&self, ctx: &PackingContext, emoji: &ck::emoji::Model) -> PackEmojiBase {
PackEmojiBase::pack_from((
Required(Id::from(&emoji.id)),
Required(EmojiBase::extract(ctx, &emoji)),
))
}
pub async fn fetch_many_emojis(
&self,
ctx: &PackingContext,
shortcodes: &[String],
host: Option<&str>,
) -> PackResult<Vec<PackEmojiBase>> {
let emojis = ctx.service.emoji_cache.get_many(shortcodes, host).await?;
let packed_emojis = emojis.iter().map(|e| self.pack_existing(ctx, &e)).collect();
Ok(packed_emojis)
}
pub async fn fetch_many_tag_emojis(
&self,
ctx: &PackingContext,
tags: &[EmojiTag<'_>],
) -> PackResult<Vec<PackEmojiBase>> {
let emojis = ctx.service.emoji_cache.get_many_tagged(tags).await?;
let packed_emojis = emojis.iter().map(|e| self.pack_existing(ctx, &e)).collect();
Ok(packed_emojis)
}
pub fn deduplicate_emoji(&self, ctx: &PackingContext, emoji_list: Vec<String>) -> Vec<String> {
emoji_list
.into_iter()
.sorted()
.dedup()
.take(ctx.limits.max_emojis)
.collect::<Vec<_>>()
}
pub async fn resolve_reaction(
&self,
ctx: &PackingContext,
reaction: &str,
) -> PackResult<Reaction> {
let parsed = parse_emoji_or_raw(reaction);
Ok(match parsed {
Either::Left(raw) => Reaction::Unknown(ReactionUnknown { raw }),
Either::Right(raw) => {
let reaction_fetched = match shortcode_tag_or_none(&raw) {
Some(tag) => {
ctx.service
.emoji_cache
.get(tag.name, tag.host)
.map_err(PackError::from)
.await?
}
None => None,
};
let reaction_ref = reaction_fetched.as_ref().map(Arc::as_ref);
resolve_reaction(raw, &move |_, _| reaction_ref)
}
})
}
pub async fn resolve_reactions_many(
&self,
ctx: &PackingContext,
reactions_raw: &[String],
) -> PackResult<Vec<Reaction>> {
let reactions_parsed = reactions_raw
.iter()
.map(String::as_ref)
.map(parse_emoji_or_raw)
.collect::<Vec<_>>();
// Pick out all successfully-parsed shortcode emojis
let reactions_to_resolve = reactions_parsed
.iter()
.map(Either::as_ref)
.filter_map(Either::right)
.filter_map(shortcode_tag_or_none)
.collect::<Vec<_>>();
let reactions_fetched = ctx
.service
.emoji_cache
.get_many_tagged(&reactions_to_resolve)
.map_err(PackError::from)
.await?;
// Left reactions and the Right ones that didn't resolve to any emoji are turned back into Unknown
let reactions_resolved = reactions_parsed
.into_iter()
.map(|val| {
val.either(
|raw| Reaction::Unknown(ReactionUnknown { raw }),
|raw| {
resolve_reaction(raw, &|shortcode, host| {
reactions_fetched
.iter()
.find(|e| e.host.as_deref() == host && e.name == shortcode)
.map(Arc::as_ref)
})
},
)
})
.collect::<Vec<_>>();
Ok(reactions_resolved)
}
}