diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 0c6b2d8..5e3b662 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +fun k(v: String) = project.property(v) as String + plugins { kotlin("jvm") version "1.9.23" kotlin("plugin.serialization") version "1.9.23" // 添加序列化插件 @@ -7,8 +9,8 @@ plugins { id("com.github.johnrengelman.shadow") version "8.0.0" // fat jar } -group = project.property("project_group") as String -version = project.property("project_version") as String +group = k("project_group") +version = k("project_version") repositories { @@ -58,6 +60,16 @@ repositories { // 协程 implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + implementation("org.apache.commons:commons-lang3:3.17.0") + implementation("com.google.guava:guava:33.3.0-jre") + + //DG_Lab 依赖库导入 + implementation("io.netty:netty-all:4.1.109.Final") + implementation("com.google.code.gson:gson:2.10.1") + implementation(files("libs/DgLab-common-${k("dg_lab_version")}.jar")) + + //生成 二维码 + implementation("com.google.zxing:core:[3.5.3,)") // 测试 testImplementation(kotlin("test")) diff --git a/gradle.properties b/gradle.properties index 60a346b..28b6dbe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,5 @@ org.gradle.downloadSources=false org.gradle.parallel=true org.gradle.degree_of_parallelism=16 project_group=top.r3944realms.ltdmanager -project_version=1.6-SNAPSHOT +project_version=1.10-SNAPSHOT +dg_lab_version=4.2.11.18 diff --git a/libs/DgLab-common-4.2.11.18.jar b/libs/DgLab-common-4.2.11.18.jar new file mode 100644 index 0000000..548593c Binary files /dev/null and b/libs/DgLab-common-4.2.11.18.jar differ diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/core/config/DgLabConfig.kt b/src/main/kotlin/top/r3944realms/ltdmanager/core/config/DgLabConfig.kt new file mode 100644 index 0000000..add2ff5 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/core/config/DgLabConfig.kt @@ -0,0 +1,130 @@ +package top.r3944realms.ltdmanager.core.config + +import top.r3944realms.ltdmanager.utils.CryptoUtil +import top.r3944realms.ltdmanager.utils.YamlUpdater + + +data class DgLabConfig( + var wsServer: WsServerConfig = WsServerConfig(), + var dgLabClient: DgLabClientConfig = DgLabClientConfig(), + var pulseData: PulseDataConfig = PulseDataConfig(), + var commandText: CommandTextConfig = CommandTextConfig(), + var replyText: ReplyTextConfig = ReplyTextConfig(), + var debug: DebugConfig = DebugConfig() +) { + data class WsServerConfig( + var localServerUrl: String = "0.0.0.0", + var localServerPort: Int = 4567, + var localServerPublishUrl: String = "ws://127.0.0.1:4567", + var localServerSecure: Boolean = false, + var localServerSslCert: String = "", + var localServerSslKey: String = "", + var encryptedLocalServerSslPassword: String? = null + ) { + val decryptedLocalServerSslPassword: String? + get() { + if (encryptedLocalServerSslPassword == null) return null + if (!isEncrypted()) return encryptedLocalServerSslPassword + return try { + val cipherText = encryptedLocalServerSslPassword!!.substring(4, encryptedLocalServerSslPassword!!.length - 1) + CryptoUtil.decrypt(cipherText) + } catch (e: Exception) { + throw IllegalStateException("localServerSslPassword 解密失败", e) + } + } + + fun encryptPassword() { + if (encryptedLocalServerSslPassword == null || isEncrypted()) return + try { + encryptedLocalServerSslPassword = "ENC(${CryptoUtil.encrypt(encryptedLocalServerSslPassword!!)})" + YamlUpdater.updateYaml( + YamlConfigLoader.configFilePath.toString(), + "dg-lab.ws-server.encrypted-local-server-ssl-password", + encryptedLocalServerSslPassword!! + ) + } catch (e: Exception) { + throw IllegalStateException("SSL 密码加密失败", e) + } + } + + private fun isEncrypted(): Boolean { + return encryptedLocalServerSslPassword != null && + encryptedLocalServerSslPassword!!.startsWith("ENC(") && + encryptedLocalServerSslPassword!!.endsWith(")") + } + //TODO: 添加有效性检测 + fun validate() { + require(localServerUrl.isNotBlank()) { "localServerUrl 未配置" } + require(localServerPort > 0) { "localServerPort 必须大于 0" } + require(localServerPublishUrl.isNotBlank()) { "localServerPublishUrl 未配置" } + if (localServerSecure) { + require(localServerSslCert.isNotBlank()) { "启用 SSL 时必须配置 localServerSslCert" } + require(localServerSslKey.isNotBlank()) { "启用 SSL 时必须配置 localServerSslKey" } + } + } + } + + data class DgLabClientConfig( + var bindTimeout: Double = 90.0, + var registerTimeout: Double = 30.0 + ) + + data class PulseDataConfig( + var customPulseData: String = "data/dg-lab-play/customPulseData.json", + var durationPerPost: Double = 8.0, + var postInterval: Double = 1.0, + var sleepAfterClear: Double = 0.5 + ) { + fun validate(maxLength: Double) { + require(durationPerPost <= maxLength * 0.1) { "PulseDataConfig.durationPerPost 超出最大时长" } + } + } + + data class CommandTextConfig( + var appendPulse: String = "增加波形", + var currentPulse: String = "当前波形", + var currentStrength: String = "当前强度", + var decreaseStrength: String = "减小强度", + var dgLabDeviceJoin: String = "绑定郊狼", + var exitGame: String = "退出游戏", + var increaseStrength: String = "加大强度", + var randomPulse: String = "随机波形", + var randomStrength: String = "随机强度", + var resetPulse: String = "重置波形", + var showPlayers: String = "当前玩家", + var showPulses: String = "可用波形", + var usage: String = "郊狼玩法" + ) + + data class ReplyTextConfig( + var bindTimeout: String = "绑定超时", + var currentPlayers: String = "当前玩家:", + var currentPulse: String = "当前波形循环为:【{}】", + var currentStrength: String = "A通道:{0}/{1} B通道:{2}/{3}", + var failedToCreateClient: String = "创建 DG-Lab 控制终端失败", + var failedToFetchStrengthInfo: String = "获取通道强度状态失败", + var failedToFetchStrengthLimit: String = "获取通道强度上限失败,控制失败", + var gameExited: String = "已退出游戏", + var invalidPulseParam: String = "波形参数错误,控制失败", + var invalidStrengthParam: String = "强度参数错误,控制失败", + var invalidTarget: String = "目标玩家不存在或郊狼 App 未绑定", + var noAvailablePulse: String = "无可用波形", + var noPlayer: String = "当前没有已连接的玩家,你可以绑定试试~", + var notBindYet: String = "你目前没有绑定 DG-Lab App", + var pleaseAtTarget: String = "使用命令的同时请 @ 想要控制的玩家", + var pleaseScanQrcode: String = "请用 DG-Lab App 扫描二维码以连接", + var pleaseSetPulseFirst: String = "请先设置郊狼波形:{}", + var pulsesEmpty: String = "当前波形循环为空", + var successfullyBind: String = "绑定成功,可以开始色色了!", + var successfullyDecreased: String = "郊狼强度减小了 {}%", + var successfullyIncreased: String = "郊狼强度加强了 {}%!", + var successfullySetPulse: String = "郊狼波形成功设置为【{}】!", + var successfullySetToStrength: String = "郊狼强度成功设置为 {}%!" + ) + + data class DebugConfig( + var enableDebug: Boolean = false, + var ideHost: String = "127.0.0.1", + var idePort: Int = 5678 + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/core/config/YamlConfigLoader.kt b/src/main/kotlin/top/r3944realms/ltdmanager/core/config/YamlConfigLoader.kt index 4847206..306ce2a 100644 --- a/src/main/kotlin/top/r3944realms/ltdmanager/core/config/YamlConfigLoader.kt +++ b/src/main/kotlin/top/r3944realms/ltdmanager/core/config/YamlConfigLoader.kt @@ -34,6 +34,7 @@ object YamlConfigLoader { config?.mail?.encryptPassword() config?.tools?.rcon?.encryptPassword() config?.blessingSkinServer?.invitationApi?.encryptToken() + config?.dgLab?.wsServer?.encryptPassword() } private fun loadConfig(): ConfigWrapper { if (!Files.exists(configFilePath)) { @@ -76,6 +77,7 @@ object YamlConfigLoader { fun loadToolConfig(): ToolConfig = config.tools fun loadMailConfig(): MailConfig = config.mail fun loadBlessingSkinServerConfig(): BlessingSkinServerConfig = config.blessingSkinServer + fun loadDgLabConfig(): DgLabConfig = config.dgLab data class ConfigWrapper( var database: DatabaseConfig = DatabaseConfig(), var crypto: CryptoConfig = CryptoConfig(), @@ -85,6 +87,7 @@ object YamlConfigLoader { var tools: ToolConfig = ToolConfig(), var mail: MailConfig = MailConfig(), var blessingSkinServer: BlessingSkinServerConfig = BlessingSkinServerConfig(), + var dgLab: DgLabConfig = DgLabConfig(), ) } \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/ClientManager.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/ClientManager.kt new file mode 100644 index 0000000..faff82f --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/ClientManager.kt @@ -0,0 +1,51 @@ +package top.r3944realms.ltdmanager.dglab.manager + +import com.r3944realms.dg_lab.manager.DGPBClientManager + +class ClientManager( + private val clients: MutableMap = mutableMapOf(), +) : IManager> { + + /** + * 添加单例客户端管理示例 + * @param key 唯一标识客户端管理的 key,比如 ID 或 name + */ + fun addClient(key: String, client: DGPBClientManager) { + clients[key] = client + } + + /** + * 移除单例客户端管理实例 + */ + fun removeClient(key: String) { + clients.remove(key)?.stop() + } + + /** + * 根据 key 获取客户端 + */ + fun getClient(key: String): DGPBClientManager? { + return clients[key] + } + + /** + * 启动所有客户端 + */ + override fun startAll() { + clients.values.forEach { it.start() } + } + + /** + * 停止所有客户端 + */ + override fun stopAll() { + clients.values.forEach { it.stop() } + } + + /** + * 获取内部 Map 实例 + */ + override fun getInstance(): MutableMap { + return clients + } +} diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/DgLabManager.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/DgLabManager.kt new file mode 100644 index 0000000..81147d5 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/DgLabManager.kt @@ -0,0 +1,102 @@ +package top.r3944realms.ltdmanager.dglab.manager + +import com.r3944realms.dg_lab.api.operation.ClientOperation +import com.r3944realms.dg_lab.api.operation.ServerOperation +import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketClientRole +import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketServerRole +import com.r3944realms.dg_lab.manager.DGPBClientManager +import com.r3944realms.dg_lab.manager.DGPBServerManager +import com.r3944realms.dg_lab.websocket.PowerBoxWSClient +import com.r3944realms.dg_lab.websocket.PowerBoxWSServer +import com.r3944realms.dg_lab.websocket.sharedData.ClientPowerBoxSharedData +import com.r3944realms.dg_lab.websocket.sharedData.ServerPowerBoxSharedData +import top.r3944realms.ltdmanager.core.config.YamlConfigLoader +import kotlin.io.path.Path + +/** + * 全局DG_Lab单例管理器 + */ +object DgLabManager { + // 可空,延迟初始化 + var serverManager: ServerManager? = null + private set + + var clientManager: ClientManager? = null + private set + + + fun createServerManager(operation: ServerOperation): DGPBServerManager { + val loadDgLabConfig = YamlConfigLoader.loadDgLabConfig() + val boxWSServer = PowerBoxWSServer.Builder.getBuilder() + .port(loadDgLabConfig.wsServer.localServerPort) + .role(WebSocketServerRole("Se-IC")) + .operation(operation) + .sharedData(ServerPowerBoxSharedData()) + .build() + if (loadDgLabConfig.wsServer.localServerSecure) { + boxWSServer.enableSSL(Path(loadDgLabConfig.wsServer.localServerSslCert).toFile(), Path(loadDgLabConfig.wsServer.localServerSslKey).toFile(), loadDgLabConfig.wsServer.decryptedLocalServerSslPassword) + } + val dgpbServerManager = DGPBServerManager(boxWSServer) + return dgpbServerManager + } + + /** + * 初始化 服务器管理类 + */ + fun initServerManager(server: DGPBServerManager) { + serverManager = ServerManager(server) + } + /** + * 初始化 客户端管理类 + */ + fun initClientManager() { + clientManager = ClientManager() + } + + /** + * 添加 客户端管理类 + */ + fun addClient(key: String, client: DGPBClientManager) { + clientManager?.addClient(key, client) + } + + /** + * 移除 客户端管理类 + */ + fun removeClient(key: String) { + clientManager?.removeClient(key) + } + + /** + * 获取 客户端管理类 + */ + fun getClient(key: String): DGPBClientManager? { + return clientManager?.getClient(key) + } + /** + * 获取 & 创建 客户端管理类 + */ + fun getClientOrCreate(key: String, operation: ClientOperation): DGPBClientManager { + val client = getClient(key) + if (client == null) { + val loadDgLabConfig = YamlConfigLoader.loadDgLabConfig() + val boxWSClient = PowerBoxWSClient.Builder.getBuilder() + .address(loadDgLabConfig.wsServer.localServerUrl) + .port(loadDgLabConfig.wsServer.localServerPort) + .role(WebSocketClientRole("QQ-$key")) + .operation(operation) + .sharedData(ClientPowerBoxSharedData()) + .build() + + if (loadDgLabConfig.wsServer.localServerSecure) { + boxWSClient.enableSSL() + } + val clientManager = DGPBClientManager( + boxWSClient + ) + this.clientManager?.addClient(key, clientManager) + return clientManager + } + return client + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/IManager.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/IManager.kt new file mode 100644 index 0000000..a6555f9 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/IManager.kt @@ -0,0 +1,7 @@ +package top.r3944realms.ltdmanager.dglab.manager + +interface IManager { + fun startAll() + fun stopAll() + fun getInstance(): T? +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/ServerManager.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/ServerManager.kt new file mode 100644 index 0000000..1971e10 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/manager/ServerManager.kt @@ -0,0 +1,43 @@ +package top.r3944realms.ltdmanager.dglab.manager + +import com.r3944realms.dg_lab.api.manager.IDGLabManager +import com.r3944realms.dg_lab.api.manager.Status +import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData +import com.r3944realms.dg_lab.manager.DGPBServerManager + +class ServerManager( + private val server: DGPBServerManager +) : IManager, IDGLabManager { + + override fun startAll() { + start() + } + + override fun stopAll() { + stop() + } + + override fun start() { + server.start() + } + + override fun stop() { + server.stop() + } + + override fun getSharedData(): ISharedData { + return server.sharedData + } + + override fun getStatus(): Status { + return server.status + } + + override fun setStatus(p0: Status?) { + server.status = p0 + } + + override fun getInstance(): DGPBServerManager { + return server + } +} diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/GameClientOperation.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/GameClientOperation.kt new file mode 100644 index 0000000..1d5cc11 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/GameClientOperation.kt @@ -0,0 +1,61 @@ +package top.r3944realms.ltdmanager.dglab.model.game + +import com.r3944realms.dg_lab.api.operation.ClientOperation +import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData + +class GameClientOperation( + val player: Player +) : ClientOperation { + + override fun ClientStartingHandler() { + println("Player ${player.id} is starting the client...") + } + + override fun ClientStartedHandler() { + println("Player ${player.id} client started successfully.") + } + + override fun ClientStartingErrorHandler() { + println("Player ${player.id} failed to start client!") + } + + override fun ClientStoppingHandler() { + println("Player ${player.id} is stopping the client...") + } + + override fun ClientStoppingErrorHandler() { + println("Player ${player.id} encountered an error while stopping.") + } + + override fun ClientStoppedHandler() { + println("Player ${player.id} client stopped.") + } + + override fun QrCodeUrlHandler(p0: String?) { + println("Player ${player.id} QR code received: $p0") + } + + override fun ShowQrCodeHandler() { + println("Player ${player.id} should display QR code.") + } + + override fun ConnectSuccessfulNoticeHandler() { + println("Player ${player.id} connected successfully.") + } + + override fun DisconnectHandler(p0: PowerBoxData?) { + println("Player ${player.id} disconnected: $p0") + } + + override fun ErrorHandler(p0: PowerBoxData?) { + println("Player ${player.id} error occurred: $p0") + } + + override fun HeartBeatHandler(p0: PowerBoxData?) { + println("Heartbeat from player ${player.id}: $p0") + } + + override fun OtherMessageHandler(p0: PowerBoxData?) { + println("Other message for player ${player.id}: $p0") + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/GameServerOperation.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/GameServerOperation.kt new file mode 100644 index 0000000..e5a46ac --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/GameServerOperation.kt @@ -0,0 +1,5 @@ +package top.r3944realms.ltdmanager.dglab.model.game + +import com.r3944realms.dg_lab.websocket.handler.server.DefaultServerOperation + +class GameServerOperation : DefaultServerOperation() \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/Player.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/Player.kt new file mode 100644 index 0000000..7defc55 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/game/Player.kt @@ -0,0 +1,8 @@ +package top.r3944realms.ltdmanager.dglab.model.game + +/** + * 玩家类,目前仅包含一个 ID + */ +data class Player( + val id: String +) \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/CustomPulseDataConverter.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/CustomPulseDataConverter.kt new file mode 100644 index 0000000..39f0dd8 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/CustomPulseDataConverter.kt @@ -0,0 +1,56 @@ +package top.r3944realms.ltdmanager.dglab.model.pulseware + +import com.r3944realms.dg_lab.api.message.data.PulseWave +import com.r3944realms.dg_lab.api.message.data.PulseWaveList + + +object CustomPulseDataConverter { + /** + * 将自定义波形数据转换为 PulseWaveList + * + * @param customPulseData Map, List[][]>> + * 每个 int[][] 包含两个长度为 4 的 int 数组,第一个是 frequencies,第二个是 strengths + * @return Map, PulseWaveList> + */ + fun convert(customPulseData: Map>>): Map { + val pulseWaveLists: MutableMap = HashMap() + + for ((name, operations) in customPulseData) { + val waveList = PulseWaveList() + waveList.name = name + + for (op in operations) { + val freqs = op[0] + val strengths = op[1] + + // 确保每个数组长度为4 + require(!(freqs.size != 4 || strengths.size != 4)) { "每个波形段必须包含 4 个频率和 4 个强度值" } + + val wave = PulseWave.fromArrays(freqs, strengths) + waveList.add(wave) + } + + pulseWaveLists[name] = waveList + } + + return pulseWaveLists + } + fun PulseWave.toSerializable(): PulseWaveSerializable = + PulseWaveSerializable(f1(), f2(), f3(), f4(), s1(), s2(), s3(), s4()) + + fun PulseWaveSerializable.toPulseWave(): PulseWave = + PulseWave.fromArrays( + intArrayOf(f1, f2, f3, f4), + intArrayOf(s1, s2, s3, s4) + ) + + fun PulseWaveList.toSerializable(): PulseWaveListSerializable = + PulseWaveListSerializable(name, list.map { it.toSerializable() }.toMutableList()) + + fun PulseWaveListSerializable.toPulseWaveList(): PulseWaveList { + val listObj = PulseWaveList() + listObj.setName(name) + list.forEach { listObj.add(it.toPulseWave()) } + return listObj + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/DefaultPulseData.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/DefaultPulseData.kt new file mode 100644 index 0000000..c4266a9 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/DefaultPulseData.kt @@ -0,0 +1,313 @@ +package top.r3944realms.ltdmanager.dglab.model.pulseware + +import com.r3944realms.dg_lab.api.message.data.PulseWave +import com.r3944realms.dg_lab.api.message.data.PulseWaveList + +object DefaultPulseData { + + fun allPulseWaveLists(): Map { + return mapOf( + "呼吸" to Breath, + "潮汐" to Tide, + "连击" to Combo, + "快速按捏" to FastPinch, + "按捏渐强" to PinchGradual, + "心跳节奏" to Heartbeat, + "压缩" to Compress, + "节奏步伐" to RhythmStep, + "颗粒摩擦" to GranularFriction, + "渐变弹跳" to GradualBounce, + "波浪涟漪" to WaveRipple, + "雨水冲刷" to RainWash, + "变速敲击" to SpeedHit, + "信号灯" to SignalLight, + "挑逗1" to Tease1, + "挑逗2" to Tease2 + ) + } + + val Breath: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "呼吸" + + // 每段频率和强度 + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 5, 10, 20)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(20, 25, 30, 40)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(40, 45, 50, 60)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(60, 65, 70, 80)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(0, 0, 0, 0), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(0, 0, 0, 0), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(0, 0, 0, 0), intArrayOf(0, 0, 0, 0)) + ) + + // 转成 PulseWave 并加入列表 + for (seg in segments) { + list.add(PulseWave.fromArrays(seg[0], seg[1])) + } + + list + } + val Tide: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "潮汐" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 4, 8, 17)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(17, 21, 25, 33)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(50, 50, 50, 50)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(50, 54, 58, 67)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(67, 71, 75, 83)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 98, 96, 92)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(92, 90, 88, 84)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(84, 82, 80, 76)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(68, 68, 68, 68)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + + val Combo: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "连击" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 92, 84, 67)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(67, 58, 50, 33)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 1)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(2, 2, 2, 2)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + val FastPinch: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "快速按捏" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(0, 0, 0, 0), intArrayOf(0, 0, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + val PinchGradual: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "按捏渐强" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(29, 29, 29, 29)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(52, 52, 52, 52)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(2, 2, 2, 2)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(73, 73, 73, 73)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(87, 87, 87, 87)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + + val Heartbeat: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "心跳节奏" + val segments = listOf( + arrayOf(intArrayOf(110, 110, 110, 110), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(110, 110, 110, 110), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(75, 75, 75, 75)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(75, 77, 79, 83)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(83, 85, 88, 92)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + val Compress: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "压缩" + val segments = listOf( + arrayOf(intArrayOf(25, 25, 24, 24), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(24, 23, 23, 23), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(22, 22, 22, 21), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(21, 21, 20, 20), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(20, 19, 19, 19), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(18, 18, 18, 17), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(17, 16, 16, 16), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(15, 15, 15, 14), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(14, 14, 13, 13), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(13, 12, 12, 12), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(11, 11, 11, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + val RhythmStep: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "节奏步伐" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 5, 10, 20)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(20, 25, 30, 40)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(40, 45, 50, 60)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(60, 65, 70, 80)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 6, 12, 25)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(25, 31, 38, 50)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(50, 56, 62, 75)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 8, 16, 33)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(33, 42, 50, 67)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 12, 25, 50)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + + val GranularFriction: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "颗粒摩擦" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + + val GradualBounce: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "渐变弹跳" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(1, 1, 1, 1)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(1, 9, 18, 34)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(34, 42, 50, 67)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(0, 0, 0, 0), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(0, 0, 0, 0), intArrayOf(0, 0, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + val WaveRipple: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "波浪涟漪" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(1, 1, 1, 1)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(1, 3, 7, 13)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(13, 25, 40, 60)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(60, 75, 90, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(50, 50, 50, 50)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + + val RainWash: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "雨水冲刷" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 5, 15, 30)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(40, 50, 60, 70)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(80, 90, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + + val SpeedHit: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "变速敲击" + val segments = listOf( + arrayOf(intArrayOf(15, 15, 15, 15), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(20, 20, 20, 20), intArrayOf(50, 50, 50, 50)), + arrayOf(intArrayOf(25, 25, 25, 25), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(20, 20, 20, 20), intArrayOf(50, 50, 50, 50)), + arrayOf(intArrayOf(15, 15, 15, 15), intArrayOf(0, 0, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + val SignalLight: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "信号灯" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 0, 0, 0)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 100, 100, 100)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + + val Tease1: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "挑逗1" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 30, 60, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 70, 40, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } + + val Tease2: PulseWaveList by lazy { + val list = PulseWaveList() + list.name = "挑逗2" + val segments = listOf( + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(0, 50, 100, 100)), + arrayOf(intArrayOf(10, 10, 10, 10), intArrayOf(100, 50, 0, 0)) + ) + segments.forEach { list.add(PulseWave.fromArrays(it[0], it[1])) } + list + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveClassTransform.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveClassTransform.kt new file mode 100644 index 0000000..5f1d25e --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveClassTransform.kt @@ -0,0 +1,4 @@ +package top.r3944realms.ltdmanager.dglab.model.pulseware + +class PulseWaveClassTransform { +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveJsonIO.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveJsonIO.kt new file mode 100644 index 0000000..187e7c9 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveJsonIO.kt @@ -0,0 +1,29 @@ +package top.r3944realms.ltdmanager.dglab.model.pulseware + +import com.r3944realms.dg_lab.api.message.data.PulseWaveList +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import top.r3944realms.ltdmanager.dglab.model.pulseware.CustomPulseDataConverter.toPulseWaveList +import top.r3944realms.ltdmanager.dglab.model.pulseware.CustomPulseDataConverter.toSerializable +import java.io.File + +object PulseWaveJsonIO { + private val json = Json { + prettyPrint = true + encodeDefaults = true + } + + fun saveToFile(map: Map, file: File) { + val serializableMap = map.mapValues { it.value.toSerializable() } + file.writeText(json.encodeToString(serializableMap)) + } + + fun loadFromFile(file: File): Map { + if (!file.exists()) return emptyMap() + val type = MapSerializer(String.serializer(), PulseWaveListSerializable.serializer()) + val data: Map = json.decodeFromString(type, file.readText()) + return data.mapValues { it.value.toPulseWaveList() } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveListSerializable.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveListSerializable.kt new file mode 100644 index 0000000..faa5bb2 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveListSerializable.kt @@ -0,0 +1,9 @@ +package top.r3944realms.ltdmanager.dglab.model.pulseware + +import kotlinx.serialization.Serializable + +@Serializable +data class PulseWaveListSerializable( + var name: String = "", + val list: MutableList = mutableListOf() +) \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveSerializable.kt b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveSerializable.kt new file mode 100644 index 0000000..af912d4 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/dglab/model/pulseware/PulseWaveSerializable.kt @@ -0,0 +1,9 @@ +package top.r3944realms.ltdmanager.dglab.model.pulseware + +import kotlinx.serialization.Serializable + +@Serializable +data class PulseWaveSerializable( + val f1: Int, val f2: Int, val f3: Int, val f4: Int, + val s1: Int, val s2: Int, val s3: Int, val s4: Int +) \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/main.kt b/src/main/kotlin/top/r3944realms/ltdmanager/main.kt index e655696..525a10e 100644 --- a/src/main/kotlin/top/r3944realms/ltdmanager/main.kt +++ b/src/main/kotlin/top/r3944realms/ltdmanager/main.kt @@ -23,6 +23,7 @@ fun main() = GlobalManager.runBlockingMain { ) val helpModule = HelpModule( moduleName = "WhiteListGroup", + keywords = listOf("help", "帮助"), groupMessagePollingModule = groupMsgPollingModule, selfId = selfQQId, selfNickName = selfNickName, @@ -83,16 +84,18 @@ fun main() = GlobalManager.runBlockingMain { moduleName = "WhiteListGroup", groupMessagePollingModule = groupMsgPollingModule, selfId = selfQQId, - commandPrefixList = listOf("口球", "mute", "杂鱼三九"), + adminsId = listOf(1283411677), + muteCommandPrefixList = listOf("口球", "mute", "Mute", "禁言"), + unmuteCommandPrefixList = listOf("解禁", "unmute", "Unmute", "解除禁言"), minBanMinutes = 1, maxBanMinutes = 15, ) - val modGroupHandlerModule = ModGroupHandlerModule( - moduleName = "ModGroup", - targetGroupId = 339340846, - answers = listOf("戏鸢", "一只戏鸢", "折戏鸢", "LostInLinearPast", "lostinlinearpast"), - pollIntervalMillis = 15_000L, - ) +// val modGroupHandlerModule = ModGroupHandlerModule( +// moduleName = "ModGroup", +// targetGroupId = 339340846, +// answers = listOf("戏鸢", "一只戏鸢", "折戏鸢", "LostInLinearPast", "lostinlinearpast"), +// pollIntervalMillis = 15_000L, +// ) // 注册模块到全局模块管理器 GlobalManager.moduleManager.registerModule(groupModule) @@ -103,7 +106,7 @@ fun main() = GlobalManager.runBlockingMain { GlobalManager.moduleManager.registerModule(invitationCodesModule) GlobalManager.moduleManager.registerModule(helpModule) GlobalManager.moduleManager.registerModule(banModule) - GlobalManager.moduleManager.registerModule(modGroupHandlerModule) +// GlobalManager.moduleManager.registerModule(modGroupHandlerModule) // 加载模块 GlobalManager.moduleManager.loadModule(groupModule.name) @@ -114,5 +117,5 @@ fun main() = GlobalManager.runBlockingMain { GlobalManager.moduleManager.loadModule(invitationCodesModule.name) GlobalManager.moduleManager.loadModule(helpModule.name) GlobalManager.moduleManager.loadModule(banModule.name) - GlobalManager.moduleManager.loadModule(modGroupHandlerModule.name) +// GlobalManager.moduleManager.loadModule(modGroupHandlerModule.name) } \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/module/BanModule.kt b/src/main/kotlin/top/r3944realms/ltdmanager/module/BanModule.kt index 3aac270..c61f853 100644 --- a/src/main/kotlin/top/r3944realms/ltdmanager/module/BanModule.kt +++ b/src/main/kotlin/top/r3944realms/ltdmanager/module/BanModule.kt @@ -6,12 +6,15 @@ import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import top.r3944realms.ltdmanager.module.common.CommandParser import top.r3944realms.ltdmanager.module.common.filter.TriggerMessageFilter -import top.r3944realms.ltdmanager.module.common.filter.type.CommandFilter import top.r3944realms.ltdmanager.module.common.filter.type.IgnoreSelfFilter +import top.r3944realms.ltdmanager.module.common.filter.type.MultiCommandFilter import top.r3944realms.ltdmanager.module.common.filter.type.NewMessageFilter import top.r3944realms.ltdmanager.napcat.data.ID import top.r3944realms.ltdmanager.napcat.data.MessageElement +import top.r3944realms.ltdmanager.napcat.data.MessageType +import top.r3944realms.ltdmanager.napcat.event.group.GetGroupShutListEvent import top.r3944realms.ltdmanager.napcat.event.message.GetFriendMsgHistoryEvent +import top.r3944realms.ltdmanager.napcat.request.group.GetGroupShutListRequest import top.r3944realms.ltdmanager.napcat.request.group.SetGroupBanRequest import top.r3944realms.ltdmanager.napcat.request.other.SendGroupMsgRequest import top.r3944realms.ltdmanager.utils.LoggerUtil @@ -25,13 +28,18 @@ class BanModule( moduleName: String, private val groupMessagePollingModule : GroupMessagePollingModule, private val selfId: Long, - commandPrefixList: List = listOf("/mute"), // 默认命令前缀 + private val adminsId: List = listOf(), + muteCommandPrefixList: List = listOf("mute"), // 默认命令前缀 + unmuteCommandPrefixList: List = listOf("unmute"), private val minBanMinutes: Int = 1, - private val maxBanMinutes: Int = 15 + private val maxBanMinutes: Int = 15, + private val factorX: Int = 2, // 系数 x,禁言倍数 + ) : BaseModule("BanModule", moduleName), PersistentState { - private val commandParser = CommandParser(commandPrefixList) - private val commandFilter = CommandFilter(commandParser) + private val banCommandParse = CommandParser(muteCommandPrefixList) + private val pardonCommandParse = CommandParser(unmuteCommandPrefixList) + private val multiCommandFilter = MultiCommandFilter(listOf(banCommandParse, pardonCommandParse)) private val stateFile: File = getStateFileInternal("command_ban_state.json", name) private val stateBackupFile: File = getStateFileInternal("command_ban_state.json.bak", name) private var banState = loadState() @@ -44,7 +52,7 @@ class BanModule( NewMessageFilter { userId -> banState.getLastTriggerTime(userId) to banState.getLastTriggerRealId(userId) }, - commandFilter + multiCommandFilter ) ) } @@ -75,6 +83,7 @@ class BanModule( val filtered = triggerFilter.filter(messages) for (msg in filtered) { processBanCommand(msg) + processUnBanCommand(msg) } } /** @@ -88,45 +97,160 @@ class BanModule( seg.data.qq?.let { "@${it}" } ?: (seg.data.text ?: "") }.trim() } + /** + * 从消息段中提取所有被 @ 的用户 ID + */ + private fun GetFriendMsgHistoryEvent.SpecificMsg.getMentionedUserIds(): List { + return this.message + .filter { it.type == MessageType.At && it.data.qq != null } + .mapNotNull { it.data.qq } + .distinctBy { + when (it) { + is ID.StringValue -> it.value + is ID.LongValue -> it.value + } + } + } + private suspend fun processUnBanCommand(msg: GetFriendMsgHistoryEvent.SpecificMsg) { + try { + pardonCommandParse.parseCommand(msg.plainText()) ?: return + // 获取所有被 @ 的用户 + val mentionedUserIds = msg.getMentionedUserIds().map { + when (it) { + is ID.StringValue -> it.value.toLong() + is ID.LongValue -> it.value + } + } // List + val send = + napCatClient.send(GetGroupShutListRequest(ID.long(groupMessagePollingModule.targetGroupId))) + val muteList = send.data.map { it.uin.toLong() } + for (target in mentionedUserIds) { + if(target !in muteList) { + sendGroupMessage("❌ 目标用户未被禁言", + msg.realId + ) + } else { + banUser(ID.long(target), groupMessagePollingModule.targetGroupId, 0) + sendGroupMessage( + "✅ 已解禁对方@(${target})", + msg.realId + ) + } + + } + + // 更新状态 + banState = banState.updateLastTrigger(msg.userId, msg.realId, msg.time) + saveState(banState) + } catch (e: Exception) { + LoggerUtil.logger.error("[$name] 执行解禁言指令失败", e) + sendGroupMessage("❌ 执行解禁言失败,请检查解指令格式或权限", msg.realId) + banState = banState.updateLastTrigger(msg.sender.userId, msg.realId, msg.time) + saveState(banState) + } + } private suspend fun processBanCommand(msg: GetFriendMsgHistoryEvent.SpecificMsg) { try { - val parsed = commandParser.parseCommand(msg.plainText()) ?: return - val (command, argument) = parsed + val parsed = banCommandParse.parseCommand(msg.plainText()) ?: return + val (_, argument) = parsed - // 参数格式: [分钟] - // 示例:/mute 5 → 自己禁言 5 分钟 - // /mute → 自己随机禁言 val parts = argument.split(" ").filter { it.isNotBlank() } + // 解析禁言时间 val durationMinutes = parts.getOrNull(0)?.toIntOrNull() ?: Random.nextInt(minBanMinutes, maxBanMinutes + 1) val durationSeconds = durationMinutes.coerceIn(minBanMinutes, maxBanMinutes) * 60 - val targetUserId = msg.sender.userId + // 获取所有被 @ 的用户 + val mentionedUserIds = msg.getMentionedUserIds() // List + val targets = mentionedUserIds.ifEmpty { listOf(ID.long(msg.sender.userId)) } - banUser(targetUserId, groupMessagePollingModule.targetGroupId, durationSeconds) - sendGroupMessage("✅ 你已被禁言 $durationMinutes 分钟", msg.realId) + for (target in targets) { + val targetLongId = when (target) { + is ID.StringValue -> target.value.toLong() + is ID.LongValue -> target.value + } - // 更新状态(保证状态保存正确) - // 禁言成功后更新状态 - banState = banState.updateLastTrigger(targetUserId, msg.realId, msg.time) + // 权限检查:非管理员不能禁言他人 + if (mentionedUserIds.isNotEmpty() && mentionedUserIds.size != 1 && msg.sender.userId !in adminsId) { + sendGroupMessage("❌ 你没有权限禁言使用禁言多用户功能", msg.realId) + continue + } + + // 禁言机器人跳过 + if (targetLongId == selfId) { + sendGroupMessage("❌ 你没有权限禁言机器人", msg.realId) + continue + } + if (targetLongId in adminsId) { + sendGroupMessage("❌ 不支持禁言管理员", msg.realId) + continue + } + + // 单 @ 且非自己,可能触发反禁自己 + if (mentionedUserIds.size == 1 && targetLongId != msg.sender.userId && msg.sender.userId !in adminsId) { + val dice = Random.nextInt(1, 7) // 1~6 + val chance = when (dice) { + 6 -> 100 + 5 -> 80 + 4 -> 60 + 3 -> 50 + 2 -> 20 + 1 -> 0 + else -> 0 + } + + val selfDuration = durationSeconds * factorX + if (Random.nextInt(100) < chance) { + // 触发反禁自己 + banUser(ID.long(msg.sender.userId), groupMessagePollingModule.targetGroupId, selfDuration) + sendGroupMessage( + "⚠️ 骰子点数: $dice, 成功概率: ${chance}% → 失败,你触发了反禁,禁言 ${selfDuration / 60} 分钟", + msg.realId + ) + } else { + // 未触发反禁自己,禁言目标 + banUser(target, groupMessagePollingModule.targetGroupId, durationSeconds) + sendGroupMessage( + "✅ 骰子点数: $dice, 成功概率: ${chance}% → 成功禁言 <@${targetLongId}>", + msg.realId + ) + } + } else { + // 多 @ 或管理员操作,直接禁言目标 + banUser(target, groupMessagePollingModule.targetGroupId, durationSeconds) + sendGroupMessage( + if (targetLongId == msg.sender.userId) { + "✅ 你已被禁言 ${durationSeconds/ 60} 分钟" + } else { + "✅ 已禁言 <@${targetLongId}> ${durationSeconds/ 60} 分钟" + }, + msg.realId + ) + } + + + } + // 更新状态 + banState = banState.updateLastTrigger(msg.userId, msg.realId, msg.time) saveState(banState) - } catch (e: Exception) { LoggerUtil.logger.error("[$name] 执行禁言指令失败", e) sendGroupMessage("❌ 执行禁言失败,请检查指令格式或权限", msg.realId) + banState = banState.updateLastTrigger(msg.sender.userId, msg.realId, msg.time) + saveState(banState) } } - private suspend fun banUser(userId: Long, groupId: Long, seconds: Int) { + + private suspend fun banUser(userId: ID, groupId: Long, seconds: Int) { val request = SetGroupBanRequest( duration = seconds.toDouble(), groupId = ID.long(groupId), - userId = ID.long(userId) + userId = userId ) napCatClient.sendUnit(request) LoggerUtil.logger.info("[$name] 已对用户 $userId 执行 $seconds 秒禁言") } - private suspend fun sendGroupMessage(text: String, replyTo: Long? = null) { val request = SendGroupMsgRequest( MessageElement.reply(ID.long(replyTo ?: 0), text), @@ -136,20 +260,36 @@ class BanModule( } override fun info(): String { - return "[$name] 指令禁言模块:用户发送 ${commandParser.getCommands().joinToString("、")} 来禁言自己," + - "支持指定分钟数或随机分钟数,范围 $minBanMinutes-$maxBanMinutes 分钟。" + return buildString { + append("[$name] 指令禁言模块:\n") + append(" - 用户发送 ${banCommandParse.getCommands().joinToString("、")} 来禁言自己或指定其他用户(需管理员权限)。\n") + append(" - 支持指定禁言分钟数或随机分钟数,范围 $minBanMinutes-$maxBanMinutes 分钟。\n") + append(" - 支持对单个 @ 用户禁言,有概率反禁自己(骰子点数决定概率)。\n") + append(" - 管理员可以禁言其他用户;非管理员尝试多个禁言对象会收到无权限提示。\n") + append(" - 用户发送 ${pardonCommandParse.getCommands().joinToString("、")} 来解禁指定用户。\n") + append(" - 仅支持对单个 @ 用户解禁言。\n") + } } override fun help(): String { return buildString { appendLine("📖 [$name] 使用帮助:") - appendLine(" - ${commandParser.getCommands().joinToString("、")} [分钟]") - appendLine(" · 不写分钟数 → 随机禁言 (范围 $minBanMinutes-$maxBanMinutes 分钟)") - appendLine(" · 写分钟数 → 自己禁言指定分钟数") - appendLine() + appendLine("指令格式:${banCommandParse.getCommands().joinToString("、")} [分钟] [@用户...]") appendLine("示例:") - appendLine(" - /mute → 随机禁言自己") - appendLine(" - /mute 5 → 禁言自己 5 分钟") + appendLine(" - <指令> → 随机禁言自己") + appendLine(" - <指令> 5 → 禁言自己 5 分钟") + appendLine(" - <指令> 4 @User123 → 禁言指定用户 4 分钟(可能失败)") + appendLine(" - <指令> 4 @User123 @User22 → 禁言指定多用户 4 分钟(需在程序管理员列表中)") + appendLine() + appendLine("⚠️ 特殊说明:") + appendLine(" - 如果 @ 单个用户且执行者非需在程序管理员,有 y% 概率触发反禁自己,") + appendLine(" 骰子点数决定概率:6 → 100%, 5 → 80%, 4 → 60%, 3 → 50%, 2 → 20%, 1 → 0%") + appendLine(" - 禁言机器人自身不会生效") + appendLine(" - 禁言状态会自动保存以便下次使用") + appendLine() + appendLine("指令格式:${pardonCommandParse.getCommands().joinToString("、")} [@用户]") + appendLine("示例:") + appendLine(" - <指令> @User123 → 解禁指定用户") } } diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/module/DGLabModule.kt b/src/main/kotlin/top/r3944realms/ltdmanager/module/DGLabModule.kt new file mode 100644 index 0000000..35950ac --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/module/DGLabModule.kt @@ -0,0 +1,14 @@ +package top.r3944realms.ltdmanager.module + +class DGLabModule( + moduleName: String, +): + BaseModule("DGLabModule", moduleName) { + override fun onLoad() { + + } + + override suspend fun onUnload() { + + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/module/ModGroupHandlerModule.kt b/src/main/kotlin/top/r3944realms/ltdmanager/module/ModGroupHandlerModule.kt index b972695..cf5d0ae 100644 --- a/src/main/kotlin/top/r3944realms/ltdmanager/module/ModGroupHandlerModule.kt +++ b/src/main/kotlin/top/r3944realms/ltdmanager/module/ModGroupHandlerModule.kt @@ -187,7 +187,7 @@ class ModGroupHandlerModule( 📝 尝试答案: ${ "\n" + record.reason.joinToString("\n") { " • $it" }} - ⚠️ 提示:请仔细阅读文档后再在群里提问,否则你会失去你的大脑🧠 + ⚠️ 提示:请仔细阅读群文档后再在群里提问,否则你会失去你的大脑🧠 """.trimIndent() } else { """ @@ -198,7 +198,7 @@ class ModGroupHandlerModule( 🔹 最终评分:SSS ⭐ 💡 该用户尚未有审核记录 - ⚠️ 提示:请仔细阅读文档后再在群里提问,否则你会失去你的大脑🧠 + ⚠️ 提示:请仔细阅读群文档后再在群里提问,否则你会失去你的大脑🧠 """.trimIndent() } } diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/module/RconPlayerListModule.kt b/src/main/kotlin/top/r3944realms/ltdmanager/module/RconPlayerListModule.kt index f474eb0..08bc764 100644 --- a/src/main/kotlin/top/r3944realms/ltdmanager/module/RconPlayerListModule.kt +++ b/src/main/kotlin/top/r3944realms/ltdmanager/module/RconPlayerListModule.kt @@ -153,6 +153,11 @@ class RconPlayerListModule( LoggerUtil.logger.error("[$name] RCON 查询失败", ex) if (ex is TimeoutException) { sendFailedMessage(napCatClient, msg.realId, msg.time, "⏳ RCON 连接超时") + // ✅ 更新触发状态 & 持久化 + lastTriggerState.lastTriggeredRealId = msg.realId + lastTriggerState.lastTriggerTime = msg.time + saveState(lastTriggerState) + return } throw ex }.onSuccess { output -> diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/module/common/filter/type/KeywordFilter.kt b/src/main/kotlin/top/r3944realms/ltdmanager/module/common/filter/type/KeywordFilter.kt index c4690bd..a4a39c2 100644 --- a/src/main/kotlin/top/r3944realms/ltdmanager/module/common/filter/type/KeywordFilter.kt +++ b/src/main/kotlin/top/r3944realms/ltdmanager/module/common/filter/type/KeywordFilter.kt @@ -8,7 +8,9 @@ import top.r3944realms.ltdmanager.napcat.event.message.GetFriendMsgHistoryEvent class KeywordFilter(private val keywords: Set) : MessageFilter { override suspend fun test(msg: GetFriendMsgHistoryEvent.SpecificMsg): Boolean { return msg.message.any { seg -> - seg.type == MessageType.Text && seg.data.text?.let { it in keywords } == true + seg.type == MessageType.Text && seg.data.text?.let { text -> + keywords.any { keyword -> text.startsWith(keyword) } + } == true } } } \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/module/common/filter/type/MultiCommandFilter.kt b/src/main/kotlin/top/r3944realms/ltdmanager/module/common/filter/type/MultiCommandFilter.kt new file mode 100644 index 0000000..c049489 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/module/common/filter/type/MultiCommandFilter.kt @@ -0,0 +1,17 @@ +package top.r3944realms.ltdmanager.module.common.filter.type + +import top.r3944realms.ltdmanager.module.common.CommandParser +import top.r3944realms.ltdmanager.module.common.filter.MessageFilter +import top.r3944realms.ltdmanager.napcat.data.MessageType +import top.r3944realms.ltdmanager.napcat.event.message.GetFriendMsgHistoryEvent + +/** 多命令解析器匹配 */ +class MultiCommandFilter(private val parsers: List) : MessageFilter { + override suspend fun test(msg: GetFriendMsgHistoryEvent.SpecificMsg): Boolean { + return msg.message.any { seg -> + seg.type == MessageType.Text && seg.data.text?.let { text -> + parsers.any { parser -> parser.containsCommand(text) } + } == true + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/r3944realms/ltdmanager/utils/QRCodeUtil.kt b/src/main/kotlin/top/r3944realms/ltdmanager/utils/QRCodeUtil.kt new file mode 100644 index 0000000..f77ee74 --- /dev/null +++ b/src/main/kotlin/top/r3944realms/ltdmanager/utils/QRCodeUtil.kt @@ -0,0 +1,60 @@ +package top.r3944realms.ltdmanager.utils + +import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType +import com.google.zxing.MultiFormatWriter +import com.google.zxing.WriterException +import com.google.zxing.common.BitArray +import com.google.zxing.common.BitMatrix +import java.awt.image.BufferedImage +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.util.* +import javax.imageio.ImageIO + +object QRCodeUtil { + private const val CHARSET = "utf-8" + private const val FORMAT = "png" + @Throws(IOException::class, WriterException::class) + fun generateQRCode(string: String?): InputStream { + return generateQRCode(string, 256, 256) + } + + @Throws(IOException::class, WriterException::class) + fun generateQRCode(text: String?, width: Int, height: Int): ByteArrayInputStream { + val hints: MutableMap = EnumMap(EncodeHintType::class.java) + hints[EncodeHintType.CHARACTER_SET] = CHARSET + + // 创建二维码编码器 + val bitMatrix: BitMatrix = + MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints) + + // 将BitMatrix转换为BufferedImage + val image = toBufferedImage(bitMatrix) + + val outputStream = ByteArrayOutputStream() + ImageIO.write(image, FORMAT, outputStream) + + return ByteArrayInputStream(outputStream.toByteArray()) // 返回 ByteArrayInputStream + } + + fun toBufferedImage(matrix: BitMatrix): BufferedImage { + val width: Int = matrix.width + val height: Int = matrix.height + val image = BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY) + val onColor = -0x1000000 + val offColor = -0x1 + val rowPixels = IntArray(width) + var row: BitArray = BitArray(width) + for (y in 0 until height) { + row = matrix.getRow(y, row) + for (x in 0 until width) { + rowPixels[x] = if (row.get(x)) onColor else offColor + } + image.setRGB(0, y, width, 1, rowPixels, 0, width) + } + return image + } +} \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index c23e1b6..10c0551 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -42,4 +42,62 @@ blessing-skin-server: path: "/api/invitation-codes/generate" # 格式为 ENC(XXX),若不是则会在加载完成配置后自动加密 encrypted-token: "your-secret-token" - +dg-lab: + ws-server: + local-server-url: "0.0.0.0" + local-server-port: 4567 + local-server-publish-url: "ws://127.0.0.1:4567" + local-server-secure: false + local-server-ssl-cert: "config/cert.p12" + local-server-ssl-key: "config/key.p12" + encrypted-local-server-ssl-password: "ENC(xxxxx)" + dg-lab-client: + bind-timeout: 90.0 + register-timeout: 30.0 + pulse-data: + custom-pulse-data: "data/dg-lab-play/customPulseData.json" + duration-per-post: 8.0 + post-interval: 1.0 + sleep-after-clear: 0.5 + command-text: + append-pulse: "增加波形" + current-pulse: "当前波形" + current-strength: "当前强度" + decrease-strength: "减小强度" + dg-lab-device-join: "绑定郊狼" + exit-game: "退出游戏" + increase-strength: "加大强度" + random-pulse: "随机波形" + random-strength: "随机强度" + reset-pulse: "重置波形" + show-players: "当前玩家" + show-pulses: "可用波形" + usage: "郊狼玩法" + reply-text: + bind-timeout: "绑定超时" + current-players: "当前玩家:" + current-pulse: "当前波形循环为:【{}】" + current-strength: "A通道:{0}/{1} B通道:{2}/{3}" + failed-to-create-client: "创建 DG-Lab 控制终端失败" + failed-to-fetch-strength-info: "获取通道强度状态失败" + failed-to-fetch-strength-limit: "获取通道强度上限失败,控制失败" + game-exited: "已退出游戏" + invalid-pulse-param: "波形参数错误,控制失败" + invalid-strength-param: "强度参数错误,控制失败" + invalid-target: "目标玩家不存在或郊狼 App 未绑定" + no-available-pulse: "无可用波形" + no-player: "当前没有已连接的玩家,你可以绑定试试~" + not-bind-yet: "你目前没有绑定 DG-Lab App" + please-at-target: "使用命令的同时请 @ 想要控制的玩家" + please-scan-qrcode: "请用 DG-Lab App 扫描二维码以连接" + please-set-pulse-first: "请先设置郊狼波形:{}" + pulses-empty: "当前波形循环为空" + successfully-bind: "绑定成功,可以开始色色了!" + successfully-decreased: "郊狼强度减小了 {}%" + successfully-increased: "郊狼强度加强了 {}%!" + successfully-set-pulse: "郊狼波形成功设置为【{}】!" + successfully-set-to-strength: "郊狼强度成功设置为 {}%!" + debug: + enable-debug: false + ide-host: "127.0.0.1" + ide-port: 5678 diff --git a/src/test/kotlin/top/r394realms/ltdmanagertest/help/helpTest.kt b/src/test/kotlin/top/r394realms/ltdmanagertest/help/helpTest.kt index e2d368f..1c31bd3 100644 --- a/src/test/kotlin/top/r394realms/ltdmanagertest/help/helpTest.kt +++ b/src/test/kotlin/top/r394realms/ltdmanagertest/help/helpTest.kt @@ -1,7 +1,39 @@ package top.r394realms.ltdmanagertest.help import top.r3944realms.ltdmanager.GlobalManager +import top.r3944realms.ltdmanager.module.BanModule +import top.r3944realms.ltdmanager.module.GroupMessagePollingModule +import top.r3944realms.ltdmanager.module.HelpModule fun main() = GlobalManager.runBlockingMain { + val groupId:Long = 920719236 + val selfQQId = 3327379836 + val selfNickName = "闲趣老土豆" + // 创建模块实例 + val groupMsgPollingModule = GroupMessagePollingModule( + moduleName = "TestGroup", + targetGroupId = groupId, + pollIntervalMillis = 5_000L, + msgHistoryCheck = 15 + ) + val helpModule = HelpModule( + moduleName = "TestGroup", + groupMessagePollingModule = groupMsgPollingModule, + selfId = selfQQId, + selfNickName = selfNickName, + ) + val banModule = BanModule( + moduleName = "TestGroup", + groupMessagePollingModule = groupMsgPollingModule, + selfId = selfQQId, + adminsId = listOf(2561098830), + muteCommandPrefixList = listOf("禁言", "口球", "mute", "Mute", "闭嘴") + ) + GlobalManager.moduleManager.registerModule(groupMsgPollingModule) + GlobalManager.moduleManager.registerModule(helpModule) + GlobalManager.moduleManager.registerModule(banModule) + GlobalManager.moduleManager.loadModule(groupMsgPollingModule.name) + GlobalManager.moduleManager.loadModule(helpModule.name) + GlobalManager.moduleManager.loadModule(banModule.name) } \ No newline at end of file diff --git a/src/test/kotlin/top/r394realms/ltdmanagertest/mod/ModTest.kt b/src/test/kotlin/top/r394realms/ltdmanagertest/mod/ModTest.kt index 78b4075..2534075 100644 --- a/src/test/kotlin/top/r394realms/ltdmanagertest/mod/ModTest.kt +++ b/src/test/kotlin/top/r394realms/ltdmanagertest/mod/ModTest.kt @@ -1,7 +1,12 @@ package top.r394realms.ltdmanagertest.mod +import kotlinx.coroutines.delay import top.r3944realms.ltdmanager.GlobalManager +import top.r3944realms.ltdmanager.GlobalManager.napCatClient import top.r3944realms.ltdmanager.module.ModGroupHandlerModule +import top.r3944realms.ltdmanager.napcat.data.ID +import top.r3944realms.ltdmanager.napcat.data.MessageType +import top.r3944realms.ltdmanager.napcat.request.message.SendForwardMsgRequest fun main() = GlobalManager.runBlockingMain { @@ -22,4 +27,4 @@ fun main() = GlobalManager.runBlockingMain { // 加载模块 GlobalManager.moduleManager.loadModule(modGroupHandlerModule.name) -} \ No newline at end of file +} diff --git a/src/test/kotlin/top/r394realms/ltdmanagertest/msg/NapCatMsgTest.kt b/src/test/kotlin/top/r394realms/ltdmanagertest/msg/NapCatMsgTest.kt index fcb7a70..88191f8 100644 --- a/src/test/kotlin/top/r394realms/ltdmanagertest/msg/NapCatMsgTest.kt +++ b/src/test/kotlin/top/r394realms/ltdmanagertest/msg/NapCatMsgTest.kt @@ -1,51 +1,85 @@ package top.r394realms.ltdmanagertest.msg +import kotlinx.coroutines.delay import top.r3944realms.ltdmanager.GlobalManager +import top.r3944realms.ltdmanager.module.ModGroupHandlerModule import top.r3944realms.ltdmanager.napcat.NapCatClient import top.r3944realms.ltdmanager.napcat.data.ID -import top.r3944realms.ltdmanager.napcat.data.MessageElement -import top.r3944realms.ltdmanager.napcat.request.other.SendGroupMsgRequest +import top.r3944realms.ltdmanager.napcat.data.MessageType +import top.r3944realms.ltdmanager.napcat.request.message.SendForwardMsgRequest + fun main() = GlobalManager.runBlockingMain { val napCatClient = NapCatClient.create() - - // 生成9x9乘法表字符串 - val multiplicationTable = buildString { - for (i in 1..9) { - for (j in 1..i) { - append("$j×$i=${i * j}\t") - } - appendLine() // 换行 - } - } - - // 生成对齐检查字符 - val alignmentCheck = buildString { - appendLine("📏 对齐检查(每个数字占位):") - appendLine("1234567890") // 数字标尺 - appendLine("─".repeat(20)) // 分隔线 - - for (i in 1..9) { - for (j in 1..i) { - val product = i * j - val placeholder = "X".repeat("$j×$i=$product".length) - append("$placeholder\t") - } - appendLine() - } - } - - napCatClient.sendUnit( - SendGroupMsgRequest( - listOf( - MessageElement.at(ID.long(2561098830), "幸福亮亮"), - MessageElement.text("\n"), - MessageElement.text("9×9乘法表:\n"), - MessageElement.text(multiplicationTable), - MessageElement.text("\n────────────────────\n"), - MessageElement.text(alignmentCheck), - MessageElement.text("\n提问前,请看文档,不看文档就提问直接肘击(") - ), - ID.long(339340846) - ) + formatAndSendForwardMessage(napCatClient, 2561098830L, "幸福亮亮") +} +private suspend fun formatAndSendForwardMessage(napCatClient: NapCatClient ,userId: Long, requesterNick: String) { + // 虚拟数据 - 模拟有审核记录的情况 + val virtualRecord = ModGroupHandlerModule.RejectRecord( + userId = userId, + reason = mutableListOf( + "模组作者是张三", + "作者是李四", + "制作人是王五", + "我不知道", + "可能是赵六吧" + ), + rejectCount = 5 ) + + // 虚拟数据 - 模拟无审核记录的情况(注释掉下面这行来测试) + // val virtualRecord = null + + val record = virtualRecord + val content = """ + 📊 用户审核记录 + ────────────────── + 🔹 用户QQ号:${record.userId} + 🔹 尝试次数:${record.rejectCount} + 🔹 最终评分:${rate(record.rejectCount)} + + 📝 尝试答案: + ${"\n" + record.reason.joinToString("\n") { " • $it" }} + + ⚠️ 提示:请仔细阅读文档后再在群里提问,否则你会失去你的大脑🧠 + """.trimIndent() + + // 创建合并转发消息 + val forwardRequest = SendForwardMsgRequest( + groupId = ID.long(339340846), + messages = listOf( + SendForwardMsgRequest.TopForwardMsg( + data = SendForwardMsgRequest.MessageData( + content = listOf( + SendForwardMsgRequest.Message( + data = SendForwardMsgRequest.PurpleData( + text = content + ), + type = MessageType.Text + ) + ), + nickname = "审核系统", + userId = ID.long(0) // 系统ID + ), + type = MessageType.Text + ) + ), + news = listOf( + SendForwardMsgRequest.ForwardModelNews("用户审核记录详情") + ), + prompt = "📋 ${requesterNick}入群审核评分${rate(record.rejectCount ?: 0)}", + source = "审核系统", + summary = "点击查看用户 $requesterNick 的审核详情" + ) + + // 发送合并转发消息 + napCatClient.sendUnit(forwardRequest) +} + +private fun rate(count: Int): String = when (count) { + 0 -> "SSS" + 1 -> "A" + 2 -> "B" + 3 -> "C" + 4 -> "D" + else -> "F" } \ No newline at end of file