feat: 添加通用SQL命令
This commit is contained in:
parent
9cb6bcef50
commit
79caa2b56e
|
|
@ -3,5 +3,5 @@ org.gradle.downloadSources=false
|
|||
org.gradle.parallel=true
|
||||
org.gradle.degree_of_parallelism=16
|
||||
project_group=top.r3944realms.ltdmanager
|
||||
project_version=1.20-SNAPSHOT
|
||||
project_version=1.21-SNAPSHOT
|
||||
dg_lab_version=4.4.14.19
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ data class ModuleConfig(
|
|||
STATE_MODULE(Modules.STATE),
|
||||
HELP_MODULE(Modules.HELP),
|
||||
GITEA_WEBHOOK_MODULE(Modules.GITEA_WEBHOOK),
|
||||
RCON_COMMAND_MODULE(Modules.RCON_COMMAND),
|
||||
UNKNOWN_MODULE("UnknownModule");
|
||||
}
|
||||
// 基础获取方法
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import top.r3944realms.ltdmanager.core.config.YamlConfigLoader
|
|||
import top.r3944realms.ltdmanager.module.exception.ConfigError
|
||||
import top.r3944realms.ltdmanager.module.gitea.GiteaEventType
|
||||
import top.r3944realms.ltdmanager.module.gitea.GiteaWebhookModule
|
||||
import top.r3944realms.ltdmanager.module.RconCommandModule
|
||||
|
||||
object ModuleFactory {
|
||||
fun createModule(config: ModuleConfig.Module): BaseModule {
|
||||
|
|
@ -23,6 +24,7 @@ object ModuleFactory {
|
|||
MOD_GROUP_HANDLER_MODULE -> createModGroupHandler(config)
|
||||
HELP_MODULE -> createHelpModule(config)
|
||||
GITEA_WEBHOOK_MODULE -> createGiteaWebhook(config)
|
||||
RCON_COMMAND_MODULE -> createRconCommand(config)
|
||||
UNKNOWN_MODULE -> throw ConfigError(ConfigError.Type.INVALID_PARAMETER, "unknown module")
|
||||
}
|
||||
}
|
||||
|
|
@ -214,4 +216,29 @@ object ModuleFactory {
|
|||
)
|
||||
}
|
||||
|
||||
private fun createRconCommand(config: ModuleConfig.Module): RconCommandModule {
|
||||
val toolConfig = YamlConfigLoader.loadToolConfig()
|
||||
val selfId = config.long("self-id")
|
||||
val selfNickName = config.string("self-nick-name")
|
||||
val allowedUsers = config.list<Long>("admin-ids").toSet()
|
||||
val commandBlocklist = config.stringList("command-blocklist").toSet()
|
||||
val commandPrefix = config.string("command-prefix")
|
||||
val rconTimeoutSec = config.getOrDefault("rcon-timeout-sec", 5L)
|
||||
val groupMessagePollingModule = resolveDependency(
|
||||
config.findDependency(GROUP_MESSAGE_POLLING_MODULE), "groupMessagePolling"
|
||||
) as GroupMessagePollingModule
|
||||
return RconCommandModule(
|
||||
config.name,
|
||||
groupMessagePollingModule,
|
||||
toolConfig.rcon.mcRconToolPath.toString(),
|
||||
toolConfig.rcon.mcRconToolConfigPath.toString(),
|
||||
rconTimeoutSec,
|
||||
selfId,
|
||||
selfNickName,
|
||||
allowedUsers,
|
||||
commandBlocklist,
|
||||
commandPrefix
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ object Modules {
|
|||
val INVITATION_CODE: String = register("InvitationCodeModule")
|
||||
val STATE: String = register("StateModule")
|
||||
val GITEA_WEBHOOK: String = register("GiteaWebhookModule")
|
||||
val RCON_COMMAND: String = register("RconCommandModule")
|
||||
fun register(name: String): String {
|
||||
MODULES.add(name)
|
||||
return name
|
||||
|
|
|
|||
|
|
@ -0,0 +1,206 @@
|
|||
package top.r3944realms.ltdmanager.module
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import top.r3944realms.ltdmanager.module.common.filter.TriggerMessageFilter
|
||||
import top.r3944realms.ltdmanager.module.common.filter.type.IgnoreSelfFilter
|
||||
import top.r3944realms.ltdmanager.module.common.filter.type.KeywordFilter
|
||||
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.msghistory.MsgHistorySpecificMsg
|
||||
import top.r3944realms.ltdmanager.napcat.data.MessageType
|
||||
import top.r3944realms.ltdmanager.napcat.request.message.SendForwardMsgRequest
|
||||
import top.r3944realms.ltdmanager.napcat.request.other.SendGroupMsgRequest
|
||||
import top.r3944realms.ltdmanager.utils.CmdUtil
|
||||
import top.r3944realms.ltdmanager.utils.LoggerUtil
|
||||
|
||||
class RconCommandModule(
|
||||
moduleName: String,
|
||||
private val groupMessagePollingModule: GroupMessagePollingModule,
|
||||
private val rconPath: String,
|
||||
private val rconConfigPath: String,
|
||||
private val rconTimeoutSec: Long,
|
||||
private val selfId: Long,
|
||||
private val selfNickName: String,
|
||||
private val allowedUsers: Set<Long>,
|
||||
private val commandBlocklist: Set<String>,
|
||||
private val commandPrefix: String,
|
||||
) : BaseModule(Modules.RCON_COMMAND, moduleName) {
|
||||
|
||||
private var scope: CoroutineScope? = null
|
||||
|
||||
private var lastTriggeredRealId: Long = -1
|
||||
private var lastTriggerTime: Long = 0
|
||||
|
||||
private val triggerFilter by lazy {
|
||||
TriggerMessageFilter(
|
||||
listOf(
|
||||
IgnoreSelfFilter(selfId),
|
||||
NewMessageFilter { lastTriggerTime to lastTriggeredRealId },
|
||||
KeywordFilter(setOf(commandPrefix)),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoad() {
|
||||
LoggerUtil.logger.info("[$name] RCON命令模块已装载")
|
||||
LoggerUtil.logger.info("[$name] 允许用户: $allowedUsers")
|
||||
LoggerUtil.logger.info("[$name] 命令黑名单: $commandBlocklist")
|
||||
|
||||
scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
scope!!.launch {
|
||||
groupMessagePollingModule.messagesFlow.collect { messages ->
|
||||
if (loaded) handleMessages(messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun onUnload() {
|
||||
scope?.cancel()
|
||||
LoggerUtil.logger.info("[$name] RCON命令模块已卸载")
|
||||
}
|
||||
|
||||
private suspend fun handleMessages(messages: List<MsgHistorySpecificMsg>) {
|
||||
val filtered = triggerFilter.filter(messages)
|
||||
val triggerMsg = filtered.maxByOrNull { it.time } ?: return
|
||||
|
||||
try {
|
||||
processCommand(triggerMsg)
|
||||
} catch (e: Exception) {
|
||||
LoggerUtil.logger.error("[$name] 处理RCON命令失败", e)
|
||||
sendReply(triggerMsg, "命令执行异常: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun processCommand(msg: MsgHistorySpecificMsg) {
|
||||
val text = msg.message.firstOrNull { it.type == MessageType.Text }?.data?.text ?: return
|
||||
val rconCommand = text.removePrefix(commandPrefix).trim()
|
||||
|
||||
if (rconCommand.isEmpty()) {
|
||||
sendReply(msg, buildHelpMessage())
|
||||
updateTriggerState(msg)
|
||||
return
|
||||
}
|
||||
|
||||
// 权限检查
|
||||
if (msg.userId !in allowedUsers) {
|
||||
LoggerUtil.logger.warn("[$name] 用户 ${msg.userId} 无权限执行RCON: $rconCommand")
|
||||
sendReply(msg, "你没有权限执行 RCON 命令")
|
||||
return
|
||||
}
|
||||
|
||||
// 危险命令检查
|
||||
val blocked = findBlocklistMatch(rconCommand)
|
||||
if (blocked != null) {
|
||||
LoggerUtil.logger.warn("[$name] 阻止危险命令: '$rconCommand' (匹配黑名单: $blocked)")
|
||||
sendReply(msg, "命令已被阻止 (匹配黑名单规则: $blocked)")
|
||||
return
|
||||
}
|
||||
|
||||
// 执行RCON
|
||||
LoggerUtil.logger.info("[$name] 用户 ${msg.userId} 执行RCON: $rconCommand")
|
||||
val output = runRconCommand(rconCommand)
|
||||
sendResult(msg, rconCommand, output)
|
||||
updateTriggerState(msg)
|
||||
}
|
||||
|
||||
private fun findBlocklistMatch(command: String): String? {
|
||||
val lower = command.lowercase().trimStart('/')
|
||||
return commandBlocklist.firstOrNull { blocked ->
|
||||
lower == blocked.lowercase() ||
|
||||
lower.startsWith(blocked.lowercase() + " ") ||
|
||||
lower.startsWith(blocked.lowercase() + "/")
|
||||
}
|
||||
}
|
||||
|
||||
private fun runRconCommand(command: String): String {
|
||||
return CmdUtil.runExeCommand(
|
||||
rconPath,
|
||||
"-c", rconConfigPath,
|
||||
"-T", "${rconTimeoutSec}s",
|
||||
command
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun sendResult(msg: MsgHistorySpecificMsg, command: String, output: String) {
|
||||
val trimmed = output.trim()
|
||||
val maxLen = 3000
|
||||
|
||||
if (trimmed.length <= maxLen) {
|
||||
sendReply(msg, buildString {
|
||||
appendLine("执行: $command")
|
||||
appendLine("─".repeat(24))
|
||||
append(trimmed.ifEmpty { "(无输出)" })
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 长输出 → 合并转发
|
||||
val chunks = trimmed.chunked(maxLen)
|
||||
val messages = chunks.map { chunk ->
|
||||
SendForwardMsgRequest.Message(
|
||||
data = SendForwardMsgRequest.PurpleData(chunk),
|
||||
type = MessageType.Text
|
||||
)
|
||||
}
|
||||
|
||||
val topMessage = SendForwardMsgRequest.TopForwardMsg(
|
||||
data = SendForwardMsgRequest.MessageData(
|
||||
content = messages,
|
||||
nickname = selfNickName,
|
||||
userId = ID.long(selfId),
|
||||
),
|
||||
type = MessageType.Node
|
||||
)
|
||||
|
||||
val request = SendForwardMsgRequest(
|
||||
groupId = ID.long(groupMessagePollingModule.targetGroupId),
|
||||
messages = listOf(topMessage),
|
||||
news = listOf(
|
||||
SendForwardMsgRequest.ForwardModelNews("RCON: $command"),
|
||||
SendForwardMsgRequest.ForwardModelNews("输出 ${trimmed.length} 字符"),
|
||||
),
|
||||
prompt = "RCON命令执行结果",
|
||||
source = "RCON",
|
||||
summary = "RCON: $command",
|
||||
)
|
||||
|
||||
napCatClient.sendUnit(request)
|
||||
}
|
||||
|
||||
private suspend fun sendReply(msg: MsgHistorySpecificMsg, text: String) {
|
||||
napCatClient.sendUnit(
|
||||
SendGroupMsgRequest(
|
||||
MessageElement.reply(ID.long(msg.realId), text),
|
||||
ID.long(groupMessagePollingModule.targetGroupId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateTriggerState(msg: MsgHistorySpecificMsg) {
|
||||
lastTriggeredRealId = msg.realId
|
||||
lastTriggerTime = msg.time
|
||||
}
|
||||
|
||||
private fun buildHelpMessage(): String = buildString {
|
||||
appendLine("RCON 命令模块")
|
||||
appendLine("用法: $commandPrefix <MC命令>")
|
||||
appendLine("─".repeat(16))
|
||||
appendLine("示例:")
|
||||
appendLine(" $commandPrefix list")
|
||||
appendLine(" $commandPrefix forge tps")
|
||||
appendLine(" $commandPrefix difficulty peaceful")
|
||||
}
|
||||
|
||||
override fun info(): String = "RCON命令模块 - 前缀: $commandPrefix, 允许用户: ${allowedUsers.size}人, 黑名单规则: ${commandBlocklist.size}条"
|
||||
override fun help(): String = buildString {
|
||||
appendLine("RCON命令模块 - 通过QQ群执行Minecraft RCON命令")
|
||||
appendLine("前缀: $commandPrefix")
|
||||
appendLine("权限: 仅以下QQ号可执行: ${allowedUsers.joinToString()}")
|
||||
appendLine("黑名单命令前缀: ${commandBlocklist.joinToString()}")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user