diff --git a/utopia-gamification/pom.xml b/utopia-gamification/pom.xml
index ea85851b..07bbfdec 100644
--- a/utopia-gamification/pom.xml
+++ b/utopia-gamification/pom.xml
@@ -27,10 +27,5 @@
utopia-test-kit
test
-
- tw.waterballsa.utopia
- utopia-test-kit
- ${revision}
-
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Action.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Action.kt
index dcfe6b6b..47c817c5 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Action.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Action.kt
@@ -11,8 +11,6 @@ abstract class Action(
abstract class Criteria {
abstract fun meet(action: Action): Boolean
-
- open val link: String = ""
}
}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Quest.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Quest.kt
index c1872623..a903a5d2 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Quest.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Quest.kt
@@ -11,6 +11,7 @@ class Quest(
val periodType: PeriodType = PeriodType.NONE,
val criteria: Action.Criteria,
val reward: Reward,
+ val link: String = "",
val nextQuestId: Int? = null,
val postMessage: String = completeMessage
)
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/MessageReactionAction.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/MessageReactionAction.kt
index e91d525d..49dfc33e 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/MessageReactionAction.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/MessageReactionAction.kt
@@ -25,7 +25,4 @@ class MessageReactionCriteria(
action.messageId == messageId && action.emoji == emoji
override fun toString(): String = "點選 $emoji 表情。"
-
- override val link: String
- get() = channelIdRule.toString()
}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/MessageSentAction.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/MessageSentAction.kt
index f35e000d..3633b90f 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/MessageSentAction.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/MessageSentAction.kt
@@ -46,8 +46,6 @@ class MessageSentCriteria(
${numberOfVoiceChannelMembersRule.toString("人")}
""".trimIndent()
- override val link: String
- get() = channelIdRule.toString()
}
class ChannelIdRule(private val channelId: String) {
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/PostAction.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/PostAction.kt
index 15d7ea3b..4bec30a7 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/PostAction.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/actions/PostAction.kt
@@ -20,7 +20,4 @@ class PostCriteria(
private fun meetCriteria(action: PostAction): Boolean = channelIdRule.meet(action.channelId)
override fun toString(): String = "發一則貼文"
-
- override val link: String
- get() = channelIdRule.toString()
}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/exception/AssignedQuestException.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/exception/AssignedQuestException.kt
new file mode 100644
index 00000000..2c287b70
--- /dev/null
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/exception/AssignedQuestException.kt
@@ -0,0 +1,4 @@
+package tw.waterballsa.utopia.utopiagamification.quest.domain.exception
+
+class AssignedQuestException(playerId: String, questId: Int) :
+ RuntimeException("Player already owns this quest. {playerId:$playerId, questId:$questId}")
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/quests/TutorialQuests.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/quests/TutorialQuests.kt
index 71e51149..d1520758 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/quests/TutorialQuests.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/quests/TutorialQuests.kt
@@ -2,15 +2,41 @@ package tw.waterballsa.utopia.utopiagamification.quest.domain.quests
import tw.waterballsa.utopia.utopiagamification.quest.domain.*
import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.*
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.firstMessageActionQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.flagPostQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.joinActivityQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.quizQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.replyToAnyoneInCareerAdvancementTopicChannelQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.selfIntroductionQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.sendContainsImageMessageInEngineerLifeChannelQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.sendMessageInVoiceChannelQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.unlockAcademyQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.watchVideoQuestId
import tw.waterballsa.utopia.utopiagamification.repositories.inmemory.repositoryimpl.InMemoryQuestRepository
private const val unlockEmoji = "🔑"
private const val missionTips = "> (要是你怕自己的訊息太突兀,只要在訊息的開頭加上 `#任務`,保證自在。)"
+class QuestIds {
+ companion object {
+ const val unlockAcademyQuestId = 1
+ const val selfIntroductionQuestId = 2
+ const val firstMessageActionQuestId = 3
+ const val sendContainsImageMessageInEngineerLifeChannelQuestId = 4
+ const val replyToAnyoneInCareerAdvancementTopicChannelQuestId = 5
+ const val watchVideoQuestId = 6
+ const val flagPostQuestId = 7
+ const val sendMessageInVoiceChannelQuestId = 8
+ const val joinActivityQuestId = 9
+ const val quizQuestId = 10
+ }
+}
+
+
val InMemoryQuestRepository.unlockAcademyQuest: Quest
- get() = findById(1) ?: save(
+ get() = findById(unlockAcademyQuestId) ?: save(
Quest(
- id = 1,
+ id = unlockAcademyQuestId,
title = "解鎖學院",
description =
"""
@@ -32,6 +58,8 @@ val InMemoryQuestRepository.unlockAcademyQuest: Quest
unlockEmoji
),
+ link = wsa.unlockEntryChannelId.toLink(),
+
reward = Reward(
100u,
100u,
@@ -39,15 +67,15 @@ val InMemoryQuestRepository.unlockAcademyQuest: Quest
RoleType.WSA_MEMBER
),
- nextQuestId = 2
+ nextQuestId = selfIntroductionQuestId
)
)
val InMemoryQuestRepository.selfIntroductionQuest: Quest
- get() = findById(2) ?: save(
+ get() = findById(selfIntroductionQuestId) ?: save(
Quest(
- id = 2,
+ id = selfIntroductionQuestId,
title = "自我介紹",
description =
"""
@@ -91,7 +119,9 @@ val InMemoryQuestRepository.selfIntroductionQuest: Quest
regexRule = getSelfIntroductionRegex()
),
- nextQuestId = 3
+ link = wsa.selfIntroChannelId.toLink(),
+
+ nextQuestId = firstMessageActionQuestId
)
)
@@ -101,9 +131,9 @@ private fun getSelfIntroductionRegex(): RegexRule =
"""【(.|\n)*】(.|\n)*工作職位:?(.|\n)*((公司產業:?(:)?(.|\n)*))?專長:?(.|\n)*興趣:?(.|\n)*簡介:?.(.|\n)*((三件關於我的事,猜猜哪一件是假的:?(:)?(.|\n)*))?""".toRegexRule()
val InMemoryQuestRepository.firstMessageActionQuest: Quest
- get() = findById(3) ?: save(
+ get() = findById(firstMessageActionQuestId) ?: save(
Quest(
- id = 3,
+ id = firstMessageActionQuestId,
title = "新生降落",
description =
"""
@@ -135,14 +165,16 @@ val InMemoryQuestRepository.firstMessageActionQuest: Quest
ChannelIdRule(wsa.discussionAreaChannelId)
),
- nextQuestId = 4
+ link = wsa.discussionAreaChannelId.toLink(),
+
+ nextQuestId = sendContainsImageMessageInEngineerLifeChannelQuestId
)
)
val InMemoryQuestRepository.sendContainsImageMessageInEngineerLifeChannelQuest: Quest
- get() = findById(4) ?: save(
+ get() = findById(sendContainsImageMessageInEngineerLifeChannelQuestId) ?: save(
Quest(
- id = 4,
+ id = sendContainsImageMessageInEngineerLifeChannelQuestId,
title = "融入大家",
description =
"""
@@ -173,14 +205,16 @@ val InMemoryQuestRepository.sendContainsImageMessageInEngineerLifeChannelQuest:
hasImageRule = BooleanRule.TRUE
),
- nextQuestId = 5
+ link = wsa.engineerLifeChannelId.toLink(),
+
+ nextQuestId = replyToAnyoneInCareerAdvancementTopicChannelQuestId
)
)
val InMemoryQuestRepository.replyToAnyoneInCareerAdvancementTopicChannelQuest: Quest
- get() = findById(5) ?: save(
+ get() = findById(replyToAnyoneInCareerAdvancementTopicChannelQuestId) ?: save(
Quest(
- id = 5,
+ id = replyToAnyoneInCareerAdvancementTopicChannelQuestId,
title = "職涯攻略",
description =
"""
@@ -214,14 +248,16 @@ val InMemoryQuestRepository.replyToAnyoneInCareerAdvancementTopicChannelQuest: Q
hasRepliedRule = BooleanRule.TRUE
),
- nextQuestId = 6
+ link = wsa.careerAdvancementTopicChannelId.toLink(),
+
+ nextQuestId = watchVideoQuestId
)
)
val InMemoryQuestRepository.watchVideoQuest: Quest
- get() = findById(6) ?: save(
+ get() = findById(watchVideoQuestId) ?: save(
Quest(
- id = 6,
+ id = watchVideoQuestId,
title = "學院精華影片",
description = """
在學會如何自在地和大家聊天交流和參與話題之後,接下來要來帶你好好逛一下這個學院。
@@ -250,14 +286,16 @@ val InMemoryQuestRepository.watchVideoQuest: Quest
ChannelIdRule(wsa.featuredVideosChannelId),
),
- nextQuestId = 7
+ link = wsa.featuredVideosChannelId.toLink(),
+
+ nextQuestId = flagPostQuestId
)
)
val InMemoryQuestRepository.flagPostQuest: Quest
- get() = findById(7) ?: save(
+ get() = findById(flagPostQuestId) ?: save(
Quest(
- id = 7,
+ id = flagPostQuestId,
title = "全民插旗:把學院當成自己的家",
description =
"""
@@ -289,14 +327,16 @@ val InMemoryQuestRepository.flagPostQuest: Quest
ChannelIdRule(wsa.flagPostChannelId)
),
- nextQuestId = 8
+ link = wsa.flagPostGuideId.toLink(),
+
+ nextQuestId = sendMessageInVoiceChannelQuestId
)
)
val InMemoryQuestRepository.sendMessageInVoiceChannelQuest: Quest
- get() = findById(8) ?: save(
+ get() = findById(sendMessageInVoiceChannelQuestId) ?: save(
Quest(
- id = 8,
+ id = sendMessageInVoiceChannelQuestId,
title = "到處吃瓜",
description =
"""
@@ -329,14 +369,16 @@ val InMemoryQuestRepository.sendMessageInVoiceChannelQuest: Quest
numberOfVoiceChannelMembersRule = AtLeastRule(2)
),
- nextQuestId = 9
+ link = "任意頻道",
+
+ nextQuestId = joinActivityQuestId
)
)
val InMemoryQuestRepository.joinActivityQuest: Quest
- get() = findById(9) ?: save(
+ get() = findById(joinActivityQuestId) ?: save(
Quest(
- id = 9,
+ id = joinActivityQuestId,
title = "參與院長主持的學院節目",
description =
"""
@@ -353,14 +395,14 @@ val InMemoryQuestRepository.joinActivityQuest: Quest
),
criteria = JoinActivityCriteria("遊戲微服務計畫:水球實況", 60, 40),
- nextQuestId = 10
+ nextQuestId = quizQuestId
)
)
val InMemoryQuestRepository.quizQuest: Quest
- get() = findById(10) ?: save(
+ get() = findById(quizQuestId) ?: save(
Quest(
- id = 10,
+ id = quizQuestId,
title = "考試",
description =
"""
@@ -389,5 +431,7 @@ val InMemoryQuestRepository.quizQuest: Quest
),
criteria = QuizCriteria("紳士考題", 4, 5),
- )
+
+
+ )
)
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/extensions/UtopiagamificationExtensions.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/extensions/UtopiagamificationExtensions.kt
index bd4775ce..657473a6 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/extensions/UtopiagamificationExtensions.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/extensions/UtopiagamificationExtensions.kt
@@ -2,7 +2,7 @@ package tw.waterballsa.utopia.utopiagamification.quest.extensions
import dev.minn.jda.ktx.messages.Embed
import net.dv8tion.jda.api.entities.User
-import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission
+import tw.waterballsa.utopia.utopiagamification.quest.listeners.presenters.AssignPlayerQuestPresenter
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneId
@@ -12,24 +12,22 @@ fun String.toDate(): LocalDateTime = LocalDateTime.parse(this)
fun OffsetDateTime.toTaipeiLocalDateTime(): LocalDateTime =
atZoneSameInstant(ZoneId.of("Asia/Taipei")).toLocalDateTime()
-
-fun Mission.publishToUser(user: User) {
+fun AssignPlayerQuestPresenter.ViewModel.publishToUser(user: User) {
user.openPrivateChannel().queue {
it.sendMessageEmbeds(
Embed {
- title = quest.title
- description = quest.description
+ title = questTitle
+ description = questDescription
color = Color.GREEN
field {
name = "任務條件"
- //轉成 string 並去除多餘的換行
- value = "${quest.criteria}".replace(Regex("\\n{2,}"), "\n")
+ value = criteria
}
field {
name = "任務位置"
- value = quest.criteria.link
+ value = link
inline = true
}
}
@@ -37,8 +35,9 @@ fun Mission.publishToUser(user: User) {
}
}
+
class Color {
companion object {
- val GREEN = 706146
+ const val GREEN = 706146
}
}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/ButtonInteractionListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/ButtonInteractionListener.kt
index 0e21c8f7..210381dd 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/ButtonInteractionListener.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/ButtonInteractionListener.kt
@@ -10,6 +10,7 @@ import tw.waterballsa.utopia.utopiagamification.quest.domain.Player
import tw.waterballsa.utopia.utopiagamification.quest.extensions.LevelSheet.Companion.toLevelRange
import tw.waterballsa.utopia.utopiagamification.quest.extensions.LevelSheet.LevelRange.Companion.LEVEL_ONE
import tw.waterballsa.utopia.utopiagamification.quest.extensions.publishToUser
+import tw.waterballsa.utopia.utopiagamification.quest.listeners.presenters.AssignPlayerQuestPresenter
import tw.waterballsa.utopia.utopiagamification.quest.usecase.ClaimMissionRewardUsecase
import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository
@@ -59,7 +60,9 @@ class UtopiaGamificationQuestListener(
}
override fun presentNextMission(mission: Mission) {
- mission.publishToUser(user)
+ val presenter = AssignPlayerQuestPresenter()
+ presenter.presentMission(mission)
+ presenter.viewModel?.publishToUser(user)
}
override fun presentRewardsNotAllowed(mission: Mission) {
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MemberJoinListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MemberJoinListener.kt
index 3ea2209f..0f52559c 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MemberJoinListener.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MemberJoinListener.kt
@@ -3,50 +3,32 @@ package tw.waterballsa.utopia.utopiagamification.quest.listeners
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent
import org.springframework.stereotype.Component
-import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission
-import tw.waterballsa.utopia.utopiagamification.quest.domain.Quest
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.unlockAcademyQuestId
import tw.waterballsa.utopia.utopiagamification.quest.extensions.publishToUser
-import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerAcceptQuestUsecase
+import tw.waterballsa.utopia.utopiagamification.quest.listeners.presenters.AssignPlayerQuestPresenter
+import tw.waterballsa.utopia.utopiagamification.quest.usecase.AssignPlayerQuestUsecase
import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository
-import tw.waterballsa.utopia.utopiagamification.repositories.QuestRepository
-import tw.waterballsa.utopia.utopiagamification.repositories.exceptions.NotFoundException.Companion.notFound
-
-private const val unlockAcademyQuestId = 1
@Component
class MemberJoinListener(
guild: Guild,
playerRepository: PlayerRepository,
- private val questRepository: QuestRepository,
- private val playerAcceptQuestUsecase: PlayerAcceptQuestUsecase,
+ private val assignPlayerQuestUsecase: AssignPlayerQuestUsecase,
) : UtopiaGamificationListener(guild, playerRepository) {
override fun onGuildMemberJoin(event: GuildMemberJoinEvent) {
with(event) {
-
+ //TODO 這個 toPlayer 會有副作用,會註冊玩家,之後會發 pr 解決這個問題
val player = user.toPlayer() ?: return
- val unlockAcademyQuest = questRepository.findById(unlockAcademyQuestId)
- ?: throw notFound(Quest::class)
- .id(unlockAcademyQuestId)
- .message("assign new member first quest")
- .build()
- val request = PlayerAcceptQuestUsecase.Request(player, unlockAcademyQuest)
+ val request = AssignPlayerQuestUsecase.Request(user.id, unlockAcademyQuestId)
+ val presenter = AssignPlayerQuestPresenter()
- val presenter = object : PlayerAcceptQuestUsecase.Presenter {
- override fun presentPlayerHasAcquiredMission() {
- sendMessageToUserPrivateChannel("已獲得新手任務,無法再次獲得!")
- }
+ assignPlayerQuestUsecase.execute(request, presenter)
- override fun presentPlayerAcquiresMission(mission: Mission) {
- mission.publishToUser(user)
- }
- }
+ val viewModel = presenter.viewModel ?: return
- playerAcceptQuestUsecase.execute(request, presenter)
+ viewModel.publishToUser(user)
}
}
-
- private fun GuildMemberJoinEvent.sendMessageToUserPrivateChannel(message: String) =
- user.openPrivateChannel().queue { it.sendMessage(message).queue() }
}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/SlashCommandListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/SlashCommandListener.kt
index 0af5a319..52f424bd 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/SlashCommandListener.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/SlashCommandListener.kt
@@ -6,29 +6,25 @@ import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData
import org.springframework.stereotype.Component
-import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission
-import tw.waterballsa.utopia.utopiagamification.quest.domain.Quest
import tw.waterballsa.utopia.utopiagamification.quest.domain.State.*
+import tw.waterballsa.utopia.utopiagamification.quest.domain.exception.AssignedQuestException
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.quizQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.unlockAcademyQuestId
import tw.waterballsa.utopia.utopiagamification.quest.extensions.publishToUser
-import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerAcceptQuestUsecase
+import tw.waterballsa.utopia.utopiagamification.quest.listeners.presenters.AssignPlayerQuestPresenter
+import tw.waterballsa.utopia.utopiagamification.quest.usecase.AssignPlayerQuestUsecase
import tw.waterballsa.utopia.utopiagamification.repositories.MissionRepository
import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository
-import tw.waterballsa.utopia.utopiagamification.repositories.QuestRepository
-import tw.waterballsa.utopia.utopiagamification.repositories.exceptions.NotFoundException.Companion.notFound
const val UTOPIA_COMMAND_NAME = "utopia"
const val FIRST_QUEST_COMMAND_NAME = "first-quest"
const val REVIEW_COMMAND_NAME = "re-render"
-private const val unlockAcademyQuestId = 1
-private const val quizQuestId = 10
-
@Component
class SlashCommandListener(
guild: Guild,
playerRepository: PlayerRepository,
- private val questRepository: QuestRepository,
- private val playerAcceptQuestUsecase: PlayerAcceptQuestUsecase,
+ private val assignPlayerQuestUsecase: AssignPlayerQuestUsecase,
private val missionRepository: MissionRepository
) : UtopiaGamificationListener(guild, playerRepository) {
@@ -57,30 +53,26 @@ class SlashCommandListener(
}
private fun SlashCommandInteractionEvent.handleFirstQuestCommand() {
+ //TODO 這個 toPlayer 會有副作用,會註冊玩家,之後會發 pr 解決這個問題
val player = user.toPlayer() ?: return
- val unlockAcademyQuest = questRepository.findById(unlockAcademyQuestId)
- ?: throw notFound(Quest::class)
- .id(unlockAcademyQuestId)
- .message("assign old member first quest")
- .build()
+ val request = AssignPlayerQuestUsecase.Request(user.id, unlockAcademyQuestId)
+ val presenter = AssignPlayerQuestPresenter()
- val request = PlayerAcceptQuestUsecase.Request(player, unlockAcademyQuest)
+ try {
+ assignPlayerQuestUsecase.execute(request, presenter)
- val presenter = object : PlayerAcceptQuestUsecase.Presenter {
- override fun presentPlayerHasAcquiredMission() {
- hook.editOriginal("已獲得新手任務,無法再次獲得。").queue()
- }
+ val viewModel = presenter.viewModel ?: return
- override fun presentPlayerAcquiresMission(mission: Mission) {
- mission.publishToUser(user)
- hook.editOriginal("已經接取第一個任務,去私訊查看任務內容。").queue()
- }
- }
+ hook.editOriginal(viewModel.assignQuestMessage).queue()
+ viewModel.publishToUser(user)
- playerAcceptQuestUsecase.execute(request, presenter)
+ } catch (e: AssignedQuestException) {
+ hook.editOriginal("已獲得新手任務,無法再次獲得!").queue()
+ }
}
+
private fun SlashCommandInteractionEvent.handleReviewCommand() {
var result = "執行結束"
val mission = missionRepository.findAllByPlayerId(user.id).last()
@@ -92,7 +84,9 @@ class SlashCommandListener(
if (state == IN_PROGRESS) {
result = "執行結束,已獲得上個任務的獎勵"
- publishToUser(user)
+ val presenter = AssignPlayerQuestPresenter()
+ presenter.presentMission(mission)
+ presenter.viewModel?.publishToUser(user)
}
if (state == CLAIMED) {
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/presenters/AssignPlayerQuestPresenter.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/presenters/AssignPlayerQuestPresenter.kt
new file mode 100644
index 00000000..162858e7
--- /dev/null
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/presenters/AssignPlayerQuestPresenter.kt
@@ -0,0 +1,29 @@
+package tw.waterballsa.utopia.utopiagamification.quest.listeners.presenters
+
+import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission
+import tw.waterballsa.utopia.utopiagamification.quest.usecase.AssignPlayerQuestUsecase
+
+class AssignPlayerQuestPresenter : AssignPlayerQuestUsecase.Presenter {
+
+ var viewModel: ViewModel? = null
+ private set
+
+ override fun presentMission(mission: Mission) {
+ viewModel = mission.toViewModel()
+ }
+
+ private fun Mission.toViewModel(): ViewModel = ViewModel(
+ quest.title,
+ quest.description,
+ "${quest.criteria}".replace(Regex("\\n{2,}"), "\n"),
+ quest.link
+ )
+
+ data class ViewModel(
+ val questTitle: String,
+ val questDescription: String,
+ val criteria: String,
+ val link: String,
+ val assignQuestMessage: String = "你已接取任務,到私訊查看內容吧!"
+ )
+}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/AssignPlayerQuestUsecase.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/AssignPlayerQuestUsecase.kt
new file mode 100644
index 00000000..87e5f2cd
--- /dev/null
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/AssignPlayerQuestUsecase.kt
@@ -0,0 +1,53 @@
+package tw.waterballsa.utopia.utopiagamification.quest.usecase
+
+import org.springframework.stereotype.Component
+import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission
+import tw.waterballsa.utopia.utopiagamification.quest.domain.Player
+import tw.waterballsa.utopia.utopiagamification.quest.domain.Quest
+import tw.waterballsa.utopia.utopiagamification.quest.domain.exception.AssignedQuestException
+import tw.waterballsa.utopia.utopiagamification.repositories.MissionRepository
+import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository
+import tw.waterballsa.utopia.utopiagamification.repositories.QuestRepository
+import tw.waterballsa.utopia.utopiagamification.repositories.exceptions.NotFoundException
+
+
+@Component
+class AssignPlayerQuestUsecase(
+ private val missionRepository: MissionRepository,
+ private val playerRepository: PlayerRepository,
+ private val questRepository: QuestRepository
+) {
+
+ fun execute(request: Request, presenter: Presenter) {
+ with(request) {
+ if (missionRepository.existMission(playerId, questId)) {
+ throw AssignedQuestException(playerId, questId)
+ }
+
+ val player = playerRepository.findPlayerById(playerId) ?: throw NotFoundException
+ .notFound(Player::class)
+ .id(playerId)
+ .message("can't assign quest.")
+ .build()
+
+ val quest = questRepository.findById(questId) ?: throw NotFoundException
+ .notFound(Quest::class)
+ .id(questId)
+ .message("can't be assigned to the player")
+ .build()
+
+ val mission = missionRepository.saveMission(Mission(player, quest))
+
+ presenter.presentMission(mission)
+ }
+ }
+
+ data class Request(
+ val playerId: String,
+ val questId: Int
+ )
+
+ interface Presenter {
+ fun presentMission(mission: Mission)
+ }
+}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerAcceptQuestUsecase.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerAcceptQuestUsecase.kt
deleted file mode 100644
index 4e1b5b18..00000000
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerAcceptQuestUsecase.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package tw.waterballsa.utopia.utopiagamification.quest.usecase
-
-import org.springframework.stereotype.Component
-import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission
-import tw.waterballsa.utopia.utopiagamification.quest.domain.Player
-import tw.waterballsa.utopia.utopiagamification.quest.domain.Quest
-import tw.waterballsa.utopia.utopiagamification.repositories.MissionRepository
-
-
-@Component
-class PlayerAcceptQuestUsecase(
- private val missionRepository: MissionRepository
-) {
-
- fun execute(request: Request, presenter: Presenter) {
- with(request) {
- if (isMissionAcquired()) {
- presenter.presentPlayerHasAcquiredMission()
- return
- }
- val mission = missionRepository.saveMission(Mission(player, quest))
- presenter.presentPlayerAcquiresMission(mission)
- }
- }
-
- private fun Request.isMissionAcquired(): Boolean =
- missionRepository.findPlayerMissionByQuestId(player.id, quest.id) != null
-
- class Request(
- val player: Player,
- val quest: Quest
- )
-
- interface Presenter {
- fun presentPlayerHasAcquiredMission()
- fun presentPlayerAcquiresMission(mission: Mission)
- }
-}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/MissionRepository.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/MissionRepository.kt
index e3c8c058..318061c4 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/MissionRepository.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/MissionRepository.kt
@@ -9,4 +9,5 @@ interface MissionRepository {
fun findAllByPlayerId(playerId: String): List
fun findAllByQuestId(questId: Int): List
fun saveMission(mission: Mission): Mission
+ fun existMission(playerId: String, questId: Int): Boolean
}
diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/mongodb/repositoryimpl/MongodbMissionRepository.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/mongodb/repositoryimpl/MongodbMissionRepository.kt
index 0da17278..31f1869e 100644
--- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/mongodb/repositoryimpl/MongodbMissionRepository.kt
+++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/mongodb/repositoryimpl/MongodbMissionRepository.kt
@@ -50,6 +50,9 @@ class MongodbMissionRepository(
return mission
}
+ override fun existMission(playerId: String, questId: Int): Boolean =
+ findPlayerMissionByQuestId(playerId, questId) != null
+
// TODO 等到 @DBRef 功能上線後,將 playerId 改成 player,讓 MongoDB 協助 join
private fun MissionDocument.toDomain(): Mission {
val player = playerRepository.findPlayerById(playerId)
diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/GenericTypedParameterResolver.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/GenericTypedParameterResolver.kt
similarity index 92%
rename from utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/GenericTypedParameterResolver.kt
rename to utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/GenericTypedParameterResolver.kt
index 7edcc40d..580b22df 100644
--- a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/GenericTypedParameterResolver.kt
+++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/GenericTypedParameterResolver.kt
@@ -1,4 +1,4 @@
-package tw.waterballsa.utopia.utopiagmification
+package tw.waterballsa.utopia.utopiagamification
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.api.extension.ParameterContext
diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/achievement/it/AchievementIntegrationTest.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/achievement/it/AchievementIntegrationTest.kt
similarity index 99%
rename from utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/achievement/it/AchievementIntegrationTest.kt
rename to utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/achievement/it/AchievementIntegrationTest.kt
index 4e02ba80..6905e831 100644
--- a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/achievement/it/AchievementIntegrationTest.kt
+++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/achievement/it/AchievementIntegrationTest.kt
@@ -1,4 +1,4 @@
-package tw.waterballsa.utopia.utopiagmification.achievement.it
+package tw.waterballsa.utopia.utopiagamification.achievement.it
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/achievement/ut/AchievementUnitTest.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/achievement/ut/AchievementUnitTest.kt
similarity index 98%
rename from utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/achievement/ut/AchievementUnitTest.kt
rename to utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/achievement/ut/AchievementUnitTest.kt
index 1ecc91e4..7ae27cac 100644
--- a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/achievement/ut/AchievementUnitTest.kt
+++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/achievement/ut/AchievementUnitTest.kt
@@ -1,4 +1,4 @@
-package tw.waterballsa.utopia.utopiagmification.achievement.ut
+package tw.waterballsa.utopia.utopiagamification.achievement.ut
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.*
diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/it/AssignPlayerQuestUsecaseTest.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/it/AssignPlayerQuestUsecaseTest.kt
new file mode 100644
index 00000000..830c58a2
--- /dev/null
+++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/it/AssignPlayerQuestUsecaseTest.kt
@@ -0,0 +1,92 @@
+package tw.waterballsa.utopia.utopiagamification.quest.it
+
+import org.assertj.core.api.Assertions.*
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.springframework.beans.factory.annotation.Autowired
+import tw.waterballsa.utopia.utopiagamification.quest.domain.Player
+import tw.waterballsa.utopia.utopiagamification.quest.domain.exception.AssignedQuestException
+import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.QuestIds.Companion.unlockAcademyQuestId
+import tw.waterballsa.utopia.utopiagamification.quest.listeners.presenters.AssignPlayerQuestPresenter
+import tw.waterballsa.utopia.utopiagamification.quest.usecase.AssignPlayerQuestUsecase
+import tw.waterballsa.utopia.utopiagamification.repositories.MissionRepository
+import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository
+import tw.waterballsa.utopia.utopiagamification.repositories.QuestRepository
+import tw.waterballsa.utopia.utopiagamification.repositories.exceptions.NotFoundException
+import tw.waterballsa.utopia.utopiatestkit.annotations.UtopiaTest
+
+@UtopiaTest
+class AssignPlayerQuestUsecaseTest @Autowired constructor(
+ private val assignPlayerQuestUsecase: AssignPlayerQuestUsecase,
+ private val playerRepository: PlayerRepository,
+ private val missionRepository: MissionRepository,
+ private val questRepository: QuestRepository
+) {
+
+ private val playerA = Player("A", "A")
+ private val request = AssignPlayerQuestUsecase.Request(playerId = playerA.id, questId = unlockAcademyQuestId)
+ private val presenter = AssignPlayerQuestPresenter()
+
+ @BeforeEach
+ fun setup() {
+ playerRepository.savePlayer(playerA)
+ }
+
+ @Test
+ fun `given playerA do not have first quest when playerA assign first quest then playerA get first quest`() {
+ //when
+ assertThatNoException().isThrownBy {
+ assignPlayerQuestUsecase.execute(request, presenter)
+ }
+
+ //then
+ val missions = missionRepository.findAllByPlayerId(playerA.id)
+ assertThat(missions).hasSize(1)
+ val mission = missions.first()
+ assertThat(mission.player.id).isEqualTo(playerA.id)
+ assertThat(mission.quest.id).isEqualTo(unlockAcademyQuestId)
+
+ val quest = questRepository.findById(request.questId)
+ val viewModel = presenter.viewModel
+ assertThat(viewModel).isNotNull
+ assertThat(viewModel?.questTitle).isEqualTo(quest?.title)
+ assertThat(viewModel?.questDescription).isEqualTo(quest?.description)
+ }
+
+ @Test
+ fun `given playerA has first quest when playerA assign first quest then playerA can't get same quest repeatedly`() {
+ //when
+ assertThatNoException().isThrownBy {
+ assignPlayerQuestUsecase.execute(request, presenter)
+ }
+
+ //then
+ assertThatExceptionOfType(AssignedQuestException::class.java).isThrownBy {
+ assignPlayerQuestUsecase.execute(request, presenter)
+ }
+ }
+
+ @Test
+ fun `when assign a quest to the non-exist player, then should be fail`() {
+ //given
+ val nonExistPlayerId = "B"
+ val failRequest = AssignPlayerQuestUsecase.Request(playerId = nonExistPlayerId, questId = unlockAcademyQuestId)
+
+ //then
+ assertThatExceptionOfType(NotFoundException::class.java).isThrownBy {
+ assignPlayerQuestUsecase.execute(failRequest, presenter)
+ }
+ }
+
+ @Test
+ fun `when assign a non-exist quest to the player A, then should be fail`() {
+ //given
+ val notExistQuestId = -1
+ val failRequest = AssignPlayerQuestUsecase.Request(playerId = playerA.id, questId = notExistQuestId)
+
+ //then
+ assertThatExceptionOfType(NotFoundException::class.java).isThrownBy {
+ assignPlayerQuestUsecase.execute(failRequest, presenter)
+ }
+ }
+}
diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestCase.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/MissionTestCase.kt
similarity index 87%
rename from utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestCase.kt
rename to utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/MissionTestCase.kt
index 034ba85c..5373a0dd 100644
--- a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestCase.kt
+++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/MissionTestCase.kt
@@ -1,4 +1,4 @@
-package tw.waterballsa.utopia.utopiagmification.quest
+package tw.waterballsa.utopia.utopiagamification.quest.ut
import tw.waterballsa.utopia.utopiagamification.quest.domain.Action
import tw.waterballsa.utopia.utopiagamification.quest.domain.Player
diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestInvocationContextProvider.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/MissionTestInvocationContextProvider.kt
similarity index 98%
rename from utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestInvocationContextProvider.kt
rename to utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/MissionTestInvocationContextProvider.kt
index f09db20d..0a8c21aa 100644
--- a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestInvocationContextProvider.kt
+++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/MissionTestInvocationContextProvider.kt
@@ -1,11 +1,10 @@
-package tw.waterballsa.utopia.utopiagmification.quest
+package tw.waterballsa.utopia.utopiagamification.quest.ut
import org.junit.jupiter.api.extension.Extension
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.api.extension.TestTemplateInvocationContext
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider
-import org.testcontainers.shaded.org.bouncycastle.asn1.x500.style.RFC4519Style.description
-import org.testcontainers.shaded.org.bouncycastle.asn1.x500.style.RFC4519Style.title
+import tw.waterballsa.utopia.utopiagamification.GenericTypedParameterResolver
import tw.waterballsa.utopia.utopiagamification.quest.domain.*
import tw.waterballsa.utopia.utopiagamification.quest.domain.PeriodType.MAIN_QUEST
import tw.waterballsa.utopia.utopiagamification.quest.domain.RoleType.EVERYONE
@@ -14,7 +13,6 @@ import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.*
import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.BooleanRule.TRUE
import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.ChannelIdRule.Companion.ANY_CHANNEL
import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.toRegexRule
-import tw.waterballsa.utopia.utopiagmification.GenericTypedParameterResolver
import java.util.UUID.randomUUID
import java.util.stream.Stream
diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/UtopiaGamificationQuestTest.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/UtopiaGamificationQuestTest.kt
similarity index 98%
rename from utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/UtopiaGamificationQuestTest.kt
rename to utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/UtopiaGamificationQuestTest.kt
index f46e1d3a..d90ce5bd 100644
--- a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/UtopiaGamificationQuestTest.kt
+++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagamification/quest/ut/UtopiaGamificationQuestTest.kt
@@ -1,4 +1,4 @@
-package tw.waterballsa.utopia.utopiagmification.quest
+package tw.waterballsa.utopia.utopiagamification.quest.ut
import org.assertj.core.api.Assertions.*
import org.junit.jupiter.api.Assertions.*