feat: 絵文字リアクションAPIを追加

This commit is contained in:
usbharu 2024-01-07 12:56:32 +09:00
parent a015566e52
commit a5618e3307
7 changed files with 197 additions and 2 deletions

View File

@ -6,4 +6,5 @@ import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
interface EmojiService {
suspend fun fetchEmoji(url: String): Pair<Emoji, CustomEmoji>
suspend fun fetchEmoji(emoji: Emoji): Pair<Emoji, CustomEmoji>
suspend fun findByEmojiName(emojiName: String): CustomEmoji?
}

View File

@ -3,6 +3,7 @@ package dev.usbharu.hideout.activitypub.service.objects.emoji
import dev.usbharu.hideout.activitypub.domain.model.Emoji
import dev.usbharu.hideout.activitypub.service.common.APResourceResolveServiceImpl
import dev.usbharu.hideout.activitypub.service.common.resolve
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
import dev.usbharu.hideout.core.service.instance.InstanceService
@ -17,7 +18,8 @@ class EmojiServiceImpl(
private val customEmojiRepository: CustomEmojiRepository,
private val instanceService: InstanceService,
private val mediaService: MediaService,
private val apResourceResolveServiceImpl: APResourceResolveServiceImpl
private val apResourceResolveServiceImpl: APResourceResolveServiceImpl,
private val applicationConfig: ApplicationConfig
) : EmojiService {
override suspend fun fetchEmoji(url: String): Pair<Emoji, CustomEmoji> {
val emoji = apResourceResolveServiceImpl.resolve<Emoji>(url, null as Long?)
@ -60,4 +62,20 @@ class EmojiServiceImpl(
return customEmojiRepository.save(customEmoji1)
}
override suspend fun findByEmojiName(emojiName: String): CustomEmoji? {
val split = emojiName.trim(':').split("@")
return when (split.size) {
1 -> {
customEmojiRepository.findByNameAndDomain(split.first(), applicationConfig.url.host)
}
2 -> {
customEmojiRepository.findByNameAndDomain(split.first(), split[1])
}
else -> throw IllegalArgumentException("Unknown Emoji Format. $emojiName")
}
}
}

View File

@ -108,6 +108,10 @@ class StatusQueryServiceImpl : StatusQueryService {
return resolveReplyAndRepost(pairs)
}
override suspend fun findByPostId(id: Long): Status {
TODO("Not yet implemented")
}
private fun resolveReplyAndRepost(pairs: List<Pair<Status, Long?>>): List<Status> {
val statuses = pairs.map { it.first }
return pairs

View File

@ -24,4 +24,16 @@ class MastodonStatusesApiContoller(private val statusesApiService: StatusesApiSe
HttpStatus.OK
)
}
override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity<Status> {
return super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji)
}
override suspend fun apiV1StatusesIdEmojiReactionsEmojiPut(id: String, emoji: String): ResponseEntity<Status> {
return super.apiV1StatusesIdEmojiReactionsEmojiPut(id, emoji)
}
override suspend fun apiV1StatusesIdGet(id: String): ResponseEntity<Status> {
return super.apiV1StatusesIdGet(id)
}
}

View File

@ -36,4 +36,6 @@ interface StatusQueryService {
tagged: String? = null,
includeFollowers: Boolean = false
): List<Status>
suspend fun findByPostId(id: Long): Status
}

View File

@ -1,17 +1,24 @@
package dev.usbharu.hideout.mastodon.service.status
import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService
import dev.usbharu.hideout.application.external.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.service.post.PostCreateDto
import dev.usbharu.hideout.core.service.post.PostService
import dev.usbharu.hideout.core.service.reaction.ReactionService
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
import dev.usbharu.hideout.domain.mastodon.model.generated.Status.Visibility.*
import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest
import dev.usbharu.hideout.mastodon.interfaces.api.status.toPostVisibility
import dev.usbharu.hideout.mastodon.interfaces.api.status.toStatusVisibility
import dev.usbharu.hideout.mastodon.query.StatusQueryService
import dev.usbharu.hideout.mastodon.service.account.AccountService
import dev.usbharu.hideout.util.EmojiUtil
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import java.time.Instant
@ -22,6 +29,23 @@ interface StatusesApiService {
statusesRequest: StatusesRequest,
userId: Long
): Status
suspend fun findById(
id: Long,
userId: Long?
): Status?
suspend fun emojiReactions(
postId: Long,
userId: Long,
emojiName: String
): Status?
suspend fun removeEmojiReactions(
postId: Long,
userId: Long,
emojiName: String
): Status?
}
@Service
@ -31,7 +55,11 @@ class StatsesApiServiceImpl(
private val mediaRepository: MediaRepository,
private val transaction: Transaction,
private val actorRepository: ActorRepository,
private val postRepository: PostRepository
private val postRepository: PostRepository,
private val statusQueryService: StatusQueryService,
private val relationshipRepository: RelationshipRepository,
private val reactionService: ReactionService,
private val emojiService: EmojiService
) :
StatusesApiService {
override suspend fun postStatus(
@ -95,6 +123,63 @@ class StatsesApiServiceImpl(
)
}
override suspend fun findById(id: Long, userId: Long?): Status? {
val status = statusQueryService.findByPostId(id)
return status(status, userId)
}
private suspend fun status(
status: Status,
userId: Long?
): Status? {
return when (status.visibility) {
public -> status
unlisted -> status
private -> {
if (userId == null) {
return null
}
val relationship =
relationshipRepository.findByUserIdAndTargetUserId(userId, status.account.id.toLong())
?: return null
if (relationship.following) {
return status
}
return null
}
direct -> null
}
}
override suspend fun emojiReactions(postId: Long, userId: Long, emojiName: String): Status? {
status(statusQueryService.findByPostId(postId), userId) ?: return null
val emoji = try {
if (EmojiUtil.isEmoji(emojiName)) {
UnicodeEmoji(emojiName)
} else {
emojiService.findByEmojiName(emojiName)!!
}
} catch (e: IllegalStateException) {
UnicodeEmoji("")
} catch (e: NullPointerException) {
UnicodeEmoji("")
}
reactionService.sendReaction(emoji, userId, postId)
return statusQueryService.findByPostId(postId)
}
override suspend fun removeEmojiReactions(postId: Long, userId: Long, emojiName: String): Status? {
reactionService.removeReaction(userId, postId)
return status(statusQueryService.findByPostId(postId), userId)
}
companion object {
private val logger = LoggerFactory.getLogger(StatusesApiService::class.java)
}

View File

@ -152,6 +152,79 @@ paths:
schema:
$ref: "#/components/schemas/Status"
/api/v1/statuses/{id}:
get:
tags:
- status
security:
- OAuth2:
- "write:statuses"
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
200:
description: 成功
content:
application/json:
schema:
$ref: "#/components/schemas/Status"
/api/v1/statuses/{id}/emoji_reactions/{emoji}:
put:
tags:
- status
security:
- OAuth2:
- "write:statuses"
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: path
name: emoji
required: true
schema:
type: string
responses:
200:
description: 成功
content:
application/json:
schema:
$ref: "#/components/schemas/Status"
delete:
tags:
- status
security:
- OAuth2:
- "write:statuses"
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: path
name: emoji
required: true
schema:
type: string
responses:
200:
description: 成功
content:
application/json:
schema:
$ref: "#/components/schemas/Status"
/api/v1/apps:
post:
tags: