1.0 能用
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package net.bluemangoo.indpjsk.data
|
||||
|
||||
import java.io.File
|
||||
|
||||
data class CaptureProfile(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val steps: List<CaptureProfileStep>
|
||||
)
|
||||
|
||||
data class CaptureProfileStep(
|
||||
val id: String,
|
||||
val name: String,
|
||||
/** 该步骤可能命中的上游域名;TW/KR 的 cdn / https 两个域名都会出现 */
|
||||
val hosts: List<String>,
|
||||
/** 匹配 path + query 的正则(Rust 侧 Regex::is_match) */
|
||||
val path: String,
|
||||
val fileName: String,
|
||||
/**
|
||||
* 该步骤抓成功后异步执行(不阻塞下一步的监听)。
|
||||
* 没有填上传码时会走这里;参数是本次抓包的结果。
|
||||
*/
|
||||
val postAction: suspend (CaptureResult) -> Unit = {},
|
||||
)
|
||||
|
||||
/** 一步抓包完成后的结果,交给 postAction 用 */
|
||||
data class CaptureResult(
|
||||
val profile: CaptureProfile,
|
||||
val step: CaptureProfileStep,
|
||||
/** 命中的原始完整 URL(https://host/path?query,MySekai 的 flag 已改写为 True) */
|
||||
val url: String,
|
||||
/** 抓到的响应体文件 */
|
||||
val file: File,
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
package net.bluemangoo.indpjsk.data
|
||||
|
||||
object PredefinedProfiles {
|
||||
|
||||
/**
|
||||
* 两个数据接口的正则(Host 已去掉,Rust 侧是拿 path + query 去匹配的):
|
||||
*
|
||||
* 1. Suite(个人信息 / 卡牌 / 仓库主数据)
|
||||
* Path 模板: /api/suite/user/{uid}
|
||||
*
|
||||
* 2. MySekai(烤森房间 / 地图全量数据)
|
||||
* Path 模板: /api/user/{uid}/mysekai?isForceAllReloadOnlyMysekai=True
|
||||
*/
|
||||
private const val SUITE_PATH = "/api/suite/user/(\\d+)(\\?isLogin=true)?\$"
|
||||
private const val MYSEKAI_PATH =
|
||||
"/api/user/(\\d+)/mysekai\\?isForceAllReloadOnlyMysekai=True"
|
||||
|
||||
private fun suiteStep(hosts: List<String>) = CaptureProfileStep(
|
||||
id = "suite",
|
||||
name = "Suite",
|
||||
hosts = hosts,
|
||||
path = SUITE_PATH,
|
||||
fileName = "suite"
|
||||
)
|
||||
|
||||
private fun mysekaiStep(hosts: List<String>) = CaptureProfileStep(
|
||||
id = "mysekai",
|
||||
name = "烤森",
|
||||
hosts = hosts,
|
||||
path = MYSEKAI_PATH,
|
||||
fileName = "mysekai"
|
||||
)
|
||||
|
||||
private val CN_HOSTS = listOf("mkcn-prod-public-60001-1.dailygn.com")
|
||||
private val JP_HOSTS = listOf("production-game-api.sekai.colorfulpalette.org")
|
||||
private val EN_HOSTS = listOf("n-production-game-api.sekai-en.com")
|
||||
private val TW_HOSTS = listOf(
|
||||
"mk-zian-obt-cdn.bytedgame.com",
|
||||
"mk-zian-obt-https.bytedgame.com"
|
||||
)
|
||||
private val KR_HOSTS = listOf(
|
||||
"mkkorea-obt-prod01-cdn.bytedgame.com",
|
||||
"mkkorea-obt-prod01-https.bytedgame.com"
|
||||
)
|
||||
|
||||
val all: List<CaptureProfile> = listOf(
|
||||
// 国服不需要烤森
|
||||
CaptureProfile(
|
||||
id = "cn",
|
||||
name = "国服",
|
||||
steps = listOf(suiteStep(CN_HOSTS))
|
||||
),
|
||||
CaptureProfile(
|
||||
id = "jp",
|
||||
name = "日服",
|
||||
steps = listOf(suiteStep(JP_HOSTS), mysekaiStep(JP_HOSTS))
|
||||
),
|
||||
CaptureProfile(
|
||||
id = "en",
|
||||
name = "国际服",
|
||||
steps = listOf(suiteStep(EN_HOSTS), mysekaiStep(EN_HOSTS))
|
||||
),
|
||||
CaptureProfile(
|
||||
id = "tw",
|
||||
name = "台服",
|
||||
steps = listOf(suiteStep(TW_HOSTS), mysekaiStep(TW_HOSTS))
|
||||
),
|
||||
CaptureProfile(
|
||||
id = "kr",
|
||||
name = "韩服",
|
||||
steps = listOf(suiteStep(KR_HOSTS), mysekaiStep(KR_HOSTS))
|
||||
),
|
||||
)
|
||||
|
||||
fun byId(id: String): CaptureProfile? = all.firstOrNull { it.id == id }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package net.bluemangoo.indpjsk.data
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
|
||||
/** 一些零碎的全局设置,暂时只有上传码 */
|
||||
object SettingsStore {
|
||||
|
||||
private const val PREFS = "settings"
|
||||
private const val KEY_UPLOAD_CODE = "upload_code"
|
||||
|
||||
fun loadUploadCode(context: Context): String =
|
||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
.getString(KEY_UPLOAD_CODE, "")
|
||||
.orEmpty()
|
||||
|
||||
fun saveUploadCode(context: Context, code: String) {
|
||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
.edit {
|
||||
putString(KEY_UPLOAD_CODE, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package net.bluemangoo.indpjsk.data
|
||||
|
||||
import android.content.Context
|
||||
import org.json.JSONObject
|
||||
import androidx.core.content.edit
|
||||
|
||||
/**
|
||||
* 每个 profile 下每个步骤「要不要抓」的持久化。
|
||||
*
|
||||
* 就一个 JSON 字符串塞进 SharedPreferences,键是 "profileId/stepId",值 Boolean,
|
||||
* 没存过的默认 true(全抓)。
|
||||
*/
|
||||
object StepSelectionStore {
|
||||
|
||||
private const val PREFS = "step_selection"
|
||||
private const val KEY_JSON = "json"
|
||||
|
||||
fun key(profileId: String, stepId: String) = "$profileId/$stepId"
|
||||
|
||||
fun loadAll(context: Context): MutableMap<String, Boolean> {
|
||||
val raw = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
.getString(KEY_JSON, null) ?: return mutableMapOf()
|
||||
return try {
|
||||
val json = JSONObject(raw)
|
||||
val out = mutableMapOf<String, Boolean>()
|
||||
json.keys().forEach { k -> out[k] = json.optBoolean(k, true) }
|
||||
out
|
||||
} catch (_: Throwable) {
|
||||
mutableMapOf()
|
||||
}
|
||||
}
|
||||
|
||||
fun isEnabled(all: Map<String, Boolean>, profileId: String, stepId: String): Boolean =
|
||||
all[key(profileId, stepId)] ?: true
|
||||
|
||||
fun saveAll(context: Context, all: Map<String, Boolean>) {
|
||||
val json = JSONObject()
|
||||
all.forEach { (k, v) -> json.put(k, v) }
|
||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
.edit { putString(KEY_JSON, json.toString()) }
|
||||
}
|
||||
|
||||
fun setEnabled(
|
||||
context: Context,
|
||||
profileId: String,
|
||||
stepId: String,
|
||||
enabled: Boolean
|
||||
): MutableMap<String, Boolean> {
|
||||
val all = loadAll(context)
|
||||
all[key(profileId, stepId)] = enabled
|
||||
saveAll(context, all)
|
||||
return all
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user