feat: improve mcbot command

This commit is contained in:
2024-10-18 11:38:45 +08:00
parent 8999e8912a
commit 838d95391e
6 files changed
+111 -82

No files matched your search

+1
View File
@@ -54,6 +54,7 @@ dependencies {
implementation(libs.aws.s3.sdk) implementation(libs.aws.s3.sdk)
implementation(libs.rconlib) implementation(libs.rconlib)
implementation(libs.mcprotocollib) implementation(libs.mcprotocollib)
implementation("dnsjava:dnsjava:3.6.2")
} }
tasks.build { tasks.build {
@@ -30,9 +30,9 @@ import cn.rtast.fancybot.enums.mc.MCVersionType
import cn.rtast.fancybot.rconManager 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.mcbot.MCBot
import cn.rtast.fancybot.util.mcbot.MCClient import cn.rtast.fancybot.util.mcbot.MCClient
import cn.rtast.fancybot.util.misc.isFullyTransparent import cn.rtast.fancybot.util.misc.isFullyTransparent
import cn.rtast.fancybot.util.misc.resolveMinecraftSrv
import cn.rtast.fancybot.util.misc.scaleImage import cn.rtast.fancybot.util.misc.scaleImage
import cn.rtast.fancybot.util.misc.toByteArray import cn.rtast.fancybot.util.misc.toByteArray
import cn.rtast.fancybot.util.misc.toURL import cn.rtast.fancybot.util.misc.toURL
@@ -54,7 +54,6 @@ import cn.rtast.rob.util.ob.OneBotListener
import cn.rtast.rob.util.ob.asMessageChain import cn.rtast.rob.util.ob.asMessageChain
import cn.rtast.rob.util.ob.asNode import cn.rtast.rob.util.ob.asNode
import okhttp3.FormBody import okhttp3.FormBody
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession
import java.awt.AlphaComposite import java.awt.AlphaComposite
import java.awt.Color import java.awt.Color
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
@@ -602,41 +601,45 @@ class RCONCommand : BaseCommand() {
class MCBotCommand : BaseCommand() { class MCBotCommand : BaseCommand() {
override val commandNames = listOf("/mcbot") override val commandNames = listOf("/mcbot")
private val userClientMap = mutableMapOf<Long, MutableList<TcpClientSession>>() private val userClientMap = mutableMapOf<Long, MutableList<MCClient>>()
override suspend fun executeGroup(listener: OneBotListener, message: GroupMessage, args: List<String>) { override suspend fun executeGroup(listener: OneBotListener, message: GroupMessage, args: List<String>) {
val clients = mutableListOf<TcpClientSession>() val clients = mutableListOf<MCClient>()
val action = args.first() val action = args.first()
when (action) { when (action) {
"s" -> { "start" -> {
val executor = Executors.newFixedThreadPool(100) val executor = Executors.newFixedThreadPool(100)
val host = args[1] val address = args[1]
val port = args[2].toInt() val host = address.split(":").first()
val count = args[3].toInt() val (newAddress, newPort) = host.resolveMinecraftSrv()
val msgContent = args.drop(4).joinToString(" ").trim() val count = args[2].toInt()
val delay = if (args.size == 5) 2000L else args[5].toLong()
val continueSendMessage = msgContent.contains("!!while!!")
(1..count).forEach { (1..count).forEach {
val botName = generateRandomString() val client = MCClient(newAddress, newPort, generateRandomString())
val client = MCClient(host, port, botName).createClient() .also { it.createClient() }
clients.add(client) executor.execute { clients.add(client.runBot()) }
}
clients.forEach {
executor.execute {
val bot = MCBot(msgContent.replace("!!while!!", ""), continueSendMessage, delay, it)
bot.run()
}
} }
userClientMap[message.sender.userId] = clients userClientMap[message.sender.userId] = clients
message.reply("成功开启任务: $address, $count")
} }
"c" -> { "cancel" -> {
if (userClientMap.containsKey(message.sender.userId)) { if (userClientMap.containsKey(message.sender.userId)) {
userClientMap[message.sender.userId]!!.forEach { it.disconnect("Bye~") } userClientMap.entries.forEach {
userClientMap.remove(message.sender.userId) if (it.key == message.sender.userId) {
message.reply("成功取消任务") it.value.forEach { it.disconnect() }
userClientMap.remove(it.key)
} }
} }
message.reply("成功取消任务")
} else {
message.reply("你还没开启任务呢")
}
}
"message" -> {
val content = args.drop(1).joinToString(" ").trim()
userClientMap[message.sender.userId]!!.forEach { it.sendChat(content) }
}
} }
} }
} }
@@ -1,55 +0,0 @@
/*
* Copyright © 2024 RTAkland
* Author: RTAkland
* Date: 2024/10/17
*/
package cn.rtast.fancybot.util.mcbot
import org.geysermc.mcprotocollib.network.Session
import org.geysermc.mcprotocollib.network.event.session.SessionAdapter
import org.geysermc.mcprotocollib.network.packet.Packet
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundLoginPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerPositionPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket
import java.time.Instant
import java.util.BitSet
class MCBot(
private val content: String = "FancyBot喵~",
private val continueSendMessage: Boolean = false,
private val sendDelay: Long = 2000L,
private val client: TcpClientSession
) : Runnable {
private fun sendMessage() =
client.send(ServerboundChatPacket(content, Instant.now().toEpochMilli(), 0L, null, 0, BitSet()))
override fun run() {
client.addListener(object : SessionAdapter() {
override fun packetReceived(session: Session, packet: Packet) {
if (packet is ClientboundLoginPacket) {
if (continueSendMessage) {
while (true) {
try {
sendMessage()
Thread.sleep(sendDelay)
} catch (_: InterruptedException) {
Thread.currentThread().interrupt()
}
}
} else {
sendMessage()
}
} else if (packet is ClientboundPlayerPositionPacket) {
val p = packet
client.send(ServerboundAcceptTeleportationPacket(p.teleportId));
}
}
})
client.connect()
}
}
@@ -7,17 +7,72 @@
package cn.rtast.fancybot.util.mcbot package cn.rtast.fancybot.util.mcbot
import cn.rtast.fancybot.util.Logger
import org.geysermc.mcprotocollib.network.Session
import org.geysermc.mcprotocollib.network.event.session.SessionAdapter
import org.geysermc.mcprotocollib.network.packet.Packet
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession import org.geysermc.mcprotocollib.network.tcp.TcpClientSession
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol import org.geysermc.mcprotocollib.protocol.MinecraftProtocol
import org.geysermc.mcprotocollib.protocol.data.game.ClientCommand
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundPlayerChatPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerCombatKillPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerPositionPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatCommandPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket
import java.time.Instant
import java.util.BitSet
class MCClient( class MCClient(
private val serverHost: String, private val serverHost: String,
private val serverPort: Int, private val serverPort: Int,
private val botName: String private val botName: String
) { ) {
fun createClient(): TcpClientSession { private lateinit var client: TcpClientSession
private val logger = Logger.getLogger<MCClient>()
fun createClient() {
val protocol = MinecraftProtocol(botName) val protocol = MinecraftProtocol(botName)
val client = TcpClientSession(serverHost, serverPort, protocol) client = TcpClientSession(serverHost, serverPort, protocol)
return client
} }
fun runBot(): MCClient {
client.addListener(object : SessionAdapter() {
override fun packetReceived(session: Session, packet: Packet) {
if (packet is ClientboundPlayerPositionPacket) {
client.send(ServerboundAcceptTeleportationPacket(packet.teleportId));
}
if (packet is ClientboundSystemChatPacket) {
logger.trace("系统 >>> {}", packet.content)
}
if (packet is ClientboundPlayerChatPacket) {
logger.trace("{} >>> {}", packet.name, packet.content)
}
if (packet is ClientboundPlayerCombatKillPacket) {
logger.info("Bot已死亡自动复活中")
client.send(ServerboundClientCommandPacket(ClientCommand.RESPAWN))
}
}
})
client.connect()
return this
}
private fun sendMessage(content: String) =
client.send(ServerboundChatPacket(content, Instant.now().toEpochMilli(), 0L, null, 0, BitSet()))
private fun executeCommand(command: String) = client.send(ServerboundChatCommandPacket(command.substring(1)))
fun sendChat(text: String) {
if (text.startsWith("/")) {
this.executeCommand(text)
} else {
this.sendMessage(text)
}
}
fun disconnect() = client.disconnect("Bye~")
} }
@@ -0,0 +1,25 @@
/*
* Copyright © 2024 RTAkland
* Author: RTAkland
* Date: 2024/10/18
*/
package cn.rtast.fancybot.util.misc
import org.xbill.DNS.Lookup
import org.xbill.DNS.SRVRecord
import org.xbill.DNS.Type
fun String.resolveMinecraftSrv(): Pair<String, Int> {
val srvDomain = "_minecraft._tcp.$this"
val lookup = Lookup(srvDomain, Type.SRV)
val result = lookup.run()
if (result != null && result.isNotEmpty()) {
val srvRecord = result[0] as SRVRecord
val target = srvRecord.target.toString().trimEnd('.')
val port = srvRecord.port
return Pair(target, port)
}
return Pair(this, 25565)
}
@@ -10,7 +10,7 @@ package cn.rtast.fancybot.util.str
import kotlin.random.Random import kotlin.random.Random
fun generateRandomString(): String { fun generateRandomString(): String {
val allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" val allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
val nameLength = Random.nextInt(3, 17) val nameLength = Random.nextInt(3, 17)
return (1..nameLength) return (1..nameLength)
.map { allowedChars.random() } .map { allowedChars.random() }