feat(完善banmodule,初步编写dglab模块): 更新版本好,完善BanModule,初步编写dglab模块
This commit is contained in:
parent
88f574eea1
commit
f95c6701e5
124
.idea/uiDesigner.xml
Normal file
124
.idea/uiDesigner.xml
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
BIN
libs/DgLab-common-4.2.11.18.jar
Normal file
BIN
libs/DgLab-common-4.2.11.18.jar
Normal file
Binary file not shown.
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -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(),
|
||||
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package top.r3944realms.ltdmanager.dglab.manager
|
||||
|
||||
import com.r3944realms.dg_lab.manager.DGPBClientManager
|
||||
|
||||
class ClientManager(
|
||||
private val clients: MutableMap<String, DGPBClientManager> = mutableMapOf(),
|
||||
) : IManager<MutableMap<String, DGPBClientManager>> {
|
||||
|
||||
/**
|
||||
* 添加单例客户端管理示例
|
||||
* @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<String, DGPBClientManager> {
|
||||
return clients
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package top.r3944realms.ltdmanager.dglab.manager
|
||||
|
||||
interface IManager<T> {
|
||||
fun startAll()
|
||||
fun stopAll()
|
||||
fun getInstance(): T?
|
||||
}
|
||||
|
|
@ -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<DGPBServerManager>, 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package top.r3944realms.ltdmanager.dglab.model.game
|
||||
|
||||
import com.r3944realms.dg_lab.websocket.handler.server.DefaultServerOperation
|
||||
|
||||
class GameServerOperation : DefaultServerOperation()
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package top.r3944realms.ltdmanager.dglab.model.game
|
||||
|
||||
/**
|
||||
* 玩家类,目前仅包含一个 ID
|
||||
*/
|
||||
data class Player(
|
||||
val id: String
|
||||
)
|
||||
|
|
@ -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<String></String>, List<int></int>[][]>>
|
||||
* 每个 int[][] 包含两个长度为 4 的 int 数组,第一个是 frequencies,第二个是 strengths
|
||||
* @return Map<String></String>, PulseWaveList>
|
||||
*/
|
||||
fun convert(customPulseData: Map<String, List<Array<IntArray>>>): Map<String, PulseWaveList> {
|
||||
val pulseWaveLists: MutableMap<String, PulseWaveList> = 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, PulseWaveList> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package top.r3944realms.ltdmanager.dglab.model.pulseware
|
||||
|
||||
class PulseWaveClassTransform {
|
||||
}
|
||||
|
|
@ -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<String, PulseWaveList>, file: File) {
|
||||
val serializableMap = map.mapValues { it.value.toSerializable() }
|
||||
file.writeText(json.encodeToString(serializableMap))
|
||||
}
|
||||
|
||||
fun loadFromFile(file: File): Map<String, PulseWaveList> {
|
||||
if (!file.exists()) return emptyMap()
|
||||
val type = MapSerializer(String.serializer(), PulseWaveListSerializable.serializer())
|
||||
val data: Map<String, PulseWaveListSerializable> = json.decodeFromString(type, file.readText())
|
||||
return data.mapValues { it.value.toPulseWaveList() }
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PulseWaveSerializable> = mutableListOf()
|
||||
)
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<String> = listOf("/mute"), // 默认命令前缀
|
||||
private val adminsId: List<Long> = listOf(),
|
||||
muteCommandPrefixList: List<String> = listOf("mute"), // 默认命令前缀
|
||||
unmuteCommandPrefixList: List<String> = 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<BanModule.BanState> {
|
||||
|
||||
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<ID> {
|
||||
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<Long>
|
||||
val send =
|
||||
napCatClient.send<GetGroupShutListEvent>(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<ID>
|
||||
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 → 解禁指定用户")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package top.r3944realms.ltdmanager.module
|
||||
|
||||
class DGLabModule(
|
||||
moduleName: String,
|
||||
):
|
||||
BaseModule("DGLabModule", moduleName) {
|
||||
override fun onLoad() {
|
||||
|
||||
}
|
||||
|
||||
override suspend fun onUnload() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -187,7 +187,7 @@ class ModGroupHandlerModule(
|
|||
📝 尝试答案:
|
||||
${ "\n" + record.reason.joinToString("\n") { " • $it" }}
|
||||
|
||||
⚠️ 提示:请仔细阅读文档后再在群里提问,否则你会失去你的大脑🧠
|
||||
⚠️ 提示:请仔细阅读群文档后再在群里提问,否则你会失去你的大脑🧠
|
||||
""".trimIndent()
|
||||
} else {
|
||||
"""
|
||||
|
|
@ -198,7 +198,7 @@ class ModGroupHandlerModule(
|
|||
🔹 最终评分:SSS ⭐
|
||||
|
||||
💡 该用户尚未有审核记录
|
||||
⚠️ 提示:请仔细阅读文档后再在群里提问,否则你会失去你的大脑🧠
|
||||
⚠️ 提示:请仔细阅读群文档后再在群里提问,否则你会失去你的大脑🧠
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ import top.r3944realms.ltdmanager.napcat.event.message.GetFriendMsgHistoryEvent
|
|||
class KeywordFilter(private val keywords: Set<String>) : 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CommandParser>) : 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<EncodeHintType, Any> = 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user