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

+2
View File
@@ -18,6 +18,7 @@ repositories {
maven("https://oss.sonatype.org/content/repositories/snapshots") maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://repo.rtast.cn/api/v4/projects/33/packages/maven") maven("https://repo.rtast.cn/api/v4/projects/33/packages/maven")
maven("https://repo.rtast.cn/api/v4/projects/19/packages/maven") maven("https://repo.rtast.cn/api/v4/projects/19/packages/maven")
maven("https://repo.rtast.cn/api/v4/projects/3/packages/maven")
} }
tasks.compileKotlin { tasks.compileKotlin {
@@ -46,6 +47,7 @@ dependencies {
implementation(libs.logback.classic) implementation(libs.logback.classic)
implementation(libs.slf4j.api) implementation(libs.slf4j.api)
implementation(libs.aws.s3.sdk) implementation(libs.aws.s3.sdk)
implementation(libs.rconlib)
} }
tasks.build { tasks.build {
+2
View File
@@ -14,6 +14,7 @@ zxingVersion = "3.5.3"
logbackClassicVersion = "1.5.8" logbackClassicVersion = "1.5.8"
sl4jVersion = "2.0.16" sl4jVersion = "2.0.16"
awsS3SDKVersion = "2.28.18" awsS3SDKVersion = "2.28.18"
rconLibVersion = "1.1.0"
[libraries] [libraries]
ronebot = { group = "cn.rtast", name = "ROneBot", version.ref = "ronebotVersion" } ronebot = { group = "cn.rtast", name = "ROneBot", version.ref = "ronebotVersion" }
@@ -31,6 +32,7 @@ zxing-javase = { group = "com.google.zxing", name = "javase", version.ref = "zxi
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logbackClassicVersion" } logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logbackClassicVersion" }
slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "sl4jVersion" } slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "sl4jVersion" }
aws-s3-sdk = { group = "software.amazon.awssdk", name = "s3", version.ref = "awsS3SDKVersion" } aws-s3-sdk = { group = "software.amazon.awssdk", name = "s3", version.ref = "awsS3SDKVersion" }
rconlib = { group = "cn.rtast", name = "RCONLib", version.ref = "rconLibVersion" }
[bundles] [bundles]
+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.ConfigManager
import cn.rtast.fancybot.util.file.NiuziBankManager import cn.rtast.fancybot.util.file.NiuziBankManager
import cn.rtast.fancybot.util.file.NiuziManager import cn.rtast.fancybot.util.file.NiuziManager
import cn.rtast.fancybot.util.file.RCONManager
import cn.rtast.fancybot.util.item.ItemManager import cn.rtast.fancybot.util.item.ItemManager
import cn.rtast.rob.ROneBotFactory import cn.rtast.rob.ROneBotFactory
import com.google.gson.Gson import com.google.gson.Gson
@@ -89,6 +90,7 @@ val itemManager = ItemManager()
val niuziManager = NiuziManager() val niuziManager = NiuziManager()
val niuziBankManager = NiuziBankManager() val niuziBankManager = NiuziBankManager()
val blackListManager = BlackListManager() val blackListManager = BlackListManager()
val rconManager = RCONManager()
val START_UP_TIME = Instant.now().epochSecond val START_UP_TIME = Instant.now().epochSecond
@@ -124,5 +126,5 @@ val commands = listOf(
ZDJDCommand(), ScanQRCodeCommand(), TTSCommand(), SlimeChunkHelperCommand(), ZDJDCommand(), ScanQRCodeCommand(), TTSCommand(), SlimeChunkHelperCommand(),
SupportMeCommand(), TheCatCommand(), TheHistoryOfTodayCommand(), MCVersionCommand(), SupportMeCommand(), TheCatCommand(), TheHistoryOfTodayCommand(), MCVersionCommand(),
MinecraftWikiCommand(), GaoKaoDaysRemainCommand(), IdiomExplainCommand(), NiuziManagerCommand(), MinecraftWikiCommand(), GaoKaoDaysRemainCommand(), IdiomExplainCommand(), NiuziManagerCommand(),
TodayEatCommand(), TodayDrinkCommand(), MCLoginCommand() TodayEatCommand(), TodayDrinkCommand(), MCLoginCommand(), RCONCommand()
) )
@@ -6,7 +6,7 @@
@file:Suppress("KDocUnresolvedReference") @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.annotations.CommandDescription
import cn.rtast.fancybot.commands.lookup.WikipediaCommand.Companion.extractPlainTextFromHtml 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.ObtainXSTSResponse
import cn.rtast.fancybot.entity.mc.oauth.RedeemCodeResponse import cn.rtast.fancybot.entity.mc.oauth.RedeemCodeResponse
import cn.rtast.fancybot.enums.mc.MCVersionType import cn.rtast.fancybot.enums.mc.MCVersionType
import cn.rtast.fancybot.rconManager
import cn.rtast.fancybot.util.Http import cn.rtast.fancybot.util.Http
import cn.rtast.fancybot.util.Logger import cn.rtast.fancybot.util.Logger
import cn.rtast.fancybot.util.misc.isFullyTransparent 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.fancybot.util.str.uriEncode
import cn.rtast.motdpinger.BedrockPing import cn.rtast.motdpinger.BedrockPing
import cn.rtast.motdpinger.JavaPing 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.GroupMessage
import cn.rtast.rob.entity.PrivateMessage import cn.rtast.rob.entity.PrivateMessage
import cn.rtast.rob.util.BaseCommand import cn.rtast.rob.util.BaseCommand
@@ -504,3 +507,90 @@ class MCLoginCommand : BaseCommand() {
} }
} }
} }
@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.MinecraftAccessTokenTable
import cn.rtast.fancybot.db.NiuziBankTable import cn.rtast.fancybot.db.NiuziBankTable
import cn.rtast.fancybot.db.NiuziTable import cn.rtast.fancybot.db.NiuziTable
import cn.rtast.fancybot.db.RCONTable
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.SchemaUtils
@@ -28,6 +29,7 @@ suspend fun initDatabase() {
SchemaUtils.createMissingTablesAndColumns(ActionTable) SchemaUtils.createMissingTablesAndColumns(ActionTable)
SchemaUtils.createMissingTablesAndColumns(BlackListTable) SchemaUtils.createMissingTablesAndColumns(BlackListTable)
SchemaUtils.createMissingTablesAndColumns(MinecraftAccessTokenTable) 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) }
}
}
}