Compare commits
10 Commits
7b66cdedcd
...
c2e7f613c7
Author | SHA1 | Date | |
---|---|---|---|
c2e7f613c7
|
|||
bc23269778
|
|||
8ef05de8ad
|
|||
6303d44ab4
|
|||
e77841fc09
|
|||
affe9a40f5 | |||
100d8bc747 | |||
5bdf6e07a7 | |||
582c0c45b2 | |||
d89cd6f9ce |
@@ -1,11 +1,11 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.20-Beta2"
|
||||
kotlin("plugin.serialization") version "2.1.10"
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1" // add shadow plugin
|
||||
}
|
||||
|
||||
group = "dev.solsynth"
|
||||
version = "1.0-SNAPSHOT"
|
||||
version = "1.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -23,10 +23,16 @@ repositories {
|
||||
dependencies {
|
||||
compileOnly("org.spigotmc:spigot-api:1.21.4-R0.1-SNAPSHOT")
|
||||
compileOnly("com.github.MilkBowl:VaultAPI:1.7")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
|
||||
// These will be packaged into the shadow JAR
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
|
||||
// Test dependencies
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
|
||||
}
|
||||
|
||||
val targetJavaVersion = 21
|
||||
@@ -34,11 +40,21 @@ kotlin {
|
||||
jvmToolchain(targetJavaVersion)
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
dependsOn("shadowJar")
|
||||
// Configure the shadowJar task
|
||||
tasks {
|
||||
shadowJar {
|
||||
archiveClassifier.set("") // so that the shadow JAR replaces the “normal” JAR
|
||||
mergeServiceFiles()
|
||||
// Optionally relocate packages to avoid conflicts with other plugins
|
||||
// e.g. relocate("kotlin", "dev.solsynth.shadow.kotlin")
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
// Make “build” produce the shadow jar
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
|
||||
processResources {
|
||||
val props = mapOf("version" to version)
|
||||
inputs.properties(props)
|
||||
filteringCharset = "UTF-8"
|
||||
@@ -46,3 +62,4 @@ tasks.processResources {
|
||||
expand(props)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,21 +2,94 @@ package dev.solsynth.snConnect
|
||||
|
||||
import dev.solsynth.snConnect.commands.SnCommand
|
||||
import dev.solsynth.snConnect.commands.SnCommandCompleter
|
||||
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.*
|
||||
import net.md_5.bungee.api.ChatColor
|
||||
import net.md_5.bungee.api.chat.ClickEvent
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
||||
import net.md_5.bungee.api.chat.HoverEvent
|
||||
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.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<String> = emptyList()
|
||||
private var destinationChatId: String? = null
|
||||
private var webSocketJob: Job? = null
|
||||
private var messages: Map<String, String> = mapOf()
|
||||
|
||||
private fun handleWebSocketPacket(packet: WebSocketPacket) {
|
||||
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().not()) 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
|
||||
val profileUrl = "https://solian.app/@${message.sender.account.name}"
|
||||
val componentBuilder = ComponentBuilder()
|
||||
.append("☀").color(ChatColor.YELLOW)
|
||||
.event(HoverEvent(HoverEvent.Action.SHOW_TEXT, arrayOf(TextComponent("Solar Network"))))
|
||||
.append(" ")
|
||||
.append(roomName).color(ChatColor.GOLD)
|
||||
.append(" ").color(ChatColor.YELLOW)
|
||||
.append(senderName).color(ChatColor.YELLOW)
|
||||
.event(ClickEvent(ClickEvent.Action.OPEN_URL, profileUrl))
|
||||
.append(" » ").color(ChatColor.GRAY)
|
||||
if (message.content != null && message.content.isNotBlank()) {
|
||||
componentBuilder.append(message.content).color(ChatColor.WHITE)
|
||||
}
|
||||
if (message.attachments.isNotEmpty()) {
|
||||
for (attachment in message.attachments) {
|
||||
val fileName = attachment.name.takeIf { it.isNotBlank() } ?: "attachment"
|
||||
val url = "https://solian.app/files/${attachment.id}"
|
||||
componentBuilder.append(" <$fileName>").color(ChatColor.BLUE)
|
||||
.event(ClickEvent(ClickEvent.Action.OPEN_URL, url))
|
||||
}
|
||||
}
|
||||
val component = componentBuilder.create()
|
||||
for (player in getOnlinePlayers()) {
|
||||
player.spigot().sendMessage(*component)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.warning("Failed to parse chat message: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnable() {
|
||||
logger.info(String.format("Enabling Version %s", description.version));
|
||||
|
||||
this.saveDefaultConfig()
|
||||
|
||||
messages = mapOf(
|
||||
"join" to "➡️ {player} joined the game.",
|
||||
"joinFirst" to "➡️ {player} first time joined the game.",
|
||||
"quit" to "⬅️ {player} left the game.",
|
||||
"death" to "💀 {player} {message}",
|
||||
"advancement" to "🎉 {player} unlocked advancement: {advancement}",
|
||||
"serverStart" to "🚀 Server started successfully",
|
||||
"serverStop" to "⏹️ Server stopped"
|
||||
) + (config.getConfigurationSection("messages")?.getValues(false) as? Map<String, String> ?: emptyMap())
|
||||
|
||||
if (!setupNetwork()) {
|
||||
logger.severe("Failed to setup Solar Network Network, check your configuration.")
|
||||
}
|
||||
@@ -26,7 +99,11 @@ class SolarNetworkConnect : JavaPlugin() {
|
||||
)
|
||||
}
|
||||
|
||||
Bukkit.getPluginCommand("solar")!!.setExecutor(SnCommand(this.sn!!))
|
||||
if (messageService != null && destinationChatId != null) {
|
||||
server.pluginManager.registerEvents(SnChatListener(messageService!!, destinationChatId!!, messages), this)
|
||||
}
|
||||
|
||||
Bukkit.getPluginCommand("solar")!!.setExecutor(SnCommand(this.sn!!, this.economy))
|
||||
Bukkit.getPluginCommand("solar")!!.tabCompleter = SnCommandCompleter()
|
||||
|
||||
logger.info(
|
||||
@@ -35,17 +112,41 @@ class SolarNetworkConnect : JavaPlugin() {
|
||||
config.getString("sn.endpoint")
|
||||
)
|
||||
);
|
||||
|
||||
// Send server start message
|
||||
destinationChatId?.let { chatId ->
|
||||
messageService?.sendMessage(chatId, messages["serverStart"] ?: "🚀 Server started successfully")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
logger.info(String.format("Disabled Version %s", description.version));
|
||||
|
||||
// Send server stop message
|
||||
destinationChatId?.let { chatId ->
|
||||
messageService?.sendMessage(chatId, messages["serverStop"] ?: "⏹️ Server stopped")
|
||||
}
|
||||
|
||||
sn?.disconnect()
|
||||
webSocketJob?.cancel()
|
||||
}
|
||||
|
||||
private fun setupNetwork(): Boolean {
|
||||
val baseUrl = config.getString("sn.endpoint") ?: return false;
|
||||
val clientId = config.getString("sn.client_id") ?: return false;
|
||||
val clientSecret = config.getString("sn.client_secret") ?: return false;
|
||||
sn = SnService(baseUrl, clientId, clientSecret);
|
||||
val botApiKey = config.getString("sn.bot_secret");
|
||||
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!!)
|
||||
webSocketJob = GlobalScope.launch {
|
||||
sn!!.connectWebSocketAsFlow().collect { packet ->
|
||||
handleWebSocketPacket(packet)
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -1,17 +1,19 @@
|
||||
package dev.solsynth.snConnect.commands
|
||||
|
||||
import dev.solsynth.snConnect.services.SnOrderService
|
||||
import dev.solsynth.snConnect.services.SnService
|
||||
import dev.solsynth.snConnect.services.*
|
||||
import net.md_5.bungee.api.chat.ClickEvent
|
||||
import net.md_5.bungee.api.chat.TextComponent
|
||||
import net.milkbowl.vault.economy.Economy
|
||||
import net.milkbowl.vault.economy.EconomyResponse
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.Sound
|
||||
import org.bukkit.command.Command
|
||||
import org.bukkit.command.CommandExecutor
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.command.TabCompleter
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
class SnCommand(private val sn: SnService) : CommandExecutor {
|
||||
class SnCommand(private val sn: SnService, private val eco: Economy?) : CommandExecutor {
|
||||
override fun onCommand(p0: CommandSender, p1: Command, p2: String, p3: Array<out String>): Boolean {
|
||||
if (p0 !is Player) {
|
||||
return false;
|
||||
@@ -26,22 +28,132 @@ class SnCommand(private val sn: SnService) : CommandExecutor {
|
||||
return true;
|
||||
}
|
||||
|
||||
val orderSrv = SnOrderService(sn);
|
||||
|
||||
if (p3[1].equals("confirm", ignoreCase = true) && p3.size >= 3) {
|
||||
// Confirming order
|
||||
val orderNumber = p3[2].toLongOrNull();
|
||||
if (orderNumber == null) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "Invalid order number, it must be a number.");
|
||||
return true;
|
||||
}
|
||||
|
||||
p0.sendMessage(ChatColor.GRAY.toString() + "Confirming payment, please stand by...");
|
||||
|
||||
val order: SnOrder
|
||||
try {
|
||||
order = orderSrv.getOrder(orderNumber);
|
||||
orderSrv.cancelOrder(orderNumber);
|
||||
} catch (_: Exception) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "An error occurred while pulling transaction. Make sure the order is exists then try again later.")
|
||||
return true;
|
||||
}
|
||||
|
||||
if (order.status != 1L) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "Order was not paid yet.");
|
||||
return true;
|
||||
}
|
||||
|
||||
val bal = order.amount.toDouble() * 100;
|
||||
eco?.depositPlayer(p0.player, bal)
|
||||
|
||||
val doneComponent = TextComponent(ChatColor.GREEN.toString() + "Done!")
|
||||
val moneyComponent = TextComponent(ChatColor.GOLD.toString() + ChatColor.BOLD + " $bal$")
|
||||
val suffixComponent =
|
||||
TextComponent(ChatColor.GREEN.toString() + " has been added to your balance.")
|
||||
|
||||
p0.playSound(p0.player!!, Sound.BLOCK_ANVIL_PLACE, 1.0F, 1.0F)
|
||||
p0.spigot().sendMessage(doneComponent, moneyComponent, suffixComponent)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creating new order
|
||||
val amount = p3[1].toDoubleOrNull();
|
||||
if (amount == null) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "You need to specific an amount of number to deposit.")
|
||||
return true;
|
||||
}
|
||||
|
||||
val orderSrv = SnOrderService(sn);
|
||||
val order = orderSrv.createOrder("Deposit to Highland MC", amount / 100);
|
||||
p0.sendMessage(ChatColor.GRAY.toString() + "Creating order, please stand by...");
|
||||
|
||||
val linkComponent = TextComponent(ChatColor.GOLD.toString() + "Click here to payment page")
|
||||
linkComponent.clickEvent =
|
||||
ClickEvent(ClickEvent.Action.OPEN_URL, "https://solsynth.dev/orders/${order.id}");
|
||||
p0.spigot().sendMessage(linkComponent);
|
||||
val order: SnOrder
|
||||
try {
|
||||
order = orderSrv.createOrder("Deposit to Highland MC", amount / 100);
|
||||
} catch (_: Exception) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "An error occurred while creating order. Try again later.")
|
||||
return true;
|
||||
}
|
||||
|
||||
else -> return false;
|
||||
val linkComponent =
|
||||
TextComponent(ChatColor.GOLD.toString() + ChatColor.UNDERLINE.toString() + ChatColor.BOLD.toString() + "click here")
|
||||
linkComponent.clickEvent =
|
||||
ClickEvent(ClickEvent.Action.OPEN_URL, "https://solsynth.dev/orders/${order.id}");
|
||||
val orderHintComponent =
|
||||
TextComponent(ChatColor.GRAY.toString() + "Order created, number " + ChatColor.WHITE + ChatColor.BOLD + "#${order.id}")
|
||||
val followingComponent = TextComponent(ChatColor.GRAY.toString() + " to the payment page.")
|
||||
p0.spigot()
|
||||
.sendMessage(orderHintComponent, linkComponent, followingComponent);
|
||||
|
||||
val afterPaidComponent =
|
||||
TextComponent(ChatColor.UNDERLINE.toString() + ChatColor.YELLOW + "After you paid, click here to confirm payment.")
|
||||
afterPaidComponent.clickEvent =
|
||||
ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sn deposit confirm ${order.id}")
|
||||
p0.spigot().sendMessage(afterPaidComponent);
|
||||
}
|
||||
|
||||
"withdraw" -> {
|
||||
if (p3.size < 2) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "You need to specific an amount to deposit.")
|
||||
return true;
|
||||
}
|
||||
|
||||
val amount = p3[1].toDoubleOrNull();
|
||||
if (amount == null) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "You need to specific an amount of number to deposit.")
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p3.size < 3) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "You need to specific a wallet account to deposit.")
|
||||
return true;
|
||||
}
|
||||
|
||||
val walletId = p3[2].toLongOrNull();
|
||||
if (walletId == null) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "You need to specific a wallet account to deposit.")
|
||||
return true;
|
||||
}
|
||||
|
||||
p0.sendMessage(ChatColor.GRAY.toString() + "Making transaction, please stand by...");
|
||||
|
||||
val bal = amount / 100;
|
||||
val resp = eco?.withdrawPlayer(p0.player, "Withdraw to Source Point - $bal SRC", amount);
|
||||
if (resp?.type != EconomyResponse.ResponseType.SUCCESS) {
|
||||
p0.sendMessage(ChatColor.RED.toString() + "Your in game account has no enough money for that.")
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
val transactionSrv = SnTransactionService(sn);
|
||||
val transaction = transactionSrv.makeTransaction(bal, "Withdraw from Highland MC", walletId);
|
||||
val transactionHintComponent =
|
||||
TextComponent(ChatColor.GREEN.toString() + "Done! transaction number " + ChatColor.WHITE + ChatColor.BOLD + "#${transaction.id}")
|
||||
|
||||
p0.playSound(p0.player!!, Sound.BLOCK_ANVIL_PLACE, 1.0F, 1.0F)
|
||||
p0.spigot().sendMessage(transactionHintComponent)
|
||||
} catch (_: Exception) {
|
||||
eco?.depositPlayer(p0.player, "Withdraw to Source Point Failed - Refund", amount)
|
||||
p0.sendMessage(ChatColor.RED.toString() + "An error occurred while making transaction. Make sure your wallet is exists then try again later.")
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
else -> {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -0,0 +1,49 @@
|
||||
package dev.solsynth.snConnect.listeners
|
||||
|
||||
import dev.solsynth.snConnect.services.SnMessageService
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.EventPriority
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.entity.PlayerDeathEvent
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent
|
||||
import org.bukkit.event.player.PlayerAdvancementDoneEvent
|
||||
import org.bukkit.event.player.PlayerJoinEvent
|
||||
import org.bukkit.event.player.PlayerQuitEvent
|
||||
|
||||
class SnChatListener(
|
||||
private val messageService: SnMessageService,
|
||||
private val destinationChatId: String,
|
||||
private val messages: Map<String, String>
|
||||
) : Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
fun onPlayerChat(event: AsyncPlayerChatEvent) {
|
||||
val message = "${event.player.name}: ${event.message}"
|
||||
messageService.sendMessage(destinationChatId, message)
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
fun onPlayerJoin(event: PlayerJoinEvent) {
|
||||
val firstTime = event.player.hasPlayedBefore();
|
||||
val templateKey = if (!firstTime) "join" else "joinFirst";
|
||||
val template = messages[templateKey]
|
||||
?: if (!firstTime) "➡️ {player} joined the game." else "➡️ {player} first time joined the game."
|
||||
val message = template.replace("{player}", event.player.name)
|
||||
messageService.sendMessage(destinationChatId, message)
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
fun onPlayerQuit(event: PlayerQuitEvent) {
|
||||
val template = messages["quit"] ?: "⬅️ {player} left the game."
|
||||
val message = template.replace("{player}", event.player.name)
|
||||
messageService.sendMessage(destinationChatId, message)
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
fun onPlayerDeath(event: PlayerDeathEvent) {
|
||||
val template = messages["death"] ?: "💀 {player} {message}"
|
||||
val message = template.replace("{player}", event.entity.name).replace("{message}", event.deathMessage ?: "")
|
||||
messageService.sendMessage(destinationChatId, message)
|
||||
}
|
||||
}
|
37
src/main/kotlin/dev/solsynth/snConnect/models/SnAccount.kt
Normal file
37
src/main/kotlin/dev/solsynth/snConnect/models/SnAccount.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
import dev.solsynth.snConnect.models.SnAccountProfile
|
||||
import dev.solsynth.snConnect.models.SnContactMethod
|
||||
import dev.solsynth.snConnect.models.SnWalletSubscriptionRef
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class SnAccount(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val nick: String,
|
||||
val language: String,
|
||||
val region: String = "",
|
||||
@SerialName("is_superuser") val isSuperuser: Boolean,
|
||||
@SerialName("automated_id") val automatedId: String?,
|
||||
val profile: SnAccountProfile,
|
||||
@SerialName("perk_subscription") val perkSubscription: SnWalletSubscriptionRef?,
|
||||
val contacts: List<SnContactMethod> = emptyList(),
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
) {
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromJson(jsonElement: JsonElement): SnAccount {
|
||||
return json.decodeFromJsonElement<SnAccount>(jsonElement)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class SnAccountProfile(
|
||||
val id: String,
|
||||
@SerialName("first_name") val firstName: String = "",
|
||||
@SerialName("middle_name") val middleName: String = "",
|
||||
@SerialName("last_name") val lastName: String = "",
|
||||
val bio: String = "",
|
||||
val gender: String = "",
|
||||
val pronouns: String = "",
|
||||
val location: String = "",
|
||||
@SerialName("time_zone") val timeZone: String = "",
|
||||
val birthday: String?,
|
||||
@SerialName("last_seen_at") val lastSeenAt: String?,
|
||||
val experience: Int,
|
||||
val level: Int,
|
||||
@SerialName("social_credits") val socialCredits: Double = 100.0,
|
||||
@SerialName("social_credits_level") val socialCreditsLevel: Int = 0,
|
||||
@SerialName("leveling_progress") val levelingProgress: Double,
|
||||
val picture: SnCloudFile?,
|
||||
val background: SnCloudFile?,
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
) {
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromJson(jsonElement: JsonElement): SnAccountProfile {
|
||||
return json.decodeFromJsonElement<SnAccountProfile>(jsonElement)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class SnAccountStatus(
|
||||
val id: String,
|
||||
val attitude: Int,
|
||||
@SerialName("is_online") val isOnline: Boolean,
|
||||
@SerialName("is_invisible") val isInvisible: Boolean,
|
||||
@SerialName("is_not_disturb") val isNotDisturb: Boolean,
|
||||
@SerialName("is_customized") val isCustomized: Boolean,
|
||||
val label: String = "",
|
||||
@SerialName("cleared_at") val clearedAt: String?,
|
||||
@SerialName("account_id") val accountId: String,
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
) {
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromJson(jsonElement: JsonElement): SnAccountStatus {
|
||||
return json.decodeFromJsonElement<SnAccountStatus>(jsonElement)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import SnAccount
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class SnChatMember(
|
||||
val id: String,
|
||||
@SerialName("chat_room_id") val chatRoomId: String,
|
||||
// val chatRoom: SnChatRoom?, // Placeholder
|
||||
@SerialName("account_id") val accountId: String,
|
||||
val account: SnAccount, // Placeholder
|
||||
val nick: String?,
|
||||
val role: Int,
|
||||
val notify: Int,
|
||||
@SerialName("joined_at") val joinedAt: String?,
|
||||
@SerialName("break_until") val breakUntil: String? = null,
|
||||
@SerialName("timeout_until") val timeoutUntil: String? = null,
|
||||
@SerialName("is_bot") val isBot: Boolean,
|
||||
// val status: SnAccountStatus?, // Placeholder
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
)
|
@@ -0,0 +1,40 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class SnChatMessage(
|
||||
val id: String,
|
||||
val type: String = "text",
|
||||
val content: String?,
|
||||
val nonce: String?,
|
||||
@SerialName("members_mentioned") val membersMentioned: List<String>? = emptyList(),
|
||||
@SerialName("edited_at") val editedAt: String?,
|
||||
val attachments: List<SnCloudFile> = emptyList(),
|
||||
// val reactions: List<SnChatReaction> = emptyList(), // Placeholder
|
||||
@SerialName("replied_message_id") val repliedMessageId: String?,
|
||||
@SerialName("forwarded_message_id") val forwardedMessageId: String?,
|
||||
@SerialName("sender_id") val senderId: String,
|
||||
val sender: SnChatMember,
|
||||
@SerialName("chat_room") val chatRoom: SnChatRoom,
|
||||
@SerialName("chat_room_id") val chatRoomId: String,
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
) {
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromJson(jsonElement: JsonElement): SnChatMessage {
|
||||
return json.decodeFromJsonElement<SnChatMessage>(jsonElement)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class SnChatReaction(
|
||||
val id: String,
|
||||
@SerialName("message_id") val messageId: String,
|
||||
@SerialName("sender_id") val senderId: String,
|
||||
// val sender: SnChatMember, // Placeholder
|
||||
val symbol: String,
|
||||
val attitude: Int,
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
)
|
26
src/main/kotlin/dev/solsynth/snConnect/models/SnChatRoom.kt
Normal file
26
src/main/kotlin/dev/solsynth/snConnect/models/SnChatRoom.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class SnChatRoom(
|
||||
val id: String,
|
||||
val name: String?,
|
||||
val description: String?,
|
||||
val type: Int,
|
||||
@SerialName("is_public") val isPublic: Boolean = false,
|
||||
@SerialName("is_community") val isCommunity: Boolean = false,
|
||||
// val picture: SnCloudFile?, // Placeholder
|
||||
// val background: SnCloudFile?, // Placeholder
|
||||
val realmId: String? = null,
|
||||
// val realm: SnRealm?, // Placeholder
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
// val members: List<SnChatMember>?, // Placeholder
|
||||
)
|
22
src/main/kotlin/dev/solsynth/snConnect/models/SnCloudFile.kt
Normal file
22
src/main/kotlin/dev/solsynth/snConnect/models/SnCloudFile.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
|
||||
@Serializable
|
||||
data class SnCloudFile(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val size: Long,
|
||||
) {
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromJson(jsonElement: JsonElement): SnCloudFile {
|
||||
return json.decodeFromJsonElement<SnCloudFile>(jsonElement)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class SnContactMethod(
|
||||
val id: String,
|
||||
val type: Int,
|
||||
@SerialName("verified_at") val verifiedAt: String?,
|
||||
@SerialName("is_primary") val isPrimary: Boolean,
|
||||
@SerialName("is_public") val isPublic: Boolean,
|
||||
val content: String,
|
||||
@SerialName("account_id") val accountId: String,
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
) {
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromJson(jsonElement: JsonElement): SnContactMethod {
|
||||
return json.decodeFromJsonElement<SnContactMethod>(jsonElement)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
|
||||
@Serializable
|
||||
data class SnWalletSubscriptionRef(
|
||||
val id: String,
|
||||
val identifier: String,
|
||||
@SerialName("account_id") val accountId: String,
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("deleted_at") val deletedAt: String?,
|
||||
) {
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromJson(jsonElement: JsonElement): SnWalletSubscriptionRef {
|
||||
return json.decodeFromJsonElement<SnWalletSubscriptionRef>(jsonElement)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package dev.solsynth.snConnect.models
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
@Serializable
|
||||
data class WebSocketPacket(
|
||||
val type: String,
|
||||
@SerialName("data") val data: JsonElement?,
|
||||
val endpoint: String?,
|
||||
@SerialName("error_message") val errorMessage: String?
|
||||
) {
|
||||
companion object {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun fromJson(jsonString: String): WebSocketPacket? {
|
||||
return try {
|
||||
json.decodeFromString(jsonString)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.IOException
|
||||
|
||||
@Serializable
|
||||
class SnOrderRequest(
|
||||
data class SnOrderRequest(
|
||||
@SerialName("client_id")
|
||||
val clientId: String,
|
||||
@SerialName("client_secret")
|
||||
@@ -19,6 +19,14 @@ class SnOrderRequest(
|
||||
val amount: Double
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SnOrderCancelRequest(
|
||||
@SerialName("client_id")
|
||||
val clientId: String,
|
||||
@SerialName("client_secret")
|
||||
val clientSecret: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SnOrder(
|
||||
val id: Long,
|
||||
@@ -66,4 +74,37 @@ class SnOrderService(private val sn: SnService) {
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
fun getOrder(id: Long): SnOrder {
|
||||
val request = Request.Builder()
|
||||
.url(sn.getUrl("wa", "/orders/$id"))
|
||||
.get()
|
||||
.build()
|
||||
sn.client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
val responseBody = response.body!!.string()
|
||||
val out = json.decodeFromString<SnOrder>(responseBody)
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelOrder(id: Long): SnOrder {
|
||||
val body = SnOrderCancelRequest(
|
||||
sn.clientId,
|
||||
sn.clientSecret,
|
||||
);
|
||||
val request = Request.Builder()
|
||||
.url(sn.getUrl("wa", "/orders/$id/cancel"))
|
||||
.post(Json.encodeToString(body).toRequestBody("application/json".toMediaTypeOrNull()))
|
||||
.build()
|
||||
|
||||
sn.client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
val responseBody = response.body!!.string()
|
||||
val out = json.decodeFromString<SnOrder>(responseBody)
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +1,98 @@
|
||||
package dev.solsynth.snConnect.services
|
||||
|
||||
import dev.solsynth.snConnect.models.WebSocketPacket
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.WebSocket
|
||||
import okhttp3.WebSocketListener
|
||||
import okhttp3.Response
|
||||
import okio.ByteString
|
||||
import java.util.logging.Logger
|
||||
|
||||
class SnService(private val baseUrl: String, val clientId: String, val clientSecret: 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)
|
||||
private var websocket: WebSocket? = null
|
||||
|
||||
fun getUrl(service: String, segment: String): String {
|
||||
return "$baseUrl/cgi/$service$segment"
|
||||
return "$baseUrl/$service$segment"
|
||||
}
|
||||
|
||||
fun getWsBaseUrl(): String {
|
||||
return baseUrl.replaceFirst("http", "ws")
|
||||
}
|
||||
|
||||
fun connectWebSocket(listener: WebSocketListener): WebSocket {
|
||||
val request = Request.Builder()
|
||||
.url("${getWsBaseUrl()}/ws")
|
||||
.apply {
|
||||
botApiKey?.let { header("Authorization", "Bearer $it") }
|
||||
}
|
||||
.build()
|
||||
return client.newWebSocket(request, listener)
|
||||
}
|
||||
|
||||
fun connectWebSocketAsFlow(): Flow<WebSocketPacket> = channelFlow {
|
||||
val url = "${getWsBaseUrl()}/ws"
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.apply {
|
||||
botApiKey?.let { header("Authorization", "Bearer $it") }
|
||||
}
|
||||
.build()
|
||||
|
||||
fun connect() {
|
||||
logger.info("Attempting WebSocket connection to $url")
|
||||
websocket = client.newWebSocket(request, object : WebSocketListener() {
|
||||
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||
logger.info("WebSocket connection opened successfully to $url")
|
||||
}
|
||||
|
||||
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||
WebSocketPacket.fromJson(text)?.let { trySend(it) }
|
||||
}
|
||||
|
||||
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
|
||||
val text = bytes.string(Charsets.UTF_8)
|
||||
WebSocketPacket.fromJson(text)?.let { trySend(it) }
|
||||
}
|
||||
|
||||
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||
logger.severe("WebSocket connection failed: ${t.message}, response: ${response?.code}")
|
||||
websocket = null
|
||||
GlobalScope.launch {
|
||||
delay(1000)
|
||||
connect()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
||||
logger.info("WebSocket connection closed: code=$code, reason=$reason")
|
||||
websocket = null
|
||||
GlobalScope.launch {
|
||||
delay(1000)
|
||||
connect()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
connect()
|
||||
|
||||
awaitClose {
|
||||
websocket?.close(1000, "Shutting down")
|
||||
websocket = null
|
||||
}
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
websocket?.close(1000, "Disconnecting")
|
||||
websocket = null
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
package dev.solsynth.snConnect.services
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
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
|
||||
|
||||
@Serializable
|
||||
data class SnTransaction (
|
||||
val id: Long,
|
||||
@SerialName("created_at")
|
||||
val createdAt: Instant,
|
||||
@SerialName("updated_at")
|
||||
val updatedAt: Instant,
|
||||
@SerialName("deleted_at")
|
||||
val deletedAt: Instant? = null,
|
||||
val remark: String,
|
||||
val amount: String,
|
||||
@SerialName("payer_id")
|
||||
val payerID: Long? = null,
|
||||
@SerialName("payee_id")
|
||||
val payeeID: Long? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SnTransactionRequest(
|
||||
@SerialName("client_id")
|
||||
val clientId: String,
|
||||
@SerialName("client_secret")
|
||||
val clientSecret: String,
|
||||
@SerialName("payee_id")
|
||||
val payeeID: Long? = null,
|
||||
@SerialName("payer_id")
|
||||
val payerID: Long? = null,
|
||||
val remark: String,
|
||||
val amount: Double
|
||||
)
|
||||
|
||||
class SnTransactionService(private val sn: SnService) {
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
fun makeTransaction(amount: Double, remark: String, payeeID: Long): SnTransaction {
|
||||
val body = SnTransactionRequest(
|
||||
sn.clientId,
|
||||
sn.clientSecret,
|
||||
amount = amount,
|
||||
remark = remark,
|
||||
payeeID = payeeID,
|
||||
);
|
||||
val request = Request.Builder()
|
||||
.url(sn.getUrl("wa", "/transactions"))
|
||||
.post(Json.encodeToString(body).toRequestBody("application/json".toMediaTypeOrNull()))
|
||||
.build()
|
||||
|
||||
sn.client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
val responseBody = response.body!!.string()
|
||||
val out = json.decodeFromString<SnTransaction>(responseBody)
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
fun getTransaction(id: Long): SnTransaction {
|
||||
val request = Request.Builder()
|
||||
.url(sn.getUrl("wa", "/transactions/$id"))
|
||||
.get()
|
||||
.build()
|
||||
sn.client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
val responseBody = response.body!!.string()
|
||||
val out = json.decodeFromString<SnTransaction>(responseBody)
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,15 @@
|
||||
sn:
|
||||
endpoint: https://api.sn.solsynth.dev
|
||||
client_id: highland-mc
|
||||
endpoint: https://api.solian.app
|
||||
client_id: goatcraft
|
||||
client_secret: 12345678
|
||||
bot_secret: 114.514.19198
|
||||
chat:
|
||||
sync_rooms: []
|
||||
outgoing_room: 00000000-0000-0000-0000-00000000008b
|
||||
messages:
|
||||
join: "➡️ {player} joined the game."
|
||||
joinFirst: "➡️ {player} first time joined the game."
|
||||
quit: "⬅️ {player} left the game."
|
||||
death: "💀 {player} {message}"
|
||||
serverStart: "🚀 Server started successfully"
|
||||
serverStop: "⏹️ Server stopped"
|
||||
|
30
src/test/kotlin/dev/solsynth/snConnect/WebSocketTest.kt
Normal file
30
src/test/kotlin/dev/solsynth/snConnect/WebSocketTest.kt
Normal file
@@ -0,0 +1,30 @@
|
||||
package dev.solsynth.snConnect
|
||||
|
||||
import dev.solsynth.snConnect.services.SnService
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
suspend fun main() {
|
||||
val service = SnService(
|
||||
baseUrl = "https://api.solian.app", // Replace with actual test server URL
|
||||
clientId = "goatcraft",
|
||||
clientSecret = "testClientSecret",
|
||||
botApiKey = "02-l7ARXnEimQaBY0Dj3SQ.g4UTdShR9GiNWBDjC7qm9Xu83t0Vq8mQ2WPaTO8S-_j6EuKcWd0Kqb_hEkFlahmhfAd5lcH7j_-N8knSIXjo3X7OSFck0E_ogwluZpGSzqbYOrlBAQc9Rk1VhHVNu_W4fi9NR6NnUwpAgyTIh2RuRHk98oVa2I4Ie-NXPybb26N3i9wXv3-wXlkml0IOrhs3FRKMbcJNmKIzEYP0KQNqs3w9TAx0R2fe9DAAQ8WvPW5iPGSbyxr_fF4Qm7tQ0rQvg89x0cUGmKQHtCTeKa2WZi7UBTbw_b4SiHMqpLMxIDQBEZQGqkJ5r37_YCyCNqe5Huha86GG7bT__m6z5emeow"
|
||||
)
|
||||
|
||||
print("server started")
|
||||
// Collect from the flow for a limited time to avoid hanging on failure
|
||||
val packets = withTimeoutOrNull(10000L) { // 10 seconds timeout
|
||||
val collectedPackets = mutableListOf<String>()
|
||||
service.connectWebSocketAsFlow().collect { packet ->
|
||||
collectedPackets.add("Received packet: type=${packet.type}, endpoint=${packet.endpoint}")
|
||||
print(collectedPackets.last())
|
||||
// In a real test, you might check packet contents or behavior
|
||||
}
|
||||
collectedPackets
|
||||
}
|
||||
|
||||
// For this test, we're mainly checking that the flow can be created and the setup doesn't crash
|
||||
// In real scenarios, you'd need a WebSocket server running at the specified URL
|
||||
print("WebSocket test finished. Packets received: ${packets?.size ?: 0}")
|
||||
assert(packets != null) // Flow started without immediate error
|
||||
}
|
Reference in New Issue
Block a user