diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/IPostRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/IPostRepository.kt index 7321fbf7..b8b3d9eb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/IPostRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/IPostRepository.kt @@ -20,4 +20,5 @@ interface IPostRepository { userId: Long?): List suspend fun findByUserId(idOrNull: Long, since: Instant?, until: Instant?, minId: Long?, maxId: Long?, limit: Int?, userId: Long?): List + suspend fun findByApId(id: String): Post? } diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt index 1c00fce0..6545d520 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt @@ -89,6 +89,12 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe override suspend fun findByUserId(idOrNull: Long, since: Instant?, until: Instant?, minId: Long?, maxId: Long?, limit: Int?, userId: Long?): List { TODO("Not yet implemented") } + + override suspend fun findByApId(id: String): Post? { + return query { + Posts.select { Posts.apId eq id }.singleOrNull()?.toPost() + } + } } object Posts : Table() { diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt index cfbb564b..372d12ad 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt @@ -22,11 +22,11 @@ import java.time.Instant @Single class ActivityPubNoteServiceImpl( - private val httpClient: HttpClient, - private val jobQueueParentService: JobQueueParentService, - private val userService: IUserService, - private val postRepository: IPostRepository, - private val activityPubUserService: ActivityPubUserService + private val httpClient: HttpClient, + private val jobQueueParentService: JobQueueParentService, + private val userService: IUserService, + private val postRepository: IPostRepository, + private val activityPubUserService: ActivityPubUserService ) : ActivityPubNoteService { private val logger = LoggerFactory.getLogger(this::class.java) @@ -48,33 +48,44 @@ class ActivityPubNoteServiceImpl( val actor = props[DeliverPostJob.actor] val postEntity = Config.configData.objectMapper.readValue(props[DeliverPostJob.post]) val note = Note( - name = "Note", - id = postEntity.url, - attributedTo = actor, - content = postEntity.text, - published = Instant.ofEpochMilli(postEntity.createdAt).toString(), - to = listOf(public, actor + "/follower") + name = "Note", + id = postEntity.url, + attributedTo = actor, + content = postEntity.text, + published = Instant.ofEpochMilli(postEntity.createdAt).toString(), + to = listOf(public, actor + "/follower") ) val inbox = props[DeliverPostJob.inbox] logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox) httpClient.postAp( - urlString = inbox, - username = "$actor#pubkey", - jsonLd = Create( - name = "Create Note", - `object` = note, - actor = note.attributedTo, - id = "${Config.configData.url}/create/note/${postEntity.id}" - ) + urlString = inbox, + username = "$actor#pubkey", + jsonLd = Create( + name = "Create Note", + `object` = note, + actor = note.attributedTo, + id = "${Config.configData.url}/create/note/${postEntity.id}" + ) ) } override suspend fun fetchNote(url: String, targetActor: String?): Note { val post = postRepository.findByUrl(url) if (post != null) { - val user = userService.findById(post.userId) - val reply = post.replyId?.let { postRepository.findOneById(it) } - return Note( + return postToNote(post) + } + val response = httpClient.getAp( + url, + "$targetActor#pubkey" + ) + val note = response.body() + return note(note, targetActor, url) + } + + private suspend fun postToNote(post: Post): Note { + val user = userService.findById(post.userId) + val reply = post.replyId?.let { postRepository.findOneById(it) } + return Note( name = "Post", id = post.apId, attributedTo = user.url, @@ -84,38 +95,35 @@ class ActivityPubNoteServiceImpl( sensitive = post.sensitive, cc = listOf(public, user.url + "/follower"), inReplyTo = reply?.url - ) - } - val response = httpClient.getAp( - url, - "$targetActor#pubkey" ) - val note = response.body() - return note(note, targetActor, url) } private suspend fun ActivityPubNoteServiceImpl.note( - note: Note, - targetActor: String?, - url: String + note: Note, + targetActor: String?, + url: String ): Note { + val findByApId = postRepository.findByApId(url) + if (findByApId != null) { + return postToNote(findByApId) + } val person = activityPubUserService.fetchPerson( - note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"), - targetActor + note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"), + targetActor ) val user = - userService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("person.url is null")) + userService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("person.url is null")) val visibility = - if (note.to.contains(public) && note.cc.contains(public)) { - Visibility.PUBLIC - } else if (note.to.find { it.endsWith("/followers") } != null && note.cc.contains(public)) { - Visibility.UNLISTED - } else if (note.to.find { it.endsWith("/followers") } != null) { - Visibility.FOLLOWERS - } else { - Visibility.DIRECT - } + if (note.to.contains(public) && note.cc.contains(public)) { + Visibility.PUBLIC + } else if (note.to.find { it.endsWith("/followers") } != null && note.cc.contains(public)) { + Visibility.UNLISTED + } else if (note.to.find { it.endsWith("/followers") } != null) { + Visibility.FOLLOWERS + } else { + Visibility.DIRECT + } val reply = note.inReplyTo?.let { fetchNote(it, targetActor) @@ -123,25 +131,25 @@ class ActivityPubNoteServiceImpl( } postRepository.save( - Post( - id = postRepository.generateId(), - userId = user.id, - overview = null, - text = note.content.orEmpty(), - createdAt = Instant.parse(note.published).toEpochMilli(), - visibility = visibility, - url = note.id ?: url, - repostId = null, - replyId = reply?.id, - sensitive = note.sensitive, - apId = note.id ?: url, - ) + Post( + id = postRepository.generateId(), + userId = user.id, + overview = null, + text = note.content.orEmpty(), + createdAt = Instant.parse(note.published).toEpochMilli(), + visibility = visibility, + url = note.id ?: url, + repostId = null, + replyId = reply?.id, + sensitive = note.sensitive, + apId = note.id ?: url, + ) ) return note } override suspend fun fetchNote(note: Note, targetActor: String?): Note = - note(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null")) + note(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null")) companion object { val public: String = "https://www.w3.org/ns/activitystreams#Public"