From 6303d44ab4460812955a613cccf05237426decfe Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 5 Oct 2025 03:06:23 +0800 Subject: [PATCH] :sparkles: Sync message to SN --- .../solsynth/snConnect/SolarNetworkConnect.kt | 23 ++++++++--- .../dev/solsynth/snConnect/WebSocketTester.kt | 39 ------------------ .../snConnect/listeners/SnChatListener.kt | 29 ++++++++++++++ .../snConnect/services/SnMessageService.kt | 40 +++++++++++++++++++ .../solsynth/snConnect/services/SnService.kt | 2 +- src/main/resources/config.yml | 11 +++-- 6 files changed, 96 insertions(+), 48 deletions(-) delete mode 100644 src/main/kotlin/dev/solsynth/snConnect/WebSocketTester.kt create mode 100644 src/main/kotlin/dev/solsynth/snConnect/listeners/SnChatListener.kt create mode 100644 src/main/kotlin/dev/solsynth/snConnect/services/SnMessageService.kt diff --git a/src/main/kotlin/dev/solsynth/snConnect/SolarNetworkConnect.kt b/src/main/kotlin/dev/solsynth/snConnect/SolarNetworkConnect.kt index f08f37b..90a975a 100644 --- a/src/main/kotlin/dev/solsynth/snConnect/SolarNetworkConnect.kt +++ b/src/main/kotlin/dev/solsynth/snConnect/SolarNetworkConnect.kt @@ -2,12 +2,14 @@ package dev.solsynth.snConnect import dev.solsynth.snConnect.commands.SnCommand import dev.solsynth.snConnect.commands.SnCommandCompleter -import dev.solsynth.snConnect.models.* +import dev.solsynth.snConnect.listeners.SnChatListener +import dev.solsynth.snConnect.models.SnChatMessage +import dev.solsynth.snConnect.models.WebSocketPacket +import dev.solsynth.snConnect.services.SnMessageService import dev.solsynth.snConnect.services.SnService import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import net.md_5.bungee.api.ChatColor -import net.md_5.bungee.api.chat.BaseComponent import net.md_5.bungee.api.chat.ClickEvent import net.md_5.bungee.api.chat.ComponentBuilder import net.md_5.bungee.api.chat.HoverEvent @@ -15,22 +17,26 @@ import net.md_5.bungee.api.chat.TextComponent import net.milkbowl.vault.economy.Economy import org.bukkit.Bukkit import org.bukkit.Bukkit.getOnlinePlayers -import org.bukkit.Color import org.bukkit.plugin.java.JavaPlugin class SolarNetworkConnect : JavaPlugin() { private var economy: Economy? = null private var sn: SnService? = null + private var messageService: SnMessageService? = null private var syncChatRooms: List = emptyList() + private var destinationChatId: String? = null private fun handleWebSocketPacket(packet: WebSocketPacket) { - logger.info("Received WebSocket packet: type=${packet.type}") + // logger.info("Received WebSocket packet: type=${packet.type}") if (packet.type.startsWith("messages") && packet.data != null) { try { when (packet.type) { "messages.new" -> { val message = SnChatMessage.fromJson(packet.data) + // Ignore automated accounts + if (message.sender.account.automatedId.isNullOrBlank()) return; + // Only some rooms got synced if (syncChatRooms.isEmpty() || syncChatRooms.contains(message.chatRoomId)) { val roomName = message.chatRoom.name ?: "DM" val senderName = message.sender.account.nick @@ -82,6 +88,10 @@ class SolarNetworkConnect : JavaPlugin() { ) } + if (messageService != null && destinationChatId != null) { + server.pluginManager.registerEvents(SnChatListener(messageService!!, destinationChatId!!), this) + } + Bukkit.getPluginCommand("solar")!!.setExecutor(SnCommand(this.sn!!, this.economy)) Bukkit.getPluginCommand("solar")!!.tabCompleter = SnCommandCompleter() @@ -102,9 +112,12 @@ class SolarNetworkConnect : JavaPlugin() { val clientId = config.getString("sn.client_id") ?: return false; val clientSecret = config.getString("sn.client_secret") ?: return false; val botApiKey = config.getString("sn.bot_secret"); - val syncRooms = config.getStringList("chat.sync_chat_rooms") + val destination = config.getString("chat.outgoing_room") ?: return false; + val syncRooms = config.getStringList("chat.sync_rooms") syncChatRooms = syncRooms + destinationChatId = destination sn = SnService(baseUrl, clientId, clientSecret, botApiKey); + messageService = SnMessageService(sn!!) GlobalScope.launch { sn!!.connectWebSocketAsFlow().collect { packet -> handleWebSocketPacket(packet) diff --git a/src/main/kotlin/dev/solsynth/snConnect/WebSocketTester.kt b/src/main/kotlin/dev/solsynth/snConnect/WebSocketTester.kt deleted file mode 100644 index 4340da0..0000000 --- a/src/main/kotlin/dev/solsynth/snConnect/WebSocketTester.kt +++ /dev/null @@ -1,39 +0,0 @@ -package dev.solsynth.snConnect - -import dev.solsynth.snConnect.services.SnService -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeoutOrNull - -class WebSocketTester { - companion object { - @JvmStatic - fun main(args: Array) { - if (args.size < 4) { - println("Usage: WebSocketTester ") - return - } - - val baseUrl = args[0] - val clientId = args[1] - val clientSecret = args[2] - val botApiKey = if (args[3] == "null") null else args[3] - - runBlocking { - val service = SnService(baseUrl, clientId, clientSecret, botApiKey) - println("Starting WebSocket test for $baseUrl") - - val result = withTimeoutOrNull(30000L) { // 30 seconds timeout - service.connectWebSocketAsFlow().collect { packet -> - println("Received packet: type=${packet.type}, endpoint=${packet.endpoint}, data=${packet.data}") - } - } - - if (result == null) { - println("WebSocket test timed out") - } else { - println("WebSocket test completed") - } - } - } - } -} diff --git a/src/main/kotlin/dev/solsynth/snConnect/listeners/SnChatListener.kt b/src/main/kotlin/dev/solsynth/snConnect/listeners/SnChatListener.kt new file mode 100644 index 0000000..b0b5376 --- /dev/null +++ b/src/main/kotlin/dev/solsynth/snConnect/listeners/SnChatListener.kt @@ -0,0 +1,29 @@ +package dev.solsynth.snConnect.listeners + +import dev.solsynth.snConnect.services.SnMessageService +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.AsyncPlayerChatEvent +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent + +class SnChatListener(private val messageService: SnMessageService, private val destinationChatId: String) : Listener { + + @EventHandler + fun onPlayerChat(event: AsyncPlayerChatEvent) { + val message = "${event.player.name}: ${event.message}" + messageService.sendMessage(destinationChatId, message) + } + + @EventHandler + fun onPlayerJoin(event: PlayerJoinEvent) { + val message = "${event.player.name} joined the game." + messageService.sendMessage(destinationChatId, message) + } + + @EventHandler + fun onPlayerQuit(event: PlayerQuitEvent) { + val message = "${event.player.name} left the game." + messageService.sendMessage(destinationChatId, message) + } +} diff --git a/src/main/kotlin/dev/solsynth/snConnect/services/SnMessageService.kt b/src/main/kotlin/dev/solsynth/snConnect/services/SnMessageService.kt new file mode 100644 index 0000000..88db2cc --- /dev/null +++ b/src/main/kotlin/dev/solsynth/snConnect/services/SnMessageService.kt @@ -0,0 +1,40 @@ +package dev.solsynth.snConnect.services + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import java.io.IOException +import java.util.UUID + +@Serializable +data class SnMessageRequest( + val content: String, + val nonce: String +) + +class SnMessageService(private val sn: SnService) { + private val json = Json { + ignoreUnknownKeys = true + } + + fun sendMessage(chatRoomId: String, content: String) { + val body = SnMessageRequest(content, UUID.randomUUID().toString()) + val request = Request.Builder() + .url(sn.getUrl("sphere", "/chat/$chatRoomId/messages")) + .post(json.encodeToString(body).toRequestBody("application/json".toMediaTypeOrNull())) + .apply { + sn.botApiKey?.let { header("Authorization", "Bearer $it") } + } + .build() + + sn.client.newCall(request).execute().use { response -> + if (!response.isSuccessful) { + val responseBody = response.body?.string() ?: "No body" + throw IOException("Unexpected code $response: $responseBody") + } + } + } +} diff --git a/src/main/kotlin/dev/solsynth/snConnect/services/SnService.kt b/src/main/kotlin/dev/solsynth/snConnect/services/SnService.kt index f537f46..4412a05 100644 --- a/src/main/kotlin/dev/solsynth/snConnect/services/SnService.kt +++ b/src/main/kotlin/dev/solsynth/snConnect/services/SnService.kt @@ -12,7 +12,7 @@ import okhttp3.Response import okio.ByteString import java.util.logging.Logger -class SnService(private val baseUrl: String, val clientId: String, val clientSecret: String, private val botApiKey: String?) { +class SnService(private val baseUrl: String, val clientId: String, val clientSecret: String, val botApiKey: String?) { val client = OkHttpClient.Builder().build(); private val logger = Logger.getLogger(SnService::class.java.name) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 7b94856..2b09b5d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,4 +1,9 @@ sn: - endpoint: https://api.sn.solsynth.dev - client_id: highland-mc - client_secret: 12345678 \ No newline at end of file + endpoint: https://api.solian.app + client_id: goatcraft + client_secret: 12345678 + bot_secret: 114.514.19198 + destination_chat_id: some_chat_id +chat: + sync_rooms: [] + outgoing_room: 00000000-0000-0000-0000-00000000008b