Skip to content

Commit

Permalink
implement mastodon's /api/v1/custom_emojis
Browse files Browse the repository at this point in the history
  • Loading branch information
zeerooth committed Nov 3, 2023
1 parent fff83f8 commit 73008a5
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 17 deletions.
34 changes: 33 additions & 1 deletion crates/kitsune-core/src/mapping/mastodon/sealed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ use diesel::{
};
use diesel_async::RunQueryDsl;
use futures_util::{future::OptionFuture, FutureExt, TryFutureExt, TryStreamExt};
use iso8601_timestamp::Timestamp;
use kitsune_db::{
model::{
account::Account as DbAccount,
custom_emoji::CustomEmoji as DbCustomEmoji,
favourite::Favourite as DbFavourite,
follower::Follow,
link_preview::LinkPreview,
Expand All @@ -36,7 +38,7 @@ use kitsune_type::mastodon::{
preview_card::PreviewType,
relationship::Relationship,
status::{Mention, StatusSource},
Account, MediaAttachment, Notification, PreviewCard, Status,
Account, CustomEmoji, MediaAttachment, Notification, PreviewCard, Status,
};
use mime::Mime;
use scoped_futures::ScopedFutureExt;
Expand Down Expand Up @@ -589,3 +591,33 @@ impl IntoMastodon for DbNotification {
})
}
}

#[async_trait]
impl IntoMastodon for (DbCustomEmoji, Option<Timestamp>) {
type Output = CustomEmoji;

fn id(&self) -> Option<Uuid> {
None
}

async fn into_mastodon(self, state: MapperState<'_>) -> Result<Self::Output> {
let shortcode = self.0.shortcode;
let url = state.url_service.custom_emoji_url(self.0.id);
let category = if self.1.is_some() {
Some(String::from("recently used"))
} else if self.0.endorsed {
Some(String::from("endorsed"))
} else if self.0.domain.is_none() {
Some(String::from("local"))
} else {
Some(self.0.domain.unwrap())
};
Ok(CustomEmoji {
shortcode,
url: url.clone(),
static_url: url,
visible_in_picker: true,
category,
})
}
}
22 changes: 13 additions & 9 deletions crates/kitsune-core/src/resolve/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,8 @@ impl PostResolver {
let mut emoji_text = String::new();
Element::Emote(emote.clone()).render(&mut emoji_text);
let _ = custom_emojis.send((emoji.id, emoji_text));

}
}
Element::Emote(emote)

}
elem => elem,
};
Expand Down Expand Up @@ -119,23 +117,29 @@ mod test {
custom_emoji::CustomEmojiService, federation_filter::FederationFilterService,
job::JobService, url::UrlService,
},
webfinger::Webfinger, try_join,
try_join,
webfinger::Webfinger,
};
use athena::JobQueue;
use speedy_uuid::Uuid;
use core::convert::Infallible;
use diesel::{QueryDsl, SelectableHelper};
use diesel_async::RunQueryDsl;
use hyper::{Body, Request, Response};
use kitsune_cache::NoopCache;
use kitsune_config::FederationFilterConfiguration;
use kitsune_db::{model::{account::Account, custom_emoji::CustomEmoji, media_attachment::NewMediaAttachment}, schema::{accounts, custom_emojis, media_attachments}};
use kitsune_db::{
model::{
account::Account, custom_emoji::CustomEmoji, media_attachment::NewMediaAttachment,
},
schema::{accounts, custom_emojis, media_attachments},
};
use kitsune_http_client::Client;
use kitsune_search::NoopSearchService;
use kitsune_storage::fs::Storage as FsStorage;
use kitsune_test::{build_ap_response, database_test, redis_test};
use pretty_assertions::assert_eq;
use scoped_futures::ScopedFutureExt;
use speedy_uuid::Uuid;
use std::sync::Arc;
use tower::service_fn;

Expand Down Expand Up @@ -238,15 +242,15 @@ mod test {
domain: None,
remote_id: String::from("https://local.domain/emoji/blobhaj_happy"),
media_attachment_id: media_attachment_ids.0,
endorsed: false
endorsed: false,
})
.execute(db_conn);
try_join!(media_fut, emoji_fut)
}.scoped()
})
.await
.expect("Failed to insert the local emoji");

db_pool
.with_connection(|db_conn| {
async {
Expand All @@ -268,7 +272,7 @@ mod test {
domain: Some(String::from("example.com")),
remote_id: String::from("https://example.com/emojis/1"),
media_attachment_id: media_attachment_ids.1,
endorsed: false
endorsed: false,
})
.execute(db_conn);
try_join!(media_fut, emoji_fut)
Expand Down
46 changes: 41 additions & 5 deletions crates/kitsune-core/src/service/custom_emoji.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ use crate::{
};

use bytes::Bytes;
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper};
use diesel::{
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods,
OptionalExtension, QueryDsl, SelectableHelper,
};
use diesel_async::RunQueryDsl;
use futures_util::{Stream, TryStreamExt};
use garde::Validate;
use iso8601_timestamp::Timestamp;
use kitsune_db::{
model::custom_emoji::CustomEmoji,
schema::{custom_emojis, media_attachments},
schema::{custom_emojis, media_attachments, posts, posts_custom_emojis},
PgPool,
};
use scoped_futures::ScopedFutureExt;
Expand Down Expand Up @@ -44,11 +48,20 @@ pub struct GetEmoji<'a> {
domain: Option<&'a str>,
}

#[derive(TypedBuilder)]
pub struct GetEmojiList {
#[builder(default)]
fetching_account_id: Option<Uuid>,
#[builder(default = 5000)]
limit: i64,
}

#[derive(TypedBuilder, Validate)]
pub struct EmojiUpload<S> {
#[garde(custom(is_allowed_filetype))]
content_type: String,
#[garde(length(max = MAX_EMOJI_SHORTCODE_LENGTH))]
#[garde(pattern("^([a-zA-Z0-9]_?)*[a-zA-Z0-9]$"))]
shortcode: String,
#[garde(skip)]
stream: S,
Expand Down Expand Up @@ -81,11 +94,34 @@ impl CustomEmojiService {
.map_err(Error::from)
}

pub async fn get_all(&self) -> Result<impl Stream<Item = Result<CustomEmoji>> + '_> {
pub async fn get_list(
&self,
get_emoji_list: GetEmojiList,
) -> Result<impl Stream<Item = Result<(CustomEmoji, Option<Timestamp>)>> + '_> {
let query = custom_emojis::table
.select(CustomEmoji::as_select())
.order(custom_emojis::id.desc())
.left_join(
posts_custom_emojis::table.inner_join(
posts::table.on(posts::account_id
.nullable()
.eq(get_emoji_list.fetching_account_id)),
),
)
.filter(
posts::account_id.is_null().or(posts::account_id
.nullable()
.eq(get_emoji_list.fetching_account_id)),
)
.filter(
custom_emojis::endorsed
.eq(true)
.or(custom_emojis::domain.is_null())
.or(posts::created_at.is_not_null()),
)
.distinct_on(custom_emojis::id)
.select((CustomEmoji::as_select(), posts::created_at.nullable()))
.limit(get_emoji_list.limit)
.into_boxed();

self.db_pool
.with_connection(|db_conn| {
async move { Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) }
Expand Down
11 changes: 11 additions & 0 deletions crates/kitsune-type/src/mastodon/custom_emoji.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[derive(Clone, Deserialize, Serialize, ToSchema)]
pub struct CustomEmoji {
pub shortcode: String,
pub url: String,
pub static_url: String,
pub visible_in_picker: bool,
pub category: Option<String>,
}
2 changes: 2 additions & 0 deletions crates/kitsune-type/src/mastodon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use speedy_uuid::Uuid;
use utoipa::ToSchema;

pub mod account;
pub mod custom_emoji;
pub mod instance;
pub mod media_attachment;
pub mod notification;
Expand All @@ -12,6 +13,7 @@ pub mod search;
pub mod status;

pub use self::account::Account;
pub use self::custom_emoji::CustomEmoji;
pub use self::instance::Instance;
pub use self::media_attachment::MediaAttachment;
pub use self::notification::Notification;
Expand Down
42 changes: 42 additions & 0 deletions kitsune/src/http/handler/mastodon/api/v1/custom_emojis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::{error::Result, http::extractor::MastodonAuthExtractor, state::Zustand};
use axum::{debug_handler, extract::State, routing, Json, Router};
use futures_util::TryStreamExt;
use kitsune_core::{
mapping::MastodonMapper,
service::custom_emoji::{CustomEmojiService, GetEmojiList},
};
use kitsune_type::mastodon::CustomEmoji;

#[debug_handler(state = crate::state::Zustand)]
#[utoipa::path(
get,
path = "/api/v1/custom_emojis",
security(
("oauth_token" = [])
),
responses(
(status = 200, description = "List of custom emojis available on the server", body = Vec<CustomEmoji>)
),
)]
pub async fn get(
State(custom_emoji_service): State<CustomEmojiService>,
State(mastodon_mapper): State<MastodonMapper>,
user_data: Option<MastodonAuthExtractor>,
) -> Result<Json<Vec<CustomEmoji>>> {
let get_emoji_list = GetEmojiList::builder()
.fetching_account_id(user_data.map(|x| x.0.account.id))
.build();

let custom_emojis: Vec<CustomEmoji> = custom_emoji_service
.get_list(get_emoji_list)
.await?
.and_then(|acc| mastodon_mapper.map(acc))
.try_collect()
.await?;

Ok(Json(custom_emojis))
}

pub fn routes() -> Router<Zustand> {
Router::new().route("/", routing::get(get))
}
2 changes: 2 additions & 0 deletions kitsune/src/http/handler/mastodon/api/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use axum::Router;

pub mod accounts;
pub mod apps;
pub mod custom_emojis;
pub mod follow_requests;
pub mod instance;
pub mod media;
Expand All @@ -14,6 +15,7 @@ pub fn routes() -> Router<Zustand> {
Router::new()
.nest("/apps", apps::routes())
.nest("/accounts", accounts::routes())
.nest("/custom_emojis", custom_emojis::routes())
.nest("/follow_requests", follow_requests::routes())
.nest("/instance", instance::routes())
.nest("/media", media::routes())
Expand Down
3 changes: 2 additions & 1 deletion kitsune/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use kitsune_core::{
activitypub::Fetcher,
event::PostEventEmitter,
service::{
account::AccountService, attachment::AttachmentService,
account::AccountService, attachment::AttachmentService, custom_emoji::CustomEmojiService,
federation_filter::FederationFilterService, instance::InstanceService, job::JobService,
notification::NotificationService, post::PostService, search::SearchService,
timeline::TimelineService, url::UrlService, user::UserService,
Expand Down Expand Up @@ -55,6 +55,7 @@ impl_from_ref! {
[
AccountService => |input: &Zustand| input.core.service.account.clone(),
AttachmentService => |input: &Zustand| input.core.service.attachment.clone(),
CustomEmojiService => |input: &Zustand| input.core.service.custom_emoji.clone(),
FederationFilterService => |input: &Zustand| input.core.service.federation_filter.clone(),
JobService => |input: &Zustand| input.core.service.job.clone(),
NotificationService => |input: &Zustand| input.core.service.notification.clone(),
Expand Down
2 changes: 1 addition & 1 deletion lib/post-process/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ impl Render for Emote<'_> {
out,
":{}__{}:",
self.shortcode,
domain.replace(".", "_").replace("-", "_")
domain.replace(['.', '-'], "_")
),
None => write!(out, ":{}:", self.shortcode),
};
Expand Down

0 comments on commit 73008a5

Please sign in to comment.