From 102980e0843fd14c949ab03cb76f024e7574a618 Mon Sep 17 00:00:00 2001
From: RobotHanzo <36107150+RobotHanzo@users.noreply.github.com>
Date: Tue, 3 Feb 2026 04:28:25 +0800
Subject: [PATCH 1/6] migrate to kotlin
---
.idea/kotlinc.xml | 10 +
.idea/modules.xml | 1 -
...obothanzo.werewolf.WerewolfHelper.main.iml | 8 -
build.gradle.kts | 17 +-
build_output.txt | 26 +
.../src/features/game/hooks/useGameState.ts | 3 -
.../werewolf/WerewolfApplication.java | 120 ----
.../dev/robothanzo/werewolf/audio/Audio.java | 56 --
.../audio/AudioPlayerSendHandler.java | 32 -
.../werewolf/commands/DurationMapper.java | 28 -
.../robothanzo/werewolf/commands/Player.java | 536 ----------------
.../robothanzo/werewolf/commands/Poll.java | 187 ------
.../robothanzo/werewolf/commands/Server.java | 337 -----------
.../robothanzo/werewolf/commands/Speech.java | 120 ----
.../werewolf/config/SecurityConfig.java | 74 ---
.../werewolf/config/SessionConfig.java | 9 -
.../werewolf/config/UserSessionFilter.java | 46 --
.../werewolf/config/WebSocketConfig.java | 25 -
.../werewolf/controller/AuthController.java | 149 -----
.../werewolf/controller/GameController.java | 211 -------
.../controller/SessionController.java | 52 --
.../werewolf/controller/SpeechController.java | 133 ----
.../werewolf/database/Database.java | 74 ---
.../database/documents/AuthSession.java | 57 --
.../werewolf/database/documents/Session.java | 264 --------
.../werewolf/database/documents/UserRole.java | 31 -
.../werewolf/listeners/ButtonListener.java | 152 -----
.../werewolf/listeners/GuildJoinListener.java | 60 --
.../listeners/MemberJoinListener.java | 30 -
.../werewolf/listeners/MessageListener.java | 114 ----
.../robothanzo/werewolf/model/Candidate.java | 64 --
.../werewolf/model/PoliceSession.java | 41 --
.../werewolf/model/SpeechOrder.java | 26 -
.../werewolf/model/SpeechSession.java | 31 -
.../security/GlobalWebSocketHandler.java | 83 ---
.../werewolf/security/SessionRepository.java | 14 -
.../security/annotations/CanManageGuild.java | 14 -
.../security/annotations/CanViewGuild.java | 14 -
.../werewolf/service/PoliceService.java | 60 --
.../service/impl/DiscordServiceImpl.java | 116 ----
.../service/impl/GameActionServiceImpl.java | 282 ---------
.../service/impl/GameSessionServiceImpl.java | 433 -------------
.../service/impl/PlayerServiceImpl.java | 247 --------
.../service/impl/PoliceServiceImpl.java | 380 ------------
.../service/impl/RoleServiceImpl.java | 312 ----------
.../service/impl/SpeechServiceImpl.java | 547 -----------------
.../robothanzo/werewolf/utils/CmdUtils.java | 84 ---
.../werewolf/utils/DiscordActionRunner.java | 89 ---
.../werewolf/utils/IdentityUtils.java | 53 --
.../robothanzo/werewolf/utils/MsgUtils.java | 46 --
.../werewolf/utils/SetupHelper.java | 222 -------
.../werewolf/WerewolfApplication.kt | 137 +++++
.../dev/robothanzo/werewolf/audio/Audio.kt | 46 ++
.../werewolf/audio/AudioPlayerSendHandler.kt | 23 +
.../werewolf/commands/DurationMapper.kt | 24 +
.../robothanzo/werewolf/commands/Player.kt | 166 +++++
.../dev/robothanzo/werewolf/commands/Poll.kt | 209 +++++++
.../robothanzo/werewolf/commands/Server.kt | 350 +++++++++++
.../robothanzo/werewolf/commands/Speech.kt | 114 ++++
.../werewolf/config/SecurityConfig.kt | 75 +++
.../werewolf/config/SessionConfig.kt | 22 +
.../werewolf/config/UserSessionFilter.kt | 40 ++
.../werewolf/config/WebSocketConfig.kt | 24 +
.../werewolf/controller/AuthController.kt | 169 ++++++
.../werewolf/controller/GameController.kt | 235 +++++++
.../werewolf/controller/SessionController.kt | 47 ++
.../werewolf/controller/SpeechController.kt | 138 +++++
.../robothanzo/werewolf/database/Database.kt | 56 ++
.../database/documents/AuthSession.kt | 53 ++
.../werewolf/database/documents/LogType.kt} | 30 +-
.../werewolf/database/documents/Session.kt | 227 +++++++
.../werewolf/database/documents/UserRole.kt | 23 +
.../werewolf/listeners/ButtonListener.kt | 155 +++++
.../werewolf/listeners/GuildJoinListener.kt | 57 ++
.../werewolf/listeners/MemberJoinListener.kt | 30 +
.../werewolf/listeners/MessageListener.kt | 113 ++++
.../robothanzo/werewolf/model/Candidate.kt | 46 ++
.../werewolf/model/PoliceSession.kt | 27 +
.../robothanzo/werewolf/model/SpeechOrder.kt | 26 +
.../werewolf/model/SpeechSession.kt | 17 +
.../security/GlobalWebSocketHandler.kt | 96 +++
.../werewolf/security/SessionRepository.kt | 12 +
.../security/annotations/CanManageGuild.kt | 8 +
.../security/annotations/CanViewGuild.kt | 8 +
.../werewolf/service/DiscordService.kt} | 24 +-
.../werewolf/service/GameActionService.kt} | 19 +-
.../werewolf/service/GameSessionService.kt} | 44 +-
.../werewolf/service/PlayerService.kt} | 24 +-
.../werewolf/service/PoliceService.kt | 103 ++++
.../werewolf/service/RoleService.kt} | 21 +-
.../werewolf/service/SpeechService.kt} | 58 +-
.../service/impl/DiscordServiceImpl.kt | 94 +++
.../service/impl/GameActionServiceImpl.kt | 461 ++++++++++++++
.../service/impl/GameSessionServiceImpl.kt | 400 ++++++++++++
.../service/impl/PlayerServiceImpl.kt | 248 ++++++++
.../service/impl/PoliceServiceImpl.kt | 554 +++++++++++++++++
.../werewolf/service/impl/RoleServiceImpl.kt | 314 ++++++++++
.../service/impl/SpeechServiceImpl.kt | 572 ++++++++++++++++++
.../dev/robothanzo/werewolf/utils/CmdUtils.kt | 75 +++
.../werewolf/utils/DiscordActionRunner.kt | 83 +++
.../werewolf/utils/IdentityUtils.kt | 51 ++
.../dev/robothanzo/werewolf/utils/MsgUtils.kt | 42 ++
.../robothanzo/werewolf/utils/SetupHelper.kt | 295 +++++++++
103 files changed, 6232 insertions(+), 6173 deletions(-)
create mode 100644 .idea/kotlinc.xml
delete mode 100644 .idea/modules/dev.robothanzo.werewolf.WerewolfHelper.main.iml
create mode 100644 build_output.txt
delete mode 100644 src/main/java/dev/robothanzo/werewolf/WerewolfApplication.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/audio/Audio.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/audio/AudioPlayerSendHandler.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/commands/DurationMapper.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/commands/Player.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/commands/Poll.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/commands/Server.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/commands/Speech.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/config/SecurityConfig.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/config/SessionConfig.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/config/UserSessionFilter.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/config/WebSocketConfig.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/controller/AuthController.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/controller/GameController.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/controller/SessionController.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/controller/SpeechController.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/database/Database.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/database/documents/AuthSession.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/database/documents/Session.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/database/documents/UserRole.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/listeners/ButtonListener.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/listeners/GuildJoinListener.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/listeners/MemberJoinListener.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/listeners/MessageListener.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/model/Candidate.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/model/PoliceSession.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/model/SpeechOrder.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/model/SpeechSession.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/security/GlobalWebSocketHandler.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/security/SessionRepository.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/security/annotations/CanManageGuild.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/security/annotations/CanViewGuild.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/service/PoliceService.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/service/impl/DiscordServiceImpl.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/service/impl/GameActionServiceImpl.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/service/impl/GameSessionServiceImpl.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/service/impl/PlayerServiceImpl.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/service/impl/PoliceServiceImpl.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/service/impl/RoleServiceImpl.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/service/impl/SpeechServiceImpl.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/utils/CmdUtils.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/utils/DiscordActionRunner.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/utils/IdentityUtils.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/utils/MsgUtils.java
delete mode 100644 src/main/java/dev/robothanzo/werewolf/utils/SetupHelper.java
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/WerewolfApplication.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/audio/Audio.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/audio/AudioPlayerSendHandler.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/commands/DurationMapper.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/commands/Player.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/commands/Poll.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/commands/Server.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/commands/Speech.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/config/SecurityConfig.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/config/SessionConfig.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/config/UserSessionFilter.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/config/WebSocketConfig.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/controller/AuthController.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/controller/GameController.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/controller/SessionController.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/controller/SpeechController.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/database/Database.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/database/documents/AuthSession.kt
rename src/main/{java/dev/robothanzo/werewolf/database/documents/LogType.java => kotlin/dev/robothanzo/werewolf/database/documents/LogType.kt} (57%)
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/database/documents/Session.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/database/documents/UserRole.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/listeners/ButtonListener.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/listeners/GuildJoinListener.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/listeners/MemberJoinListener.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/listeners/MessageListener.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/model/Candidate.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/model/PoliceSession.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/model/SpeechOrder.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/model/SpeechSession.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/security/GlobalWebSocketHandler.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/security/SessionRepository.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/security/annotations/CanManageGuild.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/security/annotations/CanViewGuild.kt
rename src/main/{java/dev/robothanzo/werewolf/service/DiscordService.java => kotlin/dev/robothanzo/werewolf/service/DiscordService.kt} (72%)
rename src/main/{java/dev/robothanzo/werewolf/service/GameActionService.java => kotlin/dev/robothanzo/werewolf/service/GameActionService.kt} (73%)
rename src/main/{java/dev/robothanzo/werewolf/service/GameSessionService.java => kotlin/dev/robothanzo/werewolf/service/GameSessionService.kt} (70%)
rename src/main/{java/dev/robothanzo/werewolf/service/PlayerService.java => kotlin/dev/robothanzo/werewolf/service/PlayerService.kt} (70%)
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/service/PoliceService.kt
rename src/main/{java/dev/robothanzo/werewolf/service/RoleService.java => kotlin/dev/robothanzo/werewolf/service/RoleService.kt} (72%)
rename src/main/{java/dev/robothanzo/werewolf/service/SpeechService.java => kotlin/dev/robothanzo/werewolf/service/SpeechService.kt} (50%)
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/service/impl/DiscordServiceImpl.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/service/impl/GameActionServiceImpl.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/service/impl/GameSessionServiceImpl.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/service/impl/PlayerServiceImpl.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/service/impl/PoliceServiceImpl.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/service/impl/RoleServiceImpl.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/service/impl/SpeechServiceImpl.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/utils/CmdUtils.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/utils/DiscordActionRunner.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/utils/IdentityUtils.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/utils/MsgUtils.kt
create mode 100644 src/main/kotlin/dev/robothanzo/werewolf/utils/SetupHelper.kt
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..9abf022
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 2dd28b9..8906e73 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -3,7 +3,6 @@
-
\ No newline at end of file
diff --git a/.idea/modules/dev.robothanzo.werewolf.WerewolfHelper.main.iml b/.idea/modules/dev.robothanzo.werewolf.WerewolfHelper.main.iml
deleted file mode 100644
index afc1871..0000000
--- a/.idea/modules/dev.robothanzo.werewolf.WerewolfHelper.main.iml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 4baaf3e..43b8b32 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,6 +2,8 @@ plugins {
java
id("org.springframework.boot") version "4.0.2"
id("io.spring.dependency-management") version "1.1.7"
+ kotlin("jvm") version "2.3.0"
+ kotlin("plugin.spring") version "2.3.0"
}
group = "dev.robothanzo.werewolf"
@@ -20,11 +22,15 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-websocket")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.mongodb:mongodb-spring-session:4.0.0-rc1")
-
+
+ // Kotlin
+ implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
+ implementation("org.jetbrains.kotlin:kotlin-reflect")
+
// Discord
implementation("net.dv8tion:JDA:6.3.0")
implementation("club.minnced:discord-webhooks:0.8.4")
- implementation("com.github.RobotHanzo:JDAInteractions:v0.1.4")
+ implementation("com.github.RobotHanzo:JDAInteractions:0.2.0")
implementation("com.github.Mokulu:discord-oauth2-api:1.0.4")
// JDA Audio supplements
@@ -54,6 +60,13 @@ tasks {
options.encoding = Charsets.UTF_8.name()
}
+ compileKotlin {
+ compilerOptions {
+ freeCompilerArgs.addAll("-Xjsr305=strict")
+ jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_25)
+ }
+ }
+
bootJar {
mainClass.set("dev.robothanzo.werewolf.WerewolfApplication")
}
diff --git a/build_output.txt b/build_output.txt
new file mode 100644
index 0000000..1c7fc6a
--- /dev/null
+++ b/build_output.txt
@@ -0,0 +1,26 @@
+WARNING: A restricted method in java.lang.System has been called
+WARNING: java.lang.System::load has been called by net.rubygrapefruit.platform.internal.NativeLibraryLoader in an unnamed module (file:/C:/Users/Nathan%20Lee/.gradle/wrapper/dists/gradle-9.3.1-bin/23ovyewtku6u96viwx3xl3oks/gradle-9.3.1/lib/native-platform-0.22-milestone-29.jar)
+WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
+WARNING: Restricted methods will be blocked in a future release unless native access is enabled
+
+Starting a Gradle Daemon (subsequent builds will be faster)
+> Task :checkKotlinGradlePluginConfigurationErrors SKIPPED
+
+> Task :compileKotlin FAILED
+e: file:///C:/Users/Nathan%20Lee/Coding%20Projects/Java%20Projects/WerewolfHelper/src/main/kotlin/dev/robothanzo/werewolf/commands/Poll.kt:45:17 Argument type mismatch: actual type is 'Runnable', but 'Function0?' was expected.
+
+FAILURE: Build failed with an exception.
+
+* What went wrong:
+Execution failed for task ':compileKotlin'.
+> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
+ > Compilation error. See log for more details
+
+* Try:
+> Run with --stacktrace option to get the stack trace.
+> Run with --info or --debug option to get more log output.
+> Run with --scan to get full insights from a Build Scan (powered by Develocity).
+> Get more help at https://help.gradle.org.
+
+BUILD FAILED in 35s
+1 actionable task: 1 executed
diff --git a/src/dashboard/src/features/game/hooks/useGameState.ts b/src/dashboard/src/features/game/hooks/useGameState.ts
index ef66cca..8a9283e 100644
--- a/src/dashboard/src/features/game/hooks/useGameState.ts
+++ b/src/dashboard/src/features/game/hooks/useGameState.ts
@@ -35,9 +35,6 @@ export const useGameState = (guildId: string | undefined, user: User | null) =>
const [showSessionExpired, setShowSessionExpired] = useState(false);
- // Helpers to update overlay
- const setOverlayVisible = (visible: boolean) => setOverlayState(prev => ({...prev, visible}));
-
// Helper to map session data to GameState players
const mapSessionToPlayers = (sessionData: any): Player[] => {
return sessionData.players.map((player: any) => ({
diff --git a/src/main/java/dev/robothanzo/werewolf/WerewolfApplication.java b/src/main/java/dev/robothanzo/werewolf/WerewolfApplication.java
deleted file mode 100644
index ac99992..0000000
--- a/src/main/java/dev/robothanzo/werewolf/WerewolfApplication.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package dev.robothanzo.werewolf;
-
-import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
-import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
-import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
-import dev.robothanzo.werewolf.service.DiscordService;
-import dev.robothanzo.werewolf.service.GameSessionService;
-import jakarta.annotation.PostConstruct;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import net.dv8tion.jda.api.JDA;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.scheduling.annotation.EnableScheduling;
-import org.springframework.stereotype.Component;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.zip.ZipEntry;
-
-@Slf4j
-@SpringBootApplication
-@EnableScheduling
-public class WerewolfApplication {
-
- public static final long AUTHOR = 466769036122783744L;
- public static final List SERVER_CREATORS = List.of(466769036122783744L, 616590798989033502L,
- 451672040227864587L);
- public static final List ROLES = List.of(
- "狼人", "女巫", "獵人", "預言家", "平民", "狼王", "狼美人", "白狼王", "夢魘", "混血兒", "守衛", "騎士", "白癡",
- "守墓人", "魔術師", "黑市商人", "邱比特", "盜賊", "石像鬼", "狼兄", "狼弟", "複製人", "血月使者", "惡靈騎士", "通靈師", "機械狼", "獵魔人");
-
- // Static bridges for legacy code
- public static JDA jda;
- public static GameSessionService gameSessionService;
- public static dev.robothanzo.werewolf.service.RoleService roleService;
- public static dev.robothanzo.werewolf.service.GameActionService gameActionService;
- public static dev.robothanzo.werewolf.service.PoliceService policeService;
- public static dev.robothanzo.werewolf.service.PlayerService playerService;
- public static dev.robothanzo.werewolf.service.SpeechService speechService;
- public static AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
-
- static void main(String[] args) {
- // Extract sounds before Spring starts
- extractSoundFiles();
- SpringApplication.run(WerewolfApplication.class, args);
- }
-
- // Component to populate static fields from Spring Context
- @Component
- @RequiredArgsConstructor
- static class StaticBridge {
- private final GameSessionService gameSessionServiceBean;
- private final DiscordService discordServiceBean;
- private final dev.robothanzo.werewolf.service.RoleService roleServiceBean;
- private final dev.robothanzo.werewolf.service.GameActionService gameActionServiceBean;
- private final dev.robothanzo.werewolf.service.PoliceService policeServiceBean;
- private final dev.robothanzo.werewolf.service.PlayerService playerServiceBean;
- private final dev.robothanzo.werewolf.service.SpeechService speechServiceBean;
-
- @PostConstruct
- public void init() {
- log.info("Initializing Static Bridge...");
- dev.robothanzo.werewolf.database.Database.initDatabase(); // Initialize legacy DB
- WerewolfApplication.gameSessionService = gameSessionServiceBean;
- WerewolfApplication.roleService = roleServiceBean;
- WerewolfApplication.gameActionService = gameActionServiceBean;
- WerewolfApplication.policeService = policeServiceBean;
- WerewolfApplication.playerService = playerServiceBean;
- WerewolfApplication.speechService = speechServiceBean;
- WerewolfApplication.jda = discordServiceBean.getJDA();
-
- // AudioPlayerManager setup if needed
- AudioSourceManagers.registerRemoteSources(playerManager);
- AudioSourceManagers.registerLocalSource(playerManager);
- }
- }
-
- public static void extractSoundFiles() {
- try {
- JarFile jarFile = new JarFile(
- new File(WerewolfApplication.class.getProtectionDomain().getCodeSource().getLocation().toURI()));
- File soundFolder = new File("sounds");
- if (!soundFolder.exists()) {
- soundFolder.mkdir();
- }
- // Logic to clean and extract (simplified from original to avoid full deletion
- // risk if not intent)
- // Original code deleted file if it was a file named "soundFolder".
-
- for (Enumeration em = jarFile.entries(); em.hasMoreElements(); ) {
- String s = em.nextElement().toString();
-
- if (s.startsWith(("sounds/")) && s.endsWith(".mp3")) {
- ZipEntry entry = jarFile.getEntry(s);
- File outputFile = new File(soundFolder, s.split("/")[s.split("/").length - 1]);
- // Only write if doesn't exist or explicit overwrite logic?
- // Legacy code overwrote.
- InputStream inStream = jarFile.getInputStream(entry);
- OutputStream out = new FileOutputStream(outputFile);
- int c;
- while ((c = inStream.read()) != -1) {
- out.write(c);
- }
- inStream.close();
- out.close();
- }
- }
- jarFile.close();
- } catch (Exception e) {
- log.warn("Failed to extract sound files (ignore if running in IDE): {}", e.getMessage());
- }
- }
-}
diff --git a/src/main/java/dev/robothanzo/werewolf/audio/Audio.java b/src/main/java/dev/robothanzo/werewolf/audio/Audio.java
deleted file mode 100644
index 5577a1c..0000000
--- a/src/main/java/dev/robothanzo/werewolf/audio/Audio.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package dev.robothanzo.werewolf.audio;
-
-import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
-import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
-import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
-import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
-import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
-import dev.robothanzo.werewolf.WerewolfApplication;
-import lombok.extern.slf4j.Slf4j;
-import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel;
-import net.dv8tion.jda.api.managers.AudioManager;
-
-import java.util.Locale;
-
-@Slf4j
-public class Audio {
- public static void play(Resource resource, AudioChannel channel) {
- try {
- AudioManager audioManager = channel.getGuild().getAudioManager();
- audioManager.openAudioConnection(channel);
- AudioPlayer player = WerewolfApplication.playerManager.createPlayer();
- audioManager.setSendingHandler(new AudioPlayerSendHandler(player));
- WerewolfApplication.playerManager.loadItem("sounds/" + resource + ".mp3", new AudioLoadResultHandler() {
- @Override
- public void trackLoaded(AudioTrack track) {
- player.startTrack(track, false);
- }
-
- @Override
- public void playlistLoaded(AudioPlaylist playlist) {
- }
-
- @Override
- public void noMatches() {
- }
-
- @Override
- public void loadFailed(FriendlyException exception) {
- log.error("Error while trying to load audio resource", exception);
- }
- });
- } catch (Exception e) {
- log.error("Error while trying to play sound resource", e);
- }
- }
-
- public enum Resource {
- EXPEL_POLL, POLICE_ENROLL, POLICE_POLL, TIMER_ENDED, ENROLL_10S_REMAINING, POLL_10S_REMAINING,
- TIMER_30S_REMAINING;
-
- @Override
- public String toString() {
- return super.toString().toLowerCase(Locale.ROOT);
- }
- }
-}
diff --git a/src/main/java/dev/robothanzo/werewolf/audio/AudioPlayerSendHandler.java b/src/main/java/dev/robothanzo/werewolf/audio/AudioPlayerSendHandler.java
deleted file mode 100644
index 49d3a66..0000000
--- a/src/main/java/dev/robothanzo/werewolf/audio/AudioPlayerSendHandler.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package dev.robothanzo.werewolf.audio;
-
-import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
-import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
-import net.dv8tion.jda.api.audio.AudioSendHandler;
-
-import java.nio.ByteBuffer;
-
-public class AudioPlayerSendHandler implements AudioSendHandler {
- private final AudioPlayer audioPlayer;
- private AudioFrame lastFrame;
-
- public AudioPlayerSendHandler(AudioPlayer audioPlayer) {
- this.audioPlayer = audioPlayer;
- }
-
- @Override
- public boolean canProvide() {
- lastFrame = audioPlayer.provide();
- return lastFrame != null;
- }
-
- @Override
- public ByteBuffer provide20MsAudio() {
- return ByteBuffer.wrap(lastFrame.getData());
- }
-
- @Override
- public boolean isOpus() {
- return true;
- }
-}
diff --git a/src/main/java/dev/robothanzo/werewolf/commands/DurationMapper.java b/src/main/java/dev/robothanzo/werewolf/commands/DurationMapper.java
deleted file mode 100644
index 393ac29..0000000
--- a/src/main/java/dev/robothanzo/werewolf/commands/DurationMapper.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package dev.robothanzo.werewolf.commands;
-
-import dev.robothanzo.jda.interactions.annotations.slash.options.IMapper;
-import dev.robothanzo.jda.interactions.annotations.slash.options.Mapper;
-
-import java.time.Duration;
-import java.util.Locale;
-
-@Mapper
-public class DurationMapper implements IMapper {
- @Override
- public Class getSourceType() {
- return String.class;
- }
-
- @Override
- public Class getTargetType() {
- return Duration.class;
- }
-
- @Override
- public Duration map(Object source) {
- String strDuration = source.toString().replaceAll("\\s+", "").replaceFirst("(\\d+d)", "P$1T");
- strDuration = strDuration.charAt(0) != 'P' ? "PT" + strDuration.replace("min", "m")
- : strDuration.replace("min", "m");
- return Duration.parse(strDuration.toUpperCase(Locale.ROOT));
- }
-}
diff --git a/src/main/java/dev/robothanzo/werewolf/commands/Player.java b/src/main/java/dev/robothanzo/werewolf/commands/Player.java
deleted file mode 100644
index 9b608eb..0000000
--- a/src/main/java/dev/robothanzo/werewolf/commands/Player.java
+++ /dev/null
@@ -1,536 +0,0 @@
-package dev.robothanzo.werewolf.commands;
-
-import dev.robothanzo.jda.interactions.annotations.slash.Command;
-import dev.robothanzo.jda.interactions.annotations.slash.Subcommand;
-import dev.robothanzo.jda.interactions.annotations.slash.options.Option;
-import dev.robothanzo.werewolf.WerewolfApplication;
-import dev.robothanzo.werewolf.database.documents.Session;
-import dev.robothanzo.werewolf.utils.CmdUtils;
-import dev.robothanzo.werewolf.utils.MsgUtils;
-import lombok.Builder;
-import lombok.Data;
-import lombok.extern.slf4j.Slf4j;
-import net.dv8tion.jda.api.EmbedBuilder;
-import net.dv8tion.jda.api.components.actionrow.ActionRow;
-import net.dv8tion.jda.api.components.buttons.Button;
-import net.dv8tion.jda.api.components.selections.EntitySelectMenu;
-import net.dv8tion.jda.api.entities.*;
-import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.EntitySelectInteractionEvent;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-
-import static com.mongodb.client.model.Filters.eq;
-import static com.mongodb.client.model.Updates.set;
-
-@Command
-@Slf4j
-public class Player {
- public static Map transferPoliceSessions = new HashMap<>(); // key is guild ID
-
- public static void transferPolice(Session session, Guild guild, Session.Player player,
- @Nullable Runnable callback) {
- if (player.isPolice()) {
- assert player.getUserId() != null;
- transferPoliceSessions.put(guild.getIdLong(), TransferPoliceSession.builder()
- .guildId(guild.getIdLong())
- .senderId(player.getUserId())
- .callback(callback)
- .build());
- EntitySelectMenu.Builder selectMenu = EntitySelectMenu
- .create("selectNewPolice", EntitySelectMenu.SelectTarget.USER)
- .setMinValues(1)
- .setMaxValues(1);
- for (Session.Player p : session.fetchAlivePlayers().values()) {
- assert p.getUserId() != null;
- if (Objects.equals(p.getUserId(), player.getUserId()))
- continue;
- User user = WerewolfApplication.jda.getUserById(p.getUserId());
- assert user != null;
- transferPoliceSessions.get(guild.getIdLong()).getPossibleRecipientIds().add(p.getUserId());
- }
- Message message = Objects
- .requireNonNull(guild.getTextChannelById(session.getCourtTextChannelId())).sendMessageEmbeds(
- new EmbedBuilder()
- .setTitle("移交警徽").setColor(MsgUtils.getRandomColor())
- .setDescription("請選擇要移交警徽的對象,若要撕掉警徽,請按下撕毀按鈕\n請在30秒內做出選擇,否則警徽將被自動撕毀").build())
- .setComponents(ActionRow.of(selectMenu.build()),
- ActionRow.of(Button.success("confirmNewPolice", "移交"),
- Button.danger("destroyPolice", "撕毀")))
- .complete();
- CmdUtils.schedule(() -> {
- if (transferPoliceSessions.remove(guild.getIdLong()) != null) {
- message.reply("警徽已自動撕毀").queue();
- }
- }, 30000);
- }
- if (callback != null)
- callback.run();
- }
-
- public static boolean playerDied(Session session, Member user, boolean lastWords, boolean isExpelled) { // returns
- // whether
- // the
- // action
- // succeeded
- Guild guild = Objects.requireNonNull(WerewolfApplication.jda.getGuildById(session.getGuildId()));
- Role spectatorRole = Objects.requireNonNull(guild.getRoleById(session.getSpectatorRoleId()));
- for (Map.Entry player : new LinkedList<>(session.getPlayers().entrySet())) {
- if (Objects.equals(user.getIdLong(), player.getValue().getUserId())) {
-
- // Check if already fully dead
- if (!player.getValue().isAlive()) {
- return false;
- }
-
- assert player.getValue().getRoles() != null;
-
- // Soft kill logic: Find first alive role and kill it
- List roles = player.getValue().getRoles();
- List deadRoles = player.getValue().getDeadRoles();
- if (deadRoles == null) {
- deadRoles = new ArrayList<>();
- player.getValue().setDeadRoles(deadRoles);
- }
-
- String killedRole = null;
- for (String role : roles) {
- long totalCount = roles.stream().filter(r -> r.equals(role)).count();
- long deadCount = deadRoles.stream().filter(r -> r.equals(role)).count();
-
- if (deadCount < totalCount) {
- killedRole = role;
- deadRoles.add(role);
- break;
- }
- }
-
- // Persist the dead role update immediately to ensure consistency
- Session.fetchCollection().updateOne(eq("guildId", session.getGuildId()),
- set("players", session.getPlayers()));
-
- // Log the death
- if (killedRole != null) {
- Map metadata = new HashMap<>();
- metadata.put("playerId", player.getValue().getId());
- metadata.put("playerName", player.getValue().getNickname());
- metadata.put("killedRole", killedRole);
- metadata.put("isExpelled", isExpelled);
- session.addLog(dev.robothanzo.werewolf.database.documents.LogType.PLAYER_DIED,
- player.getValue().getNickname() + " 的 " + killedRole + " 身份已死亡",
- metadata);
-
- // Send message to court channel
- TextChannel courtChannel = guild.getTextChannelById(session.getCourtTextChannelId());
- if (courtChannel != null) {
- courtChannel
- .sendMessage("**:skull: " + user.getAsMention() + " 已死亡**")
- .queue();
- }
- }
-
- // Check game ended logic with the newly killed role
- Session.Result result = session.hasEnded(killedRole);
- if (result != Session.Result.NOT_ENDED) {
- TextChannel channel = guild.getTextChannelById(session.getSpectatorTextChannelId());
- String judgePing = "<@&" + session.getJudgeRoleId() + "> ";
- if (channel != null) {
- if (result == Session.Result.WOLVES_DIED) {
- channel.sendMessage(judgePing + "遊戲結束,**好**人獲勝,原因:" + result.getReason()).queue();
- } else {
- channel.sendMessage(judgePing + "遊戲結束,**狼**人獲勝,原因:" + result.getReason()).queue();
- }
- lastWords = false;
- }
- }
-
- // Check if player is still alive (has remaining roles)
- if (player.getValue().isAlive()) {
- // Calculate remaining roles for the message
- List remainingRoles = new ArrayList<>(player.getValue().getRoles());
- if (player.getValue().getDeadRoles() != null) {
- for (String deadRole : player.getValue().getDeadRoles()) {
- remainingRoles.remove(deadRole);
- }
- }
- String remainingRoleName = remainingRoles.isEmpty() ? "未知" : remainingRoles.getFirst();
-
- // Not fully dead, passed out one role
- Objects.requireNonNull(guild.getTextChannelById(player.getValue().getChannelId()))
- .sendMessage("因為你死了,所以你的角色變成了 " + remainingRoleName).queue();
- Session.fetchCollection().updateOne(eq("guildId", session.getGuildId()),
- set("players", session.getPlayers()));
- if (lastWords) {
- WerewolfApplication.speechService.startLastWordsSpeech(guild,
- session.getCourtTextChannelId(),
- player.getValue(), null);
- }
- return true;
- }
-
- // Fully dead logic
- String finalKilledRole = killedRole;
- Runnable die = () -> transferPolice(session, guild, player.getValue(), () -> {
- var newSession = CmdUtils.getSession(guild); // We need to update the session as it may have been
- // tampered with by transferPolice
- if (newSession == null)
- return;
-
- // We need to fetch the updated player object from the new session to make sure
- // we have latest police status etc.
- // But assume player state is managed by references or we need to re-fetch.
- // For safety, let's use the object we have but ensure police status is false if
- // transferred.
- // Actually transferPolice callback runs AFTER transfer.
-
- if (player.getValue().isIdiot() && isExpelled) {
- player.getValue().getDeadRoles().remove(finalKilledRole);
-
- newSession.getPlayers().put(player.getKey(), player.getValue());
- Session.fetchCollection().updateOne(eq("guildId", newSession.getGuildId()),
- set("players", newSession.getPlayers()));
- WerewolfApplication.gameSessionService.broadcastSessionUpdate(newSession);
- Objects.requireNonNull(guild.getTextChannelById(newSession.getCourtTextChannelId()))
- .sendMessage(user.getAsMention() + " 是白癡,所以他會待在場上並繼續發言").queue();
- } else {
- guild.modifyMemberRoles(user, spectatorRole).queue();
- Session.fetchCollection().updateOne(eq("guildId", newSession.getGuildId()),
- set("players", newSession.getPlayers()));
- player.getValue().updateNickname(user);
- WerewolfApplication.gameSessionService.broadcastSessionUpdate(newSession);
- }
- });
-
- if (lastWords) {
- WerewolfApplication.speechService.startLastWordsSpeech(guild,
- session.getCourtTextChannelId(),
- player.getValue(), die);
- } else {
- die.run();
- }
- return true;
- }
- }
- guild.addRoleToMember(user, spectatorRole).queue(); // if they aren't found, they will become spectators
- user.modifyNickname("[旁觀] " + user.getEffectiveName()).queue();
- return true;
- }
-
- public static boolean playerRevived(Session session, Member user, String roleToRevive) {
- Guild guild = Objects.requireNonNull(WerewolfApplication.jda.getGuildById(session.getGuildId()));
- Role spectatorRole = Objects.requireNonNull(guild.getRoleById(session.getSpectatorRoleId()));
-
- for (Map.Entry player : session.getPlayers().entrySet()) {
- if (Objects.equals(user.getIdLong(), player.getValue().getUserId())) {
- List deadRoles = player.getValue().getDeadRoles();
- if (deadRoles == null || !deadRoles.contains(roleToRevive)) {
- return false; // Role is not dead or invalid
- }
-
- // Check if player WAS fully dead before this revival
- boolean wasFullyDead = !player.getValue().isAlive();
-
- // Revive the role
- deadRoles.remove(roleToRevive);
-
- // Update session immediately
- Session.fetchCollection().updateOne(eq("guildId", session.getGuildId()),
- set("players", session.getPlayers()));
-
- // Log the revival
- Map metadata = new HashMap<>();
- metadata.put("playerId", player.getValue().getId());
- metadata.put("playerName", player.getValue().getNickname());
- metadata.put("revivedRole", roleToRevive);
- session.addLog(dev.robothanzo.werewolf.database.documents.LogType.PLAYER_REVIVED,
- player.getValue().getNickname() + " 的 " + roleToRevive + " 身份已復活",
- metadata);
-
- // Handle transition from Dead -> Alive
- if (wasFullyDead) {
- guild.removeRoleFromMember(user, spectatorRole).queue();
- }
- player.getValue().updateNickname(user);
-
- // Calculate remaining roles for message
- List remainingRoles = new ArrayList<>(player.getValue().getRoles());
- for (String deadRole : deadRoles) {
- remainingRoles.remove(deadRole);
- }
- String currentRoleName = remainingRoles.isEmpty() ? "未知" : remainingRoles.getFirst();
-
- // Restore Role Logic using Session values
- long roleId = player.getValue().getRoleId();
- if (roleId != 0) {
- Role role = guild.getRoleById(roleId);
- if (role != null) {
- guild.addRoleToMember(user, role).queue();
- }
- }
-
- // Send notification
- TextChannel channel = guild.getTextChannelById(player.getValue().getChannelId());
- if (channel != null) {
- channel.sendMessage("因為你復活了,所以你的角色變成了 " + currentRoleName).queue();
- }
-
- // Broadcast updates after all changes including nickname
- WerewolfApplication.gameSessionService.broadcastSessionUpdate(session);
- return true;
- }
- }
- return false;
- }
-
- public static void selectNewPolice(EntitySelectInteractionEvent event) {
- if (transferPoliceSessions.containsKey(Objects.requireNonNull(event.getGuild()).getIdLong())) {
- Member target = event.getMentions().getMembers().getFirst();
- TransferPoliceSession session = transferPoliceSessions
- .get(Objects.requireNonNull(event.getGuild()).getIdLong());
- if (!session.getPossibleRecipientIds().contains(target.getIdLong())) {
- event.reply(":x: 你不能移交警徽給這個人").setEphemeral(true).queue();
- } else {
- if (session.getSenderId() == event.getUser().getIdLong()) {
- Session guildSession = Session.fetchCollection().find(eq("guildId", event.getGuild().getIdLong()))
- .first();
- if (guildSession == null)
- return;
- for (var player : guildSession.getPlayers().values()) {
- if (Objects.requireNonNull(player.getUserId()) == target.getIdLong()) {
- session.setRecipientId(player.getId());
- event.reply(":white_check_mark: 請按下移交來完成移交動作").setEphemeral(true).queue();
- break;
- }
- }
- } else {
- event.reply(":x: 你不是原本的警長").setEphemeral(true).queue();
- }
- }
- }
- }
-
- @dev.robothanzo.jda.interactions.annotations.Button
- public static void confirmNewPolice(ButtonInteractionEvent event) {
- if (transferPoliceSessions.containsKey(Objects.requireNonNull(event.getGuild()).getIdLong())) {
- TransferPoliceSession session = transferPoliceSessions
- .get(Objects.requireNonNull(event.getGuild()).getIdLong());
- if (session.getSenderId() == event.getUser().getIdLong()) {
- if (session.getRecipientId() != null) {
- Session.fetchCollection().updateOne(eq("guildId", event.getGuild().getIdLong()),
- set("players." + session.getRecipientId() + ".police", true));
- log.info("Transferred police to " + session.getRecipientId() + " in guild "
- + event.getGuild().getIdLong());
- transferPoliceSessions.remove(event.getGuild().getIdLong());
-
- // Update Recipient Nickname
- Session.Player recipientPlayer = Objects.requireNonNull(CmdUtils.getSession(event)).getPlayers()
- .get(session.getRecipientId().toString());
- recipientPlayer.setPolice(true);
- Long recipientDiscordId = recipientPlayer.getUserId();
- if (recipientDiscordId != null) {
- Member recipient = event.getGuild().getMemberById(recipientDiscordId);
- if (recipient != null) {
- recipientPlayer.updateNickname(recipient);
- event.reply(":white_check_mark: 警徽已移交給 " + recipient.getAsMention()).queue();
- }
- }
-
- // Update Sender Nickname
- Member sender = event.getGuild().getMemberById(session.getSenderId());
- Session.fetchCollection()
- .updateOne(eq("guildId", event.getGuild().getIdLong()),
- set("players."
- + Objects.requireNonNull(CmdUtils.getSession(event)).getPlayers().entrySet()
- .stream()
- .filter(e -> Objects.equals(e.getValue().getUserId(),
- session.getSenderId()))
- .findFirst().get().getKey()
- + ".police", false));
-
- if (sender != null) {
- // We need the player object for sender to correctly regenerate name (e.g. if
- // they are dead?)
- // Usually transfer logic happens when dead, but could be alive transfer.
- var senderEntry = Objects.requireNonNull(CmdUtils.getSession(event)).getPlayers().entrySet()
- .stream().filter(e -> Objects.equals(e.getValue().getUserId(), session.getSenderId()))
- .findFirst();
- if (senderEntry.isPresent()) {
- Session.Player senderPlayer = senderEntry.get().getValue();
- senderPlayer.setPolice(false);
- senderPlayer.updateNickname(sender);
- }
- }
- if (session.getCallback() != null)
- session.getCallback().run();
- } else {
- event.reply(":x: 請先選擇要移交警徽的對象").setEphemeral(true).queue();
- }
- } else {
- event.reply(":x: 你不是原本的警長").setEphemeral(true).queue();
- }
- }
- }
-
- @dev.robothanzo.jda.interactions.annotations.Button
- public static void destroyPolice(ButtonInteractionEvent event) {
- if (transferPoliceSessions.containsKey(Objects.requireNonNull(event.getGuild()).getIdLong())) {
- TransferPoliceSession session = transferPoliceSessions
- .get(Objects.requireNonNull(event.getGuild()).getIdLong());
- if (session.getSenderId() == event.getUser().getIdLong()) {
- transferPoliceSessions.remove(event.getGuild().getIdLong());
- event.reply(":white_check_mark: 警徽已撕毀").setEphemeral(false).queue();
- if (session.getCallback() != null)
- session.getCallback().run();
- } else {
- event.reply(":x: 你不是原本的警長").setEphemeral(true).queue();
- }
- }
- }
-
- @dev.robothanzo.jda.interactions.annotations.Button
- public void changeRoleOrder(ButtonInteractionEvent event) {
- if (event.getGuild() == null)
- return;
- event.deferReply().queue();
- Session session = CmdUtils.getSession(event);
- if (session == null)
- return;
-
- for (Session.Player player : session.getPlayers().values()) {
- if (Objects.equals(event.getUser().getIdLong(), player.getUserId())) {
- try {
- WerewolfApplication.playerService.switchRoleOrder(event.getGuild().getIdLong(),
- String.valueOf(player.getId()));
- event.getHook().editOriginal(":white_check_mark: 交換成功").queue();
- } catch (Exception e) {
- event.getHook().editOriginal(":x: " + e.getMessage()).queue();
- }
- return;
- }
- }
- event.getHook().editOriginal(":x: 你不是玩家").queue();
- }
-
- @Subcommand(description = "升官為法官")
- public void judge(SlashCommandInteractionEvent event, @Option(value = "user") User user) {
- event.deferReply().queue();
- if (!CmdUtils.isAdmin(event))
- return;
- Session session = CmdUtils.getSession(event);
- if (session == null)
- return;
- Objects.requireNonNull(event.getGuild()).addRoleToMember(
- Objects.requireNonNull(event.getGuild().getMemberById(user.getId())),
- Objects.requireNonNull(event.getGuild().getRoleById(session.getJudgeRoleId()))).queue();
- event.getHook().editOriginal(":white_check_mark:").queue();
- }
-
- @Subcommand(description = "貶官為庶民")
- public void demote(SlashCommandInteractionEvent event, @Option(value = "user") User user) {
- event.deferReply().queue();
- if (!CmdUtils.isAdmin(event))
- return;
- Session session = CmdUtils.getSession(event);
- if (session == null)
- return;
- Objects.requireNonNull(event.getGuild()).removeRoleFromMember(
- Objects.requireNonNull(event.getGuild().getMemberById(user.getId())),
- Objects.requireNonNull(event.getGuild().getRoleById(session.getJudgeRoleId()))).queue();
- event.getHook().editOriginal(":white_check_mark:").queue();
- }
-
- @Subcommand(description = "使玩家成為死人/旁觀者")
- public void died(SlashCommandInteractionEvent event, @Option(value = "user", description = "死掉的使用者") User user,
- @Option(value = "last_words", description = "是否讓他講遺言 (預設為否) (若為雙身分,只會在兩張牌都死掉的時候啟動)", optional = true) Boolean lastWords) {
- event.deferReply().queue();
- if (!CmdUtils.isAdmin(event))
- return;
- Session session = CmdUtils.getSession(event);
- if (session == null)
- return;
- if (lastWords == null)
- lastWords = false;
- Member member = Objects.requireNonNull(Objects.requireNonNull(event.getGuild()).getMemberById(user.getId()));
-
- if (playerDied(session, member, lastWords, false)) {
- event.getHook().editOriginal(":white_check_mark:").queue();
- } else {
- event.getHook().editOriginal(":x: 使用者已經死了").queue();
- }
- }
-
- @Subcommand(description = "指派玩家編號並傳送身分")
- public void assign(SlashCommandInteractionEvent event) {
- event.deferReply().queue();
- if (!CmdUtils.isAdmin(event))
- return;
- Session session = CmdUtils.getSession(event);
- if (session == null)
- return;
-
- try {
- WerewolfApplication.roleService.assignRoles(event.getGuild().getIdLong(),
- msg -> log.info("[Assign] " + msg),
- p -> {
- });
- event.getHook().editOriginal(":white_check_mark: 身分分配完成!").queue();
- } catch (Exception e) {
- event.getHook().editOriginal(":x: " + e.getMessage()).queue();
- }
- }
-
- @Subcommand(description = "列出每個玩家的身分資訊")
- public void roles(SlashCommandInteractionEvent event) {
- event.deferReply().queue();
- if (!CmdUtils.isAdmin(event))
- return;
- Session session = CmdUtils.getSession(event);
- if (session == null)
- return;
- EmbedBuilder embedBuilder = new EmbedBuilder()
- .setTitle("身分列表")
- .setColor(MsgUtils.getRandomColor());
- for (String p : session.getPlayers().keySet().stream().sorted(MsgUtils.getAlphaNumComparator()).toList()) {
- Session.Player player = session.getPlayers().get(p);
- assert player.getRoles() != null;
- embedBuilder.addField(player.getNickname(),
- String.join("、", player.getRoles()) + (player.isPolice() ? " (警長)" : "") +
- (player.isJinBaoBao() ? " (金寶寶)" : player.isDuplicated() ? " (複製人)" : ""),
- true);
- }
- event.getHook().editOriginalEmbeds(embedBuilder.build()).queue();
- }
-
- @Subcommand(description = "強制某人成為警長 (將會清除舊的警長)")
- public void force_police(SlashCommandInteractionEvent event,
- @Option(value = "user", description = "要強制成為警長的玩家") User user) {
- event.deferReply().queue();
- if (!CmdUtils.isAdmin(event))
- return;
- if (event.getGuild() == null)
- return;
-
- try {
- WerewolfApplication.gameActionService.setPolice(event.getGuild().getIdLong(), user.getIdLong());
- event.getHook().editOriginal(":white_check_mark:").queue();
- } catch (Exception e) {
- event.getHook().editOriginal(":x: " + e.getMessage()).queue();
- }
- }
-
- @Data
- @Builder
- public static class TransferPoliceSession {
- private long guildId;
- private long senderId;
- @Builder.Default
- private List possibleRecipientIds = new ArrayList<>();
- @Nullable
- private Integer recipientId; // 1 / 2 / 3..etc
- @Nullable
- private Runnable callback;
- }
-}
diff --git a/src/main/java/dev/robothanzo/werewolf/commands/Poll.java b/src/main/java/dev/robothanzo/werewolf/commands/Poll.java
deleted file mode 100644
index fc6b5b4..0000000
--- a/src/main/java/dev/robothanzo/werewolf/commands/Poll.java
+++ /dev/null
@@ -1,187 +0,0 @@
-package dev.robothanzo.werewolf.commands;
-
-import dev.robothanzo.jda.interactions.annotations.slash.Command;
-import dev.robothanzo.jda.interactions.annotations.slash.Subcommand;
-import dev.robothanzo.werewolf.WerewolfApplication;
-import dev.robothanzo.werewolf.audio.Audio;
-import dev.robothanzo.werewolf.database.documents.Session;
-import dev.robothanzo.werewolf.model.Candidate;
-import dev.robothanzo.werewolf.utils.CmdUtils;
-import dev.robothanzo.werewolf.utils.MsgUtils;
-import lombok.extern.slf4j.Slf4j;
-import net.dv8tion.jda.api.EmbedBuilder;
-import net.dv8tion.jda.api.components.actionrow.ActionRow;
-import net.dv8tion.jda.api.components.buttons.Button;
-import net.dv8tion.jda.api.entities.Member;
-import net.dv8tion.jda.api.entities.Message;
-import net.dv8tion.jda.api.entities.User;
-import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-
-@Command
-@Slf4j
-public class Poll {
- public static Map> expelCandidates = new ConcurrentHashMap<>(); // key is guild id //
- // second key is
- // candidate id
-
- public static void handleExpelPK(Session session, GuildMessageChannel channel, Message message,
- List winners) {
- if (message != null)
- message.reply("平票,請PK").queue();
- Map newCandidates = new ConcurrentHashMap<>();
- for (Candidate winner : winners) {
- winner.getElectors().clear();
- winner.setExpelPK(true);
- newCandidates.put(winner.getPlayer().getId(), winner);
- }
- expelCandidates.put(channel.getGuild().getIdLong(), newCandidates);
- WerewolfApplication.speechService.startSpeechPoll(channel.getGuild(), message,
- newCandidates.values().stream().map(Candidate::getPlayer).toList(),
- () -> startExpelPoll(session, channel, false));
- }
-
- public static void startExpelPoll(Session session, GuildMessageChannel channel, boolean allowPK) {
- Audio.play(Audio.Resource.EXPEL_POLL, channel.getGuild().getVoiceChannelById(session.getCourtVoiceChannelId()));
- EmbedBuilder embedBuilder = new EmbedBuilder().setTitle("驅逐投票")
- .setDescription("30秒後立刻計票,請加快手速!\n若要改票可直接按下要改成的對象\n若要改為棄票需按下原本投給的使用者")
- .setColor(MsgUtils.getRandomColor());
- List