diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt index 2b16e1c4..b3383ef5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt @@ -1,6 +1,8 @@ package dev.usbharu.hideout.activitypub.domain.model +import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object +import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Note @Suppress("LongParameterList") @@ -14,7 +16,9 @@ constructor( val cc: List = emptyList(), val sensitive: Boolean = false, val inReplyTo: String? = null, - val attachment: List = emptyList() + val attachment: List = emptyList(), + @JsonDeserialize(contentUsing = ObjectDeserializer::class) + val tag: List = emptyList() ) : Object( type = add(type, "Note") ), @@ -36,6 +40,7 @@ constructor( if (sensitive != other.sensitive) return false if (inReplyTo != other.inReplyTo) return false if (attachment != other.attachment) return false + if (tag != other.tag) return false return true } @@ -51,21 +56,23 @@ constructor( result = 31 * result + sensitive.hashCode() result = 31 * result + (inReplyTo?.hashCode() ?: 0) result = 31 * result + attachment.hashCode() + result = 31 * result + tag.hashCode() return result } override fun toString(): String { return "Note(" + - "id='$id', " + - "attributedTo='$attributedTo', " + - "content='$content', " + - "published='$published', " + - "to=$to, " + - "cc=$cc, " + - "sensitive=$sensitive, " + - "inReplyTo=$inReplyTo, " + - "attachment=$attachment" + - ")" + - " ${super.toString()}" + "id='$id', " + + "attributedTo='$attributedTo', " + + "content='$content', " + + "published='$published', " + + "to=$to, " + + "cc=$cc, " + + "sensitive=$sensitive, " + + "inReplyTo=$inReplyTo, " + + "attachment=$attachment, " + + "tag=$tag" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt index 7d284e0f..80e2889e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt @@ -1,10 +1,12 @@ package dev.usbharu.hideout.activitypub.service.objects.note import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException +import dev.usbharu.hideout.activitypub.domain.model.Emoji import dev.usbharu.hideout.activitypub.domain.model.Note import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService import dev.usbharu.hideout.activitypub.service.common.resolve +import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostRepository @@ -32,7 +34,8 @@ class APNoteServiceImpl( private val apResourceResolveService: APResourceResolveService, private val postBuilder: Post.PostBuilder, private val noteQueryService: NoteQueryService, - private val mediaService: MediaService + private val mediaService: MediaService, + private val emojiService: EmojiService ) : APNoteService { @@ -101,6 +104,16 @@ class APNoteServiceImpl( postRepository.findByUrl(it) } + val emojis = note.tag + .filterIsInstance() + .map { + emojiService.fetchEmoji(it).second + } + .map { + it.id + } + + val mediaList = note.attachment.map { mediaService.uploadRemoteMedia( RemoteMedia( @@ -123,7 +136,8 @@ class APNoteServiceImpl( replyId = reply?.id, sensitive = note.sensitive, apId = note.id, - mediaIds = mediaList + mediaIds = mediaList, + emojiIds = emojis ) ) return note to createRemote diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt index a026ed5e..4df40c2d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt @@ -148,6 +148,6 @@ object PostsMedia : Table("posts_media") { object PostsEmojis : Table("posts_emojis") { val postId = long("post_id").references(Posts.id) - val emojiId = long("emoji_id").references(Posts.id) + val emojiId = long("emoji_id").references(CustomEmojis.id) override val primaryKey: PrimaryKey = PrimaryKey(postId, emojiId) } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt index 9e1397a9..cfca4a97 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.activitypub.domain.model import com.fasterxml.jackson.module.kotlin.readValue import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public import dev.usbharu.hideout.application.config.ActivityPubConfig +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -51,11 +52,7 @@ class NoteSerializeTest { "attachment": [], "sensitive": false, "tag": [ - { - "type": "Mention", - "href": "https://calckey.jp/users/9bu1xzwjyb", - "name": "@trapezial@calckey.jp" - } + ] }""" @@ -77,4 +74,105 @@ class NoteSerializeTest { ) assertEquals(note, readValue) } + + @Test + fun 絵文字付きNoteのデシリアライズができる() { + val json = """{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "sensitive": "as:sensitive", + "Hashtag": "as:Hashtag", + "quoteUrl": "as:quoteUrl", + "toot": "http://joinmastodon.org/ns#", + "Emoji": "toot:Emoji", + "featured": "toot:featured", + "discoverable": "toot:discoverable", + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "misskey": "https://misskey-hub.net/ns#", + "_misskey_content": "misskey:_misskey_content", + "_misskey_quote": "misskey:_misskey_quote", + "_misskey_reaction": "misskey:_misskey_reaction", + "_misskey_votes": "misskey:_misskey_votes", + "_misskey_summary": "misskey:_misskey_summary", + "isCat": "misskey:isCat", + "vcard": "http://www.w3.org/2006/vcard/ns#" + } + ], + "id": "https://misskey.usbharu.dev/notes/9nj1omt1rn", + "type": "Note", + "attributedTo": "https://misskey.usbharu.dev/users/97ws8y3rj6", + "content": "

​:oyasumi:​

", + "_misskey_content": ":oyasumi:", + "source": { + "content": ":oyasumi:", + "mediaType": "text/x.misskeymarkdown" + }, + "published": "2023-12-21T17:32:36.853Z", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://misskey.usbharu.dev/users/97ws8y3rj6/followers" + ], + "inReplyTo": null, + "attachment": [], + "sensitive": false, + "tag": [ + { + "id": "https://misskey.usbharu.dev/emojis/oyasumi", + "type": "Emoji", + "name": ":oyasumi:", + "updated": "2023-04-07T08:21:25.246Z", + "icon": { + "type": "Image", + "mediaType": "image/png", + "url": "https://s3misskey.usbharu.dev/misskey-minio/misskey-minio/data/cf8db710-1d70-4076-8a00-dbb28131096e.png" + } + } + ] +}""" + + + val objectMapper = ActivityPubConfig().objectMapper() + + val expected = Note( + type = emptyList(), + id = "https://misskey.usbharu.dev/notes/9nj1omt1rn", + attributedTo = "https://misskey.usbharu.dev/users/97ws8y3rj6", + content = "

\u200B:oyasumi:\u200B

", + published = "2023-12-21T17:32:36.853Z", + to = listOf("https://www.w3.org/ns/activitystreams#Public"), + cc = listOf("https://misskey.usbharu.dev/users/97ws8y3rj6/followers"), + sensitive = false, + inReplyTo = null, + attachment = emptyList(), + tag = listOf( + Emoji( + type = emptyList(), + name = ":oyasumi:", + id = "https://misskey.usbharu.dev/emojis/oyasumi", + updated = "2023-04-07T08:21:25.246Z", + icon = Image( + type = emptyList(), + mediaType = "image/png", + url = "https://s3misskey.usbharu.dev/misskey-minio/misskey-minio/data/cf8db710-1d70-4076-8a00-dbb28131096e.png" + ) + ) + ) + ) + + expected.context = listOf( + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1" + ) + + val note = objectMapper.readValue(json) + + assertThat(note).isEqualTo(expected) + } } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt index cece7a5b..e5e1c957 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt @@ -73,6 +73,7 @@ class APNoteServiceImplTest { apResourceResolveService = mock(), postBuilder = Post.PostBuilder(CharacterLimit()), noteQueryService = noteQueryService, + mock(), mock() ) @@ -142,7 +143,8 @@ class APNoteServiceImplTest { apResourceResolveService = apResourceResolveService, postBuilder = Post.PostBuilder(CharacterLimit()), noteQueryService = noteQueryService, - mock() + mock(), + mock { } ) val actual = apNoteServiceImpl.fetchNote(url) @@ -190,6 +192,7 @@ class APNoteServiceImplTest { apResourceResolveService = apResourceResolveService, postBuilder = Post.PostBuilder(CharacterLimit()), noteQueryService = noteQueryService, + mock(), mock() ) @@ -240,6 +243,7 @@ class APNoteServiceImplTest { apResourceResolveService = mock(), postBuilder = postBuilder, noteQueryService = noteQueryService, + mock(), mock() ) @@ -292,6 +296,7 @@ class APNoteServiceImplTest { apResourceResolveService = mock(), postBuilder = postBuilder, noteQueryService = noteQueryService, + mock(), mock() )