feat: improve mcbot command
This commit is contained in:
6 files changed
+111
-82
No files matched your search
@@ -54,6 +54,7 @@ dependencies {
|
||||
implementation(libs.aws.s3.sdk)
|
||||
implementation(libs.rconlib)
|
||||
implementation(libs.mcprotocollib)
|
||||
implementation("dnsjava:dnsjava:3.6.2")
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
|
||||
@@ -30,9 +30,9 @@ 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.mcbot.MCBot
|
||||
import cn.rtast.fancybot.util.mcbot.MCClient
|
||||
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.toByteArray
|
||||
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.asNode
|
||||
import okhttp3.FormBody
|
||||
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession
|
||||
import java.awt.AlphaComposite
|
||||
import java.awt.Color
|
||||
import java.awt.image.BufferedImage
|
||||
@@ -602,41 +601,45 @@ class RCONCommand : BaseCommand() {
|
||||
class MCBotCommand : BaseCommand() {
|
||||
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>) {
|
||||
val clients = mutableListOf<TcpClientSession>()
|
||||
val clients = mutableListOf<MCClient>()
|
||||
val action = args.first()
|
||||
when (action) {
|
||||
"s" -> {
|
||||
"start" -> {
|
||||
val executor = Executors.newFixedThreadPool(100)
|
||||
val host = args[1]
|
||||
val port = args[2].toInt()
|
||||
val count = args[3].toInt()
|
||||
val msgContent = args.drop(4).joinToString(" ").trim()
|
||||
val delay = if (args.size == 5) 2000L else args[5].toLong()
|
||||
val continueSendMessage = msgContent.contains("!!while!!")
|
||||
val address = args[1]
|
||||
val host = address.split(":").first()
|
||||
val (newAddress, newPort) = host.resolveMinecraftSrv()
|
||||
val count = args[2].toInt()
|
||||
(1..count).forEach {
|
||||
val botName = generateRandomString()
|
||||
val client = MCClient(host, port, botName).createClient()
|
||||
clients.add(client)
|
||||
}
|
||||
clients.forEach {
|
||||
executor.execute {
|
||||
val bot = MCBot(msgContent.replace("!!while!!", ""), continueSendMessage, delay, it)
|
||||
bot.run()
|
||||
}
|
||||
val client = MCClient(newAddress, newPort, generateRandomString())
|
||||
.also { it.createClient() }
|
||||
executor.execute { clients.add(client.runBot()) }
|
||||
}
|
||||
userClientMap[message.sender.userId] = clients
|
||||
message.reply("成功开启任务: $address, $count")
|
||||
}
|
||||
|
||||
"c" -> {
|
||||
"cancel" -> {
|
||||
if (userClientMap.containsKey(message.sender.userId)) {
|
||||
userClientMap[message.sender.userId]!!.forEach { it.disconnect("Bye~") }
|
||||
userClientMap.remove(message.sender.userId)
|
||||
userClientMap.entries.forEach {
|
||||
if (it.key == message.sender.userId) {
|
||||
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
|
||||
|
||||
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.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(
|
||||
private val serverHost: String,
|
||||
private val serverPort: Int,
|
||||
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 client = TcpClientSession(serverHost, serverPort, protocol)
|
||||
return client
|
||||
client = TcpClientSession(serverHost, serverPort, protocol)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
fun generateRandomString(): String {
|
||||
val allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
||||
val allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
val nameLength = Random.nextInt(3, 17)
|
||||
return (1..nameLength)
|
||||
.map { allowedChars.random() }
|
||||
|
||||
Reference in New Issue
Block a user