Note context fetching
This commit is contained in:
parent
7dc38ada9a
commit
734ace5d05
|
@ -1455,6 +1455,7 @@ dependencies = [
|
|||
"compact_str",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"futures-util",
|
||||
"headers",
|
||||
"hyper",
|
||||
"itertools 0.11.0",
|
||||
|
|
|
@ -15,7 +15,7 @@ members = [
|
|||
"magnetar_common",
|
||||
"magnetar_sdk",
|
||||
"magnetar_mmm_parser",
|
||||
"core"
|
||||
"core",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
|
@ -38,8 +38,6 @@ headers = "0.3"
|
|||
http = "0.2"
|
||||
hyper = "0.14"
|
||||
itertools = "0.11"
|
||||
js-sys = "0.3"
|
||||
log = "0.4"
|
||||
lru = "0.12"
|
||||
miette = "5.9"
|
||||
nom = "7"
|
||||
|
@ -67,9 +65,6 @@ ts-rs = "7"
|
|||
unicode-segmentation = "1.10"
|
||||
url = "2.3"
|
||||
walkdir = "2.3"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-sys = "0.3"
|
||||
|
||||
[dependencies]
|
||||
magnetar_core = { path = "./core" }
|
||||
|
@ -101,6 +96,7 @@ itertools = { workspace = true }
|
|||
|
||||
compact_str = { workspace = true }
|
||||
either = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
miette = { workspace = true, features = ["fancy"] }
|
||||
|
|
|
@ -222,7 +222,7 @@ impl NoteResolver {
|
|||
ALIAS_RENOTE.clone(),
|
||||
)
|
||||
.join_as(
|
||||
JoinType::InnerJoin,
|
||||
JoinType::LeftJoin,
|
||||
note::Relation::User.with_alias(ALIAS_RENOTE.clone()),
|
||||
ALIAS_RENOTE_USER.clone(),
|
||||
);
|
||||
|
|
|
@ -76,19 +76,19 @@ pack!(PackNoteBase, Required<Id> as id & Required<NoteBase> as note);
|
|||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
pub struct NoteAttachmentExt {
|
||||
pub poll: Option<PackPollBase>,
|
||||
pub attachments: Vec<PackDriveFileBase>,
|
||||
}
|
||||
|
||||
pack!(
|
||||
PackNoteWithAttachments,
|
||||
Required<Id> as id & Required<NoteBase> as note & Required<NoteAttachmentExt> as attachment
|
||||
PackNoteWithMaybeAttachments,
|
||||
Required<Id> as id & Required<NoteBase> as note & Option<NoteAttachmentExt> as attachment
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
pub struct NoteDetailExt {
|
||||
pub poll: Option<PackPollBase>,
|
||||
pub parent_note: Option<Box<NoteBase>>,
|
||||
pub renoted_note: Option<Box<NoteBase>>,
|
||||
pub parent_note: Option<Box<PackNoteWithMaybeAttachments>>,
|
||||
pub renoted_note: Option<Box<PackNoteWithMaybeAttachments>>,
|
||||
}
|
||||
|
||||
pack!(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use axum::extract::{Path, Query, State};
|
||||
use axum::{debug_handler, Json};
|
||||
use axum::Json;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::model::processing::note::NoteModel;
|
||||
|
@ -11,7 +11,6 @@ use crate::service::MagnetarService;
|
|||
use crate::web::auth::MaybeUser;
|
||||
use crate::web::{ApiError, ObjectNotFound};
|
||||
|
||||
#[debug_handler]
|
||||
pub async fn handle_note(
|
||||
Path(id): Path<String>,
|
||||
Query(NoteByIdReq {
|
||||
|
@ -22,8 +21,11 @@ pub async fn handle_note(
|
|||
MaybeUser(self_user): MaybeUser,
|
||||
) -> Result<Json<Res<GetNoteById>>, ApiError> {
|
||||
let ctx = PackingContext::new(service, self_user.clone()).await?;
|
||||
let note = NoteModel
|
||||
.fetch_single(&ctx, self_user.as_deref(), &id, context, attachments)
|
||||
let note = NoteModel {
|
||||
attachments,
|
||||
with_context: context,
|
||||
}
|
||||
.fetch_single(&ctx, self_user.as_deref(), &id)
|
||||
.await?
|
||||
.ok_or_else(|| ObjectNotFound(id))?;
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use magnetar_calckey_model::ck;
|
||||
use magnetar_sdk::types::note::Reaction;
|
||||
use magnetar_sdk::types::note::{
|
||||
NoteDetailExt, PackNoteWithMaybeAttachments, PackPollBase, Reaction,
|
||||
};
|
||||
use magnetar_sdk::types::user::PackUserBase;
|
||||
use magnetar_sdk::types::{
|
||||
drive::PackDriveFileBase,
|
||||
|
@ -81,10 +83,39 @@ impl PackType<NoteBaseSource<'_>> for NoteBase {
|
|||
}
|
||||
}
|
||||
|
||||
impl PackType<&Vec<PackDriveFileBase>> for NoteAttachmentExt {
|
||||
fn extract(_context: &PackingContext, data: &Vec<PackDriveFileBase>) -> Self {
|
||||
NoteAttachmentExt {
|
||||
attachments: data.clone(),
|
||||
pub struct NoteDetailSource<'a> {
|
||||
pub parent_note: Option<&'a PackNoteWithMaybeAttachments>,
|
||||
pub renoted_note: Option<&'a PackNoteWithMaybeAttachments>,
|
||||
}
|
||||
|
||||
impl<'a> PackType<NoteDetailSource<'a>> for NoteDetailExt {
|
||||
fn extract(
|
||||
_context: &PackingContext,
|
||||
NoteDetailSource {
|
||||
parent_note,
|
||||
renoted_note,
|
||||
}: NoteDetailSource<'a>,
|
||||
) -> Self {
|
||||
NoteDetailExt {
|
||||
parent_note: parent_note.cloned().map(Box::new),
|
||||
renoted_note: renoted_note.cloned().map(Box::new),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoteAttachmentSource<'a> {
|
||||
pub attachments: &'a [PackDriveFileBase],
|
||||
pub poll: Option<&'a PackPollBase>,
|
||||
}
|
||||
|
||||
impl<'a> PackType<NoteAttachmentSource<'a>> for NoteAttachmentExt {
|
||||
fn extract(
|
||||
_context: &PackingContext,
|
||||
NoteAttachmentSource { poll, attachments }: NoteAttachmentSource<'a>,
|
||||
) -> Self {
|
||||
NoteAttachmentExt {
|
||||
attachments: attachments.to_vec(),
|
||||
poll: poll.cloned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,32 @@
|
|||
use crate::model::data::id::BaseId;
|
||||
use crate::model::data::note::NoteBaseSource;
|
||||
use crate::model::data::note::{NoteAttachmentSource, NoteBaseSource, NoteDetailSource};
|
||||
use crate::model::processing::emoji::EmojiModel;
|
||||
use crate::model::processing::user::UserModel;
|
||||
use crate::model::processing::{get_mm_token_emoji, PackResult};
|
||||
use crate::model::{PackType, PackingContext, UserRelationship};
|
||||
use compact_str::CompactString;
|
||||
use either::Either;
|
||||
use futures_util::future::try_join_all;
|
||||
use magnetar_calckey_model::ck::sea_orm_active_enums::NoteVisibilityEnum;
|
||||
use magnetar_calckey_model::note_model::{NoteResolveOptions, NoteVisibilityFilterFactory};
|
||||
use magnetar_calckey_model::note_model::{
|
||||
NoteData, NoteResolveOptions, NoteVisibilityFilterFactory,
|
||||
};
|
||||
use magnetar_calckey_model::sea_orm::prelude::Expr;
|
||||
use magnetar_calckey_model::sea_orm::sea_query::{Alias, IntoIden, PgFunc, Query, SimpleExpr};
|
||||
use magnetar_calckey_model::sea_orm::{ActiveEnum, ColumnTrait, IntoSimpleExpr};
|
||||
use magnetar_calckey_model::{ck, CalckeyDbError};
|
||||
use magnetar_sdk::mmm::Token;
|
||||
use magnetar_sdk::types::drive::PackDriveFileBase;
|
||||
use magnetar_sdk::types::emoji::EmojiContext;
|
||||
use magnetar_sdk::types::note::{NoteBase, PackNoteMaybeFull};
|
||||
use magnetar_sdk::types::note::{
|
||||
NoteAttachmentExt, NoteBase, NoteDetailExt, PackNoteBase, PackNoteMaybeFull,
|
||||
PackNoteWithMaybeAttachments,
|
||||
};
|
||||
use magnetar_sdk::types::{Id, MmXml};
|
||||
use magnetar_sdk::{mmm, Packed, Required};
|
||||
use tokio::try_join;
|
||||
|
||||
use super::drive::DriveModel;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NoteVisibilityFilterSimple(Option<String>);
|
||||
|
@ -157,7 +167,10 @@ impl SpeechTransformNyan {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct NoteModel;
|
||||
pub struct NoteModel {
|
||||
pub attachments: bool,
|
||||
pub with_context: bool,
|
||||
}
|
||||
|
||||
impl NoteModel {
|
||||
pub fn tokenize_note_text(&self, note: &ck::note::Model) -> Option<Token> {
|
||||
|
@ -172,36 +185,18 @@ impl NoteModel {
|
|||
.map(|text| mmm::Context::default().parse_ui(text))
|
||||
}
|
||||
|
||||
pub async fn fetch_single(
|
||||
pub async fn extract_base(
|
||||
&self,
|
||||
ctx: &PackingContext,
|
||||
as_user: Option<&ck::user::Model>,
|
||||
id: &str,
|
||||
show_context: bool,
|
||||
attachments: bool,
|
||||
) -> PackResult<Option<PackNoteMaybeFull>> {
|
||||
let note_resolver = ctx.service.db.get_note_resolver();
|
||||
let Some(note) = note_resolver
|
||||
.get_one(&NoteResolveOptions {
|
||||
ids: Some(vec![id.to_owned()]),
|
||||
visibility_filter: Box::new(
|
||||
NoteVisibilityFilterModel
|
||||
.new_note_visibility_filter(as_user.map(ck::user::Model::get_id)),
|
||||
),
|
||||
time_range: None,
|
||||
with_user: show_context,
|
||||
with_reply_target: show_context,
|
||||
with_renote_target: show_context,
|
||||
})
|
||||
note_data: &NoteData,
|
||||
) -> PackResult<PackNoteBase> {
|
||||
let Required(ref user) = UserModel
|
||||
.base_from_existing(ctx, ¬e_data.user)
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
.user;
|
||||
|
||||
let Required(ref user) = UserModel.base_from_existing(ctx, ¬e.user).await?.user;
|
||||
|
||||
let cw_tok = self.tokenize_note_cw(¬e.note);
|
||||
let mut text_tok = self.tokenize_note_text(¬e.note);
|
||||
let cw_tok = self.tokenize_note_cw(¬e_data.note);
|
||||
let mut text_tok = self.tokenize_note_text(¬e_data.note);
|
||||
|
||||
let mut emoji_extracted = Vec::new();
|
||||
|
||||
|
@ -212,7 +207,7 @@ impl NoteModel {
|
|||
if let Some(text_tok) = &mut text_tok {
|
||||
emoji_extracted.extend_from_slice(&get_mm_token_emoji(text_tok));
|
||||
|
||||
if note.user.is_cat && note.user.speak_as_cat {
|
||||
if note_data.user.is_cat && note_data.user.speak_as_cat {
|
||||
let transformer = SpeechTransformNyan::new();
|
||||
text_tok.walk_speech_transform(&|text| transformer.transform(text));
|
||||
}
|
||||
|
@ -221,16 +216,14 @@ impl NoteModel {
|
|||
let emoji_model = EmojiModel;
|
||||
let shortcodes = emoji_model.deduplicate_emoji(ctx, emoji_extracted);
|
||||
let emojis = emoji_model
|
||||
.fetch_many_emojis(ctx, &shortcodes, note.user.host.as_deref())
|
||||
.fetch_many_emojis(ctx, &shortcodes, note_data.user.host.as_deref())
|
||||
.await?;
|
||||
let emoji_context = &EmojiContext(emojis);
|
||||
|
||||
// TODO: Polls, reactions, attachments, ...
|
||||
|
||||
let note_base = NoteBase::extract(
|
||||
ctx,
|
||||
NoteBaseSource {
|
||||
note: ¬e.note,
|
||||
note: ¬e_data.note,
|
||||
cw_mm: cw_tok
|
||||
.as_ref()
|
||||
.map(mmm::to_xml_string)
|
||||
|
@ -249,11 +242,192 @@ impl NoteModel {
|
|||
},
|
||||
);
|
||||
|
||||
Ok(Some(PackNoteMaybeFull::pack_from((
|
||||
Required(Id::from(note.note.id.clone())),
|
||||
Ok(PackNoteBase::pack_from((
|
||||
Required(Id::from(note_data.note.id.clone())),
|
||||
Required(note_base),
|
||||
None,
|
||||
None,
|
||||
)))
|
||||
}
|
||||
|
||||
async fn extract_reply_target_base(
|
||||
&self,
|
||||
ctx: &PackingContext,
|
||||
note: &NoteData,
|
||||
) -> PackResult<Option<PackNoteBase>> {
|
||||
match note.reply.as_ref() {
|
||||
Some(r) if self.with_context => self.extract_base(ctx, r).await.map(Some),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn extract_renote_target_base(
|
||||
&self,
|
||||
ctx: &PackingContext,
|
||||
note: &NoteData,
|
||||
) -> PackResult<Option<PackNoteBase>> {
|
||||
match note.renote.as_ref() {
|
||||
Some(r) if self.with_context => self.extract_base(ctx, r).await.map(Some),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn extract_reply_target_attachemnts(
|
||||
&self,
|
||||
ctx: &PackingContext,
|
||||
drive_model: &DriveModel,
|
||||
note: &NoteData,
|
||||
) -> PackResult<Option<Vec<PackDriveFileBase>>> {
|
||||
match note.reply.as_ref() {
|
||||
Some(r) if self.with_context => {
|
||||
self.extract_attachments(ctx, drive_model, &r.note).await
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn extract_renote_target_attachemnts(
|
||||
&self,
|
||||
ctx: &PackingContext,
|
||||
drive_model: &DriveModel,
|
||||
note: &NoteData,
|
||||
) -> PackResult<Option<Vec<PackDriveFileBase>>> {
|
||||
match note.renote.as_ref() {
|
||||
Some(r) if self.with_context => {
|
||||
self.extract_attachments(ctx, drive_model, &r.note).await
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn extract_attachments(
|
||||
&self,
|
||||
ctx: &PackingContext,
|
||||
drive_model: &DriveModel,
|
||||
note: &ck::note::Model,
|
||||
) -> PackResult<Option<Vec<PackDriveFileBase>>> {
|
||||
if self.attachments {
|
||||
let futures = try_join_all(
|
||||
note.file_ids
|
||||
.iter()
|
||||
.map(|id| drive_model.get_cached_base(ctx, id)),
|
||||
);
|
||||
|
||||
let att = futures.await?.into_iter().flatten().collect::<Vec<_>>();
|
||||
|
||||
return Ok(Some(att));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn fetch_single(
|
||||
&self,
|
||||
ctx: &PackingContext,
|
||||
as_user: Option<&ck::user::Model>,
|
||||
id: &str,
|
||||
) -> PackResult<Option<PackNoteMaybeFull>> {
|
||||
let note_resolver = ctx.service.db.get_note_resolver();
|
||||
let Some(note) = note_resolver
|
||||
.get_one(&NoteResolveOptions {
|
||||
ids: Some(vec![id.to_owned()]),
|
||||
visibility_filter: Box::new(
|
||||
NoteVisibilityFilterModel
|
||||
.new_note_visibility_filter(as_user.map(ck::user::Model::get_id)),
|
||||
),
|
||||
time_range: None,
|
||||
with_user: self.with_context,
|
||||
with_reply_target: self.with_context,
|
||||
with_renote_target: self.with_context,
|
||||
})
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let drive_model = DriveModel;
|
||||
|
||||
let extract_reply_target = self.extract_reply_target_base(ctx, ¬e);
|
||||
let extract_renote_target = self.extract_renote_target_base(ctx, ¬e);
|
||||
let extract_attachments = self.extract_attachments(ctx, &drive_model, ¬e.note);
|
||||
let extract_reply_attachments =
|
||||
self.extract_reply_target_attachemnts(ctx, &drive_model, ¬e);
|
||||
let extract_renote_attachments =
|
||||
self.extract_renote_target_attachemnts(ctx, &drive_model, ¬e);
|
||||
|
||||
// TODO: Polls, reactions, ...
|
||||
|
||||
let (
|
||||
PackNoteBase { id, note },
|
||||
reply_target_pack,
|
||||
renote_target_pack,
|
||||
attachments_pack,
|
||||
reply_attachments_pack,
|
||||
renote_attachments_pack,
|
||||
) = try_join!(
|
||||
self.extract_base(ctx, ¬e),
|
||||
extract_reply_target,
|
||||
extract_renote_target,
|
||||
extract_attachments,
|
||||
extract_reply_attachments,
|
||||
extract_renote_attachments
|
||||
)?;
|
||||
|
||||
let detail = self.with_context.then(|| {
|
||||
NoteDetailExt::extract(
|
||||
ctx,
|
||||
NoteDetailSource {
|
||||
parent_note: reply_target_pack
|
||||
.map(|base| {
|
||||
PackNoteWithMaybeAttachments::pack_from((
|
||||
base.id,
|
||||
base.note,
|
||||
reply_attachments_pack.map(|attachments| {
|
||||
NoteAttachmentExt::extract(
|
||||
ctx,
|
||||
NoteAttachmentSource {
|
||||
attachments: &attachments,
|
||||
poll: None, // TODO
|
||||
},
|
||||
)
|
||||
}),
|
||||
))
|
||||
})
|
||||
.as_ref(),
|
||||
renoted_note: renote_target_pack
|
||||
.map(|base| {
|
||||
PackNoteWithMaybeAttachments::pack_from((
|
||||
base.id,
|
||||
base.note,
|
||||
renote_attachments_pack.map(|attachments| {
|
||||
NoteAttachmentExt::extract(
|
||||
ctx,
|
||||
NoteAttachmentSource {
|
||||
attachments: &attachments,
|
||||
poll: None, // TODO
|
||||
},
|
||||
)
|
||||
}),
|
||||
))
|
||||
})
|
||||
.as_ref(),
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
let attachments = attachments_pack.map(|attachments| {
|
||||
NoteAttachmentExt::extract(
|
||||
ctx,
|
||||
NoteAttachmentSource {
|
||||
attachments: &attachments,
|
||||
poll: None, // TODO
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
Ok(Some(PackNoteMaybeFull::pack_from((
|
||||
id,
|
||||
note,
|
||||
attachments,
|
||||
detail,
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue