From 8e516420cfb57d291c2bdeb076e503a2cb3d0758 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:52:46 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20Announce=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/Announce.kt | 18 ++++++++++ .../activity/announce/ApAnnounceProcessor.kt | 21 ++++++++++++ .../activitypub/domain/model/AnnounceTest.kt | 33 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/AnnounceTest.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt new file mode 100644 index 00000000..e65a996d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt @@ -0,0 +1,18 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import dev.usbharu.hideout.activitypub.domain.model.objects.Object + +open class Announce @JsonCreator constructor( + type: List = emptyList(), + @JsonProperty("object") + val apObject: String, + override val actor: String, + override val id: String, + val published: String, + val to: List = emptyList(), + val cc: List = emptyList() +) : Object( + type = add(type, "Announce") +), HasActor, HasId \ No newline at end of file diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt new file mode 100644 index 00000000..8d803503 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt @@ -0,0 +1,21 @@ +package dev.usbharu.hideout.activitypub.service.activity.announce + +import dev.usbharu.hideout.activitypub.domain.model.Announce +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService +import dev.usbharu.hideout.application.external.Transaction +import org.springframework.stereotype.Service + +@Service +class ApAnnounceProcessor(transaction: Transaction,private val apNoteService:APNoteService) : + AbstractActivityPubProcessor(transaction) { + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + apNoteService.fetchAnnounce(activity.activity) + } + + override fun isSupported(activityType: ActivityType): Boolean = ActivityType.Announce == activityType + + override fun type(): Class = Announce::class.java +} \ No newline at end of file diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/AnnounceTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/AnnounceTest.kt new file mode 100644 index 00000000..5d01861e --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/AnnounceTest.kt @@ -0,0 +1,33 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.application.config.ActivityPubConfig +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class AnnounceTest{ + @Test + fun mastodonのjsonをデシリアライズできる() { + //language=JSON + val json = """{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "https://kb.usbharu.dev/users/usbharu/statuses/111859915842276344/activity", + "type": "Announce", + "actor": "https://kb.usbharu.dev/users/usbharu", + "published": "2024-02-02T04:07:40Z", + "to": [ + "https://kb.usbharu.dev/users/usbharu/followers" + ], + "cc": [ + "https://kb.usbharu.dev/users/usbharu" + ], + "object": "https://kb.usbharu.dev/users/usbharu/statuses/111850484548963326" +}""" + + val objectMapper = ActivityPubConfig().objectMapper() + + val readValue = objectMapper.readValue(json) + + + } +} \ No newline at end of file From ac5be2e2df887b76c00b1e1a0bad1b49a922865e Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:53:32 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20Announce=E3=82=92=E5=8F=97=E3=81=91?= =?UTF-8?q?=E5=8F=96=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/objects/ObjectDeserializer.kt | 2 +- .../ExposedAnnounceQueryService.kt | 67 +++++++++++ .../activitypub/query/AnnounceQueryService.kt | 11 ++ .../service/objects/note/APNoteService.kt | 70 ++++++++++- .../hideout/core/domain/model/actor/Actor.kt | 63 ++++------ .../hideout/core/domain/model/post/Post.kt | 111 ++++++++++++++++++ .../objects/note/APNoteServiceImplTest.kt | 9 +- 7 files changed, 292 insertions(+), 41 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt index f35b0c4f..5867cd4c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt @@ -42,7 +42,7 @@ class ObjectDeserializer : JsonDeserializer() { ExtendedActivityVocabulary.OrderedCollectionPage -> null ExtendedActivityVocabulary.Accept -> p.codec.treeToValue(treeNode, Accept::class.java) ExtendedActivityVocabulary.Add -> null - ExtendedActivityVocabulary.Announce -> null + ExtendedActivityVocabulary.Announce -> p.codec.treeToValue(treeNode,Announce::class.java) ExtendedActivityVocabulary.Arrive -> null ExtendedActivityVocabulary.Block -> p.codec.treeToValue(treeNode, Block::class.java) ExtendedActivityVocabulary.Create -> p.codec.treeToValue(treeNode, Create::class.java) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt new file mode 100644 index 00000000..1921be1b --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt @@ -0,0 +1,67 @@ +package dev.usbharu.hideout.activitypub.infrastructure.exposedquery + +import dev.usbharu.hideout.activitypub.domain.model.Announce +import dev.usbharu.hideout.activitypub.query.AnnounceQueryService +import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl +import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper +import dev.usbharu.hideout.core.domain.model.post.Post +import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts +import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.select +import org.springframework.stereotype.Repository +import java.time.Instant + +@Repository +class ExposedAnnounceQueryService( + private val postRepository: PostRepository, + private val postResultRowMapper: ResultRowMapper +) : AnnounceQueryService { + override suspend fun findById(id: Long): Pair? { + return Posts + .leftJoin(Actors) + .select { Posts.id eq id } + .singleOrNull() + ?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) } + } + + override suspend fun findByApId(apId: String): Pair? { + return Posts + .leftJoin(Actors) + .select { Posts.apId eq apId } + .singleOrNull() + ?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) } + } + + + private suspend fun ResultRow.toAnnounce(): Announce? { + val repostId = this[Posts.repostId] ?: return null + val repost = postRepository.findById(repostId)?.url ?: return null + + val (to, cc) = visibility( + Visibility.values().first { visibility -> visibility.ordinal == this[Posts.visibility] }, + this[Actors.followers] + ) + + return Announce( + emptyList(), + id = this[Posts.apId], + apObject = repost, + actor = this[Actors.url], + published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(), + to = to, + cc = cc + ) + } + + private fun visibility(visibility: Visibility, followers: String?): Pair, List> { + return when (visibility) { + Visibility.PUBLIC -> listOf(APNoteServiceImpl.public) to listOf(APNoteServiceImpl.public) + Visibility.UNLISTED -> listOfNotNull(followers) to listOf(APNoteServiceImpl.public) + Visibility.FOLLOWERS -> listOfNotNull(followers) to listOfNotNull(followers) + Visibility.DIRECT -> TODO() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt new file mode 100644 index 00000000..ed56ca6c --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt @@ -0,0 +1,11 @@ +package dev.usbharu.hideout.activitypub.query + +import dev.usbharu.hideout.activitypub.domain.model.Announce +import dev.usbharu.hideout.core.domain.model.post.Post +import org.springframework.stereotype.Repository + +@Repository +interface AnnounceQueryService { + suspend fun findById(id: Long): Pair? + suspend fun findByApId(apId: String): Pair? +} \ No newline at end of file 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 7c788ae0..94f21c36 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,8 +1,10 @@ package dev.usbharu.hideout.activitypub.service.objects.note import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException +import dev.usbharu.hideout.activitypub.domain.model.Announce import dev.usbharu.hideout.activitypub.domain.model.Emoji import dev.usbharu.hideout.activitypub.domain.model.Note +import dev.usbharu.hideout.activitypub.query.AnnounceQueryService import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService import dev.usbharu.hideout.activitypub.service.common.resolve @@ -23,6 +25,9 @@ interface APNoteService { suspend fun fetchNote(url: String, targetActor: String? = null): Note = fetchNoteWithEntity(url, targetActor).first suspend fun fetchNote(note: Note, targetActor: String? = null): Note suspend fun fetchNoteWithEntity(url: String, targetActor: String? = null): Pair + + suspend fun fetchAnnounce(url: String, signerId: Long? = null): Pair + suspend fun fetchAnnounce(announce: Announce, signerId: Long? = null): Pair } @Service @@ -35,7 +40,8 @@ class APNoteServiceImpl( private val postBuilder: Post.PostBuilder, private val noteQueryService: NoteQueryService, private val mediaService: MediaService, - private val emojiService: EmojiService + private val emojiService: EmojiService, + private val announceQueryService: AnnounceQueryService ) : APNoteService { @@ -67,6 +73,68 @@ class APNoteServiceImpl( return savedNote } + override suspend fun fetchAnnounce(url: String, signerId: Long?): Pair { + logger.debug("START Fetch Announce url: {}", url) + + val post: Pair? = announceQueryService.findByApId(url) + + if (post != null) { + logger.debug("SUCCESS Found in local url: {}", url) + return post + } + + logger.info("AP GET url: {}", url) + + val announce = try { + apResourceResolveService.resolve(url, signerId) + } catch (e: ClientRequestException) { + logger.warn( + "FAILED Failed to retrieve ActivityPub resource. HTTP Status Code: {} url: {}", + e.response.status, + url + ) + throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) + } + + return fetchAnnounce(announce,signerId) + } + + override suspend fun fetchAnnounce(announce: Announce, signerId: Long?): Pair { + + val findByApId = announceQueryService.findByApId(announce.id) + + if (findByApId != null) { + return findByApId + } + + val (_, actor) = apUserService.fetchPersonWithEntity(announce.actor, null) + + val (_, post) = fetchNoteWithEntity(announce.apObject, null) + + val visibility = if (announce.to.contains(public)) { + Visibility.PUBLIC + } else if (announce.to.contains(actor.followers) && announce.cc.contains(public)) { + Visibility.UNLISTED + } else if (announce.to.contains(actor.followers)) { + Visibility.FOLLOWERS + } else { + Visibility.DIRECT + } + + val createRemote = postService.createRemote( + postBuilder.pureRepostOf( + id = postRepository.generateId(), + actorId = actor.id, + visibility = visibility, + createdAt = Instant.parse(announce.published), + url = announce.id, + repost = post, + apId = announce.id + ) + ) + return announce to createRemote + } + private suspend fun saveIfMissing( note: Note, targetActor: String?, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index 5e8b4264..e488d86b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -5,6 +5,7 @@ import dev.usbharu.hideout.application.config.CharacterLimit import org.slf4j.LoggerFactory import org.springframework.stereotype.Component import java.time.Instant +import kotlin.math.max data class Actor private constructor( val id: Long, @@ -159,18 +160,6 @@ data class Actor private constructor( "keyId must contain non-blank characters." } - require(postsCount >= 0) { - "postsCount must be greater than or equal to 0" - } - - require(followersCount >= 0) { - "followersCount must be greater than or equal to 0" - } - - require(followingCount >= 0) { - "followingCount must be greater than or equal to 0" - } - return Actor( id = id, name = limitedName, @@ -188,9 +177,9 @@ data class Actor private constructor( following = following, instance = instance, locked = locked, - followersCount = followersCount, - followingCount = followingCount, - postsCount = postsCount, + followersCount = max(0,followersCount), + followingCount = max(0,followingCount), + postsCount = max(0, postsCount), lastPostDate = lastPostDate, emojis = emojis ) @@ -212,27 +201,27 @@ data class Actor private constructor( fun withLastPostAt(lastPostDate: Instant): Actor = this.copy(lastPostDate = lastPostDate) override fun toString(): String { return "Actor(" + - "id=$id, " + - "name='$name', " + - "domain='$domain', " + - "screenName='$screenName', " + - "description='$description', " + - "inbox='$inbox', " + - "outbox='$outbox', " + - "url='$url', " + - "publicKey='$publicKey', " + - "privateKey=$privateKey, " + - "createdAt=$createdAt, " + - "keyId='$keyId', " + - "followers=$followers, " + - "following=$following, " + - "instance=$instance, " + - "locked=$locked, " + - "followersCount=$followersCount, " + - "followingCount=$followingCount, " + - "postsCount=$postsCount, " + - "lastPostDate=$lastPostDate, " + - "emojis=$emojis" + - ")" + "id=$id, " + + "name='$name', " + + "domain='$domain', " + + "screenName='$screenName', " + + "description='$description', " + + "inbox='$inbox', " + + "outbox='$outbox', " + + "url='$url', " + + "publicKey='$publicKey', " + + "privateKey=$privateKey, " + + "createdAt=$createdAt, " + + "keyId='$keyId', " + + "followers=$followers, " + + "following=$following, " + + "instance=$instance, " + + "locked=$locked, " + + "followersCount=$followersCount, " + + "followingCount=$followingCount, " + + "postsCount=$postsCount, " + + "lastPostDate=$lastPostDate, " + + "emojis=$emojis" + + ")" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index 0eabb4aa..f50e879b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -89,6 +89,114 @@ data class Post private constructor( ) } + fun pureRepostOf( + id: Long, + actorId: Long, + visibility: Visibility, + createdAt: Instant, + url: String, + repost: Post, + apId: String + ): Post { + + // リポストの公開範囲は元のポストより広くてはいけない + val fixedVisibility = if (visibility.ordinal <= repost.visibility.ordinal) { + repost.visibility + } else { + visibility + } + + require(id >= 0) { "id must be greater than or equal to 0." } + + require(actorId >= 0) { "actorId must be greater than or equal to 0." } + + + return Post( + id, + actorId, + null, + "", + "", + createdAt.toEpochMilli(), + fixedVisibility, + url, + repost.id, + null, + false, + apId, + emptyList(), + false, + emptyList() + ) + } + + fun quoteRepostOf( + id: Long, + actorId: Long, + overview: String? = null, + content: String, + createdAt: Instant, + visibility: Visibility, + url: String, + repost:Post, + replyId: Long? = null, + sensitive: Boolean = false, + apId: String = url, + mediaIds: List = emptyList(), + emojiIds: List = emptyList() + ): Post { + + // リポストの公開範囲は元のポストより広くてはいけない + val fixedVisibility = if (visibility.ordinal <= repost.visibility.ordinal) { + repost.visibility + } else { + visibility + } + + require(id >= 0) { "id must be greater than or equal to 0." } + + require(actorId >= 0) { "actorId must be greater than or equal to 0." } + + val limitedOverview = if ((overview?.length ?: 0) >= characterLimit.post.overview) { + overview?.substring(0, characterLimit.post.overview) + } else { + overview + } + + val limitedText = if (content.length >= characterLimit.post.text) { + content.substring(0, characterLimit.post.text) + } else { + content + } + + val (html, content1) = postContentFormatter.format(limitedText) + + require(url.isNotBlank()) { "url must contain non-blank characters" } + require(url.length <= characterLimit.general.url) { + "url must not exceed ${characterLimit.general.url} characters." + } + + require((replyId ?: 0) >= 0) { "replyId must be greater then or equal to 0." } + + return Post( + id = id, + actorId = actorId, + overview = limitedOverview, + content = html, + text = content1, + createdAt = createdAt.toEpochMilli(), + visibility = fixedVisibility, + url = url, + repostId = repost.id, + replyId = replyId, + sensitive = sensitive, + apId = apId, + mediaIds = mediaIds, + delted = false, + emojiIds = emojiIds + ) + } + @Suppress("LongParameterList") fun deleteOf( id: Long, @@ -117,6 +225,9 @@ data class Post private constructor( } } + fun isPureRepost():Boolean = + this.text.isEmpty() && this.content.isEmpty() && this.overview == null && this.replyId == null && this.repostId != null + fun delete(): Post { return Post( id = this.id, 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 8109bb2d..97f34d09 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 @@ -79,6 +79,7 @@ class APNoteServiceImplTest { ), noteQueryService = noteQueryService, mock(), + mock(), mock() ) @@ -152,7 +153,8 @@ class APNoteServiceImplTest { ), noteQueryService = noteQueryService, mock(), - mock { } + mock { }, + mock() ) val actual = apNoteServiceImpl.fetchNote(url) @@ -204,7 +206,8 @@ class APNoteServiceImplTest { ), noteQueryService = noteQueryService, mock(), - mock() + mock(), + mock { } ) assertThrows { apNoteServiceImpl.fetchNote(url) } @@ -255,6 +258,7 @@ class APNoteServiceImplTest { postBuilder = postBuilder, noteQueryService = noteQueryService, mock(), + mock(), mock() ) @@ -308,6 +312,7 @@ class APNoteServiceImplTest { postBuilder = postBuilder, noteQueryService = noteQueryService, mock(), + mock(), mock() ) From 09165783ae9c0268508d450c36f74f8bca671a0f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:53:55 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20Undo=20Announce=E3=82=92=E5=8F=97?= =?UTF-8?q?=E3=81=91=E5=8F=96=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/activity/undo/APUndoProcessor.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 864c848f..c6abf698 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -12,6 +12,7 @@ import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.domain.exception.resource.local.LocalUserNotFoundException import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.service.post.PostService import dev.usbharu.hideout.core.service.reaction.ReactionService import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.springframework.stereotype.Service @@ -23,7 +24,8 @@ class APUndoProcessor( private val relationshipService: RelationshipService, private val reactionService: ReactionService, private val actorRepository: ActorRepository, - private val postRepository: PostRepository + private val postRepository: PostRepository, + private val postService: PostService ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { val undo = activity.activity @@ -53,6 +55,11 @@ class APUndoProcessor( return } + "Announce" -> { + announce(undo) + return + } + else -> {} } TODO() @@ -109,6 +116,13 @@ class APUndoProcessor( return } + private suspend fun announce(undo: Undo) { + val announce = undo.apObject as Announce + + val findByApId = postRepository.findByApId(announce.id) ?: return + postService.deleteRemote(findByApId) + } + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Undo override fun type(): Class = Undo::class.java From 35d126703b35d35267a7a7e702328a1f165e6344 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:06:56 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=E5=BC=95=E7=94=A8=E3=83=AA?= =?UTF-8?q?=E3=83=9D=E3=82=B9=E3=83=88=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/activitypub/domain/model/Note.kt | 41 ++++++++----- .../exposedquery/NoteQueryServiceImpl.kt | 14 +++++ .../service/objects/note/APNoteService.kt | 58 ++++++++++++++----- 3 files changed, 83 insertions(+), 30 deletions(-) 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 60e7858c..ba888274 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,5 +1,6 @@ package dev.usbharu.hideout.activitypub.domain.model +import com.fasterxml.jackson.annotation.JsonProperty 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 @@ -18,12 +19,15 @@ constructor( val inReplyTo: String? = null, val attachment: List = emptyList(), @JsonDeserialize(contentUsing = ObjectDeserializer::class) - val tag: List = emptyList() + val tag: List = emptyList(), + val quoteUri:String? = null, + val quoteUrl:String? = null, + @JsonProperty("_misskey_quote") + val misskeyQuote:String? = null ) : Object( type = add(type, "Note") ), HasId { - override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -41,6 +45,9 @@ constructor( if (inReplyTo != other.inReplyTo) return false if (attachment != other.attachment) return false if (tag != other.tag) return false + if (quoteUri != other.quoteUri) return false + if (quoteUrl != other.quoteUrl) return false + if (misskeyQuote != other.misskeyQuote) return false return true } @@ -57,22 +64,28 @@ constructor( result = 31 * result + (inReplyTo?.hashCode() ?: 0) result = 31 * result + attachment.hashCode() result = 31 * result + tag.hashCode() + result = 31 * result + (quoteUri?.hashCode() ?: 0) + result = 31 * result + (quoteUrl?.hashCode() ?: 0) + result = 31 * result + (misskeyQuote?.hashCode() ?: 0) 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, " + - "tag=$tag" + - ")" + - " ${super.toString()}" + "id='$id', " + + "attributedTo='$attributedTo', " + + "content='$content', " + + "published='$published', " + + "to=$to, " + + "cc=$cc, " + + "sensitive=$sensitive, " + + "inReplyTo=$inReplyTo, " + + "attachment=$attachment, " + + "tag=$tag, " + + "quoteUri=$quoteUri, " + + "quoteUrl=$quoteUrl, " + + "misskeyQuote=$misskeyQuote" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt index 1ebc9511..00dd9167 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt @@ -59,6 +59,17 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v null } + val repostId = this[Posts.repostId] + val repost = if (repostId != null) { + val url = postRepository.findById(repostId)?.url + if (url == null){ + logger.warn("Failed to get repostId: $repostId") + } + url + }else{ + null + } + val visibility1 = visibility( Visibility.values().first { visibility -> visibility.ordinal == this[Posts.visibility] }, @@ -72,6 +83,9 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v to = visibility1.first, cc = visibility1.second, inReplyTo = replyTo, + misskeyQuote = repost, + quoteUri = repost, + quoteUrl = repost, sensitive = this[Posts.sensitive], attachment = mediaList.map { Document(url = it.url, mediaType = "image/jpeg") } ) 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 94f21c36..fb2b22ba 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 @@ -26,8 +26,8 @@ interface APNoteService { suspend fun fetchNote(note: Note, targetActor: String? = null): Note suspend fun fetchNoteWithEntity(url: String, targetActor: String? = null): Pair - suspend fun fetchAnnounce(url: String, signerId: Long? = null): Pair - suspend fun fetchAnnounce(announce: Announce, signerId: Long? = null): Pair + suspend fun fetchAnnounce(url: String, signerId: Long? = null): Pair + suspend fun fetchAnnounce(announce: Announce, signerId: Long? = null): Pair } @Service @@ -96,7 +96,7 @@ class APNoteServiceImpl( throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) } - return fetchAnnounce(announce,signerId) + return fetchAnnounce(announce, signerId) } override suspend fun fetchAnnounce(announce: Announce, signerId: Long?): Pair { @@ -172,6 +172,11 @@ class APNoteServiceImpl( postRepository.findByUrl(it) } + val quote = (note.misskeyQuote ?: note.quoteUri ?: note.quoteUrl)?.let { + fetchNote(it, targetActor) + postRepository.findByUrl(it) + } + val emojis = note.tag .filterIsInstance() .map { @@ -192,20 +197,41 @@ class APNoteServiceImpl( ) }.map { it.id } + val createPost = + if (quote != null) { + postBuilder.quoteRepostOf( + id = postRepository.generateId(), + actorId = person.second.id, + content = note.content, + createdAt = Instant.parse(note.published), + visibility = visibility, + url = note.id, + replyId = reply?.id, + sensitive = note.sensitive, + apId = note.id, + mediaIds = mediaList, + emojiIds = emojis, + repost = quote + ) + } else { + postBuilder.of( + id = postRepository.generateId(), + actorId = person.second.id, + content = note.content, + createdAt = Instant.parse(note.published).toEpochMilli(), + visibility = visibility, + url = note.id, + replyId = reply?.id, + sensitive = note.sensitive, + apId = note.id, + mediaIds = mediaList, + emojiIds = emojis + ) + + } + val createRemote = postService.createRemote( - postBuilder.of( - id = postRepository.generateId(), - actorId = person.second.id, - content = note.content, - createdAt = Instant.parse(note.published).toEpochMilli(), - visibility = visibility, - url = note.id, - replyId = reply?.id, - sensitive = note.sensitive, - apId = note.id, - mediaIds = mediaList, - emojiIds = emojis - ) + createPost ) return note to createRemote } From 7b9ef770dadd5115b206438d5b8357250203b2bf Mon Sep 17 00:00:00 2001 From: usbharu Date: Sat, 3 Feb 2024 16:15:27 +0900 Subject: [PATCH 5/7] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../activitypub/domain/model/Announce.kt | 4 +- .../hideout/activitypub/domain/model/Note.kt | 36 +++++++------- .../model/objects/ObjectDeserializer.kt | 2 +- .../ExposedAnnounceQueryService.kt | 1 - .../exposedquery/NoteQueryServiceImpl.kt | 5 +- .../activity/announce/ApAnnounceProcessor.kt | 2 +- .../service/objects/note/APNoteService.kt | 2 - .../hideout/core/domain/model/actor/Actor.kt | 48 +++++++++---------- .../hideout/core/domain/model/post/Post.kt | 7 +-- 9 files changed, 52 insertions(+), 55 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt index e65a996d..54fd40f1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt @@ -15,4 +15,6 @@ open class Announce @JsonCreator constructor( val cc: List = emptyList() ) : Object( type = add(type, "Announce") -), HasActor, HasId \ No newline at end of file +), + HasActor, + HasId \ No newline at end of file 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 ba888274..04910b09 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 @@ -20,10 +20,10 @@ constructor( val attachment: List = emptyList(), @JsonDeserialize(contentUsing = ObjectDeserializer::class) val tag: List = emptyList(), - val quoteUri:String? = null, - val quoteUrl:String? = null, + val quoteUri: String? = null, + val quoteUrl: String? = null, @JsonProperty("_misskey_quote") - val misskeyQuote:String? = null + val misskeyQuote: String? = null ) : Object( type = add(type, "Note") ), @@ -72,20 +72,20 @@ constructor( override fun toString(): String { return "Note(" + - "id='$id', " + - "attributedTo='$attributedTo', " + - "content='$content', " + - "published='$published', " + - "to=$to, " + - "cc=$cc, " + - "sensitive=$sensitive, " + - "inReplyTo=$inReplyTo, " + - "attachment=$attachment, " + - "tag=$tag, " + - "quoteUri=$quoteUri, " + - "quoteUrl=$quoteUrl, " + - "misskeyQuote=$misskeyQuote" + - ")" + - " ${super.toString()}" + "id='$id', " + + "attributedTo='$attributedTo', " + + "content='$content', " + + "published='$published', " + + "to=$to, " + + "cc=$cc, " + + "sensitive=$sensitive, " + + "inReplyTo=$inReplyTo, " + + "attachment=$attachment, " + + "tag=$tag, " + + "quoteUri=$quoteUri, " + + "quoteUrl=$quoteUrl, " + + "misskeyQuote=$misskeyQuote" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt index 5867cd4c..781857d3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt @@ -42,7 +42,7 @@ class ObjectDeserializer : JsonDeserializer() { ExtendedActivityVocabulary.OrderedCollectionPage -> null ExtendedActivityVocabulary.Accept -> p.codec.treeToValue(treeNode, Accept::class.java) ExtendedActivityVocabulary.Add -> null - ExtendedActivityVocabulary.Announce -> p.codec.treeToValue(treeNode,Announce::class.java) + ExtendedActivityVocabulary.Announce -> p.codec.treeToValue(treeNode, Announce::class.java) ExtendedActivityVocabulary.Arrive -> null ExtendedActivityVocabulary.Block -> p.codec.treeToValue(treeNode, Block::class.java) ExtendedActivityVocabulary.Create -> p.codec.treeToValue(treeNode, Create::class.java) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt index 1921be1b..9051e61f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt @@ -35,7 +35,6 @@ class ExposedAnnounceQueryService( ?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) } } - private suspend fun ResultRow.toAnnounce(): Announce? { val repostId = this[Posts.repostId] ?: return null val repost = postRepository.findById(repostId)?.url ?: return null diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt index 00dd9167..fa4b99f6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt @@ -62,11 +62,12 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v val repostId = this[Posts.repostId] val repost = if (repostId != null) { val url = postRepository.findById(repostId)?.url - if (url == null){ + if (url == null) { logger.warn("Failed to get repostId: $repostId") } url - }else{ + } + else { null } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt index 8d803503..a4d74f66 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt @@ -9,7 +9,7 @@ import dev.usbharu.hideout.application.external.Transaction import org.springframework.stereotype.Service @Service -class ApAnnounceProcessor(transaction: Transaction,private val apNoteService:APNoteService) : +class ApAnnounceProcessor(transaction: Transaction, private val apNoteService: APNoteService) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { apNoteService.fetchAnnounce(activity.activity) 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 fb2b22ba..df39051a 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 @@ -100,7 +100,6 @@ class APNoteServiceImpl( } override suspend fun fetchAnnounce(announce: Announce, signerId: Long?): Pair { - val findByApId = announceQueryService.findByApId(announce.id) if (findByApId != null) { @@ -227,7 +226,6 @@ class APNoteServiceImpl( mediaIds = mediaList, emojiIds = emojis ) - } val createRemote = postService.createRemote( diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index e488d86b..59f16059 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -177,8 +177,8 @@ data class Actor private constructor( following = following, instance = instance, locked = locked, - followersCount = max(0,followersCount), - followingCount = max(0,followingCount), + followersCount = max(0, followersCount), + followingCount = max(0, followingCount), postsCount = max(0, postsCount), lastPostDate = lastPostDate, emojis = emojis @@ -201,27 +201,27 @@ data class Actor private constructor( fun withLastPostAt(lastPostDate: Instant): Actor = this.copy(lastPostDate = lastPostDate) override fun toString(): String { return "Actor(" + - "id=$id, " + - "name='$name', " + - "domain='$domain', " + - "screenName='$screenName', " + - "description='$description', " + - "inbox='$inbox', " + - "outbox='$outbox', " + - "url='$url', " + - "publicKey='$publicKey', " + - "privateKey=$privateKey, " + - "createdAt=$createdAt, " + - "keyId='$keyId', " + - "followers=$followers, " + - "following=$following, " + - "instance=$instance, " + - "locked=$locked, " + - "followersCount=$followersCount, " + - "followingCount=$followingCount, " + - "postsCount=$postsCount, " + - "lastPostDate=$lastPostDate, " + - "emojis=$emojis" + - ")" + "id=$id, " + + "name='$name', " + + "domain='$domain', " + + "screenName='$screenName', " + + "description='$description', " + + "inbox='$inbox', " + + "outbox='$outbox', " + + "url='$url', " + + "publicKey='$publicKey', " + + "privateKey=$privateKey, " + + "createdAt=$createdAt, " + + "keyId='$keyId', " + + "followers=$followers, " + + "following=$following, " + + "instance=$instance, " + + "locked=$locked, " + + "followersCount=$followersCount, " + + "followingCount=$followingCount, " + + "postsCount=$postsCount, " + + "lastPostDate=$lastPostDate, " + + "emojis=$emojis" + + ")" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index f50e879b..c5d905e4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -98,7 +98,6 @@ data class Post private constructor( repost: Post, apId: String ): Post { - // リポストの公開範囲は元のポストより広くてはいけない val fixedVisibility = if (visibility.ordinal <= repost.visibility.ordinal) { repost.visibility @@ -110,7 +109,6 @@ data class Post private constructor( require(actorId >= 0) { "actorId must be greater than or equal to 0." } - return Post( id, actorId, @@ -138,14 +136,13 @@ data class Post private constructor( createdAt: Instant, visibility: Visibility, url: String, - repost:Post, + repost: Post, replyId: Long? = null, sensitive: Boolean = false, apId: String = url, mediaIds: List = emptyList(), emojiIds: List = emptyList() ): Post { - // リポストの公開範囲は元のポストより広くてはいけない val fixedVisibility = if (visibility.ordinal <= repost.visibility.ordinal) { repost.visibility @@ -225,7 +222,7 @@ data class Post private constructor( } } - fun isPureRepost():Boolean = + fun isPureRepost(): Boolean = this.text.isEmpty() && this.content.isEmpty() && this.overview == null && this.replyId == null && this.repostId != null fun delete(): Post { From 8b1d1a5b0915f22c76a1cbf146940799ba496613 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:20:07 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20equals=E3=81=A8toString=E3=82=92?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/Announce.kt | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt index 54fd40f1..5c887259 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt @@ -17,4 +17,44 @@ open class Announce @JsonCreator constructor( type = add(type, "Announce") ), HasActor, - HasId \ No newline at end of file + HasId{ + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + if (!super.equals(other)) return false + + other as Announce + + if (apObject != other.apObject) return false + if (actor != other.actor) return false + if (id != other.id) return false + if (published != other.published) return false + if (to != other.to) return false + if (cc != other.cc) return false + + return true + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + apObject.hashCode() + result = 31 * result + actor.hashCode() + result = 31 * result + id.hashCode() + result = 31 * result + published.hashCode() + result = 31 * result + to.hashCode() + result = 31 * result + cc.hashCode() + return result + } + + override fun toString(): String { + return "Announce(" + + "apObject='$apObject', " + + "actor='$actor', " + + "id='$id', " + + "published='$published', " + + "to=$to, " + + "cc=$cc" + + ")" + + " ${super.toString()}" + } +} \ No newline at end of file From 2f0629153b35ab8b92feb200a15271538826acc2 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:37:25 +0900 Subject: [PATCH 7/7] style: fix lint --- .../activitypub/domain/model/Announce.kt | 20 +++--- .../hideout/activitypub/domain/model/Note.kt | 5 +- .../ExposedAnnounceQueryService.kt | 4 +- .../exposedquery/NoteQueryServiceImpl.kt | 3 +- .../activitypub/query/AnnounceQueryService.kt | 2 +- .../activity/announce/ApAnnounceProcessor.kt | 2 +- .../service/objects/note/APNoteService.kt | 64 ++++++++++--------- .../hideout/core/domain/model/post/Post.kt | 6 +- .../relationship/RelationshipRepository.kt | 1 + .../model/MastodonNotificationRepository.kt | 1 + .../account/MastodonAccountApiController.kt | 2 +- 11 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt index 5c887259..513d4429 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt @@ -17,7 +17,7 @@ open class Announce @JsonCreator constructor( type = add(type, "Announce") ), HasActor, - HasId{ + HasId { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -48,13 +48,13 @@ open class Announce @JsonCreator constructor( override fun toString(): String { return "Announce(" + - "apObject='$apObject', " + - "actor='$actor', " + - "id='$id', " + - "published='$published', " + - "to=$to, " + - "cc=$cc" + - ")" + - " ${super.toString()}" + "apObject='$apObject', " + + "actor='$actor', " + + "id='$id', " + + "published='$published', " + + "to=$to, " + + "cc=$cc" + + ")" + + " ${super.toString()}" } -} \ No newline at end of file +} 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 04910b09..cb51a489 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 @@ -6,7 +6,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Note -@Suppress("LongParameterList") +@Suppress("LongParameterList", "CyclomaticComplexMethod") constructor( type: List = emptyList(), override val id: String, @@ -28,6 +28,8 @@ constructor( type = add(type, "Note") ), HasId { + + @Suppress("CyclomaticComplexMethod", "CognitiveComplexMethod") override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -52,6 +54,7 @@ constructor( return true } + @Suppress("CyclomaticComplexMethod") override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + id.hashCode() diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt index 9051e61f..c89a6ce1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt @@ -45,7 +45,7 @@ class ExposedAnnounceQueryService( ) return Announce( - emptyList(), + type = emptyList(), id = this[Posts.apId], apObject = repost, actor = this[Actors.url], @@ -63,4 +63,4 @@ class ExposedAnnounceQueryService( Visibility.DIRECT -> TODO() } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt index fa4b99f6..ffe2ee83 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt @@ -66,8 +66,7 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v logger.warn("Failed to get repostId: $repostId") } url - } - else { + } else { null } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt index ed56ca6c..2e01f9ee 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt @@ -8,4 +8,4 @@ import org.springframework.stereotype.Repository interface AnnounceQueryService { suspend fun findById(id: Long): Pair? suspend fun findByApId(apId: String): Pair? -} \ No newline at end of file +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt index a4d74f66..99303538 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt @@ -18,4 +18,4 @@ class ApAnnounceProcessor(transaction: Transaction, private val apNoteService: A override fun isSupported(activityType: ActivityType): Boolean = ActivityType.Announce == activityType override fun type(): Class = Announce::class.java -} \ No newline at end of file +} 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 df39051a..8cd1ad11 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 @@ -4,12 +4,14 @@ import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubRe import dev.usbharu.hideout.activitypub.domain.model.Announce import dev.usbharu.hideout.activitypub.domain.model.Emoji import dev.usbharu.hideout.activitypub.domain.model.Note +import dev.usbharu.hideout.activitypub.domain.model.Person import dev.usbharu.hideout.activitypub.query.AnnounceQueryService 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.actor.Actor import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.Visibility @@ -68,7 +70,7 @@ class APNoteServiceImpl( ) throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) } - val savedNote = saveIfMissing(note, targetActor, url) + val savedNote = saveIfMissing(note, targetActor) logger.debug("SUCCESS Fetch Note url: {}", url) return savedNote } @@ -136,34 +138,22 @@ class APNoteServiceImpl( private suspend fun saveIfMissing( note: Note, - targetActor: String?, - url: String - ): Pair = noteQueryService.findByApid(note.id) ?: saveNote(note, targetActor, url) + targetActor: String? + ): Pair = noteQueryService.findByApid(note.id) ?: saveNote(note, targetActor) - private suspend fun saveNote(note: Note, targetActor: String?, url: String): Pair { + private suspend fun saveNote(note: Note, targetActor: String?): Pair { val person = apUserService.fetchPersonWithEntity( note.attributedTo, targetActor ) val post = postRepository.findByApId(note.id) - if (post != null) { return note to post } logger.debug("VISIBILITY url: {} to: {} cc: {}", note.id, note.to, note.cc) - - val visibility = if (note.to.contains(public)) { - Visibility.PUBLIC - } else if (note.to.contains(person.second.followers) && note.cc.contains(public)) { - Visibility.UNLISTED - } else if (note.to.contains(person.second.followers)) { - Visibility.FOLLOWERS - } else { - Visibility.DIRECT - } - + val visibility = visibility(note, person) logger.debug("VISIBILITY is {} url: {}", visibility.name, note.id) val reply = note.inReplyTo?.let { @@ -176,14 +166,7 @@ class APNoteServiceImpl( postRepository.findByUrl(it) } - val emojis = note.tag - .filterIsInstance() - .map { - emojiService.fetchEmoji(it).second - } - .map { - it.id - } + val emojis = buildEmojis(note) val mediaList = note.attachment.map { mediaService.uploadRemoteMedia( @@ -228,14 +211,37 @@ class APNoteServiceImpl( ) } - val createRemote = postService.createRemote( - createPost - ) + val createRemote = postService.createRemote(createPost) return note to createRemote } + private suspend fun buildEmojis(note: Note) = note.tag + .filterIsInstance() + .map { + emojiService.fetchEmoji(it).second + } + .map { + it.id + } + + private fun visibility( + note: Note, + person: Pair + ): Visibility { + val visibility = if (note.to.contains(public)) { + Visibility.PUBLIC + } else if (note.to.contains(person.second.followers) && note.cc.contains(public)) { + Visibility.UNLISTED + } else if (note.to.contains(person.second.followers)) { + Visibility.FOLLOWERS + } else { + Visibility.DIRECT + } + return visibility + } + override suspend fun fetchNote(note: Note, targetActor: String?): Note = - saveIfMissing(note, targetActor, note.id).first + saveIfMissing(note, targetActor).first companion object { const val public: String = "https://www.w3.org/ns/activitystreams#Public" diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index c5d905e4..e9b73fe2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -223,7 +223,11 @@ data class Post private constructor( } fun isPureRepost(): Boolean = - this.text.isEmpty() && this.content.isEmpty() && this.overview == null && this.replyId == null && this.repostId != null + this.text.isEmpty() && + this.content.isEmpty() && + this.overview == null && + this.replyId == null && + this.repostId != null fun delete(): Post { return Post( diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index b08180a6..c934e18b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -36,6 +36,7 @@ interface RelationshipRepository { suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List + @Suppress("FunctionMaxLength") suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( targetId: Long, followRequest: Boolean, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt index e1294843..7ee7342c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt @@ -8,6 +8,7 @@ interface MastodonNotificationRepository { suspend fun deleteById(id: Long) suspend fun findById(id: Long): MastodonNotification? + @Suppress("FunctionMaxLength") suspend fun findByUserIdAndInTypesAndInSourceActorId( loginUser: Long, types: List, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index f577f48d..16c5900f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -84,7 +84,7 @@ class MastodonAccountApiController( maxId?.toLongOrNull(), sinceId?.toLongOrNull(), minId?.toLongOrNull(), - limit.coerceIn(0, 80) ?: 40 + limit.coerceIn(0, 80) ) ) val httpHeader = statuses.toHttpHeader(