From 2e18dc1115793340b5946e0b4ea9bf90c87dd88e Mon Sep 17 00:00:00 2001 From: wow890209 <63483663+wow890209@users.noreply.github.com> Date: Mon, 16 Oct 2023 23:41:26 +0800 Subject: [PATCH] Optimize quest repository memory (#202) --- .../listeners/JoinActivityListener.kt | 6 +- .../quest/domain/Mission.kt | 5 +- .../utopiagamification/quest/domain/Quest.kt | 12 +- .../quest/domain/quests/QuestBuilder.kt | 53 --- .../quest/domain/quests/TutorialQuests.kt | 407 +++++++++--------- .../listeners/ButtonInteractionListener.kt | 10 +- .../quest/listeners/MemberJoinListener.kt | 25 +- .../listeners/MessageReactionListener.kt | 6 +- .../quest/listeners/MessageSentListener.kt | 8 +- .../quest/listeners/PostListener.kt | 6 +- .../quest/listeners/QuizListener.kt | 28 +- .../quest/listeners/SlashCommandListener.kt | 31 +- .../listeners/UtopiaGamificationListener.kt | 4 +- .../ClaimMissionRewardUsecase.kt} | 24 +- .../PlayerAcceptQuestUsecase.kt} | 4 +- .../PlayerFulfillMissionsUsecase.kt} | 4 +- .../repositories/QuestRepository.kt | 9 + .../exceptions/NotFoundException.kt | 36 ++ .../repositoryimpl/InMemoryQuestRepository.kt | 35 ++ .../MongodbMissionRepository.kt | 14 +- .../MissionTestInvocationContextProvider.kt | 346 ++++++++------- .../quest/UtopiaGamificationQuestTest.kt | 15 +- wsa-bot-commands.md | 14 +- 23 files changed, 596 insertions(+), 506 deletions(-) delete mode 100644 utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/quests/QuestBuilder.kt rename utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/{service/ClaimMissionRewardService.kt => usecase/ClaimMissionRewardUsecase.kt} (51%) rename utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/{service/PlayerAcceptQuestService.kt => usecase/PlayerAcceptQuestUsecase.kt} (92%) rename utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/{service/PlayerFulfillMissionsService.kt => usecase/PlayerFulfillMissionsUsecase.kt} (91%) create mode 100644 utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/QuestRepository.kt create mode 100644 utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/exceptions/NotFoundException.kt create mode 100644 utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/inmemory/repositoryimpl/InMemoryQuestRepository.kt diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/activity/listeners/JoinActivityListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/activity/listeners/JoinActivityListener.kt index a51ccba0..4c38e649 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/activity/listeners/JoinActivityListener.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/activity/listeners/JoinActivityListener.kt @@ -15,7 +15,7 @@ import tw.waterballsa.utopia.utopiagamification.quest.extensions.toTaipeiLocalDa import tw.waterballsa.utopia.utopiagamification.quest.listeners.UtopiaGamificationListener import tw.waterballsa.utopia.utopiagamification.repositories.ActivityRepository import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.PlayerFulfillMissionsService +import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerFulfillMissionsUsecase import java.time.LocalDateTime.now private val log = KotlinLogging.logger {} @@ -24,7 +24,7 @@ private val log = KotlinLogging.logger {} class EventJoiningListener( guild: Guild, playerRepository: PlayerRepository, - private val playerFulfillMissionsService: PlayerFulfillMissionsService, + private val playerFulfillMissionsUsecase: PlayerFulfillMissionsUsecase, private val activityRepository: ActivityRepository ) : UtopiaGamificationListener(guild, playerRepository) { @@ -42,7 +42,7 @@ class EventJoiningListener( channelLeft?.let { val stayActivity = activityRepository.findAudienceStayActivity(it.id, player.id) ?: return@let val action = stayActivity.leave(player) ?: return - playerFulfillMissionsService.execute(action, user.claimMissionRewardPresenter) + playerFulfillMissionsUsecase.execute(action, user.claimMissionRewardPresenter) activityRepository.save(stayActivity) } } diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Mission.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Mission.kt index fe7d0a64..d9123c49 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Mission.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/Mission.kt @@ -32,11 +32,12 @@ class Mission( state = CLAIMED } - fun nextMission(): Mission? { + fun nextQuestId(): Int? { if (state == IN_PROGRESS) { return null } - return quest.nextQuest?.let { Mission(player, it) } + + return quest.nextQuestId } } 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 2258b0ca..d9e530ec 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 @@ -1,16 +1,18 @@ package tw.waterballsa.utopia.utopiagamification.quest.domain +private const val completeMessage = "任務完成!" + class Quest( val id: Int, val title: String, val description: String, - val preCondition: PreCondition, - val roleType: RoleType, - val periodType: PeriodType, + val preCondition: PreCondition = EmptyPreCondition(), + val roleType: RoleType = RoleType.EVERYONE, + val periodType: PeriodType = PeriodType.NONE, val criteria: Action.Criteria, val reward: Reward, - val nextQuest: Quest? = null, - val postMessage: String + val nextQuestId: Int? = null, + val postMessage: String = completeMessage ) class Reward( diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/quests/QuestBuilder.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/quests/QuestBuilder.kt deleted file mode 100644 index 134f34d6..00000000 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/domain/quests/QuestBuilder.kt +++ /dev/null @@ -1,53 +0,0 @@ -package tw.waterballsa.utopia.utopiagamification.quest.domain.quests - -import org.springframework.stereotype.Component -import tw.waterballsa.utopia.commons.config.WsaDiscordProperties -import tw.waterballsa.utopia.utopiagamification.quest.domain.* - -@Component -class Quests(val wsa: WsaDiscordProperties) { - private val quests = mutableListOf() - - //TODO 因為現在好測試功能正確性,之後會再全面重新做設計。包括 quest id 的設計。 - init { - quests.addAll( - listOf( - unlockAcademyQuest, - selfIntroductionQuest, - firstMessageActionQuest, - SendContainsImageMessageInEngineerLifeChannelQuest, - watchVideoQuest, - flagPostQuest, - ReplyToAnyoneInCareerAdvancementTopicChannelQuest, - SendMessageInVoiceChannelQuest, - JoinActivityQuest, - quizQuest - ) - ) - } - - fun findById(questId: Int): Quest = quests.first { it.id == questId } - - fun String.toLink(): String = "https://discord.com/channels/${wsa.guildId}/${this}" - -} - -private const val completeMessage = "任務完成!" - -class QuestBuilder { - - var id: Int = 0 - lateinit var title: String - lateinit var description: String - lateinit var reward: Reward - var roleType: RoleType = RoleType.EVERYONE - var periodType: PeriodType = PeriodType.NONE - lateinit var criteria: Action.Criteria - var preCondition: PreCondition = EmptyPreCondition() - var nextQuest: Quest? = null - var postMessage: String = completeMessage - fun build() = - Quest(id, title, description, preCondition, roleType, periodType, criteria, reward, nextQuest, postMessage) -} - -internal fun quest(block: QuestBuilder.() -> Unit): Quest = QuestBuilder().apply(block).build() 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 34ef8cb2..71e51149 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,17 @@ 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.repositories.inmemory.repositoryimpl.InMemoryQuestRepository private const val unlockEmoji = "🔑" private const val missionTips = "> (要是你怕自己的訊息太突兀,只要在訊息的開頭加上 `#任務`,保證自在。)" -val Quests.unlockAcademyQuest: Quest - get() = quest { - id = 1 - title = "解鎖學院" - description = +val InMemoryQuestRepository.unlockAcademyQuest: Quest + get() = findById(1) ?: save( + Quest( + id = 1, + title = "解鎖學院", + description = """ **歡迎你加入水球軟體學院<:WaterBall:999330661171204177> ,這裡是最充實又歡樂的軟體社群!** @@ -20,35 +22,34 @@ val Quests.unlockAcademyQuest: Quest > 來吧,為了能夠參加學院中各式各樣的線上聚會,你需要先解鎖學院,只要點個表情符號幾秒內就能解鎖學院囉! **解鎖後你會獲得基礎的「學院公民」身份。** - """.trimIndent() + """.trimIndent(), - preCondition = EmptyPreCondition() + periodType = PeriodType.MAIN_QUEST, - roleType = RoleType.EVERYONE + criteria = MessageReactionCriteria( + ChannelIdRule(wsa.unlockEntryChannelId), + wsa.unlockEntryMessageId, + unlockEmoji + ), - periodType = PeriodType.MAIN_QUEST + reward = Reward( + 100u, + 100u, + 1.0f, + RoleType.WSA_MEMBER + ), - criteria = MessageReactionCriteria( - ChannelIdRule(wsa.unlockEntryChannelId), - wsa.unlockEntryMessageId, - unlockEmoji + nextQuestId = 2 ) + ) - reward = Reward( - 100u, - 100u, - 1.0f, - RoleType.WSA_MEMBER - ) - - nextQuest = selfIntroductionQuest - } -val Quests.selfIntroductionQuest: Quest - get() = quest { - id = 2 - title = "自我介紹" - description = +val InMemoryQuestRepository.selfIntroductionQuest: Quest + get() = findById(2) ?: save( + Quest( + id = 2, + title = "自我介紹", + description = """ **來認識新朋友吧!為了讓你在學院中過得更自在一些,我會幫助你融入大家!** > **來吧!為了成為學院中的紳士,這裡要開始給你新手任務啦!** @@ -72,37 +73,39 @@ val Quests.selfIntroductionQuest: Quest 2. 3. ``` - """.trimIndent() - preCondition = QuestIdPreCondition(1) + """.trimIndent(), + preCondition = QuestIdPreCondition(1), - roleType = RoleType.WSA_MEMBER + roleType = RoleType.WSA_MEMBER, - periodType = PeriodType.MAIN_QUEST + periodType = PeriodType.MAIN_QUEST, - reward = Reward( - 100u, - 100u, - 1.0f - ) + reward = Reward( + 100u, + 100u, + 1.0f + ), - criteria = MessageSentCriteria( - ChannelIdRule(wsa.selfIntroChannelId), - regexRule = getSelfIntroductionRegex() - ) + criteria = MessageSentCriteria( + ChannelIdRule(wsa.selfIntroChannelId), + regexRule = getSelfIntroductionRegex() + ), - nextQuest = firstMessageActionQuest - } + nextQuestId = 3 + ) + ) fun String.toRegexRule(): RegexRule = RegexRule(this.toRegex()) private fun getSelfIntroductionRegex(): RegexRule = """【(.|\n)*】(.|\n)*工作職位:?(.|\n)*((公司產業:?(:)?(.|\n)*))?專長:?(.|\n)*興趣:?(.|\n)*簡介:?.(.|\n)*((三件關於我的事,猜猜哪一件是假的:?(:)?(.|\n)*))?""".toRegexRule() -val Quests.firstMessageActionQuest: Quest - get() = quest { - id = 3 - title = "新生降落" - description = +val InMemoryQuestRepository.firstMessageActionQuest: Quest + get() = findById(3) ?: save( + Quest( + id = 3, + title = "新生降落", + description = """ 水球軟體學院中主要有三個常常用來聊天和交流的頻道(話題閒聊/工程師生活/職涯攻略),讓我來帶著你慢慢融入大家吧~ @@ -114,33 +117,34 @@ val Quests.firstMessageActionQuest: Quest 大家都會熱情地和你打招呼的喲~ $missionTips - """.trimIndent() + """.trimIndent(), - preCondition = QuestIdPreCondition(2) + preCondition = QuestIdPreCondition(2), - roleType = RoleType.WSA_MEMBER + roleType = RoleType.WSA_MEMBER, - periodType = PeriodType.MAIN_QUEST - - reward = Reward( - 100u, - 100u, - 1.0f - ) - - criteria = MessageSentCriteria( - ChannelIdRule(wsa.discussionAreaChannelId) - ) + periodType = PeriodType.MAIN_QUEST, - nextQuest = SendContainsImageMessageInEngineerLifeChannelQuest + reward = Reward( + 100u, + 100u, + 1.0f + ), - } + criteria = MessageSentCriteria( + ChannelIdRule(wsa.discussionAreaChannelId) + ), -val Quests.SendContainsImageMessageInEngineerLifeChannelQuest: Quest - get() = quest { - id = 4 - title = "融入大家" - description = + nextQuestId = 4 + ) + ) + +val InMemoryQuestRepository.sendContainsImageMessageInEngineerLifeChannelQuest: Quest + get() = findById(4) ?: save( + Quest( + id = 4, + title = "融入大家", + description = """ 接著,我要帶你前往非常好融入的 ${wsa.engineerLifeChannelId.toLink()} 頻道,工程師紳士們會在這裡分享和「軟體」全然無關的生活話題。 @@ -151,32 +155,34 @@ val Quests.SendContainsImageMessageInEngineerLifeChannelQuest: Quest $missionTips - """.trimIndent() - preCondition = QuestIdPreCondition(3) - - roleType = RoleType.WSA_MEMBER + """.trimIndent(), + preCondition = QuestIdPreCondition(3), - periodType = PeriodType.MAIN_QUEST + roleType = RoleType.WSA_MEMBER, - reward = Reward( - 100u, - 100u, - 1.0f - ) + periodType = PeriodType.MAIN_QUEST, - criteria = MessageSentCriteria( - ChannelIdRule(wsa.engineerLifeChannelId), - hasImageRule = BooleanRule.TRUE - ) + reward = Reward( + 100u, + 100u, + 1.0f + ), - nextQuest = ReplyToAnyoneInCareerAdvancementTopicChannelQuest - } + criteria = MessageSentCriteria( + ChannelIdRule(wsa.engineerLifeChannelId), + hasImageRule = BooleanRule.TRUE + ), -val Quests.ReplyToAnyoneInCareerAdvancementTopicChannelQuest: Quest - get() = quest { - id = 5 - title = "職涯攻略" - description = + nextQuestId = 5 + ) + ) + +val InMemoryQuestRepository.replyToAnyoneInCareerAdvancementTopicChannelQuest: Quest + get() = findById(5) ?: save( + Quest( + id = 5, + title = "職涯攻略", + description = """ 最後,是充滿含金量和高談闊論的 ${wsa.careerAdvancementTopicChannelId.toLink()}。 @@ -188,34 +194,36 @@ val Quests.ReplyToAnyoneInCareerAdvancementTopicChannelQuest: Quest $missionTips - """.trimIndent() + """.trimIndent(), - preCondition = QuestIdPreCondition(4) + preCondition = QuestIdPreCondition(4), - roleType = RoleType.WSA_MEMBER + roleType = RoleType.WSA_MEMBER, - periodType = PeriodType.MAIN_QUEST + periodType = PeriodType.MAIN_QUEST, - reward = Reward( - 100u, - 100u, - 1.0f, - ) + reward = Reward( + 100u, + 100u, + 1.0f, + ), - criteria = + criteria = MessageSentCriteria( ChannelIdRule(wsa.careerAdvancementTopicChannelId), hasRepliedRule = BooleanRule.TRUE - ) + ), - nextQuest = watchVideoQuest - } - -val Quests.watchVideoQuest: Quest - get() = quest { - id = 6 - title = "學院精華影片" - description = """ + nextQuestId = 6 + ) + ) + +val InMemoryQuestRepository.watchVideoQuest: Quest + get() = findById(6) ?: save( + Quest( + id = 6, + title = "學院精華影片", + description = """ 在學會如何自在地和大家聊天交流和參與話題之後,接下來要來帶你好好逛一下這個學院。 我認為:「一個好的社群,會留下大家的足跡,這樣的社群就像是一座觀光勝地,逛都逛不完。」 @@ -224,32 +232,34 @@ val Quests.watchVideoQuest: Quest 這個任務非常簡單,請你在 ${wsa.featuredVideosChannelId.toLink()} 論壇中,找一部精華影片來看,並在留言區留下你的觀影心得,或是任何一種支持或想法都可以喔! - """.trimIndent() + """.trimIndent(), - reward = Reward( - 100u, - 100u, - 1.0f, - ) - - preCondition = QuestIdPreCondition(5) + reward = Reward( + 100u, + 100u, + 1.0f, + ), - roleType = RoleType.WSA_MEMBER + preCondition = QuestIdPreCondition(5), - periodType = PeriodType.MAIN_QUEST + roleType = RoleType.WSA_MEMBER, - criteria = MessageSentCriteria( - ChannelIdRule(wsa.featuredVideosChannelId) - ) + periodType = PeriodType.MAIN_QUEST, - nextQuest = flagPostQuest - } + criteria = MessageSentCriteria( + ChannelIdRule(wsa.featuredVideosChannelId), + ), -val Quests.flagPostQuest: Quest - get() = quest { - id = 7 - title = "全民插旗:把學院當成自己的家" - description = + nextQuestId = 7 + ) + ) + +val InMemoryQuestRepository.flagPostQuest: Quest + get() = findById(7) ?: save( + Quest( + id = 7, + title = "全民插旗:把學院當成自己的家", + description = """ 讓大家認識了你之後,還不夠!接下來我要教你如何「把學院當成自己的家!」 在學院中,大家都會在 ${wsa.flagPostChannelId.toLink()} 論壇中,開「個人串」來記錄自己的各項心得或是日誌。 @@ -262,31 +272,33 @@ val Quests.flagPostQuest: Quest 所以請你練習看看,先開一個屬於你的「個人串」吧。 - """.trimIndent() //TODO: 尚未將暱稱條件加入 criteria ,並且貼文的名稱要打上 `<你的暱稱>` + """.trimIndent(), //TODO: 尚未將暱稱條件加入 criteria ,並且貼文的名稱要打上 `<你的暱稱>` - preCondition = QuestIdPreCondition(6) + preCondition = QuestIdPreCondition(6), - roleType = RoleType.WSA_MEMBER + roleType = RoleType.WSA_MEMBER, - periodType = PeriodType.MAIN_QUEST + periodType = PeriodType.MAIN_QUEST, - reward = Reward( - 100u, - 100u, - 1.0f - ) - criteria = PostCriteria( - ChannelIdRule(wsa.flagPostChannelId) - ) - - nextQuest = SendMessageInVoiceChannelQuest - } + reward = Reward( + 100u, + 100u, + 1.0f + ), + criteria = PostCriteria( + ChannelIdRule(wsa.flagPostChannelId) + ), -val Quests.SendMessageInVoiceChannelQuest: Quest - get() = quest { - id = 8 - title = "到處吃瓜" - description = + nextQuestId = 8 + ) + ) + +val InMemoryQuestRepository.sendMessageInVoiceChannelQuest: Quest + get() = findById(8) ?: save( + Quest( + id = 8, + title = "到處吃瓜", + description = """ 水球軟體學院的其中一項最受大家喜愛的文化,就是所謂的「吃瓜文化」啦! @@ -298,55 +310,59 @@ val Quests.SendMessageInVoiceChannelQuest: Quest 很好玩吧!給你一個挑戰,加入「超過 2 人」的任意語音頻道中,並在該語音頻道的訊息區發表 1 則訊息(可以和大家打招呼,或是問問大家在幹什麼)。 - """.trimIndent() + """.trimIndent(), - reward = Reward( - 100u, - 100u, - 1.0f - ) + reward = Reward( + 100u, + 100u, + 1.0f + ), - preCondition = QuestIdPreCondition(7) + preCondition = QuestIdPreCondition(7), - roleType = RoleType.WSA_MEMBER + roleType = RoleType.WSA_MEMBER, - periodType = PeriodType.MAIN_QUEST + periodType = PeriodType.MAIN_QUEST, - criteria = MessageSentCriteria( - ChannelIdRule.ANY_CHANNEL, - numberOfVoiceChannelMembersRule = AtLeastRule(2) - ) - - nextQuest = JoinActivityQuest - } + criteria = MessageSentCriteria( + ChannelIdRule.ANY_CHANNEL, + numberOfVoiceChannelMembersRule = AtLeastRule(2) + ), -val Quests.JoinActivityQuest: Quest - get() = quest { - id = 9 - title = "參與院長主持的學院節目" - description = + nextQuestId = 9 + ) + ) + +val InMemoryQuestRepository.joinActivityQuest: Quest + get() = findById(9) ?: save( + Quest( + id = 9, + title = "參與院長主持的學院節目", + description = """ 在水球軟體學院中,每週都會有 3~5 個線上聚會,之前在全盛時期甚至一週有 7~10 個活動呢!(軟體英文派對、Amazon 共學會、遊戲微服務計畫(軟體工程讀書會)、純函式話題聚會、人工智慧共學會、Spring Boot 培訓班、水球遊戲微服務計畫實況、Leetcode 刷題屠龍會⋯⋯) 我想邀請你參與學院中最穩定長跑的節目,也就是週六院長主持的「遊戲微服務計畫:水球實況」。在過去的 40 場節目中就平均有 90 幾位觀眾參與,是非常熱血和高含金量的節目,主要在討論「軟體工程各大方法論的實務運用,並且以線上遊戲作為示範」。 想一睹學院各種節目主持的風采嗎?先參加一次「遊戲微服務計畫:水球實況」並和大家一起線上嗨吧!學習就是要和大家一起吃瓜的啦~! - """.trimIndent() - - reward = Reward( - 100u, - 100u, - 1.0f - ) + """.trimIndent(), - criteria = JoinActivityCriteria("遊戲微服務計畫:水球實況", 60, 40) - nextQuest = quizQuest - } + reward = Reward( + 100u, + 100u, + 1.0f + ), -val Quests.quizQuest: Quest - get() = quest { - id = 10 - title = "考試" - description = + criteria = JoinActivityCriteria("遊戲微服務計畫:水球實況", 60, 40), + nextQuestId = 10 + ) + ) + +val InMemoryQuestRepository.quizQuest: Quest + get() = findById(10) ?: save( + Quest( + id = 10, + title = "考試", + description = """ 恭喜你,你已經通過了一連串的新手試煉,接下來是最後一項「任務」,也就是「轉職任務」! 只要做完這最後一項新手任務,你就能獲得「學院一轉紳士」的身份組! @@ -358,19 +374,20 @@ val Quests.quizQuest: Quest 考試時間為 10 分鐘,到學院指令區輸入以下指令吧! [ /quiz name: 紳士考題 ] - """.trimIndent() + """.trimIndent(), - preCondition = QuestIdPreCondition(8) + preCondition = QuestIdPreCondition(8), - roleType = RoleType.WSA_MEMBER + roleType = RoleType.WSA_MEMBER, - periodType = PeriodType.MAIN_QUEST + periodType = PeriodType.MAIN_QUEST, - reward = Reward( - 100u, - 100u, - 1.0f - ) + reward = Reward( + 100u, + 100u, + 1.0f + ), - criteria = QuizCriteria("紳士考題", 4, 5) - } + criteria = QuizCriteria("紳士考題", 4, 5), + ) + ) 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 9d64bc33..14bab171 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 @@ -7,8 +7,8 @@ import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle import org.springframework.stereotype.Component import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission import tw.waterballsa.utopia.utopiagamification.quest.extensions.publishToUser +import tw.waterballsa.utopia.utopiagamification.quest.usecase.ClaimMissionRewardUsecase import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.ClaimMissionRewardService const val BUTTON_QUEST_TAG = "quest" @@ -25,7 +25,7 @@ class RewardButton { class UtopiaGamificationQuestListener( guild: Guild, playerRepository: PlayerRepository, - private val claimMissionRewardService: ClaimMissionRewardService, + private val claimMissionRewardUsecase: ClaimMissionRewardUsecase, ) : UtopiaGamificationListener(guild, playerRepository) { override fun onButtonInteraction(event: ButtonInteractionEvent) { @@ -42,8 +42,8 @@ class UtopiaGamificationQuestListener( val player = user.toPlayer() ?: return - val request = ClaimMissionRewardService.Request(player, questId) - val presenter = object : ClaimMissionRewardService.Presenter { + val request = ClaimMissionRewardUsecase.Request(player, questId) + val presenter = object : ClaimMissionRewardUsecase.Presenter { override fun presentPlayerExpNotification(mission: Mission) { publishMessage( """ @@ -63,7 +63,7 @@ class UtopiaGamificationQuestListener( } } - claimMissionRewardService.execute(request, presenter) + claimMissionRewardUsecase.execute(request, presenter) } } 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 8f356484..3ea2209f 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 @@ -4,27 +4,36 @@ 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.quests.Quests -import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.unlockAcademyQuest +import tw.waterballsa.utopia.utopiagamification.quest.domain.Quest import tw.waterballsa.utopia.utopiagamification.quest.extensions.publishToUser +import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerAcceptQuestUsecase import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.PlayerAcceptQuestService +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 quests: Quests, - private val playerAcceptQuestService: PlayerAcceptQuestService, + private val questRepository: QuestRepository, + private val playerAcceptQuestUsecase: PlayerAcceptQuestUsecase, ) : UtopiaGamificationListener(guild, playerRepository) { override fun onGuildMemberJoin(event: GuildMemberJoinEvent) { with(event) { val player = user.toPlayer() ?: return - val request = PlayerAcceptQuestService.Request(player, quests.unlockAcademyQuest) + 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 presenter = object : PlayerAcceptQuestService.Presenter { + val presenter = object : PlayerAcceptQuestUsecase.Presenter { override fun presentPlayerHasAcquiredMission() { sendMessageToUserPrivateChannel("已獲得新手任務,無法再次獲得!") } @@ -34,7 +43,7 @@ class MemberJoinListener( } } - playerAcceptQuestService.execute(request, presenter) + playerAcceptQuestUsecase.execute(request, presenter) } } diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MessageReactionListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MessageReactionListener.kt index f3591396..a5e646b6 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MessageReactionListener.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MessageReactionListener.kt @@ -4,14 +4,14 @@ import net.dv8tion.jda.api.entities.Guild import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent import org.springframework.stereotype.Component import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.MessageReactionAction +import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerFulfillMissionsUsecase import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.PlayerFulfillMissionsService @Component class MessageReactionListener( guild: Guild, playerRepository: PlayerRepository, - private val playerFulfillMissionsService: PlayerFulfillMissionsService, + private val playerFulfillMissionsUsecase: PlayerFulfillMissionsUsecase, ) : UtopiaGamificationListener(guild, playerRepository) { override fun onMessageReactionAdd(event: MessageReactionAddEvent) { @@ -25,7 +25,7 @@ class MessageReactionListener( emoji.name ) - playerFulfillMissionsService.execute(action, user.claimMissionRewardPresenter) + playerFulfillMissionsUsecase.execute(action, user.claimMissionRewardPresenter) } } } diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MessageSentListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MessageSentListener.kt index 94426141..6d5a9c82 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MessageSentListener.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/MessageSentListener.kt @@ -7,14 +7,14 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent import net.dv8tion.jda.api.events.message.MessageUpdateEvent import org.springframework.stereotype.Component import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.MessageSentAction +import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerFulfillMissionsUsecase import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.PlayerFulfillMissionsService @Component class MessageSentListener( guild: Guild, playerRepository: PlayerRepository, - private val playerFulfillMissionsService: PlayerFulfillMissionsService + private val playerFulfillMissionsUsecase: PlayerFulfillMissionsUsecase ) : UtopiaGamificationListener(guild, playerRepository) { override fun onMessageReceived(event: MessageReceivedEvent) { @@ -35,7 +35,7 @@ class MessageSentListener( (channel as? VoiceChannel)?.members?.size ?: 0 ) - playerFulfillMissionsService.execute(action, user.claimMissionRewardPresenter) + playerFulfillMissionsUsecase.execute(action, user.claimMissionRewardPresenter) } } @@ -57,7 +57,7 @@ class MessageSentListener( (channel as? VoiceChannel)?.members?.size ?: 0 ) - playerFulfillMissionsService.execute(action, user.claimMissionRewardPresenter) + playerFulfillMissionsUsecase.execute(action, user.claimMissionRewardPresenter) } } } diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/PostListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/PostListener.kt index 1b38ec43..2ab52fcd 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/PostListener.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/PostListener.kt @@ -4,14 +4,14 @@ import net.dv8tion.jda.api.entities.Guild import net.dv8tion.jda.api.events.guild.GuildAuditLogEntryCreateEvent import org.springframework.stereotype.Component import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.PostAction +import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerFulfillMissionsUsecase import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.PlayerFulfillMissionsService @Component class PostListener( guild: Guild, playerRepository: PlayerRepository, - private val playerFulfillMissionsService: PlayerFulfillMissionsService + private val playerFulfillMissionsUsecase: PlayerFulfillMissionsUsecase ) : UtopiaGamificationListener(guild, playerRepository) { override fun onGuildAuditLogEntryCreate(event: GuildAuditLogEntryCreateEvent) { @@ -25,7 +25,7 @@ class PostListener( channel.id ) - playerFulfillMissionsService.execute(action, user.claimMissionRewardPresenter) + playerFulfillMissionsUsecase.execute(action, user.claimMissionRewardPresenter) } } diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/QuizListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/QuizListener.kt index 246a4ccd..3d4d60b8 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/QuizListener.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/QuizListener.kt @@ -7,12 +7,15 @@ import org.springframework.stereotype.Component import tw.waterballsa.utopia.jda.domains.QuizEndEvent import tw.waterballsa.utopia.jda.domains.QuizPreparationStartEvent import tw.waterballsa.utopia.jda.domains.UtopiaEvent +import tw.waterballsa.utopia.utopiagamification.quest.domain.Quest import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.QuizAction -import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.Quests -import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.quizQuest +import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerFulfillMissionsUsecase import tw.waterballsa.utopia.utopiagamification.repositories.MissionRepository import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.PlayerFulfillMissionsService +import tw.waterballsa.utopia.utopiagamification.repositories.QuestRepository +import tw.waterballsa.utopia.utopiagamification.repositories.exceptions.NotFoundException.Companion.notFound + +private const val quizQuestId = 10 private val log = KotlinLogging.logger {} @@ -20,9 +23,9 @@ private val log = KotlinLogging.logger {} class QuizListener( guild: Guild, playerRepository: PlayerRepository, - private val playerFulfillMissionsService: PlayerFulfillMissionsService, + private val playerFulfillMissionsUsecase: PlayerFulfillMissionsUsecase, private val missionRepository: MissionRepository, - private val quests: Quests, + private val questRepository: QuestRepository, private val jda: JDA ) : UtopiaGamificationListener(guild, playerRepository) { @@ -44,9 +47,16 @@ class QuizListener( } } - private fun QuizPreparationStartEvent.hasInProgressQuizQuest(): Boolean = - missionRepository.findInProgressMissionsByPlayerId(quizTakerId) - .any { it.quest.title == quests.quizQuest.title } + private fun QuizPreparationStartEvent.hasInProgressQuizQuest(): Boolean { + val quizQuest = questRepository.findById(quizQuestId) + ?: throw notFound(Quest::class) + .id(quizQuestId) + .message("check player has quiz quest") + .build() + + return missionRepository.findInProgressMissionsByPlayerId(quizTakerId) + .any { it.quest.title == quizQuest.title } + } private fun onQuizEnd(event: QuizEndEvent) { with(event) { @@ -61,7 +71,7 @@ class QuizListener( log.info { """[quiz end] { quizTakerId : "$quizTakerId", quizName : "$quizName", correctCount : "$correctCount" } """ } - playerFulfillMissionsService.execute(action, user.claimMissionRewardPresenter) + playerFulfillMissionsUsecase.execute(action, user.claimMissionRewardPresenter) } } } 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 e2e7aa90..0af5a319 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 @@ -7,25 +7,28 @@ 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.quests.Quests -import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.quizQuest -import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.unlockAcademyQuest import tw.waterballsa.utopia.utopiagamification.quest.extensions.publishToUser +import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerAcceptQuestUsecase import tw.waterballsa.utopia.utopiagamification.repositories.MissionRepository import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.PlayerAcceptQuestService +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 quests: Quests, - private val playerAcceptQuestService: PlayerAcceptQuestService, + private val questRepository: QuestRepository, + private val playerAcceptQuestUsecase: PlayerAcceptQuestUsecase, private val missionRepository: MissionRepository ) : UtopiaGamificationListener(guild, playerRepository) { @@ -44,7 +47,7 @@ class SlashCommandListener( return } - deferReply().setEphemeral(true).queue() + deferReply(true).queue() when (subcommandName) { FIRST_QUEST_COMMAND_NAME -> handleFirstQuestCommand() @@ -56,9 +59,15 @@ class SlashCommandListener( private fun SlashCommandInteractionEvent.handleFirstQuestCommand() { val player = user.toPlayer() ?: return - val request = PlayerAcceptQuestService.Request(player, quests.unlockAcademyQuest) + val unlockAcademyQuest = questRepository.findById(unlockAcademyQuestId) + ?: throw notFound(Quest::class) + .id(unlockAcademyQuestId) + .message("assign old member first quest") + .build() + + val request = PlayerAcceptQuestUsecase.Request(player, unlockAcademyQuest) - val presenter = object : PlayerAcceptQuestService.Presenter { + val presenter = object : PlayerAcceptQuestUsecase.Presenter { override fun presentPlayerHasAcquiredMission() { hook.editOriginal("已獲得新手任務,無法再次獲得。").queue() } @@ -69,7 +78,7 @@ class SlashCommandListener( } } - playerAcceptQuestService.execute(request, presenter) + playerAcceptQuestUsecase.execute(request, presenter) } private fun SlashCommandInteractionEvent.handleReviewCommand() { @@ -87,7 +96,7 @@ class SlashCommandListener( } if (state == CLAIMED) { - result = if (quest.id == quests.quizQuest.id) { + result = if (quest.id == quizQuestId) { "你已完成全部的新手任務!" } else { jda.retrieveUserById("620215716993433612").queue { diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/UtopiaGamificationListener.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/UtopiaGamificationListener.kt index a61a4aaa..6a1d2f34 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/UtopiaGamificationListener.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/listeners/UtopiaGamificationListener.kt @@ -8,8 +8,8 @@ import net.dv8tion.jda.api.interactions.components.buttons.Button import tw.waterballsa.utopia.jda.UtopiaListener import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission import tw.waterballsa.utopia.utopiagamification.quest.domain.Player +import tw.waterballsa.utopia.utopiagamification.quest.usecase.PlayerFulfillMissionsUsecase import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository -import tw.waterballsa.utopia.utopiagamification.quest.service.PlayerFulfillMissionsService open class UtopiaGamificationListener( private val guild: Guild, @@ -39,7 +39,7 @@ open class UtopiaGamificationListener( } protected val User.claimMissionRewardPresenter - get() = object : PlayerFulfillMissionsService.Presenter { + get() = object : PlayerFulfillMissionsUsecase.Presenter { override fun presentClaimMissionReward(mission: Mission) { openPrivateChannel().queue { it.sendMessage(mission.postMessage) diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/ClaimMissionRewardService.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/ClaimMissionRewardUsecase.kt similarity index 51% rename from utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/ClaimMissionRewardService.kt rename to utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/ClaimMissionRewardUsecase.kt index 8d7fb613..351a5594 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/ClaimMissionRewardService.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/ClaimMissionRewardUsecase.kt @@ -1,13 +1,19 @@ -package tw.waterballsa.utopia.utopiagamification.quest.service +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 +import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository +import tw.waterballsa.utopia.utopiagamification.repositories.QuestRepository +import tw.waterballsa.utopia.utopiagamification.repositories.exceptions.NotFoundException.Companion.notFound @Component -class ClaimMissionRewardService( +class ClaimMissionRewardUsecase( private val missionRepository: MissionRepository, + private val playerRepository: PlayerRepository, + private val questRepository: QuestRepository ) { fun execute(request: Request, presenter: Presenter) { @@ -21,15 +27,21 @@ class ClaimMissionRewardService( mission.rewardPlayer() + val player = playerRepository.savePlayer(mission.player) missionRepository.saveMission(mission) presenter.presentPlayerExpNotification(mission) - mission.nextMission()?.let { nextMission -> + val nextQuestId = mission.nextQuestId() ?: return - missionRepository.saveMission(nextMission) - presenter.presentNextMission(nextMission) - } + val nextQuest = questRepository.findById(nextQuestId) + ?: throw notFound(Quest::class) + .id(nextQuestId) + .message("assign player next quest id") + .build() + + missionRepository.saveMission(Mission(player, nextQuest)) + presenter.presentNextMission(Mission(player, nextQuest)) } } diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/PlayerAcceptQuestService.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerAcceptQuestUsecase.kt similarity index 92% rename from utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/PlayerAcceptQuestService.kt rename to utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerAcceptQuestUsecase.kt index eeff1e34..4e1b5b18 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/PlayerAcceptQuestService.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerAcceptQuestUsecase.kt @@ -1,4 +1,4 @@ -package tw.waterballsa.utopia.utopiagamification.quest.service +package tw.waterballsa.utopia.utopiagamification.quest.usecase import org.springframework.stereotype.Component import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission @@ -8,7 +8,7 @@ import tw.waterballsa.utopia.utopiagamification.repositories.MissionRepository @Component -class PlayerAcceptQuestService( +class PlayerAcceptQuestUsecase( private val missionRepository: MissionRepository ) { diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/PlayerFulfillMissionsService.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerFulfillMissionsUsecase.kt similarity index 91% rename from utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/PlayerFulfillMissionsService.kt rename to utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerFulfillMissionsUsecase.kt index 9f6a5ab0..f34f8b0a 100644 --- a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/service/PlayerFulfillMissionsService.kt +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/quest/usecase/PlayerFulfillMissionsUsecase.kt @@ -1,4 +1,4 @@ -package tw.waterballsa.utopia.utopiagamification.quest.service +package tw.waterballsa.utopia.utopiagamification.quest.usecase import org.springframework.stereotype.Component import tw.waterballsa.utopia.utopiagamification.quest.domain.Action @@ -6,7 +6,7 @@ import tw.waterballsa.utopia.utopiagamification.quest.domain.Mission import tw.waterballsa.utopia.utopiagamification.repositories.MissionRepository @Component -class PlayerFulfillMissionsService( +class PlayerFulfillMissionsUsecase( private val missionRepository: MissionRepository ) { diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/QuestRepository.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/QuestRepository.kt new file mode 100644 index 00000000..c98b3349 --- /dev/null +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/QuestRepository.kt @@ -0,0 +1,9 @@ +package tw.waterballsa.utopia.utopiagamification.repositories + +import tw.waterballsa.utopia.utopiagamification.quest.domain.Quest + +interface QuestRepository { + + fun findById(id: Int): Quest? + fun save(quest: Quest): Quest +} diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/exceptions/NotFoundException.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/exceptions/NotFoundException.kt new file mode 100644 index 00000000..9a192574 --- /dev/null +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/exceptions/NotFoundException.kt @@ -0,0 +1,36 @@ +package tw.waterballsa.utopia.utopiagamification.repositories.exceptions + +import kotlin.reflect.KClass + +class NotFoundException( + resourceType: KClass<*>, + id: String, + message: String +) : RuntimeException("Resource (${resourceType.simpleName}) not found: (id = $id) $message.") { + + companion object { + + fun notFound(resource: KClass<*>): Builder = Builder(resource, "", "") + } + + class Builder( + private val resourceType: KClass<*>, + private var id: String, + private var message: String + ) { + + fun id(id: Any): Builder { + this.id = id.toString() + return this + } + + fun message(message: String): Builder { + this.message = message + return this + } + + fun build(): NotFoundException { + return NotFoundException(resourceType, id, message) + } + } +} diff --git a/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/inmemory/repositoryimpl/InMemoryQuestRepository.kt b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/inmemory/repositoryimpl/InMemoryQuestRepository.kt new file mode 100644 index 00000000..215de633 --- /dev/null +++ b/utopia-gamification/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/inmemory/repositoryimpl/InMemoryQuestRepository.kt @@ -0,0 +1,35 @@ +package tw.waterballsa.utopia.utopiagamification.repositories.inmemory.repositoryimpl + +import org.springframework.stereotype.Component +import tw.waterballsa.utopia.commons.config.WsaDiscordProperties +import tw.waterballsa.utopia.utopiagamification.quest.domain.Quest +import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.* +import tw.waterballsa.utopia.utopiagamification.repositories.QuestRepository + +@Component +class InMemoryQuestRepository(val wsa: WsaDiscordProperties) : QuestRepository { + + private val quests = mutableListOf() + + init { + unlockAcademyQuest + selfIntroductionQuest + firstMessageActionQuest + sendContainsImageMessageInEngineerLifeChannelQuest + replyToAnyoneInCareerAdvancementTopicChannelQuest + watchVideoQuest + flagPostQuest + sendMessageInVoiceChannelQuest + joinActivityQuest + quizQuest + } + + override fun findById(id: Int): Quest? = quests.find { it.id == id } + + override fun save(quest: Quest): Quest { + quests.add(quest) + return quest + } + + fun String.toLink(): String = "https://discord.com/channels/${wsa.guildId}/${this}" +} 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 44d97b5d..0da17278 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 @@ -3,11 +3,14 @@ package tw.waterballsa.utopia.utopiagamification.repositories.mongodb.repository import org.springframework.stereotype.Component import tw.waterballsa.utopia.mongo.gateway.* 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.State import tw.waterballsa.utopia.utopiagamification.quest.domain.State.* -import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.Quests 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 import java.time.LocalDateTime import java.util.UUID.fromString @@ -15,7 +18,7 @@ import java.util.UUID.fromString class MongodbMissionRepository( private val repository: MongoCollection, private val playerRepository: PlayerRepository, - private val quests: Quests + private val questRepository: QuestRepository ) : MissionRepository { override fun findPlayerMissionByQuestId(playerId: String, questId: Int): Mission? = repository.find( @@ -43,15 +46,16 @@ class MongodbMissionRepository( ).map { it.toDomain() } override fun saveMission(mission: Mission): Mission { - playerRepository.savePlayer(mission.player) repository.save(mission.toDocument()) return mission } // TODO 等到 @DBRef 功能上線後,將 playerId 改成 player,讓 MongoDB 協助 join private fun MissionDocument.toDomain(): Mission { - val player = playerRepository.findPlayerById(playerId) ?: throw RuntimeException("not find player") - val quest = quests.findById(questId) + val player = playerRepository.findPlayerById(playerId) + ?: throw notFound(Player::class).id(playerId).message("mission to domain").build() + val quest = questRepository.findById(questId) + ?: throw notFound(Quest::class).id(questId).message("mission to domain").build() return Mission(fromString(id), player, quest, state, completedTime) } diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestInvocationContextProvider.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestInvocationContextProvider.kt index 30018946..f09db20d 100644 --- a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestInvocationContextProvider.kt +++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/MissionTestInvocationContextProvider.kt @@ -4,18 +4,16 @@ 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.quest.domain.* import tw.waterballsa.utopia.utopiagamification.quest.domain.PeriodType.MAIN_QUEST import tw.waterballsa.utopia.utopiagamification.quest.domain.RoleType.EVERYONE import tw.waterballsa.utopia.utopiagamification.quest.domain.RoleType.WSA_MEMBER +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.quest import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.toRegexRule -import tw.waterballsa.utopia.utopiagamification.quest.domain.EmptyPreCondition -import tw.waterballsa.utopia.utopiagamification.quest.domain.Player -import tw.waterballsa.utopia.utopiagamification.quest.domain.QuestIdPreCondition -import tw.waterballsa.utopia.utopiagamification.quest.domain.Reward -import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.* import tw.waterballsa.utopia.utopiagmification.GenericTypedParameterResolver import java.util.UUID.randomUUID import java.util.stream.Stream @@ -31,16 +29,16 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '考試', when player was act 5 correct quizzes, then mission should be completed", player, - quest { - id = 10 - title = "考試" - description = "" - preCondition = QuestIdPreCondition(8) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 10, + title = "考試", + description = "", + preCondition = QuestIdPreCondition(8), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = QuizCriteria("紳士考題", 4, 5) - }, + ), QuizAction(player, "紳士考題", 5), isMatchAction = true, isMissionCompleted = true @@ -49,16 +47,16 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '考試', when player was act 0 correct quiz, then mission should be failed", player, - quest { - id = 10 - title = "考試" - description = "" - preCondition = QuestIdPreCondition(8) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) - criteria = QuizCriteria("紳士考題", 4, 5) - }, + Quest( + id = 10, + title = "考試", + description = "", + preCondition = QuestIdPreCondition(8), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), + criteria = QuizCriteria("紳士考題", 4, 5), + ), QuizAction(player, "紳士考題", 0), isMatchAction = true, isMissionCompleted = false @@ -67,19 +65,19 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '到處吃瓜', when player sent a message in two people channel, then mission should be completed", player, - quest { - id = 8 - title = "到處吃瓜" - description = "" - reward = Reward(100u, 100u, 1.0f) - preCondition = QuestIdPreCondition(7) - roleType = WSA_MEMBER - periodType = MAIN_QUEST + Quest( + id = 8, + title = "到處吃瓜", + description = "", + reward = Reward(100u, 100u, 1.0f), + preCondition = QuestIdPreCondition(7), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, criteria = MessageSentCriteria( ANY_CHANNEL, numberOfVoiceChannelMembersRule = AtLeastRule(2) ) - }, + ), MessageSentAction( player, "eatWatermelonEveryWhere", @@ -95,19 +93,19 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '到處吃瓜', when player sent a message in zero person channel, then mission should be failed", player, - quest { - id = 8 - title = "到處吃瓜" - description = "" - reward = Reward(100u, 100u, 1.0f) - preCondition = QuestIdPreCondition(7) - roleType = WSA_MEMBER - periodType = MAIN_QUEST + Quest( + id = 8, + title = "到處吃瓜", + description = "", + reward = Reward(100u, 100u, 1.0f), + preCondition = QuestIdPreCondition(7), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, criteria = MessageSentCriteria( ANY_CHANNEL, numberOfVoiceChannelMembersRule = AtLeastRule(2) ) - }, + ), MessageSentAction( player, "test", @@ -123,16 +121,16 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept flagPost mission, when player post a message in right channel, then mission should be completed", player, - quest { - id = 7 - title = "全民插旗:把學院當成自己的家" - description = "" - preCondition = QuestIdPreCondition(6) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 7, + title = "全民插旗:把學院當成自己的家", + description = "", + preCondition = QuestIdPreCondition(6), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = PostCriteria(ChannelIdRule("flagPostChannelId")) - }, + ), PostAction( player, "flagPostChannelId", @@ -144,16 +142,16 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept flagPost mission, when player post a message in wrong channel, then mission should be failed", player, - quest { - id = 7 - title = "全民插旗:把學院當成自己的家" - description = "" - preCondition = QuestIdPreCondition(6) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 7, + title = "全民插旗:把學院當成自己的家", + description = "", + preCondition = QuestIdPreCondition(6), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = PostCriteria(ChannelIdRule("flagPostChannelId")) - }, + ), PostAction( player, "featuredVideosChannelId", @@ -165,17 +163,16 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '學院精華影片', when player send a message in right channel, then mission should be completed", player, - quest { - id = 6 - title = "學院精華影片" - description = "" - reward = Reward(100u, 100u, 1.0f) - preCondition = QuestIdPreCondition(5) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 6, + title = "學院精華影片", + description = "", + preCondition = QuestIdPreCondition(5), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = MessageSentCriteria(ChannelIdRule("featuredVideosChannelId")) - }, + ), MessageSentAction( player, "featuredVideosChannelId", @@ -191,17 +188,16 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '學院精華影片', when player send a message in wrong channel, then mission should be failed", player, - quest { - id = 6 - title = "學院精華影片" - description = "" - reward = Reward(100u, 100u, 1.0f) - preCondition = QuestIdPreCondition(5) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 6, + title = "學院精華影片", + description = "", + preCondition = QuestIdPreCondition(5), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = MessageSentCriteria(ChannelIdRule("featuredVideosChannelId")) - }, + ), MessageSentAction( player, "flagPostChannelId", @@ -217,19 +213,19 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '職涯攻略', when player reply a message in right channel, then mission should be completed", player, - quest { - id = 5 - title = "職涯攻略" - description = "" - preCondition = QuestIdPreCondition(4) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 5, + title = "職涯攻略", + description = "", + preCondition = QuestIdPreCondition(4), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = MessageSentCriteria( ChannelIdRule("careerAdvancementTopicChannelId"), hasRepliedRule = TRUE ) - }, + ), MessageSentAction( player, "careerAdvancementTopicChannelId", @@ -245,20 +241,20 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '職涯攻略', when player send a message in right channel, then mission should be failed", player, - quest { - id = 5 - title = "職涯攻略" - description = "" - preCondition = QuestIdPreCondition(4) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 5, + title = "職涯攻略", + description = "", + preCondition = QuestIdPreCondition(4), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = - MessageSentCriteria( - ChannelIdRule("careerAdvancementTopicChannelId"), - hasRepliedRule = TRUE - ) - }, + MessageSentCriteria( + ChannelIdRule("careerAdvancementTopicChannelId"), + hasRepliedRule = TRUE + ) + ), MessageSentAction( player, "careerAdvancementTopicChannelId", @@ -274,20 +270,20 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '融入大家', when player send a image in correct channel, then mission should be completed", player, - quest { + Quest( - id = 4 - title = "融入大家" - description = "這是一個PO照片任務" - preCondition = QuestIdPreCondition(3) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + id = 4, + title = "融入大家", + description = "這是一個PO照片任務", + preCondition = QuestIdPreCondition(3), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = MessageSentCriteria( ChannelIdRule("engineerLifeChannelId"), hasImageRule = TRUE ) - }, + ), MessageSentAction( player, "engineerLifeChannelId", @@ -303,19 +299,19 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '融入大家', when player send a plain text in correct channel, then mission should be failed", player, - quest { - id = 4 - title = "融入大家" - description = "這是一個PO照片任務" - preCondition = QuestIdPreCondition(3) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 4, + title = "融入大家", + description = "這是一個PO照片任務", + preCondition = QuestIdPreCondition(3), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = MessageSentCriteria( ChannelIdRule("engineerLifeChannelId"), hasImageRule = TRUE ) - }, + ), MessageSentAction( player, "engineerLifeChannelId", @@ -331,16 +327,16 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '新生降落', when player send a message in correct channel, then mission should be completed", player, - quest { - id = 3 - title = "新生降落" - description = "" - preCondition = QuestIdPreCondition(2) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 3, + title = "新生降落", + description = "", + preCondition = QuestIdPreCondition(2), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = MessageSentCriteria(ChannelIdRule("discussionAreaChannelId")) - }, + ), MessageSentAction( player, "discussionAreaChannelId", @@ -356,16 +352,16 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '新生降落', when player send an image in wrong channel, then mission should be failed", player, - quest { - id = 3 - title = "新生降落" - description = "" - preCondition = QuestIdPreCondition(2) - roleType = WSA_MEMBER - periodType = MAIN_QUEST - reward = Reward(100u, 100u, 1.0f) + Quest( + id = 3, + title = "新生降落", + description = "", + preCondition = QuestIdPreCondition(2), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f), criteria = MessageSentCriteria(ChannelIdRule("discussionAreaChannelId")) - }, + ), MessageSentAction( player, "careerAdvancementTopicChannelId", @@ -381,19 +377,19 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '自我介紹', when player sent a message with wrong pattern, then mission should be failed", player, - quest { - id = 2 - title = "自我介紹" - description = "" - reward = Reward(100u, 100u, 1.0f) - preCondition = QuestIdPreCondition(1) - roleType = WSA_MEMBER - periodType = MAIN_QUEST + Quest( + id = 2, + title = "自我介紹", + description = "", + reward = Reward(100u, 100u, 1.0f), + preCondition = QuestIdPreCondition(1), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, criteria = MessageSentCriteria( ChannelIdRule("selfIntroChannelId"), regexRule = """【(.|\n)*】(.|\n)*工作職位:?(.|\n)*((公司產業:?(:)?(.|\n)*))?專長:?(.|\n)*興趣:?(.|\n)*簡介:?.(.|\n)*((三件關於我的事,猜猜哪一件是假的:?(:)?(.|\n)*))?""".toRegexRule() ) - }, MessageSentAction( + ), MessageSentAction( player, "selfIntroChannelId", """三件關於我的事,猜猜哪一件是假的:""", @@ -408,19 +404,19 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '自我介紹', when player sent a message with right pattern, then mission should be completed", player, - quest { - id = 2 - title = "自我介紹" - description = "" - reward = Reward(100u, 100u, 1.0f) - preCondition = QuestIdPreCondition(1) - roleType = WSA_MEMBER - periodType = MAIN_QUEST + Quest( + id = 2, + title = "自我介紹", + description = "", + reward = Reward(100u, 100u, 1.0f), + preCondition = QuestIdPreCondition(1), + roleType = WSA_MEMBER, + periodType = MAIN_QUEST, criteria = MessageSentCriteria( ChannelIdRule("selfIntroChannelId"), regexRule = """【(.|\n)*】(.|\n)*工作職位:?(.|\n)*((公司產業:?(:)?(.|\n)*))?專長:?(.|\n)*興趣:?(.|\n)*簡介:?.(.|\n)*((三件關於我的事,猜猜哪一件是假的:?(:)?(.|\n)*))?""".toRegexRule() ) - }, MessageSentAction( + ), MessageSentAction( player, "selfIntroChannelId", """ 【 playerA 】 工作職位: <工作職位> @@ -444,20 +440,20 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '解鎖學院', when player react wrong emoji, then mission should be failed", player, - quest { - id = 1 - title = "解鎖學院" - description = "" - reward = Reward(100u, 100u, 1.0f, WSA_MEMBER) - preCondition = EmptyPreCondition() - roleType = EVERYONE - periodType = MAIN_QUEST + Quest( + id = 1, + title = "解鎖學院", + description = "", + reward = Reward(100u, 100u, 1.0f, WSA_MEMBER), + preCondition = EmptyPreCondition(), + roleType = EVERYONE, + periodType = MAIN_QUEST, criteria = MessageReactionCriteria( ChannelIdRule("unlockEntryChannelId"), "unlockEntryMessageId", "🔑" ) - }, + ), MessageReactionAction( player, "unlockEntryMessageId", @@ -470,20 +466,20 @@ class MissionTestInvocationContextProvider : TestTemplateInvocationContextProvid MissionTestCase( "given player accept mission '解鎖學院', when player react right emoji, then mission should be completed", player, - quest { - id = 1 - title = "解鎖學院" - description = "" - reward = Reward(100u, 100u, 1.0f, WSA_MEMBER) - preCondition = EmptyPreCondition() - roleType = EVERYONE - periodType = MAIN_QUEST + Quest( + id = 1, + title = "解鎖學院", + description = "", + preCondition = EmptyPreCondition(), + roleType = EVERYONE, + periodType = MAIN_QUEST, + reward = Reward(100u, 100u, 1.0f, WSA_MEMBER), criteria = MessageReactionCriteria( ChannelIdRule("unlockEntryChannelId"), "unlockEntryMessageId", "🔑" ) - }, + ), MessageReactionAction( player, "unlockEntryMessageId", diff --git a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/UtopiaGamificationQuestTest.kt b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/UtopiaGamificationQuestTest.kt index 74e1d7ab..83828834 100644 --- a/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/UtopiaGamificationQuestTest.kt +++ b/utopia-gamification/src/test/kotlin/tw/waterballsa/utopia/utopiagmification/quest/UtopiaGamificationQuestTest.kt @@ -7,9 +7,8 @@ import org.junit.jupiter.api.TestTemplate import org.junit.jupiter.api.extension.* import tw.waterballsa.utopia.utopiagamification.activity.domain.Activity import tw.waterballsa.utopia.utopiagamification.activity.domain.Activity.State.ACTIVE -import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.JoinActivityCriteria -import tw.waterballsa.utopia.utopiagamification.quest.domain.quests.quest import tw.waterballsa.utopia.utopiagamification.quest.domain.* +import tw.waterballsa.utopia.utopiagamification.quest.domain.actions.JoinActivityCriteria import java.util.* import java.util.UUID.randomUUID @@ -22,13 +21,13 @@ class UtopiaGamificationQuestTest { @BeforeEach fun setup() { playerA = Player(id = randomUUID().toString(), name = "A") - quest = quest { - id = 9 - title = "參與院長主持的學院節目" - description = "" - reward = Reward(100u, 100u, 1.0f) + quest = Quest( + id = 9, + title = "參與院長主持的學院節目", + description = "", + reward = Reward(100u, 100u, 1.0f), criteria = JoinActivityCriteria("遊戲微服務計畫:水球實況", 1, 0) - } + ) activity = Activity( "遊戲微服務計畫:水球實況", "hostId", diff --git a/wsa-bot-commands.md b/wsa-bot-commands.md index f78d575a..900df236 100644 --- a/wsa-bot-commands.md +++ b/wsa-bot-commands.md @@ -60,8 +60,12 @@ | quiz | name(STRING): The quiz you want to start. | The quiz for utopia. | ## utopia -| Commands | Arguments | Description | -|:-------------------:| --------------------------- | ------------------------------------- | -| utopia first-quest | | accept first quest | -| utopia re-render | | re-render in_progress/completed quest | -| utopia assign-quest | quest-id(INTEGER): quest id | accept quest | \ No newline at end of file +| Commands | Arguments | Description | +|:------------------:| --------- | ------------------------------------- | +| utopia first-quest | | get first quest | +| utopia re-render | | re-render in_progress/completed quest | + +## rock-paper-scissors +| Commands | Arguments | Description | +|:-------------------:| --------- | ------------------------------------- | +| rock-paper-scissors | | start a new rock paper scissors game! | \ No newline at end of file