[+] Wacca user/status/login

pull/29/head
Azalea 2024-03-28 03:37:14 -04:00
parent 39d62099df
commit 00c5edcea7
6 changed files with 71 additions and 28 deletions

View File

@ -111,6 +111,10 @@ fun Str.asDateTime() = try { LocalDateTime.parse(this, DateTimeFormatter.ISO_LOC
catch (e: Exception) { try { LocalDateTime.parse(this, ALT_DATETIME_FORMAT) } catch (e: Exception) { try { LocalDateTime.parse(this, ALT_DATETIME_FORMAT) }
catch (e: Exception) { null } } catch (e: Exception) { null } }
val Calendar.year get() = get(Calendar.YEAR)
val Calendar.month get() = get(Calendar.MONTH) + 1
val Calendar.day get() = get(Calendar.DAY_OF_MONTH)
// Encodings // Encodings
fun Long.toHex(len: Int = 16): Str = "0x${this.toString(len).padStart(len, '0').uppercase()}" fun Long.toHex(len: Int = 16): Str = "0x${this.toString(len).padStart(len, '0').uppercase()}"
fun Map<String, Any>.toUrl() = entries.joinToString("&") { (k, v) -> "$k=$v" } fun Map<String, Any>.toUrl() = entries.joinToString("&") { (k, v) -> "$k=$v" }

View File

@ -41,6 +41,17 @@ inline fun <reified T> Str.parseJackson() = if (contains("null")) {
else JACKSON.readValue(this, T::class.java) else JACKSON.readValue(this, T::class.java)
fun <T> T.toJson() = JACKSON.writeValueAsString(this) fun <T> T.toJson() = JACKSON.writeValueAsString(this)
inline fun <reified T> String.json() = try {
JACKSON.readValue(this, T::class.java)
}
catch (e: Exception) {
println("Failed to parse JSON: $this")
throw e
}
fun String.jsonMap(): Map<String, Any?> = json()
fun String.jsonArray(): List<Map<String, Any?>> = json()
// KotlinX Serialization // KotlinX Serialization
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)

View File

@ -12,24 +12,25 @@ import io.ktor.client.utils.*
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import java.util.*
val empty = emptyList<Any>() val empty = emptyList<Any>()
@RestController @RestController
@API("/g/wacca/") @API("/g/wacca/")
class WaccaServer(val rp: WaccaRepos, val cardRepo: CardRepository) { class WaccaServer(val rp: WaccaRepos, val cardRepo: CardRepository) {
val handlerMap = mutableMapOf<String, (BaseRequest, List<Any>) -> List<Any>>() val handlerMap = mutableMapOf<String, (BaseRequest, List<Any>) -> Any>()
val cacheMap = mutableMapOf<String, String>() val cacheMap = mutableMapOf<String, String>()
val log = logger() val log = logger()
init { api() } init { init() }
// DSL Functions // DSL Functions
fun options(u: WaccaUser?) = u?.let { rp.option.findByUser(u).associate { it.optId to it.value } } ?: emptyMap() fun options(u: WaccaUser?) = u?.let { rp.option.findByUser(u).associate { it.optId to it.value } } ?: emptyMap()
operator fun Map<Int, Int>.get(type: WaccaOptionType) = getOrDefault(type.id, type.default) operator fun Map<Int, Int>.get(type: WaccaOptionType) = getOrDefault(type.id, type.default)
operator fun String.minus(value: Any) = value operator fun String.minus(value: Any) = value
operator fun String.invoke(block: (BaseRequest, List<Any>) -> List<Any>) { handlerMap[this.lowercase()] = block } operator fun String.invoke(block: (BaseRequest, List<Any>) -> Any) { handlerMap[this.lowercase()] = block }
infix fun String.cached(block: () -> Any) { cacheMap[this.lowercase()] = block().toJson() } infix fun String.cached(block: () -> Any) { cacheMap[this.lowercase()] = block().toJson() }
/** Convert "3.07.01.JPN.26935.S" into "3.7.1" */ /** Convert "3.07.01.JPN.26935.S" into "3.7.1" */
@ -54,13 +55,18 @@ class WaccaServer(val rp: WaccaRepos, val cardRepo: CardRepository) {
if (path in cacheMap) return resp(cacheMap[path]!!) if (path in cacheMap) return resp(cacheMap[path]!!)
if (path !in handlerMap) return resp("[]", 1, "Not Found") if (path !in handlerMap) return resp("[]", 1, "Not Found")
log.info("Wacca $path < $body")
val br = JACKSON.parse<BaseRequest>(body) val br = JACKSON.parse<BaseRequest>(body)
val resp = handlerMap[path]!!(br, br.params) return handlerMap[path]!!(br, br.params).let { when (it) {
return resp(resp.toJson()) is String -> return resp(it)
is List<*> -> return resp(it.toJson())
else -> Error("Invalid response type ${it.javaClass}")
} }.let { log.info("Wacca $path > $it") }
} }
} }
fun WaccaServer.api() { fun WaccaServer.init() {
"housing/get" cached { ls("housingId" - 39, "isNewCab" - 0) } "housing/get" cached { ls("housingId" - 39, "isNewCab" - 0) }
"housing/start" cached { ls("regionId" - 1, "recommendSongList" - ls(1269, 1007, 1270, 1002, 1020, 1003, 1008, "housing/start" cached { ls("regionId" - 1, "recommendSongList" - ls(1269, 1007, 1270, 1002, 1020, 1003, 1008,
1211, 1018, 1092, 1056, 32, 1260, 1230, 1258, 1251, 2212, 1264, 1125, 1037, 2001, 1272, 1126, 1119, 1104, 1070, 1211, 1018, 1092, 1056, 32, 1260, 1230, 1258, 1251, 2212, 1264, 1125, 1037, 2001, 1272, 1126, 1119, 1104, 1070,
@ -90,7 +96,7 @@ fun WaccaServer.api() {
"user/status/create" { _, (uid, name) -> "user/status/create" { _, (uid, name) ->
val u = rp.user.save(WaccaUser().apply { val u = rp.user.save(WaccaUser().apply {
card = cardRepo.findByExtId(uid.long())() ?: (400 - "Card not found") card = cardRepo.findByExtId(uid.long())() ?: (404 - "Card not found")
username = name.toString() username = name.toString()
}) })
@ -107,4 +113,22 @@ fun WaccaServer.api() {
ls(u.lStatus()) ls(u.lStatus())
} }
"user/status/login" api@ { _, (uid) ->
if (uid == 0) return@api "[[], [], [], 0, [2077, 1, 1, 1, [], []], 0, []]"
val u = rp.user.findByCardExtId(uid.long()) ?: (404 - "User not found")
// Record login
u.loginCountConsec = u.loginCount++
if (millis() - u.lastLoginDate.time > 86400000) { // Is new day
u.loginCountDays++
u.loginCountToday = 0
if (millis() - u.lastLoginDate.time < 172800000) u.loginCountDaysConsec++
}
u.loginCountToday++
u.lastLoginDate = Date()
"[[], [], [], 0, [2077, 1, 1, 1, [], []], ${u.lastLoginDate.time / 1000}, []]"
}
} }

View File

@ -51,7 +51,8 @@ class WaccaUser : BaseEntity() {
var lastFolderOrder = 0 var lastFolderOrder = 0
var lastFolderId = 0 var lastFolderId = 0
var lastSongOrder = 0 var lastSongOrder = 0
var lastLoginDate: String? = null @Temporal(TemporalType.TIME)
var lastLoginDate: Date = Date(0)
var gateTutorialFlags: String? = null var gateTutorialFlags: String? = null
fun lStatus() = ls(card.extId, username, 1, xp, danLevel, danType, wp, ls(0, 0, 0), loginCount, loginCountDays, fun lStatus() = ls(card.extId, username, 1, xp, danLevel, danType, wp, ls(0, 0, 0), loginCount, loginCountDays,

View File

@ -12,17 +12,6 @@ const val FTK = "test"
const val HOST = "http://localhost" const val HOST = "http://localhost"
val ACCESS_CODE = "9900" + (1..16).map { Random.nextInt(0..9) }.joinToString("") val ACCESS_CODE = "9900" + (1..16).map { Random.nextInt(0..9) }.joinToString("")
inline fun <reified T> String.json() = try {
JACKSON.readValue(this, T::class.java)
}
catch (e: Exception) {
println("Failed to parse JSON: $this")
throw e
}
fun String.jsonMap(): Map<String, Any?> = json()
fun String.jsonArray(): List<Map<String, Any?>> = json()
suspend fun registerUser(): Long { suspend fun registerUser(): Long {
val resp = HTTP.post(HOST.ensureEndingSlash() + "api/v2/frontier/register-card") { val resp = HTTP.post(HOST.ensureEndingSlash() + "api/v2/frontier/register-card") {
parameter("ftk", FTK) parameter("ftk", FTK)

View File

@ -28,31 +28,45 @@ class WaccaTest : StringSpec({
return PostResp(resp, res["params"] as List<Any>) return PostResp(resp, res["params"] as List<Any>)
} }
infix fun List<Any?>.exp(expected: List<Any?>) {
expected.size shouldBe size
for (i in indices) {
val a = this[i]
when (val b = expected[i]) {
null -> {} // Use null to ignore the value
is List<*> -> a as List<Any> exp b as List<Any>
else -> a shouldBe b
}
}
}
infix fun List<Any?>.exp(json: String) = exp(json.jsonArray())
beforeTest { beforeTest {
if (uid == 0L) uid = registerUser() if (uid == 0L) uid = registerUser()
} }
"housing/get #1" { "housing/get #1" {
post("housing/get", "[]").res shouldBe "[39, 0]".jsonArray() post("housing/get", "[]").res exp "[39, 0]"
} }
"housing/start #1" { "housing/start #1" {
post("housing/start", """["", "2024/03/24 10:39:36, ApiUserStatusLogout,0\\n2024/03/24 10:51:06, ApiUserStatusLogout,0\\n2024/03/24 10:54:19, ApiUserStatusLogout,0\\n2024/03/24 10:59:33, ApiAdvertiseGetNews,0\\n2024/03/24 11:10:31, ApiAdvertiseGetNews,0\\n2024/03/24 11:11:04, ApiUserStatusLogout,0\\n2024/03/24 11:19:51, ,0\\n2024/03/24 11:20:14, ApiAdvertiseGetNews,0\\n", "", [[1, "SERVER"], [2, "JPN"]]]""").res shouldBe post("housing/start", """["", "2024/03/24 10:39:36, ApiUserStatusLogout,0\\n2024/03/24 10:51:06, ApiUserStatusLogout,0\\n2024/03/24 10:54:19, ApiUserStatusLogout,0\\n2024/03/24 10:59:33, ApiAdvertiseGetNews,0\\n2024/03/24 11:10:31, ApiAdvertiseGetNews,0\\n2024/03/24 11:11:04, ApiUserStatusLogout,0\\n2024/03/24 11:19:51, ,0\\n2024/03/24 11:20:14, ApiAdvertiseGetNews,0\\n", "", [[1, "SERVER"], [2, "JPN"]]]""").res exp
"[1, [1269, 1007, 1270, 1002, 1020, 1003, 1008, 1211, 1018, 1092, 1056, 32, 1260, 1230, 1258, 1251, 2212, 1264, 1125, 1037, 2001, 1272, 1126, 1119, 1104, 1070, 1047, 1044, 1027, 1004, 1001, 24, 2068, 2062, 2021, 1275, 1249, 1207, 1203, 1107, 1021, 1009, 9, 4, 3, 23, 22, 2014, 13, 1276, 1247, 1240, 1237, 1128, 1114, 1110, 1109, 1102, 1045, 1043, 1036, 1035, 1030, 1023, 1015]]".jsonArray() "[1, [1269, 1007, 1270, 1002, 1020, 1003, 1008, 1211, 1018, 1092, 1056, 32, 1260, 1230, 1258, 1251, 2212, 1264, 1125, 1037, 2001, 1272, 1126, 1119, 1104, 1070, 1047, 1044, 1027, 1004, 1001, 24, 2068, 2062, 2021, 1275, 1249, 1207, 1203, 1107, 1021, 1009, 9, 4, 3, 23, 22, 2014, 13, 1276, 1247, 1240, 1237, 1128, 1114, 1110, 1109, 1102, 1045, 1043, 1036, 1035, 1030, 1023, 1015]]"
} }
"advertise/GetNews #1" { "advertise/GetNews #1" {
post("advertise/GetNews", "[]").res shouldBe post("advertise/GetNews", "[]").res exp
"[[], [], [], [], [], [], [], [], []]".jsonArray() "[[], [], [], [], [], [], [], [], []]"
} }
"user/status/get #1" { "user/status/get #1" {
post("user/status/get", """["$uid"]""").res shouldBe post("user/status/get", """["$uid"]""").res exp
"""[[0, "", 1, 0, 0, 0, 0, [0, 0, 0], 0, 0, 0, 0, 0, 0, 0], 104001, 102001, 1, [2, "1.0.0"], []]""".jsonArray() """[[0, "", 1, 0, 0, 0, 0, [0, 0, 0], 0, 0, 0, 0, 0, 0, 0], 104001, 102001, 1, [2, "1.0.0"], []]"""
} }
"user/status/create #1" { "user/status/create #1" {
post("user/status/create", """["$uid", "AZA"]""").res shouldBe post("user/status/create", """["$uid", "AZA"]""").res exp
"""[[$uid, "AZA", 1, 0, 0, 0, 0, [0, 0, 0], 0, 0, 0, 0, 0, 0, 0]]""".jsonArray() """[[$uid, "AZA", 1, 0, 0, 0, 0, [0, 0, 0], 0, 0, 0, 0, 0, 0, 0]]"""
} }
}) })