From 98638af478a1c8a86899acdf9d927591eaca8e20 Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Mon, 31 Jul 2023 01:07:14 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7?=
 =?UTF-8?q?=E3=83=A7=E3=83=B3=E6=83=85=E5=A0=B1=E3=81=AE=E9=85=8D=E9=80=81?=
 =?UTF-8?q?=E3=81=AE=E5=AE=9F=E8=A3=85=E3=82=92=E4=BD=9C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../kotlin/dev/usbharu/hideout/Application.kt |  7 +++
 .../usbharu/hideout/domain/model/ap/Like.kt   |  4 +-
 .../domain/model/hideout/entity/User.kt       |  4 +-
 .../hideout/domain/model/job/HideoutJob.kt    |  1 +
 .../hideout/routing/api/internal/v1/Users.kt  | 16 +++---
 .../ActivityPubReactionServiceImpl.kt         | 57 +++++++++++++++++++
 .../activitypub/ActivityPubServiceImpl.kt     |  5 +-
 .../service/reaction/ReactionServiceImpl.kt   |  2 +-
 8 files changed, 82 insertions(+), 14 deletions(-)
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReactionServiceImpl.kt

diff --git a/src/main/kotlin/dev/usbharu/hideout/Application.kt b/src/main/kotlin/dev/usbharu/hideout/Application.kt
index 5052c79c..90701529 100644
--- a/src/main/kotlin/dev/usbharu/hideout/Application.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/Application.kt
@@ -8,6 +8,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
 import dev.usbharu.hideout.config.Config
 import dev.usbharu.hideout.config.ConfigData
 import dev.usbharu.hideout.domain.model.job.DeliverPostJob
+import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
 import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
 import dev.usbharu.hideout.plugins.*
 import dev.usbharu.hideout.repository.IUserRepository
@@ -135,4 +136,10 @@ fun Application.worker() {
             activityPubService.processActivity(this, it)
         }
     }
+
+    kJob.register(DeliverReactionJob) {
+        execute {
+            activityPubService.processActivity(this, it)
+        }
+    }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt
index c4eb3abf..625a83ef 100644
--- a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt
@@ -11,13 +11,13 @@ open class Like : Object {
 
     protected constructor() : super()
     constructor(
-        type: List<String>,
+        type: List<String> = emptyList(),
         name: String?,
         actor: String?,
         id: String?,
         `object`: String?,
         content: String?,
-        tag: List<Object>
+        tag: List<Object> = emptyList()
     ) : super(
         type = add(type, "Like"),
         name = name,
diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt
index 45af9cc2..6754df4f 100644
--- a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt
@@ -18,7 +18,7 @@ data class User(
 ) {
     override fun toString(): String {
         return "User(id=$id, name='$name', domain='$domain', screenName='$screenName', description='$description'," +
-                " password=****, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey'," +
-                " privateKey=****, createdAt=$createdAt)"
+            " password=****, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey'," +
+            " privateKey=****, createdAt=$createdAt)"
     }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/job/HideoutJob.kt
index a1b896b8..9d10cb23 100644
--- a/src/main/kotlin/dev/usbharu/hideout/domain/model/job/HideoutJob.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/job/HideoutJob.kt
@@ -21,4 +21,5 @@ object DeliverReactionJob : HideoutJob("DeliverReactionJob") {
     val postUrl = string("postUrl")
     val actor = string("actor")
     val inbox = string("inbox")
+    val id = string("id")
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Users.kt b/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Users.kt
index 8609e439..24a45006 100644
--- a/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Users.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Users.kt
@@ -42,11 +42,11 @@ fun Route.users(userService: IUserService, userApiService: IUserApiService) {
             authenticate(TOKEN_AUTH, optional = true) {
                 get {
                     val userParameter = (
-                            call.parameters["name"]
-                                ?: throw ParameterNotExistException(
-                                    "Parameter(name='userName@domain') does not exist."
-                                )
+                        call.parameters["name"]
+                            ?: throw ParameterNotExistException(
+                                "Parameter(name='userName@domain') does not exist."
                             )
+                        )
                     if (userParameter.toLongOrNull() != null) {
                         return@get call.respond(userApiService.findById(userParameter.toLong()))
                     } else {
@@ -92,11 +92,11 @@ fun Route.users(userService: IUserService, userApiService: IUserApiService) {
             route("/following") {
                 get {
                     val userParameter = (
-                            call.parameters["name"]
-                                ?: throw ParameterNotExistException(
-                                    "Parameter(name='userName@domain') does not exist."
-                                )
+                        call.parameters["name"]
+                            ?: throw ParameterNotExistException(
+                                "Parameter(name='userName@domain') does not exist."
                             )
+                        )
                     if (userParameter.toLongOrNull() != null) {
                         return@get call.respond(userApiService.findFollowings(userParameter.toLong()))
                     }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReactionServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReactionServiceImpl.kt
new file mode 100644
index 00000000..3f60c8a7
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReactionServiceImpl.kt
@@ -0,0 +1,57 @@
+package dev.usbharu.hideout.service.activitypub
+
+import dev.usbharu.hideout.config.Config
+import dev.usbharu.hideout.domain.model.ap.Like
+import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
+import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
+import dev.usbharu.hideout.exception.PostNotFoundException
+import dev.usbharu.hideout.plugins.postAp
+import dev.usbharu.hideout.repository.IPostRepository
+import dev.usbharu.hideout.service.job.JobQueueParentService
+import dev.usbharu.hideout.service.user.IUserService
+import io.ktor.client.*
+import kjob.core.job.JobProps
+import org.koin.core.annotation.Single
+
+@Single
+class ActivityPubReactionServiceImpl(
+    private val userService: IUserService,
+    private val jobQueueParentService: JobQueueParentService,
+    private val iPostRepository: IPostRepository,
+    private val httpClient: HttpClient
+) : ActivityPubReactionService {
+    override suspend fun reaction(like: Reaction) {
+        val followers = userService.findFollowersById(like.userId)
+        val user = userService.findById(like.userId)
+        val post =
+            iPostRepository.findOneById(like.postId) ?: throw PostNotFoundException("${like.postId} was not found.")
+        followers.forEach { follower ->
+            jobQueueParentService.schedule(DeliverReactionJob) {
+                props[it.actor] = user.url
+                props[it.reaction] = "❤"
+                props[it.inbox] = follower.inbox
+                props[it.postUrl] = post.url
+                props[it.id] = post.id.toString()
+            }
+        }
+    }
+
+    override suspend fun reactionJob(props: JobProps<DeliverReactionJob>) {
+        val inbox = props[DeliverReactionJob.inbox]
+        val actor = props[DeliverReactionJob.actor]
+        val postUrl = props[DeliverReactionJob.postUrl]
+        val id = props[DeliverReactionJob.id]
+        val content = props[DeliverReactionJob.reaction]
+        httpClient.postAp(
+            urlString = inbox,
+            username = "$actor#pubkey",
+            jsonLd = Like(
+                name = "Like",
+                actor = actor,
+                `object` = postUrl,
+                id = "${Config.configData.url}/like/note/$id",
+                content = content
+            )
+        )
+    }
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt
index e5360563..d369b006 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt
@@ -6,6 +6,7 @@ import dev.usbharu.hideout.config.Config.configData
 import dev.usbharu.hideout.domain.model.ActivityPubResponse
 import dev.usbharu.hideout.domain.model.ap.Follow
 import dev.usbharu.hideout.domain.model.job.DeliverPostJob
+import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
 import dev.usbharu.hideout.domain.model.job.HideoutJob
 import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
 import dev.usbharu.hideout.exception.JsonParseException
@@ -22,7 +23,8 @@ class ActivityPubServiceImpl(
     private val activityPubUndoService: ActivityPubUndoService,
     private val activityPubAcceptService: ActivityPubAcceptService,
     private val activityPubCreateService: ActivityPubCreateService,
-    private val activityPubLikeService: ActivityPubLikeService
+    private val activityPubLikeService: ActivityPubLikeService,
+    private val activityPubReactionService: ActivityPubReactionService
 ) : ActivityPubService {
 
     val logger: Logger = LoggerFactory.getLogger(this::class.java)
@@ -71,6 +73,7 @@ class ActivityPubServiceImpl(
             )
 
             DeliverPostJob -> activityPubNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
+            DeliverReactionJob -> activityPubReactionService.reactionJob(job.props as JobProps<DeliverReactionJob>)
         }
     }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt
index 0c8daadf..a0e62e54 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt
@@ -20,7 +20,7 @@ class ReactionServiceImpl(
 
     override suspend fun sendReaction(name: String, userId: Long, postId: Long) {
         if (reactionRepository.reactionAlreadyExist(postId, userId, 0)) {
-            //delete
+            // delete
         } else {
             val reaction = Reaction(reactionRepository.generateId(), 0, postId, userId)
             reactionRepository.save(reaction)