-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
689 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
...n/src/main/kotlin/tw/waterballsa/utopia/utopiagamification/RegisterGamificationCommand.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package tw.waterballsa.utopia.utopiagamification | ||
|
||
import net.dv8tion.jda.api.entities.Guild | ||
import net.dv8tion.jda.api.interactions.commands.Command | ||
import net.dv8tion.jda.api.interactions.commands.OptionType.STRING | ||
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.jda.extensions.addOptionalOption | ||
import tw.waterballsa.utopia.utopiagamification.quest.listeners.UtopiaGamificationListener | ||
import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository | ||
|
||
const val UTOPIA_COMMAND_NAME = "utopia" | ||
const val FIRST_QUEST_COMMAND_NAME = "first-quest" | ||
const val REVIEW_COMMAND_NAME = "re-render" | ||
const val OPTION_COMMAND_NAME = "options" | ||
|
||
private const val LEADERBOARD_COMMAND_NAME = "leaderboard" | ||
private const val LEADERBOARD_OPTION_MY_RANK = "my-rank" | ||
|
||
@Component | ||
class RegisterGamificationCommand( | ||
guild : Guild, | ||
playerRepository: PlayerRepository | ||
) : UtopiaGamificationListener(guild, playerRepository){ | ||
override fun commands(): List<CommandData> = listOf( | ||
Commands.slash(UTOPIA_COMMAND_NAME, "utopia command") | ||
.addSubcommands( | ||
// 任務系統 | ||
SubcommandData(FIRST_QUEST_COMMAND_NAME, "get first quest"), | ||
// 查詢並重發任務 | ||
SubcommandData(REVIEW_COMMAND_NAME, "re-render in_progress/completed quest"), | ||
// 排行榜 | ||
SubcommandData(LEADERBOARD_COMMAND_NAME, "leaderboard") | ||
.addOptionalOption( | ||
STRING, | ||
OPTION_COMMAND_NAME, | ||
LEADERBOARD_OPTION_MY_RANK, | ||
Command.Choice(LEADERBOARD_OPTION_MY_RANK, LEADERBOARD_OPTION_MY_RANK) | ||
) | ||
) | ||
) | ||
} |
164 changes: 164 additions & 0 deletions
164
...c/main/kotlin/tw/waterballsa/utopia/utopiagamification/leaderboard/LeaderBoardListener.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package tw.waterballsa.utopia.utopiagamification.leaderboard | ||
|
||
import dev.minn.jda.ktx.messages.Embed | ||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent | ||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent | ||
import net.dv8tion.jda.api.interactions.components.buttons.Button | ||
import org.springframework.stereotype.Component | ||
import tw.waterballsa.utopia.gamification.leaderboard.domain.LeaderBoardItem | ||
import tw.waterballsa.utopia.jda.UtopiaListener | ||
import tw.waterballsa.utopia.utopiagamification.leaderboard.repository.LeaderBoardRepository | ||
import tw.waterballsa.utopia.utopiagamification.repositories.query.Page | ||
import tw.waterballsa.utopia.utopiagamification.repositories.query.PageRequest | ||
import tw.waterballsa.utopia.utopiagamification.repositories.query.Pageable | ||
|
||
private const val UTOPIA_COMMAND_NAME = "utopia" | ||
private const val LEADERBOARD_PREVIOUS_BUTTON = "utopia-leaderboard-previous" | ||
private const val LEADERBOARD_NEXT_BUTTON = "utopia-leaderboard-next" | ||
private const val LEADERBOARD_OPTION = "options" | ||
private const val LEADERBOARD_SUBCOMMAND_NAME = "leaderboard" | ||
private const val LEADERBOARD_MY_RANK = "my-rank" | ||
private const val LEADERBOARD_PREVIOUS_STRING = "上一頁" | ||
private const val LEADERBOARD_NEXT_STRING = "下一頁" | ||
|
||
@Component | ||
class LeaderBoardListener( | ||
private val leaderBoardRepository: LeaderBoardRepository, | ||
) : UtopiaListener() { | ||
|
||
override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { | ||
|
||
with(event) { | ||
if (name != UTOPIA_COMMAND_NAME || subcommandName != LEADERBOARD_SUBCOMMAND_NAME) { | ||
return | ||
} | ||
|
||
val isLeaderboardQuery = options.isEmpty() | ||
if (isLeaderboardQuery) { | ||
queryLeaderboard() | ||
} | ||
|
||
val leaderboardOption = getOption(LEADERBOARD_OPTION)?.asString ?: return | ||
|
||
val isSelfRankQuery = leaderboardOption == LEADERBOARD_MY_RANK | ||
if (isSelfRankQuery) { | ||
querySelfRank() | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* 使用 Discord Embedded Message: | ||
* - 印出多列:`<rank> <@userId> Lv.<等級> Exp: <經驗值> $<賞金>` ,每一列代表一個排名。 | ||
* Discord Embedded Message 下方有兩個按鈕,”Previous Page” 和 “Next Page”。 | ||
*/ | ||
private fun SlashCommandInteractionEvent.queryLeaderboard() { | ||
val pageable = PageRequest.of(0, 10) | ||
val page = leaderBoardRepository.findAll(pageable) | ||
|
||
reply("").addEmbeds( | ||
Embed { description = page.createLeaderBoardRankDescription() } | ||
).addActionRow( | ||
createLeaderBoardEmbedButton(page) | ||
).queue() | ||
} | ||
|
||
/** | ||
* 假設是 leaderboard my-rank 指令的話,會去 query 指定的 player | ||
* 然後 output「妳的排名為第 N 名」到 discord channel | ||
*/ | ||
private fun SlashCommandInteractionEvent.querySelfRank() { | ||
val rank = leaderBoardRepository.queryPlayerRank(user.id)?.let { | ||
"你的排名為第 ${it.rank} 名" | ||
} ?: "找不到你的排名" | ||
reply("").addEmbeds( | ||
Embed { description = rank } | ||
).queue() | ||
} | ||
|
||
override fun onButtonInteraction(event: ButtonInteractionEvent) { | ||
with(event) { | ||
if (!button.isLeaderBoardButton()) { | ||
return | ||
} | ||
|
||
deferEdit().queue() | ||
|
||
val pageable: Pageable = button.toPageable() | ||
val page = leaderBoardRepository.findAll(pageable) | ||
|
||
hook.editMessageEmbedsById( | ||
messageId, | ||
Embed { | ||
description = page.createLeaderBoardRankDescription() | ||
} | ||
).setActionRow( | ||
createLeaderBoardEmbedButton(page) | ||
).queue() | ||
} | ||
} | ||
|
||
private fun Button.isLeaderBoardButton(): Boolean { | ||
return listOf( | ||
LEADERBOARD_PREVIOUS_BUTTON, | ||
LEADERBOARD_NEXT_BUTTON | ||
).any { id.toString().contains(it) } | ||
} | ||
|
||
private fun Button.toPageable(): Pageable { | ||
val splitButtonIds = id.toString().split("_") | ||
return when (splitButtonIds.size) { | ||
3 -> { | ||
PageRequest.of(splitButtonIds[1].toInt(), splitButtonIds[2].toInt()) | ||
} | ||
else -> { | ||
PageRequest.ofSize(10) | ||
} | ||
} | ||
} | ||
|
||
private fun Page<LeaderBoardItem>.createLeaderBoardRankDescription(): String { | ||
return getContent().joinToString(separator = "\n") { | ||
"[${it.rank}] ${it.name}, ${it.level}, EXP=${it.exp}, Bounty=${it.bounty}" | ||
} | ||
} | ||
|
||
private fun createLeaderBoardEmbedButton(page: Page<LeaderBoardItem>) : List<Button> { | ||
val buttons = mutableListOf<Button>() | ||
if (page.hasPrevious()) { | ||
buttons.add( | ||
Button.primary( | ||
makeButtonId(LEADERBOARD_PREVIOUS_BUTTON, page.previousPageable()), | ||
LEADERBOARD_PREVIOUS_STRING | ||
).asEnabled() | ||
) | ||
} else { | ||
buttons.add( | ||
Button.primary( | ||
makeButtonId(LEADERBOARD_PREVIOUS_BUTTON, page.getPageable()), | ||
LEADERBOARD_PREVIOUS_STRING | ||
).asDisabled() | ||
) | ||
} | ||
if (page.hasNext()) { | ||
buttons.add( | ||
Button.success( | ||
makeButtonId(LEADERBOARD_NEXT_BUTTON, page.nextPageable()), | ||
LEADERBOARD_NEXT_STRING | ||
).asEnabled() | ||
) | ||
} else { | ||
buttons.add( | ||
Button.success( | ||
makeButtonId(LEADERBOARD_NEXT_BUTTON, page.getPageable()), | ||
LEADERBOARD_NEXT_STRING | ||
).asDisabled() | ||
) | ||
} | ||
return buttons | ||
} | ||
|
||
private fun makeButtonId(prefix: String, pageable: Pageable): String { | ||
return "${prefix}_${pageable.getPageNumber()}_${pageable.getPageSize()}" | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
...ain/kotlin/tw/waterballsa/utopia/utopiagamification/leaderboard/domain/LeaderBoardItem.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package tw.waterballsa.utopia.gamification.leaderboard.domain | ||
|
||
data class LeaderBoardItem( | ||
val playerId: String, | ||
val name: String, | ||
val exp: ULong, | ||
val level: UInt, | ||
val bounty: UInt, | ||
var rank: Int = 0, | ||
) |
10 changes: 10 additions & 0 deletions
10
.../tw/waterballsa/utopia/utopiagamification/leaderboard/repository/LeaderBoardRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package tw.waterballsa.utopia.utopiagamification.leaderboard.repository | ||
|
||
import tw.waterballsa.utopia.gamification.leaderboard.domain.LeaderBoardItem | ||
import tw.waterballsa.utopia.utopiagamification.repositories.PageableRepository | ||
|
||
interface LeaderBoardRepository: PageableRepository<LeaderBoardItem> { | ||
|
||
fun queryPlayerRank(playerId: String): LeaderBoardItem? | ||
|
||
} |
38 changes: 38 additions & 0 deletions
38
...erballsa/utopia/utopiagamification/leaderboard/repository/MongodbLeaderBoardRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package tw.waterballsa.utopia.gamification.repositories.mongodb.repositoryimpl | ||
|
||
import org.springframework.stereotype.Component | ||
import tw.waterballsa.utopia.gamification.leaderboard.domain.LeaderBoardItem | ||
import tw.waterballsa.utopia.utopiagamification.leaderboard.repository.LeaderBoardRepository | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Player | ||
import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository | ||
import tw.waterballsa.utopia.utopiagamification.repositories.page | ||
import tw.waterballsa.utopia.utopiagamification.repositories.query.Page | ||
import tw.waterballsa.utopia.utopiagamification.repositories.query.Pageable | ||
|
||
@Component | ||
class MongodbLeaderBoardRepository( | ||
private val playerRepository: PlayerRepository | ||
) : LeaderBoardRepository { | ||
|
||
override fun findAll(pageable: Pageable): Page<LeaderBoardItem> = playerRepository.findAll() | ||
.rank() | ||
.page(pageable) | ||
|
||
override fun queryPlayerRank(playerId: String): LeaderBoardItem? = playerRepository.findAll() | ||
.rank() | ||
.find { it.playerId == playerId } | ||
|
||
} | ||
|
||
|
||
private fun Collection<Player>.rank(): List<LeaderBoardItem> = | ||
sortedWith(rankOrder) | ||
.mapIndexed { index, it -> LeaderBoardItem(it.id, it.name, it.exp, it.level, it.bounty, index + 1) } | ||
|
||
private val rankOrder : Comparator<Player> = | ||
compareByDescending<Player> { it.level } | ||
.thenByDescending { it.exp } | ||
.thenByDescending { it.bounty } | ||
.thenBy { it.levelUpgradeDate } | ||
.thenBy { it.joinDate } | ||
.thenBy { it.id } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
...c/main/kotlin/tw/waterballsa/utopia/utopiagamification/repositories/PageableRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package tw.waterballsa.utopia.utopiagamification.repositories | ||
|
||
import tw.waterballsa.utopia.utopiagamification.repositories.query.Pageable | ||
import tw.waterballsa.utopia.utopiagamification.repositories.query.Page | ||
import tw.waterballsa.utopia.utopiagamification.repositories.query.PageImpl | ||
|
||
interface PageableRepository<T> { | ||
fun findAll(pageable: Pageable): Page<T> | ||
} | ||
|
||
fun <T> Collection<T>.page(pageable: Pageable): Page<T> = | ||
drop(pageable.getOffset().toInt()) | ||
.take(pageable.getPageSize()) | ||
.let { PageImpl(it.toList(), pageable, size.toLong()) } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.