feat: add rcon command

This commit is contained in:
2024-10-13 14:16:00 +08:00
parent c9d2ddeff7
commit 73b417775a
7 files changed
+195 -2

No files matched your search

+3 -1
View File
@@ -30,6 +30,7 @@ import cn.rtast.fancybot.util.file.BlackListManager
import cn.rtast.fancybot.util.file.ConfigManager
import cn.rtast.fancybot.util.file.NiuziBankManager
import cn.rtast.fancybot.util.file.NiuziManager
import cn.rtast.fancybot.util.file.RCONManager
import cn.rtast.fancybot.util.item.ItemManager
import cn.rtast.rob.ROneBotFactory
import com.google.gson.Gson
@@ -89,6 +90,7 @@ val itemManager = ItemManager()
val niuziManager = NiuziManager()
val niuziBankManager = NiuziBankManager()
val blackListManager = BlackListManager()
val rconManager = RCONManager()
val START_UP_TIME = Instant.now().epochSecond
@@ -124,5 +126,5 @@ val commands = listOf(
ZDJDCommand(), ScanQRCodeCommand(), TTSCommand(), SlimeChunkHelperCommand(),
SupportMeCommand(), TheCatCommand(), TheHistoryOfTodayCommand(), MCVersionCommand(),
MinecraftWikiCommand(), GaoKaoDaysRemainCommand(), IdiomExplainCommand(), NiuziManagerCommand(),
TodayEatCommand(), TodayDrinkCommand(), MCLoginCommand()
TodayEatCommand(), TodayDrinkCommand(), MCLoginCommand(), RCONCommand()
)
@@ -6,7 +6,7 @@
@file:Suppress("KDocUnresolvedReference")
package cn.rtast.fancybot.commands.lookup
package cn.rtast.fancybot.commands.misc
import cn.rtast.fancybot.annotations.CommandDescription
import cn.rtast.fancybot.commands.lookup.WikipediaCommand.Companion.extractPlainTextFromHtml
@@ -27,6 +27,7 @@ import cn.rtast.fancybot.entity.mc.oauth.ObtainXSTSPayload
import cn.rtast.fancybot.entity.mc.oauth.ObtainXSTSResponse
import cn.rtast.fancybot.entity.mc.oauth.RedeemCodeResponse
import cn.rtast.fancybot.enums.mc.MCVersionType
import cn.rtast.fancybot.rconManager
import cn.rtast.fancybot.util.Http
import cn.rtast.fancybot.util.Logger
import cn.rtast.fancybot.util.misc.isFullyTransparent
@@ -41,6 +42,8 @@ import cn.rtast.fancybot.util.str.toJson
import cn.rtast.fancybot.util.str.uriEncode
import cn.rtast.motdpinger.BedrockPing
import cn.rtast.motdpinger.JavaPing
import cn.rtast.rcon.RCon
import cn.rtast.rcon.exceptions.AuthFailedException
import cn.rtast.rob.entity.GroupMessage
import cn.rtast.rob.entity.PrivateMessage
import cn.rtast.rob.util.BaseCommand
@@ -503,4 +506,91 @@ class MCLoginCommand : BaseCommand() {
message.reply(msg)
}
}
}
@CommandDescription("通过RCON来控制MC服务器(仅限私聊执行)")
class RCONCommand : BaseCommand() {
override val commandNames = listOf("/rcon", "/r")
override suspend fun executeGroup(listener: OneBotListener, message: GroupMessage, args: List<String>) {
message.reply("请在私聊中使用此命令")
}
override suspend fun executePrivate(listener: OneBotListener, message: PrivateMessage, args: List<String>) {
if (args.isEmpty()) {
val msg = MessageChain.Builder()
.addText("这条指令用于在群内操作mc服务器")
.addNewLine()
.addText("使用`/r add <name> <host> <port> <password>` 可以创建一条配置")
.addNewLine()
.addText("使用`/r <name> <command>`可以执行命令")
.addNewLine()
.addText("使用`/r remove <name>`可以移除这条RCON配置")
.build()
message.reply(msg)
return
}
val action = args.first()
when (action) {
"get" -> {
val allRcons = rconManager.getAllRconsById(message.sender.userId)
if (allRcons.isEmpty()) {
message.reply("你还没有RCON配置呢, 发送`/r add <name> <host> <port> <password>`来创建一个配置吧")
return
}
val messages = mutableListOf<MessageChain>()
.also { it.add(listOf("所有配置如下").asMessageChain()) }
allRcons.forEach {
val msg = MessageChain.Builder()
.addText("名称: ${it.name}")
.addNewLine()
.addText("地址: ${it.host}:${it.port}")
.addNewLine()
.addText("密码: *********")
.build()
messages.add(msg)
}
message.reply(messages.asNode(configManager.selfId))
}
"add" -> {
val name = args[1]
val host = args[2]
val port = args[3].toInt()
val password = args[4]
rconManager.insertConfig(message.sender.userId, name, host, port, password)
message.reply("成功增加一条记录: $name")
}
"remove" -> {
val name = args.last()
val count = rconManager.removeRRCON(message.sender.userId, name)
if (count == 0) {
message.reply("RCON($name)不存在")
} else {
message.reply("成功移除了$name")
}
}
else -> {
val name = args.first().trim()
val command = args.drop(1).joinToString(" ").trim()
val rconData = rconManager.getRcon(message.sender.userId, name)
if (rconData == null) {
message.reply("RCON($name)配置不存在")
} else {
try {
val result = rconManager.executeCommand(message.sender.userId, name, command)
val messages = mutableListOf<MessageChain>()
.also { it.add(listOf("执行结果如下").asMessageChain()) }
.also { it.add(listOf(result).asMessageChain()) }
.asNode(configManager.selfId)
message.reply(messages)
} catch (_: AuthFailedException) {
message.reply("执行失败, 密码不正确")
}
}
}
}
}
}
@@ -0,0 +1,29 @@
/*
* Copyright © 2024 RTAkland
* Author: RTAkland
* Date: 2024/10/13
*/
package cn.rtast.fancybot.db
import org.jetbrains.exposed.sql.Table
object RCONTable : Table("rcon") {
val id = integer("id").autoIncrement()
val host = varchar("host", 50)
val port = integer("port")
val userId = long("user_id")
val password = varchar("password", 128)
val name = varchar("name", 50)
override val primaryKey = PrimaryKey(id)
}
data class RCONEntity(
val host: String,
val port: Int,
val userId: Long,
val name: String,
val password: String
)
@@ -14,6 +14,7 @@ import cn.rtast.fancybot.db.JrrpTable
import cn.rtast.fancybot.db.MinecraftAccessTokenTable
import cn.rtast.fancybot.db.NiuziBankTable
import cn.rtast.fancybot.db.NiuziTable
import cn.rtast.fancybot.db.RCONTable
import kotlinx.coroutines.Dispatchers
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
@@ -28,6 +29,7 @@ suspend fun initDatabase() {
SchemaUtils.createMissingTablesAndColumns(ActionTable)
SchemaUtils.createMissingTablesAndColumns(BlackListTable)
SchemaUtils.createMissingTablesAndColumns(MinecraftAccessTokenTable)
SchemaUtils.createMissingTablesAndColumns(RCONTable)
}
}
@@ -0,0 +1,66 @@
/*
* Copyright © 2024 RTAkland
* Author: RTAkland
* Date: 2024/10/13
*/
package cn.rtast.fancybot.util.file
import cn.rtast.fancybot.db.MinecraftAccessTokenTable
import cn.rtast.fancybot.db.RCONEntity
import cn.rtast.fancybot.db.RCONTable
import cn.rtast.fancybot.db.RCONTable.host
import cn.rtast.fancybot.db.RCONTable.password
import cn.rtast.fancybot.db.RCONTable.port
import cn.rtast.fancybot.util.suspendedTransaction
import cn.rtast.rcon.RCon
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
class RCONManager {
suspend fun getRcon(userId: Long, name: String): RCONEntity? {
return suspendedTransaction {
RCONTable.selectAll().where { (RCONTable.userId eq userId) and (RCONTable.name eq name) }
.map { RCONEntity(it[host], it[port], it[RCONTable.userId], it[RCONTable.name], it[password]) }
}.singleOrNull()
}
suspend fun getAllRconsById(userId: Long): List<RCONEntity> {
return suspendedTransaction {
RCONTable.selectAll().where { (RCONTable.userId eq userId) }
.map { RCONEntity(it[host], it[port], it[RCONTable.userId], it[RCONTable.name], it[password]) }
}
}
suspend fun executeCommand(userId: Long, name: String, command: String): String {
val rconData = getRcon(userId, name)!!
val rcon = RCon(rconData.host, rconData.port)
rcon.authenticate(rconData.password)
val result = rcon.executeCommand(command).body
rcon.close()
return result
}
suspend fun insertConfig(userId: Long, name: String, host: String, port: Int, password: String) {
suspendedTransaction {
RCONTable.insert {
it[RCONTable.userId] = userId
it[RCONTable.name] = name
it[RCONTable.host] = host
it[RCONTable.port] = port
it[RCONTable.password] = password
}
}
}
suspend fun removeRRCON(userId: Long, name: String): Int {
return suspendedTransaction {
MinecraftAccessTokenTable.deleteWhere { (RCONTable.userId eq userId) and (RCONTable.name eq name) }
}
}
}