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.*