diff --git a/build.gradle.kts b/build.gradle.kts index fd3f1a9..e4c1656 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,13 @@ @file:Suppress("VulnerableLibrariesLocal") plugins { - val kotlinVersion = "2.0.20" + val kotlinVersion = "2.1.0" + val ktorVersion = "3.0.1" kotlin("jvm") version kotlinVersion kotlin("plugin.spring") version kotlinVersion - id("org.springframework.boot") version "3.3.3" + id("org.springframework.boot") version "3.4.0" id("io.spring.dependency-management") version "1.1.6" + id("io.ktor.plugin") version ktorVersion } group = "me.kuku" @@ -17,13 +19,25 @@ repositories { mavenCentral() } +fun DependencyHandlerScope.ktor() { + implementation("io.ktor:ktor-server-core") + implementation("io.ktor:ktor-server-status-pages") + implementation("io.ktor:ktor-server-call-logging") + implementation("io.ktor:ktor-server-content-negotiation") + implementation("io.ktor:ktor-server-netty") + + implementation("io.ktor:ktor-serialization-jackson") + + implementation("io.ktor:ktor-client-core") + implementation("io.ktor:ktor-client-okhttp") + implementation("io.ktor:ktor-client-content-negotiation") + implementation("io.ktor:ktor-client-logging") +} + dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive") - implementation("org.springframework.boot:spring-boot-starter-mail") implementation("com.github.pengrad:java-telegram-bot-api:7.9.1") - implementation("me.kuku:utils:2.3.12.1") - implementation("me.kuku:ktor-spring-boot-starter:2.3.12.0") implementation("org.jsoup:jsoup:1.17.2") val ociVersion = "3.48.0" implementation("com.oracle.oci.sdk:oci-java-sdk-core:$ociVersion") @@ -33,9 +47,8 @@ dependencies { } annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") implementation("com.google.zxing:javase:3.5.3") - implementation("net.consensys.cava:cava-bytes:0.5.0") - implementation("net.consensys.cava:cava-crypto:0.5.0") implementation("com.aallam.openai:openai-client:3.8.2") + ktor() testImplementation("org.springframework.boot:spring-boot-starter-test") } @@ -64,3 +77,7 @@ tasks.test { tasks.bootJar { archiveFileName.set("tgbot.jar") } + +application { + mainClass.set("me.kuku.tgbot.TelegramApplicationKt") +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1e2fbf0..81aa1c0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/me/kuku/telegram/config/KtorConfig.kt b/src/main/kotlin/me/kuku/telegram/config/KtorConfig.kt index dc156ef..d411d24 100644 --- a/src/main/kotlin/me/kuku/telegram/config/KtorConfig.kt +++ b/src/main/kotlin/me/kuku/telegram/config/KtorConfig.kt @@ -1,41 +1,27 @@ package me.kuku.telegram.config -import com.fasterxml.jackson.databind.ObjectMapper import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.plugins.* import io.ktor.server.plugins.statuspages.* import io.ktor.server.response.* -import me.kuku.pojo.CommonResult -import me.kuku.utils.Jackson -import org.springframework.context.annotation.Bean -import org.springframework.stereotype.Component -@Component -class KtorConfig { - fun Application.statusPages() { +fun Application.statusPages() { - install(StatusPages) { + install(StatusPages) { - exception { call, cause -> - call.respond( - HttpStatusCode.BadRequest, - CommonResult.failure(code = 400, message = cause.message ?: "参数丢失", data = null) - ) - } - - exception { call, cause -> - call.respond(HttpStatusCode.InternalServerError, CommonResult.failure(cause.toString())) - throw cause - } + exception { call, cause -> + call.respond( + HttpStatusCode.BadRequest, + mapOf("code" to 400, "message" to cause.message) + ) } + exception { call, cause -> + call.respond(HttpStatusCode.InternalServerError, mapOf("code" to 500, "message" to cause.message)) + throw cause + } } - @Bean - fun objectMapper(): ObjectMapper { - return Jackson.objectMapper - } - -} +} \ No newline at end of file diff --git a/src/main/kotlin/me/kuku/telegram/config/TelegramConfig.kt b/src/main/kotlin/me/kuku/telegram/config/TelegramConfig.kt index e0363d3..61c5637 100644 --- a/src/main/kotlin/me/kuku/telegram/config/TelegramConfig.kt +++ b/src/main/kotlin/me/kuku/telegram/config/TelegramConfig.kt @@ -2,11 +2,13 @@ package me.kuku.telegram.config import com.pengrad.telegrambot.TelegramBot import com.pengrad.telegrambot.UpdatesListener +import io.ktor.client.request.* +import io.ktor.http.* import jakarta.annotation.PostConstruct import kotlinx.coroutines.runBlocking import me.kuku.telegram.context.* import me.kuku.telegram.utils.SpringUtils -import me.kuku.utils.OkHttpUtils +import me.kuku.telegram.utils.client import okhttp3.OkHttpClient import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.ApplicationListener @@ -198,9 +200,10 @@ val api: String tempApi = configApi.ifEmpty { "https://api.jpa.cc" } } try { - val response = OkHttpUtils.get(tempApi!!) - response.close() - if (response.code == 200) return tempApi!! + val response = runBlocking { + client.get(tempApi!!) + } + if (response.status == HttpStatusCode.OK) return tempApi!! else error(apiErrorMsg) } catch (e: Exception) { error(apiErrorMsg) diff --git a/src/main/kotlin/me/kuku/telegram/context/Context.kt b/src/main/kotlin/me/kuku/telegram/context/Context.kt index 472799c..15b8711 100644 --- a/src/main/kotlin/me/kuku/telegram/context/Context.kt +++ b/src/main/kotlin/me/kuku/telegram/context/Context.kt @@ -10,8 +10,8 @@ import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.* import com.pengrad.telegrambot.request.* import com.pengrad.telegrambot.response.SendResponse +import kotlinx.coroutines.runBlocking import me.kuku.telegram.utils.CacheManager -import me.kuku.utils.JobManager import org.springframework.stereotype.Component import java.io.Serializable import java.time.Duration @@ -50,8 +50,11 @@ abstract class MessageContext: Context() { suspend fun Message.delete(timeout: Long = 0) { if (timeout > 0) { - JobManager.delay(timeout) { - bot.asyncExecute(DeleteMessage(chatId, this@delete.messageId())) + Thread.startVirtualThread { + runBlocking { + delete(timeout) + bot.asyncExecute(DeleteMessage(chatId, this@delete.messageId())) + } } } else { bot.asyncExecute(DeleteMessage(chatId, this.messageId())) diff --git a/src/main/kotlin/me/kuku/telegram/context/TelegramExtensions.kt b/src/main/kotlin/me/kuku/telegram/context/TelegramExtensions.kt index c77bf3f..b8104ed 100644 --- a/src/main/kotlin/me/kuku/telegram/context/TelegramExtensions.kt +++ b/src/main/kotlin/me/kuku/telegram/context/TelegramExtensions.kt @@ -12,7 +12,7 @@ import io.ktor.client.call.* import io.ktor.client.request.* import me.kuku.telegram.config.TelegramConfig import me.kuku.telegram.utils.SpringUtils -import me.kuku.utils.client +import me.kuku.telegram.utils.client fun inlineKeyboardButton(text: String, callbackData: String): InlineKeyboardButton = InlineKeyboardButton(text).callbackData(callbackData) diff --git a/src/main/kotlin/me/kuku/telegram/entity/BaiduEntity.kt b/src/main/kotlin/me/kuku/telegram/entity/BaiduEntity.kt index 2163aab..c21979c 100644 --- a/src/main/kotlin/me/kuku/telegram/entity/BaiduEntity.kt +++ b/src/main/kotlin/me/kuku/telegram/entity/BaiduEntity.kt @@ -1,7 +1,6 @@ package me.kuku.telegram.entity import kotlinx.coroutines.flow.toList -import me.kuku.utils.OkUtils import org.springframework.data.annotation.Id import org.springframework.data.mongodb.core.mapping.Document import org.springframework.data.repository.kotlin.CoroutineCrudRepository @@ -16,8 +15,8 @@ class BaiduEntity: BaseEntity() { var tieBaSToken: String = "" var sign: Status = Status.OFF - fun otherCookie(sToken: String): String { - return OkUtils.cookieStr(cookie, "BDUSS") + "STOKEN=$sToken; " + private fun otherCookie(sToken: String): String { + return "BDUSS=.*?;".toRegex().find(sToken)!!.value + "STOKEN=$sToken; " } fun teiBaCookie(): String { diff --git a/src/main/kotlin/me/kuku/telegram/entity/DouYuEntity.kt b/src/main/kotlin/me/kuku/telegram/entity/DouYuEntity.kt index 505b9c8..4fe3f99 100644 --- a/src/main/kotlin/me/kuku/telegram/entity/DouYuEntity.kt +++ b/src/main/kotlin/me/kuku/telegram/entity/DouYuEntity.kt @@ -12,7 +12,6 @@ class DouYuEntity: BaseEntity() { @Id var id: String? = null var cookie: String = "" - var appCookie: String = "" var live: Status = Status.OFF var fishGroup: Status = Status.OFF var push: Status = Status.OFF diff --git a/src/main/kotlin/me/kuku/telegram/extension/CommentExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/CommentExtension.kt deleted file mode 100644 index 3a89796..0000000 --- a/src/main/kotlin/me/kuku/telegram/extension/CommentExtension.kt +++ /dev/null @@ -1,32 +0,0 @@ -package me.kuku.telegram.extension - -import com.pengrad.telegrambot.request.SendMessage -import me.kuku.telegram.config.TelegramConfig -import me.kuku.telegram.context.AbilitySubscriber -import me.kuku.telegram.context.asyncExecute -import me.kuku.telegram.context.nextMessage -import org.springframework.stereotype.Component - -@Component -class CommentExtension( - private val telegramConfig: TelegramConfig -) { - - fun AbilitySubscriber.comment() { - sub("comment") { - val creatorId = telegramConfig.creatorId - if (creatorId <= 0) { - sendMessage("机器人拥有者没有设置id") - return@sub - } - val message1 = sendMessage("请发送留言的内容").message() - val message2 = nextMessage() - val sendMessage = SendMessage(creatorId, "#留言\n${message2.text()}") - bot.asyncExecute(sendMessage) - message1.delete() - message2.delete() - sendMessage("留言成功") - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/me/kuku/telegram/extension/ExecExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/ExecExtension.kt index 0d28ecb..9374acb 100644 --- a/src/main/kotlin/me/kuku/telegram/extension/ExecExtension.kt +++ b/src/main/kotlin/me/kuku/telegram/extension/ExecExtension.kt @@ -116,11 +116,9 @@ class ExecExtension( } callback("hostLocSign") { val hostLocEntity = firstArg() - for (i in 0 until 12) { - editMessageText("正在进行第${i}次访问HostLoc用户主页") - HostLocLogic.singleSign(hostLocEntity.cookie) - } - editMessageText("HostLoc签到成功") + editMessageText("后台进行访问HostLoc用户主页,结果稍后发送") + HostLocLogic.sign(hostLocEntity.cookie) + sendMessage("#手动执行结果\nHostLoc签到成功") } } @@ -185,14 +183,14 @@ class ExecExtension( callback("leXinStepExec") { editMessageText("请发送乐心运动下需要刷的步数") val step = nextMessage().text().toIntOrNull() ?: error("步数不为数字") - val res = LeXinStepLogic.modifyStepCount(firstArg(), step) - editMessageText(res.message) + LeXinStepLogic.modifyStepCount(firstArg(), step) + editMessageText("修改步数成功") } callback("xiaomiStepExec") { editMessageText("请发送小米运动下需要刷的步数") val step = nextMessage().text().toIntOrNull() ?: error("步数不为数字") - val res = XiaomiStepLogic.modifyStepCount(firstArg(), step) - editMessageText(res.message) + XiaomiStepLogic.modifyStepCount(firstArg(), step) + editMessageText("修改步数成功") } } @@ -204,8 +202,7 @@ class ExecExtension( editMessageText("微博", markup) } callback("superTalkSign") { - val result = WeiboLogic.superTalkSign(firstArg()) - editMessageText(result.message) + WeiboLogic.superTalkSign(firstArg()) } } diff --git a/src/main/kotlin/me/kuku/telegram/extension/LogExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/LogExtension.kt index 553e317..fe8d907 100644 --- a/src/main/kotlin/me/kuku/telegram/extension/LogExtension.kt +++ b/src/main/kotlin/me/kuku/telegram/extension/LogExtension.kt @@ -6,10 +6,10 @@ import me.kuku.telegram.context.AbilitySubscriber import me.kuku.telegram.context.TelegramSubscribe import me.kuku.telegram.context.inlineKeyboardButton import me.kuku.telegram.entity.LogService -import me.kuku.utils.DateTimeFormatterUtils import org.springframework.stereotype.Component import java.time.LocalDate import java.time.LocalDateTime +import java.time.format.DateTimeFormatter @Component class LogExtension( @@ -29,9 +29,9 @@ class LogExtension( if (list.isEmpty()) list.add(arrayOf(InlineKeyboardButton("无").callbackData("logNone"))) val up = before.minusDays(1).toLocalDate() - val upStr = DateTimeFormatterUtils.format(up, "yyyy-MM-dd") + val upStr = up.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) val down = before.plusDays(1).toLocalDate() - val downStr = DateTimeFormatterUtils.format(down, "yyyy-MM-dd") + val downStr = down.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) val deepButton = arrayOf(inlineKeyboardButton("上一天", "log-$upStr"), inlineKeyboardButton("下一天", "log-$downStr")) list.add(deepButton) return InlineKeyboardMarkup(*list.toTypedArray()) @@ -41,7 +41,7 @@ class LogExtension( fun AbilitySubscriber.logShow() { sub("log") { val before = LocalDate.now().atTime(0, 0) - sendMessage("${DateTimeFormatterUtils.format(before, "yyyy-MM-dd")}的自动签到日志,点击可查看详情", + sendMessage("${before.format(DateTimeFormatter.ofPattern( "yyyy-MM-dd"))}的自动签到日志,点击可查看详情", replyMarkup(before, before.plusDays(1), tgId)) } } @@ -49,7 +49,7 @@ class LogExtension( fun TelegramSubscribe.logSwitch() { callbackStartsWith("log-") { val data = query.data().substring(4) - val before = DateTimeFormatterUtils.parseToLocalDate(data, "yyyy-MM-dd") + val before = LocalDate.parse(data, DateTimeFormatter.ofPattern("yyyy-MM-dd")) val beforeTime = before.atTime(0, 0) editMessageText("${before}的自动签到日志", replyMarkup(beforeTime, beforeTime.plusDays(1), tgId), returnButton = false) diff --git a/src/main/kotlin/me/kuku/telegram/extension/LoginExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/LoginExtension.kt index b4dad86..c1e9a6f 100644 --- a/src/main/kotlin/me/kuku/telegram/extension/LoginExtension.kt +++ b/src/main/kotlin/me/kuku/telegram/extension/LoginExtension.kt @@ -14,7 +14,6 @@ import me.kuku.telegram.entity.* import me.kuku.telegram.exception.QrcodeScanException import me.kuku.telegram.logic.* import me.kuku.telegram.utils.* -import me.kuku.utils.* import org.springframework.stereotype.Service @Service @@ -80,7 +79,7 @@ class LoginExtension( callback("baiduQrcodeLogin") { val qrcode = baiduLogic.getQrcode() var photoMessage: Message? - OkHttpKtUtils.getBytes(qrcode.image).let { + client.get(qrcode.image).body().let { val photo = SendPhoto(chatId, it) photoMessage = bot.asyncExecute(photo).message() editMessageText("请使用百度app扫描以下二维码登陆,百度网盘等均可", returnButton = false) @@ -94,14 +93,11 @@ class LoginExtension( } delay(3000) try { - val result = baiduLogic.checkQrcode(qrcode) - if (result.success()) { - val newEntity = result.data() - baiduEntity.cookie = newEntity.cookie - baiduService.save(baiduEntity) - editMessageText("绑定百度成功") - } - } catch (ignore: Exception) {} + val newEntity = baiduLogic.checkQrcode(qrcode) + baiduEntity.cookie = newEntity.cookie + baiduService.save(baiduEntity) + editMessageText("绑定百度成功") + } catch (ignore: QrcodeScanException) {} } } } @@ -127,24 +123,18 @@ class LoginExtension( break } delay(3000) - val result = BiliBiliLogic.loginByQr2(qrcode) - when (result.code) { - 0 -> continue - 200 -> { - val newEntity = result.data() - val biliBiliEntity = biliBiliService.findByTgId(query.from().id()) ?: BiliBiliEntity().init() - biliBiliEntity.cookie = newEntity.cookie - biliBiliEntity.userid = newEntity.userid - biliBiliEntity.token = newEntity.token - biliBiliService.save(biliBiliEntity) - editMessageText("绑定哔哩哔哩成功") - break - } - else -> { - editMessageText("哔哩哔哩登陆失败,${result.message}") - break - } + val newEntity = try { + BiliBiliLogic.loginByQr2(qrcode) + } catch (e: QrcodeScanException) { + continue } + val biliBiliEntity = biliBiliService.findByTgId(query.from().id()) ?: BiliBiliEntity().init() + biliBiliEntity.cookie = newEntity.cookie + biliBiliEntity.userid = newEntity.userid + biliBiliEntity.token = newEntity.token + biliBiliService.save(biliBiliEntity) + editMessageText("绑定哔哩哔哩成功") + break } photoMessage?.delete() } @@ -174,22 +164,16 @@ class LoginExtension( break } delay(3000) - val result = douYuLogic.checkQrcode(qrcode) - when (result.code) { - 0 -> continue - 200 -> { - val newEntity = result.data() - val douYuEntity = douYuService.findByTgId(query.from().id()) ?: DouYuEntity().init() - douYuEntity.cookie = newEntity.cookie - douYuService.save(douYuEntity) - editMessageText("绑定斗鱼成功") - break - } - else -> { - editMessageText("绑定斗鱼失败,${result.message}") - break - } + val newEntity = try { + douYuLogic.checkQrcode(qrcode) + } catch (e: QrcodeScanException) { + continue } + val douYuEntity = douYuService.findByTgId(query.from().id()) ?: DouYuEntity().init() + douYuEntity.cookie = newEntity.cookie + douYuService.save(douYuEntity) + editMessageText("绑定斗鱼成功") + break } photoMessage?.delete() } @@ -235,7 +219,7 @@ class LoginExtension( callback("huyaQrcodeLogin") { val qrcode = huYaLogic.getQrcode() val photoMessage: Message? - OkHttpKtUtils.getBytes(qrcode.url).let { + client.get(qrcode.url).body().let { val photo = SendPhoto(chatId, it) photoMessage = bot.asyncExecute(photo).message() editMessageText("请使用虎牙App扫描二维码登录", returnButton = false) @@ -247,22 +231,16 @@ class LoginExtension( break } delay(3000) - val result = huYaLogic.checkQrcode(qrcode) - when (result.code) { - 0 -> continue - 200 -> { - val newEntity = result.data() - val huYaEntity = huYaService.findByTgId(tgId) ?: HuYaEntity().init() - huYaEntity.cookie = newEntity.cookie - huYaService.save(huYaEntity) - editMessageText("绑定虎牙成功") - break - } - else -> { - editMessageText("绑定虎牙失败,${result.message}") - break - } + val newEntity = try { + huYaLogic.checkQrcode(qrcode) + } catch (e: QrcodeScanException) { + continue } + val huYaEntity = huYaService.findByTgId(tgId) ?: HuYaEntity().init() + huYaEntity.cookie = newEntity.cookie + huYaService.save(huYaEntity) + editMessageText("绑定虎牙成功") + break } photoMessage?.delete() } @@ -281,21 +259,15 @@ class LoginExtension( it.mid = kuGouLogic.mid() } val mid = kuGouEntity.mid - val result = kuGouLogic.sendMobileCode(phone.toString(), mid) - val message = if (result.success()) { - editMessageText("请发送酷狗短信验证码") - val code = nextMessage(1000 * 60 * 2).text() - val verifyResult = kuGouLogic.verifyCode(phone.toString(), code, mid) - if (verifyResult.success()) { - val newKuGouEntity = verifyResult.data() - kuGouEntity.kuGoo = newKuGouEntity.kuGoo - kuGouEntity.token = newKuGouEntity.token - kuGouEntity.userid = newKuGouEntity.userid - kuGouService.save(kuGouEntity) - "绑定成功" - } else verifyResult.message - } else result.message - editMessageText(message) + kuGouLogic.sendMobileCode(phone.toString(), mid) + editMessageText("请发送酷狗短信验证码") + val code = nextMessage(1000 * 60 * 2).text() + val newKuGouEntity = kuGouLogic.verifyCode(phone.toString(), code, mid) + kuGouEntity.kuGoo = newKuGouEntity.kuGoo + kuGouEntity.token = newKuGouEntity.token + kuGouEntity.userid = newKuGouEntity.userid + kuGouService.save(kuGouEntity) + editMessageText("绑定酷狗成功") } } @@ -312,8 +284,8 @@ class LoginExtension( callback("miHoYoCookieLogin") { editMessageText("请发送米哈游的cookie") val cookie = nextMessage().text() - val ticket = OkUtils.cookie(cookie, "login_ticket") ?: "" - val accountId = OkUtils.cookie(cookie, "account_id") ?: "" + val ticket = RegexUtils.extract(cookie, "login_ticket", ";") ?: "" + val accountId = RegexUtils.extract(cookie, " account_id", ";") ?: "" val newEntity = miHoYoService.findByTgId(tgId) ?: MiHoYoEntity().init() newEntity.cookie = cookie newEntity.ticket = ticket @@ -337,20 +309,19 @@ class LoginExtension( break } delay(3000) - val result = miHoYoLogic.qrcodeLogin2(qrcode) - when (result.code) { - 200 -> { - val miHoYoEntity = result.data() - val newEntity = miHoYoService.findByTgId(tgId) ?: MiHoYoEntity().init() - newEntity.fix = miHoYoEntity.fix - newEntity.aid = miHoYoEntity.aid - newEntity.mid = miHoYoEntity.mid - newEntity.cookie = miHoYoEntity.cookie - miHoYoService.save(newEntity) - editMessageText("绑定米哈游成功") - break - } + val miHoYoEntity = try { + miHoYoLogic.qrcodeLogin2(qrcode) + } catch (e: QrcodeScanException) { + continue } + val newEntity = miHoYoService.findByTgId(tgId) ?: MiHoYoEntity().init() + newEntity.fix = miHoYoEntity.fix + newEntity.aid = miHoYoEntity.aid + newEntity.mid = miHoYoEntity.mid + newEntity.cookie = miHoYoEntity.cookie + miHoYoService.save(newEntity) + editMessageText("绑定米哈游成功") + break } } finally { photoMessage?.delete() @@ -411,14 +382,11 @@ class LoginExtension( val phone = nextMessage().text() editMessageText("请发送小米运动密码") val password = nextMessage().text() - val result = XiaomiStepLogic.login(phone, password) - if (result.success()) { - val newEntity = result.data() - val stepEntity = stepService.findByTgId(tgId) ?: StepEntity().init() - stepEntity.miLoginToken = newEntity.miLoginToken - stepService.save(stepEntity) - editMessageText("绑定小米运动成功") - } else editMessageText("绑定小米运动失败,${result.message}") + val newEntity = XiaomiStepLogic.login(phone, password) + val stepEntity = stepService.findByTgId(tgId) ?: StepEntity().init() + stepEntity.miLoginToken = newEntity.miLoginToken + stepService.save(stepEntity) + editMessageText("绑定小米运动成功") } callback("leXinStepLogin") { editMessageText("请选择乐心运动登录方式", InlineKeyboardMarkup( @@ -430,17 +398,13 @@ class LoginExtension( val phone = nextMessage().text() editMessageText("请发送乐心运动密码") val password = nextMessage().text() - val result = LeXinStepLogic.login(phone, password) - val message = if (result.success()) { - val newStepEntity = result.data() - val stepEntity = stepService.findByTgId(query.from().id()) ?: StepEntity().init() - stepEntity.leXinCookie = newStepEntity.leXinCookie - stepEntity.leXinUserid = newStepEntity.leXinUserid - stepEntity.leXinAccessToken = newStepEntity.leXinAccessToken - stepService.save(stepEntity) - "绑定乐心运动成功" - } else result.message - editMessageText(message) + val newStepEntity = LeXinStepLogic.login(phone, password) + val stepEntity = stepService.findByTgId(query.from().id()) ?: StepEntity().init() + stepEntity.leXinCookie = newStepEntity.leXinCookie + stepEntity.leXinUserid = newStepEntity.leXinUserid + stepEntity.leXinAccessToken = newStepEntity.leXinAccessToken + stepService.save(stepEntity) + editMessageText("绑定乐心运动成功") } } @@ -531,16 +495,14 @@ class LoginExtension( if (++i >= 20) break try { delay(3000) - val result = smZdmLogic.wechatQrcode2(wechatQrcode) - if (result.code == 200) { - val smZdmEntity = smZdmService.findByTgId(tgId) ?: SmZdmEntity().init() - smZdmEntity.cookie = result.data().cookie - smZdmService.save(smZdmEntity) - editMessageText("绑定什么值得买成功") - fail = false - break - } - } catch (ignore: Exception) { + val newEntity = smZdmLogic.wechatQrcode2(wechatQrcode) + val smZdmEntity = smZdmService.findByTgId(tgId) ?: SmZdmEntity().init() + smZdmEntity.cookie = newEntity.cookie + smZdmService.save(smZdmEntity) + editMessageText("绑定什么值得买成功") + fail = false + break + } catch (ignore: QrcodeScanException) { } } photoMessage?.delete() @@ -561,16 +523,17 @@ class LoginExtension( while (true) { if (++i >= 20) break delay(3000) - val result = smZdmLogic.appQrcode2(appQrcode) - if (result.code == 200) { - val newEntity = result.data() - val smZdmEntity = smZdmService.findByTgId(tgId) ?: SmZdmEntity().init() - smZdmEntity.cookie = newEntity.cookie - smZdmService.save(smZdmEntity) - editMessageText("绑定什么值得买成功") - fail = false - break + val newEntity = try { + smZdmLogic.appQrcode2(appQrcode) + } catch (e: QrcodeScanException) { + continue } + val smZdmEntity = smZdmService.findByTgId(tgId) ?: SmZdmEntity().init() + smZdmEntity.cookie = newEntity.cookie + smZdmService.save(smZdmEntity) + editMessageText("绑定什么值得买成功") + fail = false + break } photoMessage?.delete() if (fail) diff --git a/src/main/kotlin/me/kuku/telegram/extension/ManagerExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/ManagerExtension.kt index 7d77c52..59c7e65 100644 --- a/src/main/kotlin/me/kuku/telegram/extension/ManagerExtension.kt +++ b/src/main/kotlin/me/kuku/telegram/extension/ManagerExtension.kt @@ -311,8 +311,16 @@ class ManagerExtension( val split = inlineKeyboardButton("以下是手动暂停与恢复时间按钮", "not") val info = try { secondArg() - } catch (e: Exception) { LeiShenLogic.userInfo(leiShenEntity) } - val infoStr = if (info.pauseStatusId == 1) "暂停" else "恢复" + } catch (e: Exception) { + try { + LeiShenLogic.userInfo(leiShenEntity) + } catch (ex: Exception) { + leiShenEntity.status = Status.OFF + leiShenService.save(leiShenEntity) + null + } + } + val infoStr = if (info == null) "获取失败" else if (info.pauseStatusId == 1) "暂停" else "恢复" val pause = inlineKeyboardButton("($infoStr)暂停/恢复时间", "leiShenPauseRecover") val markup = InlineKeyboardMarkup( arrayOf(signButton), diff --git a/src/main/kotlin/me/kuku/telegram/extension/OpenaiExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/OpenaiExtension.kt index d72849a..2d459dc 100644 --- a/src/main/kotlin/me/kuku/telegram/extension/OpenaiExtension.kt +++ b/src/main/kotlin/me/kuku/telegram/extension/OpenaiExtension.kt @@ -8,6 +8,7 @@ import com.pengrad.telegrambot.model.PhotoSize import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.EditMessageText import com.pengrad.telegrambot.request.GetFile +import io.ktor.util.* import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach @@ -18,7 +19,6 @@ import me.kuku.telegram.context.asyncExecute import me.kuku.telegram.context.byteArray import me.kuku.telegram.entity.BotConfigService import me.kuku.telegram.utils.CacheManager -import me.kuku.utils.base64Encode import org.springframework.stereotype.Component import java.time.Duration @@ -48,7 +48,7 @@ class OpenaiExtension( ?.forEach { photoSize -> val getFile = GetFile(photoSize.fileId()) val getFileResponse = bot.asyncExecute(getFile) - val base64 = getFileResponse.file().byteArray().base64Encode() + val base64 = getFileResponse.file().byteArray().encodeBase64() photoList.add(base64) } diff --git a/src/main/kotlin/me/kuku/telegram/extension/PushExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/PushExtension.kt index c043739..e2eb58a 100644 --- a/src/main/kotlin/me/kuku/telegram/extension/PushExtension.kt +++ b/src/main/kotlin/me/kuku/telegram/extension/PushExtension.kt @@ -13,13 +13,12 @@ import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.server.util.* -import me.kuku.ktor.plugins.getOrFail -import me.kuku.ktor.plugins.receiveJsonNode -import me.kuku.pojo.CommonResult import me.kuku.telegram.context.* import me.kuku.telegram.entity.PushEntity import me.kuku.telegram.entity.PushService -import me.kuku.utils.client +import me.kuku.telegram.ktor.context.KtorRouter +import me.kuku.telegram.utils.client +import me.kuku.telegram.utils.toJsonNode import org.springframework.stereotype.Component import java.util.UUID @@ -76,9 +75,9 @@ class PushExtension( class PushController( private val pushService: PushService, private val telegramBot: TelegramBot -) { +): KtorRouter { - fun Routing.push() { + override fun Routing.route() { route("push") { @@ -90,7 +89,7 @@ class PushController( sendMessage.parseMode(ParseMode.valueOf(parseMode)) } telegramBot.asyncExecute(sendMessage) - respond(CommonResult.success()) + respond("{}") } get { @@ -101,9 +100,9 @@ class PushController( } post { - val jsonNode = call.receiveJsonNode() - val key = jsonNode.getOrFail("key").asText() - val text = jsonNode.getOrFail("text").asText() + val jsonNode = call.receiveText().toJsonNode() + val key = jsonNode["key"].asText() + val text = jsonNode["text"].asText() val parseMode = jsonNode["parseMode"]?.asText() call.push(key, text, parseMode) } diff --git a/src/main/kotlin/me/kuku/telegram/extension/ToolExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/ToolExtension.kt index 174fbe4..c023b8e 100644 --- a/src/main/kotlin/me/kuku/telegram/extension/ToolExtension.kt +++ b/src/main/kotlin/me/kuku/telegram/extension/ToolExtension.kt @@ -1,5 +1,6 @@ package me.kuku.telegram.extension +import com.fasterxml.jackson.databind.JsonNode import com.pengrad.telegrambot.model.request.InlineKeyboardButton import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup import com.pengrad.telegrambot.model.request.InputMediaPhoto @@ -14,16 +15,15 @@ import kotlinx.coroutines.sync.withLock import me.kuku.telegram.context.* import me.kuku.telegram.entity.BiliBiliService import me.kuku.telegram.logic.BiliBiliLogic -import me.kuku.telegram.logic.ToolLogic import me.kuku.telegram.logic.YgoLogic +import me.kuku.telegram.utils.RegexUtils +import me.kuku.telegram.utils.client import me.kuku.telegram.utils.githubCommit -import me.kuku.utils.* import org.springframework.stereotype.Service @Service class ToolExtension( private val ygoLogic: YgoLogic, - private val toolLogic: ToolLogic, private val biliBiliService: BiliBiliService ) { @@ -46,7 +46,7 @@ class ToolExtension( answerCallbackQuery("获取成功") val id = query.data().split("-")[1] val card = ygoLogic.searchDetail(id.toLong()) - val sendPhoto = SendPhoto(chatId, OkHttpKtUtils.getBytes(card.imageUrl)) + val sendPhoto = SendPhoto(chatId, client.get(card.imageUrl).bodyAsBytes()) sendPhoto.caption("中文名:${card.chineseName}\n日文名:${card.japaneseName}\n英文名:${card.englishName}\n效果:\n${card.effect}\n链接:${card.url}") bot.asyncExecute(sendPhoto) } @@ -77,9 +77,9 @@ class ToolExtension( val r18 = kotlin.runCatching { if (firstArg().lowercase() == "r18") 1 else 0 }.getOrDefault(0) - val jsonNode = OkHttpKtUtils.getJson("https://api.lolicon.app/setu/v2?r18=$r18") + val jsonNode = client.get("https://api.lolicon.app/setu/v2?r18=$r18").body() val url = jsonNode["data"][0]["urls"]["original"].asText() - val bytes = OkHttpKtUtils.getBytes(url) + val bytes = client.get(url).bodyAsBytes() if (bytes.size > 1024 * 10 * 1024) { val sendDocument = SendDocument(chatId, bytes).fileName("lolicon.jpg") message.messageThreadId()?.let { @@ -98,12 +98,12 @@ class ToolExtension( val r18 = kotlin.runCatching { if (firstArg().lowercase() == "r18") 1 else 0 }.getOrDefault(0) - val jsonNode = OkHttpKtUtils.getJson("https://api.lolicon.app/setu/v2?num=5&r18=$r18") + val jsonNode = client.get("https://api.lolicon.app/setu/v2?num=5&r18=$r18").body() val list = jsonNode["data"].map { node -> node["urls"]["original"].asText() } val inputMediaList = mutableListOf() for (i in list.indices) { val s = list[i] - val bytes = OkHttpKtUtils.getBytes(s) + val bytes = client.get(s).bodyAsBytes() if (bytes.size > 1024 * 10 * 1024) continue val mediaPhoto = InputMediaPhoto(bytes) inputMediaList.add(mediaPhoto) @@ -114,25 +114,6 @@ class ToolExtension( } bot.asyncExecute(sendMediaGroup) } - sub("saucenao") { - val photos = update.message().photo() - val photoSize = photos.max() ?: error("未发现图片") - val getFile = GetFile(photoSize.fileId()) - val fileResponse = bot.asyncExecute(getFile) - val byteArray = fileResponse.file().byteArray() - val list = toolLogic.saucenao(byteArray) - if (list.isEmpty()) error("未找到结果") - val result = list[0] - sendMessage(""" - 相似度:${result.similarity} - 名字:${result.indexName} - 标题:${result.title} - 预览链接:${result.thumbnail} - 源链接:${result.extUrls} - 作者:${result.author} - 作者主页:${result.authUrl} - """.trimIndent()) - } } fun AbilitySubscriber.dyna() { @@ -164,7 +145,7 @@ class ToolExtension( val html = client.get(htmlUrl) { userAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1") }.bodyAsText() - val id = MyUtils.regex("(?<=video_id=)", "&", html) ?: error("获取抖音视频失败") + val id = RegexUtils.extract(html, "(?<=video_id=)", "&") ?: error("获取抖音视频失败") val response = client.get("https://m.douyin.com/aweme/v1/playwm/?video_id=$id&ratio=720p&line=0") val url = response.headers["Location"] ?: error("获取抖音视频失败") val bytes = client.get(url).body() diff --git a/src/main/kotlin/me/kuku/telegram/extension/UpdateExtension.kt b/src/main/kotlin/me/kuku/telegram/extension/UpdateExtension.kt index abc3860..ff576c8 100644 --- a/src/main/kotlin/me/kuku/telegram/extension/UpdateExtension.kt +++ b/src/main/kotlin/me/kuku/telegram/extension/UpdateExtension.kt @@ -11,9 +11,9 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import me.kuku.telegram.context.MixSubscribe import me.kuku.telegram.context.Privacy -import me.kuku.utils.client -import me.kuku.utils.convertValue -import me.kuku.utils.setJsonBody +import me.kuku.telegram.utils.client +import me.kuku.telegram.utils.convertValue +import me.kuku.telegram.utils.setJsonBody import org.jsoup.Jsoup import org.springframework.stereotype.Component import java.io.FileOutputStream diff --git a/src/main/kotlin/me/kuku/telegram/ktor/KtorConfiguration.kt b/src/main/kotlin/me/kuku/telegram/ktor/KtorConfiguration.kt new file mode 100644 index 0000000..f9860f0 --- /dev/null +++ b/src/main/kotlin/me/kuku/telegram/ktor/KtorConfiguration.kt @@ -0,0 +1,48 @@ +package me.kuku.telegram.ktor + +import io.ktor.server.engine.* +import io.ktor.server.netty.* +import io.ktor.server.routing.* +import me.kuku.telegram.ktor.context.KtorModule +import me.kuku.telegram.ktor.context.KtorRouter +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.ApplicationContext +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +@EnableConfigurationProperties(KtorProperties::class) +class KtorConfiguration( + private val properties: KtorProperties +) { + + + @Bean + fun applicationEngine(context: ApplicationContext): EmbeddedServer { + return embeddedServer(Netty, host = properties.host, port = properties.port) { + val modules = context.getBeansOfType(KtorModule::class.java).values + val routes = context.getBeansOfType(KtorRouter::class.java).values + + //注册模块 + modules.forEach { + it.apply { register() } + } + + //注册路由 + routing { + routes.forEach { + it.apply { route() } + } + } + }.start() + } + + +} + +@ConfigurationProperties(prefix = "spring.ktor") +open class KtorProperties( + open var host: String = "0.0.0.0", + open var port: Int = 8080 +) \ No newline at end of file diff --git a/src/main/kotlin/me/kuku/telegram/ktor/context/KtorModule.kt b/src/main/kotlin/me/kuku/telegram/ktor/context/KtorModule.kt new file mode 100644 index 0000000..94a1d0d --- /dev/null +++ b/src/main/kotlin/me/kuku/telegram/ktor/context/KtorModule.kt @@ -0,0 +1,9 @@ +package me.kuku.telegram.ktor.context + +import io.ktor.server.application.* + +interface KtorModule { + + fun Application.register() + +} \ No newline at end of file diff --git a/src/main/kotlin/me/kuku/telegram/ktor/context/KtorRouter.kt b/src/main/kotlin/me/kuku/telegram/ktor/context/KtorRouter.kt new file mode 100644 index 0000000..1db7462 --- /dev/null +++ b/src/main/kotlin/me/kuku/telegram/ktor/context/KtorRouter.kt @@ -0,0 +1,9 @@ +package me.kuku.telegram.ktor.context + +import io.ktor.server.routing.* + +interface KtorRouter { + + fun Routing.route() + +} \ No newline at end of file diff --git a/src/main/kotlin/me/kuku/telegram/logic/BaiduLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/BaiduLogic.kt index 5e1171f..cd7c456 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/BaiduLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/BaiduLogic.kt @@ -3,14 +3,19 @@ package me.kuku.telegram.logic import com.fasterxml.jackson.databind.JsonNode import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.request.forms.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.network.sockets.* import kotlinx.coroutines.delay -import me.kuku.pojo.CommonResult -import me.kuku.pojo.UA import me.kuku.telegram.entity.BaiduEntity import me.kuku.telegram.entity.BaiduService -import me.kuku.utils.* +import me.kuku.telegram.exception.qrcodeNotScanned +import me.kuku.telegram.exception.qrcodeScanned +import me.kuku.telegram.utils.* import org.jsoup.Jsoup import org.springframework.stereotype.Service +import java.net.SocketTimeoutException import java.util.* @Service @@ -20,78 +25,81 @@ class BaiduLogic ( suspend fun getQrcode(): BaiduQrcode { val uuid = UUID.randomUUID().toString() - val jsonNode = OkHttpKtUtils.getJsonp("https://passport.baidu.com/v2/api/getqrcode?lp=pc&qrloginfrom=pc&gid=$uuid&callback=tangram_guid_${System.currentTimeMillis()}&apiver=v3&tt=${System.currentTimeMillis()}&tpl=mn&logPage=traceId:pc_loginv5_1653405990,logPage:loginv5&_=${System.currentTimeMillis()}") - val url = "https://" + jsonNode.getString("imgurl") - val sign = jsonNode.getString("sign") + val jsonNode = client.get("https://passport.baidu.com/v2/api/getqrcode?lp=pc&qrloginfrom=pc&gid=$uuid&callback=tangram_guid_${System.currentTimeMillis()}&apiver=v3&tt=${System.currentTimeMillis()}&tpl=mn&logPage=traceId:pc_loginv5_1653405990,logPage:loginv5&_=${System.currentTimeMillis()}") + .bodyAsText().jsonpToJsonNode() + val url = "https://" + jsonNode["imgurl"].asText() + val sign = jsonNode["sign"].asText() return BaiduQrcode(url, sign, uuid) } - suspend fun checkQrcode(baiduQrcode: BaiduQrcode): CommonResult { - val jsonNode = - OkHttpKtUtils.getJsonp("https://passport.baidu.com/channel/unicast?channel_id=${baiduQrcode.sign}&gid=${baiduQrcode.uuid}&tpl=mn&_sdkFrom=1&callback=tangram_guid_${System.currentTimeMillis()}&apiver=v3&tt=${System.currentTimeMillis()}&_=${System.currentTimeMillis()}") - return when (jsonNode.getInteger("errno")) { - 1 -> CommonResult.failure("未扫码或已失效") + suspend fun checkQrcode(baiduQrcode: BaiduQrcode): BaiduEntity { + val jsonNode = try { + client.get("https://passport.baidu.com/channel/unicast?channel_id=${baiduQrcode.sign}&gid=${baiduQrcode.uuid}&tpl=mn&_sdkFrom=1&callback=tangram_guid_${System.currentTimeMillis()}&apiver=v3&tt=${System.currentTimeMillis()}&_=${System.currentTimeMillis()}") + .bodyAsText().jsonpToJsonNode() + } catch (e: SocketTimeoutException) { + qrcodeNotScanned() + } + return when (jsonNode["errno"].asInt()) { + 1 -> qrcodeNotScanned() 0 -> { - val ss = jsonNode.getString("channel_v").toJsonNode() - if (ss.getInteger("status") == 0) { - val v = ss.getString("v") - val response = OkHttpKtUtils.get("https://passport.baidu.com/v3/login/main/qrbdusslogin?v=${System.currentTimeMillis()}&bduss=$v") - response.close() - val cookie = OkUtils.cookie(response) - CommonResult.success(BaiduEntity().also { + val ss = jsonNode["channel_v"].asText().toJsonNode() + if (ss["status"].asInt() == 0) { + val v = ss["v"].asText() + val response = client.get("https://passport.baidu.com/v3/login/main/qrbdusslogin?v=${System.currentTimeMillis()}&bduss=$v") + val cookie = response.setCookie().renderCookieHeader() + BaiduEntity().also { it.cookie = cookie - }) - } else CommonResult.failure("已扫码") + } + } else qrcodeScanned() } - else -> CommonResult.failure("未知错误") + else -> error("未知错误") } } - private fun ybbDefaultHeader(): MutableMap { + private fun HttpMessageBuilder.ybbDefaultHeader() { val map = mutableMapOf() map["X-Channel-Name"] = "xiaomi" map["X-Device-Name"] = "android" map["X-Client-Version"] = "2.3.14" map["X-System-Version"] = "31" map["X-Auth-Timestamp"] = System.currentTimeMillis().toString() - return map + map.forEach { headers.append(it.key, it.value) } } - suspend fun ybbWatchAd(baiduEntity: BaiduEntity, version: String = "v2"): String { - val preJsonNode = OkHttpKtUtils.getJson("https://api-gt.baidu.com/v1/server/task?version=$version", ybbDefaultHeader().also { - it["cookie"] = baiduEntity.cookie - }) - if (!preJsonNode.getBoolean("success")) error(preJsonNode["errors"]["message_cn"].asText()) + suspend fun ybbWatchAd(baiduEntity: BaiduEntity, version: String = "v2") { + val preJsonNode = client.get("https://api-gt.baidu.com/v1/server/task?version=$version") { + ybbDefaultHeader() + cookieString(baiduEntity.cookie) + }.body() + if (!preJsonNode["success"].asBoolean()) error(preJsonNode["errors"]["message_cn"].asText()) val preResult = preJsonNode["result"] - val ll = preResult.filter { it.getString("name") in listOf("看视频送时长", "看视频送积分") } + val ll = preResult.filter { it["name"].asText() in listOf("看视频送时长", "看视频送积分") } if (ll.isEmpty()) error("没有这个任务") - val sign = ll[0].getString("sign") + val sign = ll[0]["sign"].asText() val time = System.currentTimeMillis() val tenTime = time / 1000 val jsonNode = Jackson.createObjectNode() jsonNode.put("end_time", tenTime) jsonNode.put("start_time", tenTime) - jsonNode.put("task", ll[0].getInteger("id")) + jsonNode.put("task", ll[0]["id"].asInt()) jsonNode.put("sign", sign) - val resultJsonObject = OkHttpKtUtils.postJson( - "https://api-gt.baidu.com/v1/server/task${if (version.contains("v3")) "?version=v3" else ""}", - OkUtils.json(jsonNode), ybbDefaultHeader().also { - it["cookie"] = baiduEntity.cookie - } - ) - return if (resultJsonObject.getBoolean("success")) "观看广告成功!" - else error(resultJsonObject["errors"]["message_cn"].asText()) + val resultJsonObject = client.post("https://api-gt.baidu.com/v1/server/task${if (version.contains("v3")) "?version=v3" else ""}") { + setJsonBody(jsonNode) + ybbDefaultHeader() + cookieString(baiduEntity.cookie) + }.body() + if (!resultJsonObject["success"].asBoolean()) error(resultJsonObject["errors"]["message_cn"].asText()) } - suspend fun ybbSign(baiduEntity: BaiduEntity): String { - val map = ybbDefaultHeader() - map["cookie"] = baiduEntity.cookie - map["referer"] = "https://ybb.baidu.com/m/pages/h5/sign-activity?channel=xiaomi&device=android&appversion=2.3.14&cuid=8D795D0D8C8AB781BD0E0B807B0B1B0F%7CVCUIVQGDM&systemversion=31" - map["user-agent"] = "Mozilla/5.0 (Linux; Android 12; M2007J3SC Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36 com.baidu.ybb/2.3.14" - val jsonObject = OkHttpKtUtils.postJson("https://ybb.baidu.com/api/v1/server/scores", - OkUtils.json("""{"type": "daily"}"""), map) - return if (jsonObject.getBoolean("success")) "成功" - else error(jsonObject["errors"]["message_cn"].asText()) + suspend fun ybbSign(baiduEntity: BaiduEntity) { + val jsonNode = client.post("https://ybb.baidu.com/api/v1/server/scores") { + setJsonBody("""{"type": "daily"}""") + ybbDefaultHeader() + cookieString(baiduEntity.cookie) + referer("https://ybb.baidu.com/m/pages/h5/sign-activity?channel=xiaomi&device=android&appversion=2.3.14&cuid=8D795D0D8C8AB781BD0E0B807B0B1B0F%7CVCUIVQGDM&systemversion=31") + userAgent("Mozilla/5.0 (Linux; Android 12; M2007J3SC Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36 com.baidu.ybb/2.3.14") + }.body() + if (!jsonNode["success"].asBoolean()) error(jsonNode["errors"]["message_cn"].asText()) } private fun JsonNode.check() { @@ -101,12 +109,8 @@ class BaiduLogic ( suspend fun ybbExchangeVip(baiduEntity: BaiduEntity) { val jsonNode = client.post("https://api-gt.baidu.com/v1/server/reward_records") { setJsonBody("""{"award_id":48}""") - headers { - ybbDefaultHeader().forEach { (t, u) -> - append(t, u) - } - append("cookie", baiduEntity.cookie) - } + ybbDefaultHeader() + cookieString(baiduEntity.cookie) }.body() jsonNode.check() } @@ -114,11 +118,19 @@ class BaiduLogic ( private suspend fun getSToken(baiduEntity: BaiduEntity, url: String): String { val cookie = baiduEntity.cookie val headers = mapOf("cookie" to cookie, "user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36") - val response = OkHttpKtUtils.get("https://wappass.baidu.com/v3/login/api/auth?jump=¬jump=1&return_type=3&tpl=tb&u=${url.toUrlEncode()}", headers).apply { close() } - if (response.code !in listOf(302, 301)) throw RuntimeException("您的百度cookie已失效!") - val firstUrl = response.header("location")!! - val firstResponse = OkHttpKtUtils.get(firstUrl, headers).apply { close() } - return OkUtils.cookie(firstResponse, "STOKEN")!! + val response = client.get("https://wappass.baidu.com/v3/login/api/auth?jump=¬jump=1&return_type=3&tpl=tb&u=${url.toUrlEncode()}") { + headers { + headers.forEach { append(it.key, it.value) } + } + } + if (response.status.value !in listOf(302, 301)) throw RuntimeException("您的百度cookie已失效!") + val firstUrl = response.headers["location"]!! + val firstResponse = client.get(firstUrl) { + headers { + headers.forEach { append(it.key, it.value) } + } + } + return firstResponse.setCookie().find { it.name == "STOKEN" }?.value ?: error("获取sToken失败") } private suspend fun saveSToken(baiduEntity: BaiduEntity, url: String): String { @@ -128,13 +140,12 @@ class BaiduLogic ( return sToken } - suspend fun tieBaSign(baiduEntity: BaiduEntity): String { + suspend fun tieBaSign(baiduEntity: BaiduEntity) { val sToken = baiduEntity.tieBaSToken val url = "https://tieba.baidu.com/f/like/mylike?v=${System.currentTimeMillis()}" if (sToken.isEmpty()) saveSToken(baiduEntity, url) - val headers = mapOf("user-agent" to UA.PC.value, "cookie" to baiduEntity.teiBaCookie()) - val likeHtml = OkHttpKtUtils.getStr(url, - headers) + val headers = mapOf("user-agent" to "", "cookie" to baiduEntity.teiBaCookie()) + val likeHtml = client.get(url) { headers { headers.forEach { append(it.key, it.value) } } }.bodyAsText() if (likeHtml.isEmpty()) saveSToken(baiduEntity, url) val trElements = Jsoup.parse(likeHtml).getElementsByTag("tr") val list = mutableListOf() @@ -145,13 +156,18 @@ class BaiduLogic ( for (s in list) { delay(5000) val html = - OkHttpKtUtils.getStr("https://tieba.baidu.com/f?kw=${s.toUrlEncode()}&fr=index", headers) - val tbs = MyUtils.regex("'tbs': \"", "\"", html)!! - val jsonObject = OkHttpKtUtils.postJson("https://tieba.baidu.com/sign/add", mapOf("ie" to "utf-8", "kw" to s, "tbs" to tbs), - headers) - if (!arrayOf(1101, 0).contains(jsonObject.getInteger("no"))) error(jsonObject.getString("error")) + client.get("https://tieba.baidu.com/f?kw=${s.toUrlEncode()}&fr=index") { headers { headers.forEach { append(it.key, it.value) } } }.bodyAsText() + val tbs = RegexUtils.extract(html, "'tbs': \"", "\"")!! + val jsonObject = client.submitForm("https://tieba.baidu.com/sign/add", + parameters { + mapOf("ie" to "utf-8", "kw" to s, "tbs" to tbs).forEach { append(it.key, it.value) } + }) { + headers { + headers.forEach { append(it.key, it.value) } + } + }.body() + if (!arrayOf(1101, 0).contains(jsonObject["no"].asInt())) error(jsonObject["error"].asText()) } - return "贴吧签到成功!" } } diff --git a/src/main/kotlin/me/kuku/telegram/logic/BiliBiliLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/BiliBiliLogic.kt index 5757678..dfe46fe 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/BiliBiliLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/BiliBiliLogic.kt @@ -4,38 +4,48 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.module.kotlin.contains import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.request.forms.* import io.ktor.client.statement.* import io.ktor.http.* import kotlinx.coroutines.delay -import me.kuku.pojo.CommonResult -import me.kuku.pojo.UA import me.kuku.telegram.entity.BiliBiliEntity -import me.kuku.telegram.utils.ffmpeg -import me.kuku.utils.* +import me.kuku.telegram.exception.qrcodeExpire +import me.kuku.telegram.exception.qrcodeNotScanned +import me.kuku.telegram.exception.qrcodeScanned +import me.kuku.telegram.utils.* import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.toRequestBody import okio.ByteString import java.io.File +import java.io.InputStream +import java.nio.file.Files +import java.nio.file.Path +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import kotlin.io.path.absolutePathString +import kotlin.io.path.deleteIfExists object BiliBiliLogic { - suspend fun getIdByName(username: String): CommonResult> { + suspend fun getIdByName(username: String): List { val enUsername = username.toUrlEncode() - val jsonNode = OkHttpKtUtils.getJsonp("https://api.bilibili.com/x/web-interface/search/type?context=&search_type=bili_user&page=1&order=&keyword=$enUsername&category_id=&user_type=&order_sort=&changing=mid&__refresh__=true&_extra=&highlight=1&single_column=0&jsonp=jsonp&callback=__jp2", - OkUtils.referer("https://search.bilibili.com/topic?keyword=$enUsername")) + val jsonNode = client.get("https://api.bilibili.com/x/web-interface/search/type?context=&search_type=bili_user&page=1&order=&keyword=$enUsername&category_id=&user_type=&order_sort=&changing=mid&__refresh__=true&_extra=&highlight=1&single_column=0&jsonp=jsonp&callback=__jp2") { + referer("https://search.bilibili.com/topic?keyword=$enUsername") + }.bodyAsText().jsonpToJsonNode() val dataJsonNode = jsonNode["data"] - return if (dataJsonNode.getInteger("numCommonResults") != 0) { + return if (dataJsonNode["numCommonResults"].asInt() != 0) { val jsonArray = dataJsonNode["result"] val list = mutableListOf() for (obj in jsonArray) { list.add( - BiliBiliPojo(userId = obj.getString("mid"), - name = obj.getString("uname")) + BiliBiliPojo(userId = obj["mid"].asText(), + name = obj["uname"].asText()) ) } - CommonResult.success(list) - } else CommonResult.failure("not result") + list + } else error("not result") } private fun convert(jsonNode: JsonNode): BiliBiliPojo { @@ -45,9 +55,9 @@ object BiliBiliLogic { val forwardJsonNode = descJsonNode["origin"] biliBiliPojo.userId = infoJsonNode?.get("uid")?.asText() ?: "" biliBiliPojo.name = infoJsonNode?.get("uname")?.asText() ?: "" - biliBiliPojo.id = descJsonNode.getString("dynamic_id") - biliBiliPojo.rid = descJsonNode.getString("rid") - biliBiliPojo.time = (descJsonNode.getString("timestamp") + "000").toLong() + biliBiliPojo.id = descJsonNode["dynamic_id"].asText() + biliBiliPojo.rid = descJsonNode["rid"].asText() + biliBiliPojo.time = (descJsonNode["timestamp"].asText() + "000").toLong() biliBiliPojo.bvId = descJsonNode.get("bvid")?.asText() ?: "" biliBiliPojo.isForward = !forwardJsonNode.isNull if (!forwardJsonNode.isNull) { @@ -60,13 +70,13 @@ object BiliBiliLogic { forwardJsonNode.get("timestamp")?.asText()?.let { biliBiliPojo.forwardTime = (it + "000").toLong() } - biliBiliPojo.forwardId = forwardJsonNode.getString("dynamic_id") + biliBiliPojo.forwardId = forwardJsonNode["dynamic_id"].asText() } var text: String? = null - jsonNode["card"]?.asText()?.let { Jackson.parse(it) }?.let { cardJsonNode -> + jsonNode["card"]?.asText()?.let { Jackson.readTree(it) }?.let { cardJsonNode -> if (biliBiliPojo.userId.isEmpty()) { val collectionJsonNode = cardJsonNode["collection"] - biliBiliPojo.userId = collectionJsonNode.getString("id") + biliBiliPojo.userId = collectionJsonNode["id"].asText() biliBiliPojo.name = collectionJsonNode["name"]?.asText() ?: "" } val itemJsonNode = cardJsonNode["item"] @@ -81,16 +91,16 @@ object BiliBiliLogic { if (text == null) text = itemJsonNode["description"]?.asText() if (text == null) text = itemJsonNode["content"]?.asText() itemJsonNode["pictures"]?.forEach { - picList.add(it.getString("img_src")) + picList.add(it["img_src"].asText()) } } if (text == null) { cardJsonNode["vest"]?.let { - text = it.getString("content") + text = it["content"].asText() } } if (text == null && cardJsonNode.contains("title")) { - text = cardJsonNode.getString("title") + "------" + cardJsonNode.getString("summary") + text = cardJsonNode["title"].asText() + "------" + cardJsonNode["summary"].asText() } cardJsonNode["pub_location"]?.asText()?.let { location -> biliBiliPojo.ipFrom = location @@ -110,22 +120,22 @@ object BiliBiliLogic { val forwardItemJsonNode = forwardContentJsonNode["item"] biliBiliPojo.forwardText = forwardItemJsonNode["description"]?.asText() ?: "" if (biliBiliPojo.forwardText.isEmpty()) - biliBiliPojo.forwardText = forwardItemJsonNode.getString("content") + biliBiliPojo.forwardText = forwardItemJsonNode["content"].asText() val forwardPicJsonArray = forwardItemJsonNode["pictures"] if (forwardPicJsonArray != null) { for (obj in forwardPicJsonArray) { - forwardPicList.add(obj.getString("img_src")) + forwardPicList.add(obj["img_src"].asText()) } } val forwardUserJsonNode = forwardContentJsonNode["user"] if (forwardUserJsonNode != null) { - biliBiliPojo.forwardUserId = forwardUserJsonNode.getString("uid") - biliBiliPojo.forwardName = forwardUserJsonNode["name"]?.asText() ?: forwardUserJsonNode.getString("uname") + biliBiliPojo.forwardUserId = forwardUserJsonNode["uid"].asText() + biliBiliPojo.forwardName = forwardUserJsonNode["name"]?.asText() ?: forwardUserJsonNode["uname"].asText() } else { val forwardOwnerJsonNode = forwardContentJsonNode["owner"] if (forwardOwnerJsonNode != null) { - biliBiliPojo.forwardUserId = forwardOwnerJsonNode.getString("mid") - biliBiliPojo.forwardName = forwardOwnerJsonNode.getString("name") + biliBiliPojo.forwardUserId = forwardOwnerJsonNode["mid"].asText() + biliBiliPojo.forwardName = forwardOwnerJsonNode["name"].asText() } } @@ -163,10 +173,10 @@ object BiliBiliLogic { val bvId = biliBiliPojo.bvId val ipFrom = biliBiliPojo.ipFrom val forwardBvId = biliBiliPojo.forwardBvId - var ss = "#${biliBiliPojo.name}\n来自:${ipFrom.ifEmpty { "无" }}\n发布时间:${DateTimeFormatterUtils.format(biliBiliPojo.time, pattern)}" + + var ss = "#${biliBiliPojo.name}\n来自:${ipFrom.ifEmpty { "无" }}\n发布时间:${Instant.ofEpochMilli(biliBiliPojo.time).atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern(pattern))}" + "\n内容:${biliBiliPojo.text}\n动态链接:https://t.bilibili.com/${biliBiliPojo.id}\n视频链接:${if (bvId.isNotEmpty()) "https://www.bilibili.com/video/$bvId" else "无"}" if (biliBiliPojo.isForward) { - ss += "\n转发自:#${biliBiliPojo.forwardName}\n发布时间:${DateTimeFormatterUtils.format(biliBiliPojo.forwardTime, pattern)}\n" + + ss += "\n转发自:#${biliBiliPojo.forwardName}\n发布时间:${Instant.ofEpochMilli(biliBiliPojo.forwardTime).atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern(pattern))}\n" + "内容:${biliBiliPojo.forwardText}\n动态链接:https://t.bilibili.com/${biliBiliPojo.forwardId}\n视频链接:${if (forwardBvId.isNotEmpty()) "https://www.bilibili.com/video/$forwardBvId" else "无"}" } return ss @@ -176,103 +186,110 @@ object BiliBiliLogic { val htmlUrl = "https://www.bilibili.com/video/$bvId/" val response = client.get(htmlUrl) { cookieString(biliBiliEntity.cookie) - userAgent(UA.PC.value) } return if (response.status != HttpStatusCode.OK) { error("错误:${response.status}") } else { val html = response.bodyAsText() - val jsonNode = MyUtils.regex("window.__playinfo__=", "().use { + val path = Path.of("tmp", "$bvId.mp4") + Files.copy(it, path) + path } - OkHttpKtUtils.getByteStream(audioUrl, OkUtils.referer(htmlUrl)).use { - audioFile = IOUtils.writeTmpFile("${bvId}.mp3", it, false) + val audioFile = client.get(audioUrl) { referer(htmlUrl) }.body().use { + val path = Path.of("tmp", "$bvId.mp3") + Files.copy(it, path) + path } - val videoPath = videoFile.absolutePath - val audioPath = audioFile.absolutePath + val videoPath = videoFile.absolutePathString() + val audioPath = audioFile.absolutePathString() val outputPath = videoPath.replace(bvId, "${bvId}output") ffmpeg("ffmpeg -i $videoPath -i $audioPath -c:v copy -c:a aac -strict experimental $outputPath") - videoFile.delete() - audioFile.delete() + videoFile.deleteIfExists() + audioFile.deleteIfExists() File(outputPath) } } - suspend fun getDynamicById(id: String, offsetId: String = "0"): CommonResult> { - val jsonNode = OkHttpKtUtils.getJson("https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?visitor_uid=0&host_uid=$id&offset_dynamic_id=$offsetId&need_top=1", - OkUtils.referer("https://space.bilibili.com/$id/dynamic")) + suspend fun getDynamicById(id: String, offsetId: String = "0"): List { + val jsonNode = client.get("https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?visitor_uid=0&host_uid=$id&offset_dynamic_id=$offsetId&need_top=1") { + referer("https://space.bilibili.com/$id/dynamic") + }.body() // next_offset 下一页开头 val dataJsonNode = jsonNode["data"] - val jsonArray = dataJsonNode["cards"] ?: return CommonResult.failure("动态未找到") + val jsonArray = dataJsonNode["cards"] ?: error("动态未找到") val list = mutableListOf() for (obj in jsonArray) { val extraJsonNode = obj["extra"] - if (extraJsonNode != null && 1 == extraJsonNode.getInteger("is_space_top")) continue + if (extraJsonNode != null && 1 == extraJsonNode["is_space_top"].asInt()) continue list.add(convert(obj)) } - return CommonResult.success(message = dataJsonNode.getString("next_offset"), data = list) + return list } suspend fun loginByQr1(): BiliBiliQrcode { - val jsonNode = OkHttpKtUtils.getJson("https://passport.bilibili.com/x/passport-login/web/qrcode/generate?source=main-fe-header") + val jsonNode = client.get("https://passport.bilibili.com/x/passport-login/web/qrcode/generate?source=main-fe-header").body() val data = jsonNode["data"] return BiliBiliQrcode(data["url"].asText(), data["qrcode_key"].asText()) } - suspend fun loginByQr2(qrcode: BiliBiliQrcode): CommonResult { - val response = OkHttpKtUtils.get("https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${qrcode.key}&source=main-fe-header") - val jsonNode = OkUtils.json(response) + suspend fun loginByQr2(qrcode: BiliBiliQrcode): BiliBiliEntity { + val response = client.get("https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${qrcode.key}&source=main-fe-header") + val jsonNode = response.body() val data = jsonNode["data"] return when(data["code"].asInt()) { - 86101 -> CommonResult.failure("二维码未扫描", code = 0) - 86090 -> CommonResult.failure("二维码已扫描", code = 0) - 86038 -> CommonResult.failure("您的二维码已过期!!", null) + 86101 -> qrcodeNotScanned() + 86090 -> qrcodeScanned() + 86038 -> qrcodeExpire() 0 -> { - val firstCookie = OkUtils.cookie(response) + val firstCookie = response.setCookie().renderCookieHeader() val url = data["url"].asText() - val token = MyUtils.regex("bili_jct=", "\\u0026", url)!! + val token = RegexUtils.extract(url, "bili_jct=", "\\u0026")!! val urlJsonNode = - OkHttpKtUtils.getJson("https://passport.bilibili.com/x/passport-login/web/sso/list?biliCSRF=$token", - OkUtils.cookie(firstCookie)) + client.get("https://passport.bilibili.com/x/passport-login/web/sso/list?biliCSRF=$token") { + cookieString(firstCookie) + }.body() val sso = urlJsonNode["data"]["sso"] var cookie = "" sso.forEach { val innerUrl = it.asText() - val innerResponse = OkHttpKtUtils.post(innerUrl, "".toRequestBody("application/x-www-form-urlencoded".toMediaType()), - mapOf("Referer" to "https://www.bilibili.com/", "Origin" to "https://www.bilibili.com", - "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36")) - innerResponse.close() - cookie = OkUtils.cookie(innerResponse) + val innerResponse = client.submitForm(innerUrl, parameters { }) { + headers { + mapOf("Referer" to "https://www.bilibili.com/", "Origin" to "https://www.bilibili.com", + "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36") + .forEach { append(it.key, it.value) } + } + } + cookie = innerResponse.setCookie().renderCookieHeader() } - val userid = MyUtils.regex("DedeUserID=", "; ", cookie)!! - val fingerJsonNode = OkHttpKtUtils.getJson("https://api.bilibili.com/x/frontend/finger/spi") + val userid = RegexUtils.extract(cookie, "DedeUserID=", "; ")!! + val fingerJsonNode = client.get("https://api.bilibili.com/x/frontend/finger/spi").body() val fingerData = fingerJsonNode["data"] val fingerCookie = "buvid3=${fingerData["b_3"].asText()}; buvid4=${fingerData["b_4"].asText()}; " val biliBiliEntity = BiliBiliEntity() biliBiliEntity.cookie = cookie + fingerCookie biliBiliEntity.userid = userid biliBiliEntity.token = token - CommonResult.success(biliBiliEntity) + biliBiliEntity } - else -> CommonResult.failure(data["message"].asText(), null) + else -> error(data["message"].asText()) } } - suspend fun friendDynamic(biliBiliEntity: BiliBiliEntity): CommonResult> { - val jsonNode = OkHttpKtUtils.getJson("https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new?type_list=268435455", - OkUtils.cookie(biliBiliEntity.cookie)) - return if (jsonNode.getInteger("code") != 0) CommonResult.failure("cookie已失效") + suspend fun friendDynamic(biliBiliEntity: BiliBiliEntity): List { + val jsonNode = client.get("https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new?type_list=268435455") { + cookieString(biliBiliEntity.cookie) + }.body() + return if (jsonNode["code"].asInt() != 0) error("cookie已失效") else { val list = mutableListOf() jsonNode["data"]["cards"].forEach{ list.add(convert(it)) } - CommonResult.success(list) + list } } @@ -303,101 +320,117 @@ object BiliBiliLogic { } suspend fun liveSign(biliBiliEntity: BiliBiliEntity): String { - val jsonNode = OkHttpKtUtils.getJson("https://api.live.bilibili.com/xlive/web-ucenter/v1/sign/DoSign", - OkUtils.cookie(biliBiliEntity.cookie)) - return if (jsonNode.getInteger("code") == 0) "成功" - else error(jsonNode.getString("message")) + val jsonNode = client.get("https://api.live.bilibili.com/xlive/web-ucenter/v1/sign/DoSign") { + cookieString(biliBiliEntity.cookie) + }.body() + return if (jsonNode["code"].asInt() == 0) "成功" + else error(jsonNode["message"].asText()) } - suspend fun like(biliBiliEntity: BiliBiliEntity, id: String, isLike: Boolean): CommonResult { + suspend fun like(biliBiliEntity: BiliBiliEntity, id: String, isLike: Boolean) { val map = mapOf("uid" to biliBiliEntity.userid, "dynamic_id" to id, "up" to if (isLike) "1" else "2", "csrf_token" to biliBiliEntity.token) - val jsonNode = OkHttpKtUtils.postJson("https://api.vc.bilibili.com/dynamic_like/v1/dynamic_like/thumb", map, - OkUtils.cookie(biliBiliEntity.cookie)) - return if (jsonNode.getInteger("code") == 0) CommonResult.success() - else CommonResult.failure("赞动态失败,${jsonNode.getString("message")}") + val jsonNode = client.submitForm("https://api.vc.bilibili.com/dynamic_like/v1/dynamic_like/thumb", + parameters { map.forEach { append(it.key, it.value) } }) { + cookieString(biliBiliEntity.cookie) + }.body() + if (jsonNode["code"].asInt() != 0) error("赞动态失败,${jsonNode["message"].asText()}") } - suspend fun comment(biliBiliEntity: BiliBiliEntity, rid: String, type: String, content: String): CommonResult { + suspend fun comment(biliBiliEntity: BiliBiliEntity, rid: String, type: String, content: String) { val map = mapOf("oid" to rid, "type" to type, "message" to content, "plat" to "1", "jsoup" to "jsoup", "csrf_token" to biliBiliEntity.token) - val jsonNode = OkHttpKtUtils.postJson("https://api.bilibili.com/x/v2/reply/add", map, OkUtils.cookie(biliBiliEntity.cookie)) - return if (jsonNode.getInteger("code") == 0) CommonResult.success() - else CommonResult.failure("评论动态失败,${jsonNode.getString("message")}") + val jsonNode = client.submitForm("https://api.bilibili.com/x/v2/reply/add", + parameters { map.forEach { append(it.key, it.value) } }) { + cookieString(biliBiliEntity.cookie) + }.body() + if (jsonNode["code"].asInt() != 0) error("评论动态失败,${jsonNode["message"].asText()}") } - suspend fun forward(biliBiliEntity: BiliBiliEntity, id: String, content: String): CommonResult { + suspend fun forward(biliBiliEntity: BiliBiliEntity, id: String, content: String) { val map = mapOf("uid" to biliBiliEntity.userid, "dynamic_id" to id, "content" to content, "extension" to "{\"emoji_type\":1}", "at_uids" to "", "ctrl" to "[]", "csrf_token" to biliBiliEntity.token) - val jsonNode = OkHttpKtUtils.postJson("https://api.vc.bilibili.com/dynamic_repost/v1/dynamic_repost/repost", map, - OkUtils.cookie(biliBiliEntity.cookie)) - return if (jsonNode.getInteger("code") == 0) CommonResult.success() - else CommonResult.failure("转发动态失败,${jsonNode.getString("message")}") + val jsonNode = client.submitForm("https://api.vc.bilibili.com/dynamic_repost/v1/dynamic_repost/repost", parameters { + map.forEach { append(it.key, it.value) } + }) { + cookieString(biliBiliEntity.cookie) + }.body() + if (jsonNode["code"].asInt() != 0) error("转发动态失败,${jsonNode["message"].asText()}") } - suspend fun tossCoin(biliBiliEntity: BiliBiliEntity, rid: String, count: Int = 1): CommonResult { + suspend fun tossCoin(biliBiliEntity: BiliBiliEntity, rid: String, count: Int = 1) { val map = mapOf("aid" to rid, "multiply" to count.toString(), "select_like" to "1", "cross_domain" to "true", "csrf" to biliBiliEntity.token) - val jsonNode = OkHttpKtUtils.postJson("https://api.bilibili.com/x/web-interface/coin/add", map, - OkUtils.headers(biliBiliEntity.cookie, "https://www.bilibili.com/video/")) - return if (jsonNode.getInteger("code") == 0) CommonResult.success() - else CommonResult.failure("对该动态(视频)投硬币失败,${jsonNode.getString("message")}") + val jsonNode = client.submitForm("https://api.bilibili.com/x/web-interface/coin/add", parameters { + map.forEach { append(it.key, it.value) } + }) { + cookieString(biliBiliEntity.cookie) + }.body() + if (jsonNode["code"].asInt() != 0) error("对该动态(视频)投硬币失败,${jsonNode["message"].asText()}") } - suspend fun favorites(biliBiliEntity: BiliBiliEntity, rid: String, name: String): CommonResult { + suspend fun favorites(biliBiliEntity: BiliBiliEntity, rid: String, name: String) { val userid = biliBiliEntity.userid val cookie = biliBiliEntity.cookie val token = biliBiliEntity.token - val firstJsonNode = OkHttpKtUtils.getJson("https://api.bilibili.com/x/v3/fav/folder/created/list-all?type=2&rid=$rid&up_mid=$userid", - OkUtils.cookie(cookie)) - if (firstJsonNode.getInteger("code") != 0) return CommonResult.failure("收藏失败,请重新绑定哔哩哔哩") + val firstJsonNode = client.get("https://api.bilibili.com/x/v3/fav/folder/created/list-all?type=2&rid=$rid&up_mid=$userid") { + cookieString(cookie) + }.body() + if (firstJsonNode["code"].asInt() != 0) error("收藏失败,请重新绑定哔哩哔哩") val jsonArray = firstJsonNode["data"]["list"] var favoriteId: String? = null for (obj in jsonArray) { - if (obj.getString("title") == name) { - favoriteId = obj.getString("id") + if (obj["title"].asText() == name) { + favoriteId = obj["id"].asText() } } if (favoriteId == null) { val map = mapOf("title" to name, "privacy" to "0", "jsonp" to "jsonp", "csrf" to token) - val jsonNode = OkHttpKtUtils.postJson("https://api.bilibili.com/x/v3/fav/folder/add", map, - OkUtils.cookie(cookie)) - if (jsonNode.getInteger("code") != 0) return CommonResult.failure("您并没有该收藏夹,而且创建该收藏夹失败,请重试!!") + val jsonNode = client.submitForm("https://api.bilibili.com/x/v3/fav/folder/add", + parameters { map.forEach { append(it.key, it.value) } }) { + cookieString(cookie) + }.body() + if (jsonNode["code"].asInt() != 0) error("您并没有该收藏夹,而且创建该收藏夹失败,请重试!!") favoriteId = jsonNode["data"]["id"].asText() } - val map = mapOf("rid" to rid, "type" to "2", "add_media_ids" to favoriteId!!, + val map = mapOf("rid" to rid, "type" to "2", "add_media_ids" to favoriteId, "del_media_ids" to "", "jsonp" to "jsonp", "csrf" to token) - val jsonNode = OkHttpKtUtils.postJson("https://api.bilibili.com/x/v3/fav/resource/deal", map, - OkUtils.cookie(cookie)) - return if (jsonNode.getInteger("code") == 0) CommonResult.success() - else CommonResult.failure("收藏视频失败," + jsonNode.getString("message")) + val jsonNode = client.submitForm("https://api.bilibili.com/x/v3/fav/resource/deal", + parameters { map.forEach { append(it.key, it.value ?: "") } }) { + cookieString(cookie) + }.body() + if (jsonNode["code"].asInt() != 0) error("收藏视频失败," + jsonNode["message"].asText()) } - private suspend fun uploadImage(biliBiliEntity: BiliBiliEntity, byteString: ByteString): CommonResult { - val body = MultipartBody.Builder().setType(MultipartBody.FORM) - .addFormDataPart("file_up", "123.jpg", OkUtils.stream(byteString)) - .addFormDataPart("biz", "draw") - .addFormDataPart("category", "daily").build() - val jsonNode = OkHttpKtUtils.postJson("https://api.vc.bilibili.com/api/v1/drawImage/upload", body, - OkUtils.cookie(biliBiliEntity.cookie)) - return if (jsonNode.getInteger("code") == 0) CommonResult.success(jsonNode["data"]) - else CommonResult.failure("图片上传失败," + jsonNode.getString("message"), null) + private suspend fun uploadImage(biliBiliEntity: BiliBiliEntity, byteArray: ByteArray): JsonNode { + val jsonNode = client.submitFormWithBinaryData("https://api.vc.bilibili.com/api/v1/drawImage/upload", + formData { + append("biz", "draw") + append("category", "daily") + append("file_up", byteArray) + }) { + cookieString(biliBiliEntity.cookie) + }.body() + return if (jsonNode["code"].asInt() == 0) jsonNode["data"] + else error("图片上传失败," + jsonNode["message"].asText()) } - suspend fun publishDynamic(biliBiliEntity: BiliBiliEntity, content: String, images: List): CommonResult { + suspend fun publishDynamic(biliBiliEntity: BiliBiliEntity, content: String, images: List) { val jsonArray = Jackson.createArrayNode() images.forEach{ - jsonArray.addPOJO(uploadImage(biliBiliEntity, OkHttpKtUtils.getByteString(it))) + jsonArray.addPOJO(uploadImage(biliBiliEntity, client.get(it).body())) } val map = mapOf("biz" to "3", "category" to "3", "type" to "0", "pictures" to jsonArray.toString(), "title" to "", "tags" to "", "description" to content, "content" to content, "setting" to "{\"copy_forbidden\":0,\"cachedTime\":0}", "from" to "create.dynamic.web", "up_choose_comment" to "0", "extension" to "{\"emoji_type\":1,\"from\":{\"emoji_type\":1},\"flag_cfg\":{}}", "at_uids" to "", "at_control" to "", "csrf_token" to biliBiliEntity.token) - val jsonNode = OkHttpKtUtils.postJson("https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/create_draw", map, - OkUtils.cookie(biliBiliEntity.cookie)) - return if (jsonNode.getInteger("code") == 0) CommonResult.success() - else CommonResult.failure("发布动态失败," + jsonNode.getString("message")) + val jsonNode = client.submitForm("https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/create_draw", parameters { + map.forEach { append(it.key, it.value) } + }) { + cookieString(biliBiliEntity.cookie) + }.body() + if (jsonNode["code"].asInt() != 0) error("发布动态失败," + jsonNode["message"].asText()) } suspend fun ranking(biliBiliEntity: BiliBiliEntity): List { @@ -410,26 +443,27 @@ object BiliBiliLogic { val list = mutableListOf() for (singleJsonNode in jsonArray) { val biliBiliRanking = BiliBiliRanking() - biliBiliRanking.aid = singleJsonNode.getString("aid") - biliBiliRanking.cid = singleJsonNode.getString("cid") - biliBiliRanking.title = singleJsonNode.getString("title") - biliBiliRanking.desc = singleJsonNode.getString("desc") + biliBiliRanking.aid = singleJsonNode["aid"].asText() + biliBiliRanking.cid = singleJsonNode["cid"].asText() + biliBiliRanking.title = singleJsonNode["title"].asText() + biliBiliRanking.desc = singleJsonNode["desc"].asText() biliBiliRanking.username = singleJsonNode["owner"]["name"].asText() - biliBiliRanking.dynamic = singleJsonNode.getString("dynamic") - biliBiliRanking.bv = singleJsonNode.getString("bvid") + biliBiliRanking.dynamic = singleJsonNode["dynamic"].asText() + biliBiliRanking.bv = singleJsonNode["bvid"].asText() biliBiliRanking.duration = singleJsonNode["bvid"].asInt() list.add(biliBiliRanking) } return list } - suspend fun report(biliBiliEntity: BiliBiliEntity, aid: String, cid: String, proGRes: Int): String { + suspend fun report(biliBiliEntity: BiliBiliEntity, aid: String, cid: String, proGRes: Int) { val map = mapOf("aid" to aid, "cid" to cid, "progres" to proGRes.toString(), "csrf" to biliBiliEntity.token) - val jsonNode = OkHttpKtUtils.postJson("https://api.bilibili.com/x/v2/history/report", map, - OkUtils.cookie(biliBiliEntity.cookie)) - return if (jsonNode.getInteger("code") == 0) "成功" - else error(jsonNode.getString("message")) + val jsonNode = client.submitForm("https://api.bilibili.com/x/v2/history/report", + parameters { map.forEach { append(it.key, it.value) } }) { + cookieString(biliBiliEntity.cookie) + }.body() + if (jsonNode["code"].asInt() != 0) error(jsonNode["message"].asText()) } private fun JsonNode.check() { @@ -454,10 +488,8 @@ object BiliBiliLogic { "from_spmid" to "333.1007.tianma.1-1-1.click", "csrf" to biliBiliEntity.token, ) - val jsonNode = client.post("https://api.bilibili.com/x/click-interface/click/web/h5") { - setFormDataContent { - map.forEach { (t, u) -> append(t, u) } - } + val jsonNode = client.submitForm("https://api.bilibili.com/x/click-interface/click/web/h5", + parameters { map.forEach { (t, u) -> append(t, u) } }) { cookieString(biliBiliEntity.cookie) }.body() jsonNode.check() @@ -472,84 +504,87 @@ object BiliBiliLogic { map["video_duration"] = biliBiliRanking.duration.toString() map["last_play_progress_time"] = (biliBiliRanking.duration - 2).toString() map["max_play_progress_time"] = (biliBiliRanking.duration - 2).toString() - val watchJsonNode = client.post("https://api.bilibili.com/x/click-interface/web/heartbeat") { - setFormDataContent { - map.forEach { (t, u) -> append(t, u) } - } + val watchJsonNode = client.submitForm("https://api.bilibili.com/x/click-interface/web/heartbeat", + parameters { map.forEach { (t, u) -> append(t, u) } }) { cookieString(biliBiliEntity.cookie) }.body() watchJsonNode.check() } - suspend fun share(biliBiliEntity: BiliBiliEntity, aid: String): String { - val jsonNode = client.post("https://api.bilibili.com/x/web-interface/share/add") { - cookieString(biliBiliEntity.cookie) - setFormDataContent { + suspend fun share(biliBiliEntity: BiliBiliEntity, aid: String) { + val jsonNode = client.submitForm("https://api.bilibili.com/x/web-interface/share/add", + parameters { append("aid", aid) append("csrf", biliBiliEntity.token) - } + }) { + cookieString(biliBiliEntity.cookie) }.body() - return if (jsonNode.getInteger("code") in listOf(0, 71000)) "成功" - else error(jsonNode.getString("message")) + if (jsonNode["code"].asInt() !in listOf(0, 71000)) error(jsonNode["message"].asText()) } suspend fun getReplay(biliBiliEntity: BiliBiliEntity, oid: String, page: Int): List { - val jsonNode = OkHttpKtUtils.getJsonp( - "https://api.bilibili.com/x/v2/reply?callback=jQuery17207366906764958399_${System.currentTimeMillis()}&jsonp=jsonp&pn=$page&type=1&oid=$oid&sort=2&_=${System.currentTimeMillis()}", - OkUtils.headers(biliBiliEntity.cookie, "https://www.bilibili.com/")) - return if (jsonNode.getInteger("code") == 0) { + val jsonNode = client.get( + "https://api.bilibili.com/x/v2/reply?callback=jQuery17207366906764958399_${System.currentTimeMillis()}&jsonp=jsonp&pn=$page&type=1&oid=$oid&sort=2&_=${System.currentTimeMillis()}") { + cookieString(biliBiliEntity.cookie) + referer("https://www.bilibili.com/") + }.bodyAsText().jsonpToJsonNode() + return if (jsonNode["code"].asInt() == 0) { val jsonArray = jsonNode["data"]["replies"] val list = mutableListOf() for (obj in jsonArray) { - val biliReplay = BiliBiliReplay(obj.getString("rpid"), obj["content"].getString("message")) + val biliReplay = BiliBiliReplay(obj["rpid"].asText(), obj["content"]["message"].asText()) list.add(biliReplay) } list }else listOf() } - suspend fun reportComment(biliBiliEntity: BiliBiliEntity, oid: String, rpId: String, reason: Int): CommonResult { + suspend fun reportComment(biliBiliEntity: BiliBiliEntity, oid: String, rpId: String, reason: Int) { // 违法违规 9 色情 2 低俗 10 赌博诈骗 12 // 人身攻击 7 侵犯隐私 15 // 垃圾广告 1 引战 4 剧透 5 刷屏 3 抢楼 16 内容不相关 8 青少年不良信息 17 // 其他 0 val map = mapOf("oid" to oid, "type" to "1", "rpid" to rpId, "reason" to reason.toString(), "content" to "", "ordering" to "heat", "jsonp" to "jsonp", "csrf" to biliBiliEntity.token) - val jsonNode = OkHttpKtUtils.postJson("https://api.bilibili.com/x/v2/reply/report", map, - OkUtils.headers(biliBiliEntity.cookie, "https://www.bilibili.com/")) - return if (jsonNode.getInteger("code") == 0) CommonResult.success(message = "举报评论成功!!") - else CommonResult.failure("举报评论失败!!") + val jsonNode = client.submitForm("https://api.bilibili.com/x/v2/reply/report", + parameters { map.forEach { append(it.key, it.value) } }) { + cookieString(biliBiliEntity.cookie) + referer("https://www.bilibili.com/") + }.body() + if (jsonNode["code"].asInt() != 0) error("举报评论失败!!") } suspend fun getOidByBvId(bvId: String): String { - val html = OkHttpKtUtils.getStr("https://www.bilibili.com/video/$bvId", - OkUtils.ua(UA.PC)) - val jsonStr = MyUtils.regex("INITIAL_STATE__=", ";\\(function\\(\\)", html)!! + val html = client.get("https://www.bilibili.com/video/$bvId").bodyAsText() + val jsonStr = RegexUtils.extract(html, "INITIAL_STATE__=", ";\\(function\\(\\)")!! val jsonNode = jsonStr.toJsonNode() - return jsonNode.getString("aid") + return jsonNode["aid"].asText() } - suspend fun followed(biliBiliEntity: BiliBiliEntity): CommonResult> { + suspend fun followed(biliBiliEntity: BiliBiliEntity): List { val list = mutableListOf() var i = 1 while (true) { val jsonNode = onceFollowed(biliBiliEntity, i++) - if (jsonNode.getInteger("code") == 0) { + if (jsonNode["code"].asInt() == 0) { val jsonArray = jsonNode["data"]["list"] if (jsonArray.size() == 0) break for (any in jsonArray) { - list.add(BiliBiliFollowed(any.getString("mid"), any.getString("uname"))) + list.add(BiliBiliFollowed(any["mid"].asText(), any["uname"].asText())) } - } else return CommonResult.failure(jsonNode.getString("message")) + } else error(jsonNode["message"].asText()) } - return CommonResult.success(list) + return list } private suspend fun onceFollowed(biliBiliEntity: BiliBiliEntity, i: Int): JsonNode { val headers = mapOf("referer" to "https://space.bilibili.com/${biliBiliEntity.userid}/fans/follow", - "user-agent" to UA.PC.value, "cookie" to biliBiliEntity.cookie) - return OkHttpKtUtils.getJsonp("https://api.bilibili.com/x/relation/followings?vmid=${biliBiliEntity.userid}&pn=$i&ps=100&order=desc&order_type=attention&jsonp=jsonp&callback=__jp5", - headers) + "user-agent" to "", "cookie" to biliBiliEntity.cookie) + return client.get("https://api.bilibili.com/x/relation/followings?vmid=${biliBiliEntity.userid}&pn=$i&ps=100&order=desc&order_type=attention&jsonp=jsonp&callback=__jp5") { + headers { + headers.forEach { (t, u) -> append(t, u) } + } + }.bodyAsText().jsonpToJsonNode() } diff --git a/src/main/kotlin/me/kuku/telegram/logic/CaptchaLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/CaptchaLogic.kt index f8e1300..d6f165b 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/CaptchaLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/CaptchaLogic.kt @@ -8,9 +8,9 @@ import kotlinx.coroutines.delay import me.kuku.telegram.config.TelegramConfig import me.kuku.telegram.entity.BotConfigService import me.kuku.telegram.entity.ConfigService -import me.kuku.utils.Jackson -import me.kuku.utils.client -import me.kuku.utils.setJsonBody +import me.kuku.telegram.utils.Jackson +import me.kuku.telegram.utils.client +import me.kuku.telegram.utils.setJsonBody import org.springframework.stereotype.Service @Service diff --git a/src/main/kotlin/me/kuku/telegram/logic/DouYuLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/DouYuLogic.kt index aa5ba29..7dc579c 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/DouYuLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/DouYuLogic.kt @@ -5,16 +5,15 @@ import com.fasterxml.jackson.databind.node.NullNode import com.fasterxml.jackson.module.kotlin.contains import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.request.forms.* import io.ktor.client.statement.* import io.ktor.http.* import kotlinx.coroutines.delay -import me.kuku.pojo.CommonResult -import me.kuku.pojo.UA import me.kuku.telegram.entity.DouYuEntity import me.kuku.telegram.entity.DouYuService -import me.kuku.utils.* +import me.kuku.telegram.exception.qrcodeNotScanned +import me.kuku.telegram.utils.* import org.springframework.stereotype.Service -import java.nio.charset.Charset @Service class DouYuLogic( @@ -24,14 +23,12 @@ class DouYuLogic( private val referer = "https://passport.douyu.com/index/login?passport_reg_callback=PASSPORT_REG_SUCCESS_CALLBACK&passport_login_callback=PASSPORT_LOGIN_SUCCESS_CALLBACK&passport_close_callback=PASSPORT_CLOSE_CALLBACK&passport_dp_callback=PASSPORT_DP_CALLBACK&type=login&client_id=1&state=https%3A%2F%2Fwww.douyu.com%2F" suspend fun getQrcode(): DouYuQrcode { - val jsonNode = client.post("https://passport.douyu.com/scan/generateCode") { - setFormDataContent { + val jsonNode = client.submitForm("https://passport.douyu.com/scan/generateCode", + parameters { append("client_id", "1") append("isMultiAccount", "0") - } - headers { - referer(referer) - } + }) { + referer(referer) }.body() if (jsonNode["error"].asInt() != 0) error(jsonNode["data"].asText()) val data = jsonNode["data"] @@ -41,7 +38,7 @@ class DouYuLogic( } - suspend fun checkQrcode(douYuQrcode: DouYuQrcode): CommonResult { + suspend fun checkQrcode(douYuQrcode: DouYuQrcode): DouYuEntity { val checkResponse = client.get("https://passport.douyu.com/japi/scan/auth?time=${System.currentTimeMillis()}&code=${douYuQrcode.code}") { headers { @@ -50,16 +47,16 @@ class DouYuLogic( } val jsonNode = checkResponse.body() return when (jsonNode["error"].asInt()) { - -2,1 -> CommonResult.failure("客户端已扫码或未扫码", code = 0) + -2,1 -> qrcodeNotScanned() 0 -> { val url = jsonNode["data"]["url"].asText() val response = client.get("${url}&callback=appClient_json_callback&_=${System.currentTimeMillis()}") { referer("https://passport.douyu.com/") } - val cookie = response.cookie() + checkResponse.cookie() - CommonResult.success(DouYuEntity().also { it.cookie = cookie }) + val cookie = response.setCookie().renderCookieHeader() + checkResponse.setCookie().renderCookieHeader() + DouYuEntity().also { it.cookie = cookie } } - else -> CommonResult.failure(jsonNode["data"].asText()) + else -> error(jsonNode["data"].asText()) } } @@ -87,7 +84,7 @@ class DouYuLogic( val cookies = authResponse.setCookie() for (obj in cookies) { val name = obj.name - val queryValue = OkUtils.cookie(cookie, name) + val queryValue = cookies.find { it.name == name }?.value if (queryValue == null) cookie += "$name=${obj.value}; " else { cookie = cookie.replace(queryValue, obj.value) @@ -97,26 +94,27 @@ class DouYuLogic( douYuService.save(douYuEntity) } - suspend fun room(douYuEntity: DouYuEntity): CommonResult> { + suspend fun room(douYuEntity: DouYuEntity): List { renewCookie(douYuEntity) var i = 1 val resultList = mutableListOf() while (true) { - val jsonNode = OkHttpKtUtils.getJson("https://www.douyu.com/wgapi/livenc/liveweb/follow/list?sort=0&cid1=0&page=${i++}", - OkUtils.headers(douYuEntity.cookie, "", UA.PC)) - if (jsonNode.getInteger("error") == 0) { + val jsonNode = client.get("https://www.douyu.com/wgapi/livenc/liveweb/follow/list?sort=0&cid1=0&page=${i++}") { + cookieString(douYuEntity.cookie) + }.body() + if (jsonNode["error"].asInt() == 0) { val list = jsonNode["data"]["list"] ?: break if (list is NullNode) break if (list.isEmpty) break for (singleJsonNode in list) { - val douYuRoom = DouYuRoom(singleJsonNode.getString("room_name"), singleJsonNode.getString("nickname"), - "https://www.douyu.com${singleJsonNode.getString("url")}", singleJsonNode.getString("game_name"), singleJsonNode.getInteger("show_status") == 1 && singleJsonNode.getInteger("videoLoop") == 0 , - singleJsonNode.getString("online"), singleJsonNode.getLong("room_id"), singleJsonNode["room_src"].asText().replace("/dy1", "")) + val douYuRoom = DouYuRoom(singleJsonNode["room_name"].asText(), singleJsonNode["nickname"].asText(), + "https://www.douyu.com${singleJsonNode["url"].asText()}", singleJsonNode["game_name"].asText(), singleJsonNode["show_status"].asInt() == 1 && singleJsonNode["videoLoop"].asInt() == 0 , + singleJsonNode["online"].asText(), singleJsonNode["room_id"].asLong(), singleJsonNode["room_src"].asText().replace("/dy1", "")) resultList.add(douYuRoom) } - } else return CommonResult.failure(jsonNode.getString("msg")) + } else error(jsonNode["msg"].asText()) } - return CommonResult.success(resultList) + return resultList } private suspend fun yuBaCookie(douYuEntity: DouYuEntity): String { @@ -134,7 +132,7 @@ class DouYuLogic( referer("https://yuba.douyu.com/") } } - return authResponse.cookie() + return authResponse.setCookie().renderCookieHeader() } suspend fun fishGroup(douYuEntity: DouYuEntity) { @@ -155,17 +153,18 @@ class DouYuLogic( val infoNode = infoResponse.body() val exp = infoNode["data"]["group_exp"].asInt() val isSign = infoNode["data"]["is_signed"].asInt() - val infoCookie = infoResponse.cookie() + val setCookie = infoResponse.setCookie() + val infoCookie = setCookie.renderCookieHeader() if (isSign == 0) { - val signNode = client.post("https://yuba.douyu.com/ybapi/topic/sign?timestamp=${timestamp()}") { - setFormDataContent { + val signNode = client.submitForm("https://yuba.douyu.com/ybapi/topic/sign?timestamp=${timestamp()}", + parameters { append("group_id", id.toString()) append("cur_exp", exp.toString()) - } + }) { headers { cookieString(cookie + infoCookie) referer("https://yuba.douyu.com/group/$id") - append("x-csrf-token", OkUtils.cookie(infoCookie, "acf_yb_t")!!) + append("x-csrf-token", setCookie.find { it.name == "acf_yb_t" }?.value ?: "") } }.bodyAsText().toJsonNode() if (signNode["status_code"].asInt() != 200) error(signNode["data"].asText()) @@ -222,54 +221,6 @@ class DouYuLogic( return list } - private fun String.token(): String { - return this.substring(this.indexOf('.') + 1, this.lastIndexOf('.')).base64Decode() - .toString(Charset.defaultCharset()).toJsonNode()["token"].asText() - } - - suspend fun appSign(douYuEntity: DouYuEntity) { - val appCookie = douYuEntity.appCookie - val did = OkUtils.cookie(appCookie, "acf_did") ?: error("cookie格式不正确") - val auth = OkUtils.cookie(appCookie, "acf_auth") ?: error("cookie格式不正确") - val token = auth.token() - val response = client.post("https://apiv2.douyucdn.cn/h5nc/sign/sendSign") { - setFormDataContent { - append("client_sys", "android1") - append("did", did) - append("token", token) - } - headers { - append("cookie", appCookie) - } - } - if (response.contentType() == ContentType.Text.Html) error("cookie已失效") - } - - private suspend fun joinOrClock(douYuEntity: DouYuEntity, url: String) { - val appCookie = douYuEntity.appCookie - val dyToken = OkUtils.cookie(appCookie, "dy_token") ?: error("cookie格式不正确") - val auth = OkUtils.cookie(appCookie, "acf_auth") ?: error("cookie格式不正确") - val token = auth.token() - val response = client.post(url) { - setFormDataContent { - append("token", token) - append("dy_token", dyToken) - } - headers { - append("cookie", appCookie) - } - } - if (response.contentType() == ContentType.Text.Html) error("cookie已失效") - } - - suspend fun joinSign(douYuEntity: DouYuEntity) { - joinOrClock(douYuEntity, "https://apiv2.douyucdn.cn/h5nc/userSignActivity/joinSignActivity") - } - - suspend fun clockSign(douYuEntity: DouYuEntity) { - joinOrClock(douYuEntity, "https://apiv2.douyucdn.cn/h5nc/userSignActivity/clockSignActivity") - } - } data class DouYuQrcode(val url: String, val code: String) diff --git a/src/main/kotlin/me/kuku/telegram/logic/ECloudLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/ECloudLogic.kt index 63f0abe..9f1417d 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/ECloudLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/ECloudLogic.kt @@ -3,13 +3,15 @@ package me.kuku.telegram.logic import com.fasterxml.jackson.databind.JsonNode import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.request.forms.* import io.ktor.client.request.headers import io.ktor.client.statement.* +import io.ktor.http.* import io.ktor.util.* import kotlinx.coroutines.delay import me.kuku.telegram.entity.ECloudEntity import me.kuku.telegram.entity.ECloudService -import me.kuku.utils.* +import me.kuku.telegram.utils.* import org.springframework.stereotype.Service @Service @@ -23,9 +25,9 @@ class ECloudLogic( val location = it.headers["location"] ?: error("未能成功跳转") client.get(location).let { response -> val ltUrl = response.headers["location"] ?: error("未能成功跳转") - val lt = MyUtils.regex("lt=", "&", ltUrl) ?: error("未能成功获取lt") - val reqId = MyUtils.regex("reqId=", "&", ltUrl) ?: error("未能成功获取reqId") - listOf(response.cookie(), lt, reqId, ltUrl) + val lt = RegexUtils.extract(ltUrl, "lt=", "&") ?: error("未能成功获取lt") + val reqId = RegexUtils.extract(ltUrl, "reqId=", "&") ?: error("未能成功获取reqId") + listOf(response.setCookie().renderCookieHeader(), lt, reqId, ltUrl) } } val headers = StringValues.build { @@ -34,40 +36,35 @@ class ECloudLogic( append("referer", refererUrl) append("reqId", reqId) } - val configJsonNode = client.post("https://open.e.189.cn/api/logbox/oauth2/appConf.do") { - setFormDataContent { + val configJsonNode = client.submitForm("https://open.e.189.cn/api/logbox/oauth2/appConf.do", + parameters { append("version", "2.0") append("appKey", "cloud") - } + }) { headers { appendAll(headers) } }.bodyAsText().toJsonNode() - val encryptJsonNode = client.post("https://open.e.189.cn/api/logbox/config/encryptConf.do") { - setFormDataContent { - append("appId", "cloud") - } - }.bodyAsText().toJsonNode() + val encryptJsonNode = client.submitForm("https://open.e.189.cn/api/logbox/config/encryptConf.do", + parameters { append("appId", "cloud") }).bodyAsText().toJsonNode() val paramId = configJsonNode["data"]?.get("paramId")?.asText() ?: error("not found paramId") val encryptData = encryptJsonNode["data"] val pre = encryptData["pre"].asText() val pubKey = encryptData["pubKey"].asText() - client.post("https://open.e.189.cn/api/logbox/oauth2/needcaptcha.do") { - setFormDataContent { + client.submitForm("https://open.e.189.cn/api/logbox/oauth2/needcaptcha.do", + parameters { append("accountType", "01") append("userName", pre + username.rsaEncrypt(pubKey)) append("appKey", "cloud") - } - }.bodyAsText().takeIf { it == "0" } ?: error("需要验证码,请在任意设备成功登陆一次再试") - val response = client.post("https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do") { - headers { appendAll(headers) } - setFormDataContent { + }).bodyAsText().takeIf { it == "0" } ?: error("需要验证码,请在任意设备成功登陆一次再试") + val response = client.submitForm("https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do", + parameters { append("version", "v2.0") append("apToken", "") append("appKey", "cloud") append("accountType", "01") - append("userName", pre + username.rsaEncrypt(pubKey).base64Decode().hex()) - append("epd", pre + password.rsaEncrypt(pubKey).base64Decode().hex()) + append("userName", pre + username.rsaEncrypt(pubKey).decodeBase64Bytes().hex()) + append("epd", pre + password.rsaEncrypt(pubKey).decodeBase64Bytes().hex()) append("captchaType", "") append("validateCode", "") append("smsValidateCode", "") @@ -83,14 +80,15 @@ class ECloudLogic( append("isOauth2", "false") append("state", "") append("paramId", paramId) - } + }) { + headers { appendAll(headers) } } val jsonNode = response.bodyAsText().toJsonNode() if (jsonNode["result"].asText() != "0") error(jsonNode["msg"].asText()) val toUrl = jsonNode["toUrl"].asText() - val eCookie = response.cookie() + val eCookie = response.setCookie().renderCookieHeader() val loginResponse = client.get(toUrl) - val resultCookie = loginResponse.cookie() + val resultCookie = loginResponse.setCookie().renderCookieHeader() return ECloudEntity().also { it.eCookie = eCookie it.cookie = resultCookie @@ -102,7 +100,7 @@ class ECloudLogic( } private suspend fun updateCookie(entity: ECloudEntity) { - val jsonNode = client.get("https://cloud.189.cn/api/portal/listFiles.action?noCache=0.${MyUtils.randomNum(16)}&fileId=-11") + val jsonNode = client.get("https://cloud.189.cn/api/portal/listFiles.action?noCache=0.${RandomUtils.num(16)}&fileId=-11") .body() if (jsonNode.has("errorCode")) { val response1 = @@ -115,7 +113,7 @@ class ECloudLogic( } val location2 = response2.headers["location"] ?: error("未能成功跳转") val response3 = client.get(location2) - val cookie = response3.cookie() + val cookie = response3.setCookie().renderCookieHeader() entity.cookie = cookie eCloudService.save(entity) } diff --git a/src/main/kotlin/me/kuku/telegram/logic/HostLocLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/HostLocLogic.kt index a7a548b..7b68689 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/HostLocLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/HostLocLogic.kt @@ -1,18 +1,74 @@ package me.kuku.telegram.logic +import io.ktor.client.* +import io.ktor.client.engine.okhttp.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.logging.* import io.ktor.client.request.* import io.ktor.client.request.forms.* import io.ktor.client.statement.* import io.ktor.http.* +import io.ktor.serialization.jackson.* import kotlinx.coroutines.delay -import me.kuku.pojo.UA -import me.kuku.utils.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import me.kuku.telegram.utils.* import org.jsoup.Jsoup +import java.lang.Exception +import java.util.concurrent.TimeUnit +import javax.crypto.Cipher +import javax.crypto.SecretKey +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec +import kotlin.random.Random object HostLocLogic { + private val mutex = Mutex() + + private val client by lazy { + HttpClient(OkHttp) { + engine { + config { + followRedirects(false) + } + + addInterceptor { chain -> + val request = chain.request() + val response = chain.proceed(request) + if (response.code != 200) { + val html = response.body?.string() ?: return@addInterceptor response + val cookie = prepareCookie(html) + if (cookie.isNotEmpty()) { + val headerCookie = request.headers["cookie"] ?: "" + val newCookie = "$headerCookie$cookie" + val newRequest = request.newBuilder() + .header("cookie", newCookie) + .build() + TimeUnit.SECONDS.sleep(2) + chain.proceed(newRequest) + } else response + } else response + } + } + + followRedirects = false + + install(ContentNegotiation) { + jackson() + } + + defaultRequest { + header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36") + } + + install(Logging) + + } + } + suspend fun login(username: String, password: String): String { - val prepareCookie = prepareCookie() val response = client.submitForm("https://hostloc.com/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1", parameters { append("fastloginfield", "username") append("username", username) @@ -21,51 +77,55 @@ object HostLocLogic { append("quickforward", "yes") append("handlekey", "ls") }) { - cookieString(prepareCookie) referer("https://hostloc.com/forum.php") } val str = response.bodyAsText() return if (str.contains("https://hostloc.com/forum.php")) - response.cookie() + response.setCookie().renderCookieHeader() else error("账号或密码错误或其他原因登录失败!") } private suspend fun checkLogin(cookie: String) { - val prepareCookie = prepareCookie() - val newCookie = prepareCookie + cookie val html = client.get("https://hostloc.com/home.php?mod=spacecp") { - cookieString(newCookie) + cookieString(cookie) }.bodyAsText() + delay(1000) val text = Jsoup.parse(html).getElementsByTag("title").first()!!.text() val b = text.contains("个人资料") if (!b) error("cookie已失效") } + suspend fun singleSign(cookie: String) { - val prepareCookie = prepareCookie() - val newCookie = prepareCookie + cookie - checkLogin(newCookie) - val url = "https://hostloc.com/space-uid-${MyUtils.randomInt(10000, 50000)}.html" - kotlin.runCatching { - OkHttpKtUtils.get(url, OkUtils.headers(newCookie, "https://hostloc.com/forum.php", UA.PC)) - .close() + mutex.withLock { + checkLogin(cookie) + val url = "https://hostloc.com/space-uid-${Random.nextInt(10000, 50000)}.html" + kotlin.runCatching { + client.get(url) { + cookieString(cookie) + referer("https://hostloc.com/forum.php") + } + delay(1000) + } } } suspend fun sign(cookie: String) { - val prepareCookie = prepareCookie() - val newCookie = prepareCookie + cookie - checkLogin(newCookie) - val urlList = mutableListOf() - for (i in 0..12) { - val num = MyUtils.randomInt(10000, 50000) - urlList.add("https://hostloc.com/space-uid-$num.html") - } - for (url in urlList) { - delay(5000) - kotlin.runCatching { - OkHttpKtUtils.get(url, OkUtils.headers(newCookie, "https://hostloc.com/forum.php", UA.PC)) - .close() + mutex.withLock { + checkLogin(cookie) + val urlList = mutableListOf() + for (i in 0..12) { + val num = Random.nextInt(10000, 50000) + urlList.add("https://hostloc.com/space-uid-$num.html") + } + for (url in urlList) { + delay(5000) + kotlin.runCatching { + client.get(url) { + cookieString(cookie) + referer("https://hostloc.com/forum.php") + } + } } } } @@ -73,8 +133,9 @@ object HostLocLogic { suspend fun post(): List { val list = mutableListOf() val html = kotlin.runCatching { - OkHttpKtUtils.getStr("https://hostloc.com/forum.php?mod=forumdisplay&fid=45&filter=author&orderby=dateline", - OkUtils.headers("", "https://hostloc.com/forum.php", UA.PC)) + client.get("https://hostloc.com/forum.php?mod=forumdisplay&fid=45&filter=author&orderby=dateline") { + referer("https://hostloc.com/forum.php") + }.bodyAsText() }.onFailure { return list } @@ -86,7 +147,7 @@ object HostLocLogic { val url = "https://hostloc.com/${s.attr("href")}" val time = ele.select("em a span").first()?.text() ?: "" val name = ele.select("cite a").first()?.text() ?: "" - val id = MyUtils.regex("tid=", "&", url)?.toInt() ?: 0 + val id = RegexUtils.extract(url, "tid=", "&")?.toInt() ?: 0 val hostLocPost = HostLocPost(id, name, time, title, url) list.add(hostLocPost) } @@ -94,7 +155,9 @@ object HostLocLogic { } suspend fun postContent(url: String, cookie: String = ""): String { - val str = OkHttpKtUtils.getStr(url, OkUtils.headers(cookie, "", UA.PC)) + val str = client.get(url) { + cookieString(cookie) + }.bodyAsText() val pct = Jsoup.parse(str).getElementsByClass("pct") return pct.first()?.text() ?: "未获取到内容,需要权限查看" } @@ -125,21 +188,31 @@ object HostLocLogic { return bytes } - private suspend fun prepareCookie(): String { - val html = client.get("https://hostloc.com/forum.php") { - }.bodyAsText() - val group = MyUtils.regexGroup("(?<=toNumbers\\(\").*?(?=\"\\))", html) + private fun prepareCookie(html: String): String { + val group = "(?<=toNumbers\\(\").*?(?=\"\\))".toRegex().findAll(html).map { it.value }.toList() if (group.isEmpty()) return "" val a = intArrToByteArr(toNumbers(group[0])) val b = intArrToByteArr(toNumbers(group[1])) val c = intArrToByteArr(toNumbers(group[2])) - val decryptedValue = AESUtils.decryptLoc(a, b, c)!! - val cookieValue = HexUtils.byteArrayToHex(decryptedValue) + val decryptedValue = decrypt(a, b, c)!! + val cookieValue = decryptedValue.hex() return "cnsL7=$cookieValue; " } + private fun decrypt(aseKey: ByteArray?, iv: ByteArray, data: ByteArray): ByteArray? { + return try { + val cipher = Cipher.getInstance("AES/CBC/NoPadding") + val secretKey: SecretKey = SecretKeySpec(aseKey, "AES") + cipher.init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(iv)) + cipher.doFinal(data) + } catch (e: Exception) { + e.printStackTrace() + null + } + } + } diff --git a/src/main/kotlin/me/kuku/telegram/logic/HuYaLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/HuYaLogic.kt index 0c800f7..349e680 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/HuYaLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/HuYaLogic.kt @@ -1,60 +1,70 @@ package me.kuku.telegram.logic -import me.kuku.pojo.CommonResult -import me.kuku.pojo.UA +import com.fasterxml.jackson.databind.JsonNode +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.http.* import me.kuku.telegram.entity.HuYaEntity -import me.kuku.utils.* +import me.kuku.telegram.exception.qrcodeExpire +import me.kuku.telegram.exception.qrcodeNotScanned +import me.kuku.telegram.utils.* import org.springframework.stereotype.Service @Service class HuYaLogic { suspend fun getQrcode(): HuYaQrcode { - val requestId = MyUtils.randomNum(8) - val response = OkHttpKtUtils.post("https://udblgn.huya.com/qrLgn/getQrId", OkUtils.json(""" - {"uri":"70001","version":"2.4","context":"WB-b11031a6ccf245169759e35fc6adc5d9-C9D11B3412B00001BAEA164B1FD4176D-","requestId":"$requestId","appId":"5002","data":{"behavior":"%7B%22a%22%3A%22m%22%2C%22w%22%3A520%2C%22h%22%3A340%2C%22b%22%3A%5B%5D%7D","type":"","domainList":"","page":"https%3A%2F%2Fwww.huya.com%2F"}} - """.trimIndent())) - val jsonNode = OkUtils.json(response) - val qrId = jsonNode["data"].getString("qrId") - return HuYaQrcode("https://udblgn.huya.com/qrLgn/getQrImg?k=$qrId&appId=5002", qrId, OkUtils.cookie(response), requestId) + val requestId = RandomUtils.num(8) + val response = client.post("https://udblgn.huya.com/qrLgn/getQrId") { + setJsonBody(""" + {"uri":"70001","version":"2.4","context":"WB-b11031a6ccf245169759e35fc6adc5d9-C9D11B3412B00001BAEA164B1FD4176D-","requestId":"$requestId","appId":"5002","data":{"behavior":"%7B%22a%22%3A%22m%22%2C%22w%22%3A520%2C%22h%22%3A340%2C%22b%22%3A%5B%5D%7D","type":"","domainList":"","page":"https%3A%2F%2Fwww.huya.com%2F"}} + """.trimIndent()) + } + val jsonNode = response.body() + val qrId = jsonNode["data"]["qrId"].asText() + return HuYaQrcode("https://udblgn.huya.com/qrLgn/getQrImg?k=$qrId&appId=5002", qrId, response.setCookie().renderCookieHeader(), requestId) } - suspend fun checkQrcode(huYaQrcode: HuYaQrcode): CommonResult { - val response = OkHttpKtUtils.post("https://udblgn.huya.com/qrLgn/tryQrLogin", OkUtils.json(""" - {"uri":"70003","version":"2.4","context":"WB-b11031a6ccf245169759e35fc6adc5d9-C9D11B3412B00001BAEA164B1FD4176D-","requestId":"${huYaQrcode.requestId}","appId":"5002","data":{"qrId":"${huYaQrcode.id}","remember":"1","domainList":"","behavior":"%7B%22a%22%3A%22m%22%2C%22w%22%3A520%2C%22h%22%3A340%2C%22b%22%3A%5B%5D%7D","page":"https%3A%2F%2Fwww.huya.com%2F"}} - """.trimIndent()), OkUtils.cookie(huYaQrcode.cookie)) - val jsonNode = OkUtils.json(response) - return when (val stage = jsonNode["data"].getInteger("stage")) { - 0, 1 -> CommonResult.failure("等待扫码", null, 0) + suspend fun checkQrcode(huYaQrcode: HuYaQrcode): HuYaEntity { + val response = client.post("https://udblgn.huya.com/qrLgn/tryQrLogin") { + setJsonBody(""" + {"uri":"70003","version":"2.4","context":"WB-b11031a6ccf245169759e35fc6adc5d9-C9D11B3412B00001BAEA164B1FD4176D-","requestId":"${huYaQrcode.requestId}","appId":"5002","data":{"qrId":"${huYaQrcode.id}","remember":"1","domainList":"","behavior":"%7B%22a%22%3A%22m%22%2C%22w%22%3A520%2C%22h%22%3A340%2C%22b%22%3A%5B%5D%7D","page":"https%3A%2F%2Fwww.huya.com%2F"}} + """.trimIndent()) + cookieString(huYaQrcode.cookie) + } + val jsonNode = response.body() + return when (val stage = jsonNode["data"]["stage"].asInt()) { + 0, 1 -> qrcodeNotScanned() 2 -> { - val cookie = OkUtils.cookie(response) - CommonResult.success(HuYaEntity().also { + val cookie = response.setCookie().renderCookieHeader() + HuYaEntity().also { it.cookie = cookie - }) + } } - 5 -> CommonResult.failure("二维码已失效") - else -> CommonResult.failure("错误代码为$stage") + 5 -> qrcodeExpire() + else -> error("错误代码为$stage") } } - fun live(huYaEntity: HuYaEntity): CommonResult> { + suspend fun live(huYaEntity: HuYaEntity): List { var i = 0 val resultList = mutableListOf() while (true) { - val response = OkHttpUtils.get("https://live.huya.com/liveHttpUI/getUserSubscribeToInfoList?iPageIndex=${i++}&_=${System.currentTimeMillis()}", - OkUtils.headers(huYaEntity.cookie, "", UA.PC)) - if (response.code == 200) { - val jsonNode = OkUtils.json(response) + val response = client.get("https://live.huya.com/liveHttpUI/getUserSubscribeToInfoList?iPageIndex=${i++}&_=${System.currentTimeMillis()}") { + cookieString(huYaEntity.cookie) + } + if (response.status == HttpStatusCode.OK) { + val jsonNode = response.body() val list = jsonNode["vItems"] if (list.isEmpty) break for (ss in list) { - val huYaLive = HuYaLive(ss.getLong("iRoomId"), ss.getString("sLiveDesc"), ss.getString("sGameName"), - ss.getInteger("iIsLive") == 1, ss.getString("sNick"), ss.getString("sVideoCaptureUrl"), "https://www.huya.com/${ss.getLong("iRoomId")}") + val huYaLive = HuYaLive(ss["iRoomId"].asLong(), ss["sLiveDesc"].asText(), ss["sGameName"].asText(), + ss["iIsLive"].asInt() == 1, ss["sNick"].asText(), ss["sVideoCaptureUrl"].asText(), "https://www.huya.com/${ss["iRoomId"].asLong()}") resultList.add(huYaLive) } - } else return CommonResult.failure>("查询失败,可能cookie已失效").also { response.close() } + } else error("查询失败,可能cookie已失效") } - return CommonResult.success(resultList) + return resultList } } diff --git a/src/main/kotlin/me/kuku/telegram/logic/KuGouLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/KuGouLogic.kt index d6828ed..66f5af4 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/KuGouLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/KuGouLogic.kt @@ -3,12 +3,14 @@ package me.kuku.telegram.logic import com.fasterxml.jackson.databind.JsonNode import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.request.forms.* +import io.ktor.client.statement.* import io.ktor.http.* -import me.kuku.pojo.CommonResult -import me.kuku.pojo.UA import me.kuku.telegram.config.api import me.kuku.telegram.entity.KuGouEntity -import me.kuku.utils.* +import me.kuku.telegram.exception.qrcodeExpire +import me.kuku.telegram.exception.qrcodeNotScanned +import me.kuku.telegram.utils.* import org.springframework.stereotype.Service import java.util.* @@ -27,7 +29,7 @@ class KuGouLogic { fun mid(): String{ val s = e() + e() + "-" + e() + "-" + e() + "-" + e() + "-" + e() + e() + e() - return MD5Utils.toMD5(s) + return s.md5() } private fun clientTime(): Int{ @@ -42,8 +44,8 @@ class KuGouLogic { sb.append("$k=$v&") } list.sort() - val s = StringUtils.join(list, "") - val signature = MD5Utils.toMD5(s) + val s = list.joinToString("") + val signature = s.md5() sb.append("signature=$signature") return sb.toString() } @@ -59,8 +61,8 @@ class KuGouLogic { list.addFirst(ss) list.add(other) list.add(ss) - val s = StringUtils.join(list, "") - val signature = MD5Utils.toMD5(s) + val s = list.joinToString("") + val signature = s.md5() sb.append("signature=$signature") return sb.toString() } @@ -79,42 +81,43 @@ class KuGouLogic { "appid" to "1014", "clientver" to "8131", "clienttime" to clientTime().toString(), "uuid" to newMid, "mid" to newMid, "type" to "1" ) - val jsonNode = OkHttpKtUtils.getJson("https://login-user.kugou.com/v1/qrcode?${signature(map)}") - val qrcode = jsonNode["data"].getString("qrcode") + val jsonNode = client.get("https://login-user.kugou.com/v1/qrcode?${signature(map)}").body() + val qrcode = jsonNode["data"]["qrcode"].asText() return KuGouQrcode("https://h5.kugou.com/apps/loginQRCode/html/index.html?qrcode=$qrcode&appid=1014", qrcode, newMid) } - suspend fun checkQrcode(kuGouQrcode: KuGouQrcode): CommonResult { + suspend fun checkQrcode(kuGouQrcode: KuGouQrcode): KuGouEntity { val map = mutableMapOf("appid" to "1014", "clientver" to "8131", "clienttime" to clientTime().toString(), "qrcode" to kuGouQrcode.qrcode, "dfid" to "-", "mid" to kuGouQrcode.mid, "plat" to "4", "uuid" to kuGouQrcode.mid) - val jsonNode = OkHttpKtUtils.getJson( + val jsonNode = client.get( "https://login-user.kugou.com/v1/get_userinfo_qrcode?${signature(map)}" - ) - val dataStatus = jsonNode["data"].getInteger("status") + ).body() + val dataStatus = jsonNode["data"]["status"].asInt() return when (dataStatus) { - 1, 2 -> CommonResult.failure(code = 0, message = "二维码未被扫描或已被扫描") - 0 -> CommonResult.failure("二维码已失效!") + 1, 2 -> qrcodeNotScanned() + 0 -> qrcodeExpire() 4 -> { - val token = jsonNode["data"].getString("token") - val userid = jsonNode["data"].getLong("userid") + val token = jsonNode["data"]["token"].asText() + val userid = jsonNode["data"]["userid"].asLong() val response = - OkHttpKtUtils.get("https://login-user.kugou.com/v1/autologin?a_id=1014&userid=$userid&t=$token&ct=${clientTime()}&callback=qrcodeLoginCallback&domain=kugou.com&uuid=${kuGouQrcode.mid}&mid=$${kuGouQrcode.mid}&plat=4&dfid=-&kguser_jv=180925") - val cookie = OkUtils.cookie(response) - val kuGoo = OkUtils.cookie(cookie, "KuGoo") + client.get("https://login-user.kugou.com/v1/autologin?a_id=1014&userid=$userid&t=$token&ct=${clientTime()}&callback=qrcodeLoginCallback&domain=kugou.com&uuid=${kuGouQrcode.mid}&mid=$${kuGouQrcode.mid}&plat=4&dfid=-&kguser_jv=180925") + val setCookie = response.setCookie() + val cookie = setCookie.renderCookieHeader() + val kuGoo = setCookie.find { it.name == "KuGoo" }?.value val kuGouEntity = KuGouEntity() kuGouEntity.token = token kuGouEntity.userid = userid kuGouEntity.mid = kuGouQrcode.mid kuGouEntity.kuGoo = kuGoo!! - CommonResult.success(kuGouEntity) + kuGouEntity } - else -> CommonResult.failure("未知的错误代码:$dataStatus") + else -> error("未知的错误代码:$dataStatus") } } - suspend fun sendMobileCode(phone: String, mid: String): CommonResult { + suspend fun sendMobileCode(phone: String, mid: String) { val time = System.currentTimeMillis() val map = mutableMapOf( // 1058 @@ -126,25 +129,29 @@ class KuGouLogic { "dfid" to "-", "srcappid" to "2919" ) - val preJsonNode = OkHttpKtUtils.postJson( - "$api/exec/kuGou", - mutableMapOf("phone" to phone, "time" to time.toString()) - ) + val preJsonNode = client.submitForm("$api/exec/kuGou", + parameters { + append("phone", phone) + append("time", time.toString()) + } + ).body() val params = preJsonNode["params"]?.asText() ?: error("获取加密参数失败,可能手机号格式不正确") - val pk = preJsonNode.getString("pk") + val pk = preJsonNode["pk"].asText() val mobile = phone.substring(0, 2) + "********" + phone.substring(phone.length - 1) val other = "{\"plat\":4,\"clienttime_ms\":$time,\"businessid\":5,\"pk\":\"$pk\",\"params\":\"$params\",\"mobile\":\"$mobile\"}" - val jsonNode = OkHttpKtUtils.postJson( - "https://gateway.kugou.com/v8/send_mobile_code?${signature2(map, other)}", - OkUtils.text(other), - mutableMapOf("x-router" to "loginservice.kugou.com", "referer" to "https://m3ws.kugou.com/", - "user-agent" to UA.PC.value) - ) - return if (jsonNode.getInteger("error_code") == 0) CommonResult.success() - else CommonResult.failure(jsonNode.getString("data")) + val jsonNode = client.post("https://gateway.kugou.com/v8/send_mobile_code?${signature2(map, other)}") { + setBody(other) + contentType(ContentType.Text.Plain) + headers { + mapOf("x-router" to "loginservice.kugou.com", "referer" to "https://m3ws.kugou.com/").forEach { + append(it.key, it.value) + } + } + }.body() + if (jsonNode["error_code"].asInt() != 0) error(jsonNode["data"].asText()) } - suspend fun verifyCode(phone: String, code: String, mid: String): CommonResult { + suspend fun verifyCode(phone: String, code: String, mid: String): KuGouEntity { val time = clientTime() val map = mutableMapOf( "appid" to "3116", @@ -156,91 +163,99 @@ class KuGouLogic { "srcappid" to "2919" ) val other = "{\"plat\":4,\"mobile\":\"$phone\",\"code\":\"$code\",\"expire_day\":60,\"support_multi\":1,\"userid\":\"\",\"force_login\":0}" - val response = OkHttpKtUtils.post( - "https://login-user.kugou.com/v2/loginbyverifycode/?${signature2(map, other)}", - OkUtils.text(other), - mutableMapOf("x-router" to "loginservice.kugou.com", "referer" to "https://m3ws.kugou.com/", - "user-agent" to UA.PC.value) - ) - val jsonNode = OkUtils.json(response) - return if (jsonNode.getInteger("error_code") == 0) { - val cookie = OkUtils.cookie(response) - val kuGoo = jsonNode["data"].getString("value") - val token = OkUtils.cookie(cookie, "t") - val userid = OkUtils.cookie(cookie, "KugooID")!! - CommonResult.success(KuGouEntity().also { + val response = client.post("https://login-user.kugou.com/v2/loginbyverifycode/?${signature2(map, other)}") { + setBody(other) + contentType(ContentType.Text.Plain) + headers { + mapOf("x-router" to "loginservice.kugou.com", "referer" to "https://m3ws.kugou.com/").forEach { + append(it.key, it.value) + } + } + } + val jsonNode = response.body() + return if (jsonNode["error_code"].asInt() == 0) { + val setCookie = response.setCookie() + val kuGoo = jsonNode["data"]["value"].asText() + val token = setCookie.find { it.name == "t" }?.value + val userid = setCookie.find { it.name == "KugooID" }!!.value + KuGouEntity().also { it.token = token!! it.userid = userid.toLong() it.kuGoo = kuGoo it.mid = mid - }) + } } - else CommonResult.failure(jsonNode.getString("data")) + else error(jsonNode["data"].asText()) } - suspend fun login(username: String, password: String, mid: String?): CommonResult { + suspend fun login(username: String, password: String, mid: String?): KuGouEntity { val newMid = mid ?: mid() - val md5Pwd = MD5Utils.toMD5(password) + val md5Pwd = password.md5() val params = "appid=1058&username=$username&pwd=$md5Pwd&code=&ticket=&clienttime=${clientTime()}&expire_day=60&autologin=false&redirect_uri=&state=&callback=loginModule.loginCallback&login_ver=1&mobile=&mobile_code=&plat=4&dfid=-&mid=$newMid&kguser_jv=180925" - val headers = OkUtils.headers("", "https://m3ws.kugou.com/", UA.PC) - val response = - OkHttpKtUtils.get("https://login-user.kugou.com/v1/login/?$params", headers) - val jsonNode = OkUtils.jsonp(response) + val response = client.get("https://login-user.kugou.com/v1/login/?$params") { + referer("https://m3ws.kugou.com/") + } + val jsonNode = response.bodyAsText().jsonpToJsonNode() return when (jsonNode["errorCode"]?.asInt()){ 30791 -> { // 验证码 - CommonResult.failure("需要验证验证码,请使用短信验证码登陆") + error("需要验证验证码,请使用短信验证码登陆") } null -> { - val cookie = OkUtils.cookie(response) - val kuGoo = OkUtils.cookie(cookie, "KuGoo") - val token = jsonNode.getString("token") - val userid = jsonNode.getLong("userid") - CommonResult.success(KuGouEntity().also { + val cookie = response.setCookie() + val kuGoo = cookie.find { it.name == "KuGoo" }?.value + val token = jsonNode["token"].asText() + val userid = jsonNode["userid"].asLong() + KuGouEntity().also { it.token = token it.userid = userid it.kuGoo = kuGoo!! it.mid = newMid - }) + } } - 30768 -> CommonResult.failure("需要短信验证码!请直接使用短信验证码登录!<酷狗验证码 phone>") - else -> CommonResult.failure(jsonNode.getString("errorMsg")) + 30768 -> error("需要短信验证码!请直接使用短信验证码登录!") + else -> error(jsonNode["errorMsg"].asText()) } } - suspend fun musicianSign(kuGouEntity: KuGouEntity): String { + suspend fun musicianSign(kuGouEntity: KuGouEntity) { // 1014 // 1058 val kuGoo = kuGouEntity.kuGoo - val aId = MyUtils.regex("a_id=", "&", kuGoo)!! + val aId = RegexUtils.extract(kuGoo, "a_id=", "&")!! val time = System.currentTimeMillis().toString() val map = mutableMapOf("appid" to aId, "token" to kuGouEntity.token, "kugouid" to kuGouEntity.userid.toString(), "srcappid" to "2919", "clientver" to "20000", "clienttime" to time, "dfid" to "-", "mid" to time, "uuid" to time) - val jsonNode = - OkHttpKtUtils.postJson("https://h5activity.kugou.com/v1/musician/do_signed?${signature2(map)}", mapOf()) - return if (jsonNode.getInteger("errcode") == 0) "酷狗音乐人签到成功!" - else error("酷狗音乐人签到失败!" + jsonNode.getString("errmsg")) + val jsonNode = client.submitForm("https://h5activity.kugou.com/v1/musician/do_signed?${signature2(map)}") + .body() + if (jsonNode["errcode"].asInt() != 0) error("酷狗音乐人签到失败!" + jsonNode["errmsg"].asText()) } - suspend fun listenMusic(kuGouEntity: KuGouEntity): String { + suspend fun listenMusic(kuGouEntity: KuGouEntity) { // val aId = MyUtils.regex("a_id=", "&", kuGouEntity.kuGoo)!! val map = mutableMapOf("userid" to kuGouEntity.userid.toString(), "token" to kuGouEntity.token, "appid" to "3116", "clientver" to "10547", "clienttime" to (System.currentTimeMillis() / 1000).toString(), - "mid" to kuGouEntity.mid, "uuid" to MyUtils.randomLetter(32), "dfid" to "-") + "mid" to kuGouEntity.mid, "uuid" to RandomUtils.letter(32), "dfid" to "-") val other = """{"mixsongid":273263741}""" - val jsonNode = OkHttpKtUtils.postJson("https://gateway.kugou.com/v2/report/listen_song?${signature3(map, other)}", - OkUtils.text(other), mapOf("x-router" to "youth.kugou.com", "User-Agent" to "Android12-1070-10536-130-0-ReportPlaySongToServerProtocol-wifi")) - val code = jsonNode.getInteger("error_code") - return if (code == 0 || code == 130012) "成功" - else error(jsonNode.getString("error_msg")) + val jsonNode = client.post("https://gateway.kugou.com/v2/report/listen_song?${signature3(map, other)}") { + setBody(other) + contentType(ContentType.Text.Plain) + headers { + mapOf("x-router" to "youth.kugou.com", "User-Agent" to "Android12-1070-10536-130-0-ReportPlaySongToServerProtocol-wifi").forEach { + append(it.key, it.value) + } + } + }.body() + val code = jsonNode["error_code"].asInt() + if (code != 0 && code != 130012) error(jsonNode["error_msg"].asText()) } suspend fun watchAd(kuGouEntity: KuGouEntity) { val map = mutableMapOf("userid" to kuGouEntity.userid.toString(), "token" to kuGouEntity.token, "appid" to "3116", "clientver" to "10780", "clienttime" to (System.currentTimeMillis() / 1000).toString(), - "mid" to kuGouEntity.mid, "uuid" to MyUtils.randomLetter(32), "dfid" to "-") + "mid" to kuGouEntity.mid, "uuid" to RandomUtils.letter(32), "dfid" to "-") val now = System.currentTimeMillis() val before = now - 15 * 1423 val other = """ diff --git a/src/main/kotlin/me/kuku/telegram/logic/LeiShenLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/LeiShenLogic.kt index 1463f19..4dbfac4 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/LeiShenLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/LeiShenLogic.kt @@ -7,8 +7,11 @@ import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.client.statement.* import me.kuku.telegram.entity.LeiShenEntity -import me.kuku.utils.* +import me.kuku.telegram.utils.* import java.net.URLEncoder +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter object LeiShenLogic { @@ -24,7 +27,7 @@ object LeiShenLogic { "$key=$value" } val convert = sortedFields.joinToString("&") + "&key=5C5A639C20665313622F51E93E3F2783" - val md5 = MD5Utils.toMD5(convert) + val md5 = convert.md5() params.put("sign", md5) val jsonNode = client.post("https://webapi.leigod.com/api/auth/login/v1") { setJsonBody(params) @@ -34,7 +37,8 @@ object LeiShenLogic { val info = data["login_info"] val accountToken = info["account_token"].asText() val nnToken = info["nn_token"].asText() - val expiryTime = DateTimeFormatterUtils.parseToDate(info["expiry_time"].asText(), "yyyy-MM-dd HH:mm:ss").time + val expiryTime = LocalDateTime.parse(info["expiry_time"].asText(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + .atZone(ZoneId.systemDefault()).toEpochSecond() return LeiShenEntity().also { it.accountToken = accountToken it.nnToken = nnToken diff --git a/src/main/kotlin/me/kuku/telegram/logic/LinuxDoLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/LinuxDoLogic.kt index 824e5ea..b57217e 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/LinuxDoLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/LinuxDoLogic.kt @@ -3,11 +3,13 @@ package me.kuku.telegram.logic import com.fasterxml.jackson.databind.JsonNode import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.request.forms.* import io.ktor.client.statement.* +import io.ktor.http.* import me.kuku.telegram.config.api import me.kuku.telegram.entity.LinuxDoEntity import me.kuku.telegram.entity.LinuxDoService -import me.kuku.utils.* +import me.kuku.telegram.utils.* import org.jsoup.Jsoup import org.springframework.stereotype.Service @@ -61,8 +63,14 @@ class LinuxDoLogic( } val jsonNode = csrfResponse.body() val csrf = jsonNode["csrf"].asText() - val tempCookie = csrfResponse.cookie() - val response = client.post("https://linux.do/session") { + val tempCookie = csrfResponse.setCookie().renderCookieHeader() + val response = client.submitForm("https://linux.do/session", + parameters { + append("login", username) + append("password", password) + append("second_factor_method", "1") + append("timezone", "Asia/Shanghai") + }) { headers { append("X-CSRF-Token", csrf) append("X-Requested-With", "XMLHttpRequest") @@ -70,16 +78,10 @@ class LinuxDoLogic( origin("https://linux.do") referer("https://linux.do/") } - setFormDataContent { - append("login", username) - append("password", password) - append("second_factor_method", "1") - append("timezone", "Asia/Shanghai") - } } val loginNode = response.body() if (loginNode.has("error")) error(loginNode["error"].asText()) - return response.cookie() + return response.setCookie().renderCookieHeader() } diff --git a/src/main/kotlin/me/kuku/telegram/logic/MiHoYoLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/MiHoYoLogic.kt index c33c8fb..745001f 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/MiHoYoLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/MiHoYoLogic.kt @@ -4,10 +4,12 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.JsonNode import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.util.* import kotlinx.coroutines.delay -import me.kuku.pojo.CommonResult import me.kuku.telegram.entity.MiHoYoEntity -import me.kuku.utils.* +import me.kuku.telegram.exception.qrcodeNotScanned +import me.kuku.telegram.utils.* import org.springframework.stereotype.Service import java.util.* @@ -18,7 +20,7 @@ class MiHoYoLogic( context(HttpRequestBuilder) private fun MiHoYoFix.append() { - val jsonNode = Jackson.parse(Jackson.toJsonString(this@MiHoYoFix)) + val jsonNode = Jackson.readTree(Jackson.writeValueAsString(this@MiHoYoFix)) headers { for (entry in jsonNode.fields()) { val key = entry.key @@ -107,7 +109,7 @@ class MiHoYoLogic( return MiHoYoQrcode(fix, data["url"].asText(), data["ticket"].asText()) } - suspend fun qrcodeLogin2(qrcode: MiHoYoQrcode): CommonResult { + suspend fun qrcodeLogin2(qrcode: MiHoYoQrcode): MiHoYoEntity { val response = client.post("https://passport-api.miyoushe.com/account/ma-cn-passport/web/queryQRLoginStatus") { setJsonBody("""{"ticket":"${qrcode.ticket}"}""") qrcode.fix.append() @@ -116,9 +118,9 @@ class MiHoYoLogic( jsonNode.check() val data = jsonNode["data"] return when (val status = data["status"].asText()) { - "Created", "Scanned" -> CommonResult.fail("未扫码", null, 0) + "Created", "Scanned" -> qrcodeNotScanned() "Confirmed" -> { - var cookie = response.cookie() + var cookie = response.setCookie().renderCookieHeader() val loginResponse = client.post("https://bbs-api.miyoushe.com/user/wapi/login") { setJsonBody("""{"gids":"2"}""") qrcode.fix.append() @@ -128,16 +130,17 @@ class MiHoYoLogic( } val loginJsonNode = loginResponse.body() loginJsonNode.check() - cookie += loginResponse.cookie() + val setCookie = loginResponse.setCookie() + cookie += setCookie.renderCookieHeader() val entity = MiHoYoEntity() entity.fix = qrcode.fix entity.cookie = cookie val userInfo = data["user_info"] entity.aid = userInfo["aid"].asText() entity.mid = userInfo["mid"].asText() - val token = OkUtils.cookie(cookie, "cookie_token_v2") ?: "" + val token = setCookie.find { it.name == "cookie_token_v2" }?.value ?: "" entity.token = token - CommonResult.success(entity) + entity } else -> error("米游社登陆失败,未知的状态:$status") } @@ -182,7 +185,7 @@ class MiHoYoLogic( val rr = twoCaptchaLogic.geeTest(gt, challenge,"https://static.mohoyo.com", tgId = tgId) val aiGis = "$sessionId;" + """ {"geetest_challenge":"${rr.challenge}","geetest_seccode":"${rr.validate}|jordan","geetest_validate":"${rr.validate}"} - """.trimIndent().base64Encode() + """.trimIndent().encodeBase64() headerMap["x-rpc-aigis"] = aiGis headerMap["DS"] = ds().ds jsonNode = client.post("https://passport-api.mihoyo.com/account/ma-cn-passport/app/loginByPassword") { @@ -218,7 +221,7 @@ class MiHoYoLogic( setJsonBody("""{"account":"${account.rsaEncrypt(rsaKey)}","password":"${password.rsaEncrypt(rsaKey)}"}""") fix.loginHeader() } - var cookie = response.cookie() + var cookie = response.setCookie().renderCookieHeader() var loginJsonNode = response.body() val code = loginJsonNode["retcode"].asInt() if (code == -3101) { @@ -233,16 +236,18 @@ class MiHoYoLogic( setJsonBody("""{"account":"${account.rsaEncrypt(rsaKey)}","password":"${password.rsaEncrypt(rsaKey)}"}""") fix.loginHeader() } - cookie = response.cookie() + cookie = response.setCookie().renderCookieHeader() loginJsonNode = response.body() } if (loginJsonNode["retcode"].asInt() != 0) error(loginJsonNode["message"].asText()) val infoDataJsonNode = loginJsonNode["data"]["user_info"] val aid = infoDataJsonNode["aid"].asText() val mid = infoDataJsonNode["mid"].asText() - val loginResponse = OkHttpKtUtils.post("https://bbs-api.mihoyo.com/user/wapi/login", - OkUtils.json("{\"gids\":\"2\"}"), OkUtils.cookie(cookie)).also { it.close() } - val finaCookie = OkUtils.cookie(loginResponse) + val loginResponse = client.post("https://bbs-api.mihoyo.com/user/wapi/login") { + setJsonBody("{\"gids\":\"2\"}") + cookieString(cookie) + } + val finaCookie = loginResponse.body() cookie += finaCookie val entity = MiHoYoEntity().also { it.cookie = cookie @@ -269,14 +274,14 @@ class MiHoYoLogic( append("X-Rpc-Mi_referrer", "https://user.mihoyo.com/") append("X-Rpc-Source", "accountWebsite") append("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36") - append("cookie", "_MHYUUID=${fix.device}; DEVICEFP_SEED_ID=${MyUtils.randomNum(16)}; DEVICEFP_SEED_TIME=${System.currentTimeMillis()}; DEVICEFP=${fix.fp};") + append("cookie", "_MHYUUID=${fix.device}; DEVICEFP_SEED_ID=${RandomUtils.num(16)}; DEVICEFP_SEED_TIME=${System.currentTimeMillis()}; DEVICEFP=${fix.fp};") } } private fun webDs(): MiHoYoDs { val salt = "mx1x4xCahVMVUDJIkJ9H3jsHcUsiASGZ" val time = System.currentTimeMillis() / 1000 - val randomLetter = MyUtils.randomLetter(6) + val randomLetter = RandomUtils.letter(6) val md5 = "salt=$salt&t=$time&r=$randomLetter".md5() val ds = "$time,$randomLetter,$md5" return MiHoYoDs("2.64.0", "4", ds) @@ -285,7 +290,7 @@ class MiHoYoLogic( private fun hubDs(): MiHoYoDs { val salt = "AcpNVhfh0oedCobdCyFV8EE1jMOVDy9q" val time = System.currentTimeMillis() / 1000 - val randomLetter = MyUtils.randomLetter(6) + val randomLetter = RandomUtils.letter(6) val md5 = "salt=$salt&t=$time&r=$randomLetter".md5() val ds = "$time,$randomLetter,$md5" return MiHoYoDs("2.60.1", "2", ds) @@ -296,7 +301,7 @@ class MiHoYoLogic( val t = System.currentTimeMillis() / 1000 val r = (100001..200000).random().toString() val q = params?.let { urlParams -> urlEncode(urlParams) } ?: "" - val b = data?.let { Jackson.toJsonString(it) } ?: "{}" + val b = data?.let { Jackson.writeValueAsString(it) } ?: "{}" val c = "salt=$salt&t=$t&r=$r&b=$b&q=$q".md5() val ds = "$t,$r,$c" return MiHoYoDs("2.60.1", "2", ds) @@ -305,7 +310,7 @@ class MiHoYoLogic( private fun ds(): MiHoYoDs { val salt = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS" val time = System.currentTimeMillis() / 1000 - val randomLetter = MyUtils.randomLetter(6) + val randomLetter = RandomUtils.letter(6) val md5 = "salt=$salt&t=$time&r=$randomLetter".md5() val ds = "$time,$randomLetter,$md5" return MiHoYoDs("2.63.1", "5", ds) @@ -315,7 +320,7 @@ class MiHoYoLogic( val salt = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS" val t = System.currentTimeMillis() / 1000 val r = (100001..200000).random().toString() - val b = data?.let { Jackson.toJsonString(it) } ?: "{}" + val b = data?.let { Jackson.writeValueAsString(it) } ?: "{}" val q = params?.let { urlParams -> urlEncode(urlParams) } ?: "" val c = "salt=$salt&t=$t&r=$r&b=$b&q=$q".md5() val ds = "$t,$r,$c" @@ -329,7 +334,7 @@ class MiHoYoLogic( private suspend fun sign(miHoYoEntity: MiHoYoEntity, jsonNode: JsonNode, geeTest: GeeTest? = null): JsonNode { return client.post("https://api-takumi.mihoyo.com/event/luna/sign") { setJsonBody(""" - {"act_id":"e202311201442471","region":"${jsonNode["region"].asText()}","uid":"${jsonNode.getString("game_uid")}","lang":"zh-cn"} + {"act_id":"e202311201442471","region":"${jsonNode["region"].asText()}","uid":"${jsonNode["game_uid"].asText()}","lang":"zh-cn"} """.trimIndent()) miHoYoEntity.fix.appAppend() headers { @@ -345,14 +350,15 @@ class MiHoYoLogic( } suspend fun sign(miHoYoEntity: MiHoYoEntity, tgId: Long? = null) { - val ssJsonNode = OkHttpKtUtils.getJson("https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn", - OkUtils.cookie(miHoYoEntity.cookie)) - if (ssJsonNode.getInteger("retcode") != 0) error(ssJsonNode.getString("message")) + val ssJsonNode = client.get("https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn") { + cookieString(miHoYoEntity.cookie) + }.body() + if (ssJsonNode["retcode"].asInt() != 0) error(ssJsonNode["message"].asText()) val jsonArray = ssJsonNode["data"]["list"] if (jsonArray.size() == 0) error("您还没有原神角色!!") for (obj in jsonArray) { val jsonNode = sign(miHoYoEntity, obj) - when (jsonNode.getInteger("retcode")) { + when (jsonNode["retcode"].asInt()) { 0, -5003 -> { val data = jsonNode["data"] val gt = data["gt"]?.asText() ?: "" @@ -472,7 +478,7 @@ class MiHoYoLogic( class MiHoYoFix { var referer: String = "https://user.miyoushe.com/" @JsonProperty("X-Rpc-Device_fp") - var fp: String = MyUtils.randomLetterLowerNum(13) + var fp: String = RandomUtils.letter(13) @JsonProperty("X-Rpc-Device_id") var device: String = UUID.randomUUID().toString() @JsonProperty("User-Agent") diff --git a/src/main/kotlin/me/kuku/telegram/logic/NodeSeekLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/NodeSeekLogic.kt index 214bd2c..9965f00 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/NodeSeekLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/NodeSeekLogic.kt @@ -3,10 +3,14 @@ package me.kuku.telegram.logic import com.fasterxml.jackson.databind.JsonNode import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.request.forms.* import io.ktor.client.statement.* +import io.ktor.http.* import me.kuku.telegram.config.api import me.kuku.telegram.entity.NodeSeekEntity -import me.kuku.utils.* +import me.kuku.telegram.utils.client +import me.kuku.telegram.utils.toJsonNode +import me.kuku.telegram.utils.toUrlEncode object NodeSeekLogic { @@ -25,14 +29,14 @@ object NodeSeekLogic { } suspend fun login(username: String, password: String, token: String? = null): String { - val jsonNode = client.post("$api/nodeseek/login") { - setFormDataContent { + val jsonNode = client.submitForm("$api/nodeseek/login", + parameters { append("username", username) append("password", password) token?.let { append("token", token) } - } + }) { }.bodyAsText().toJsonNode() if (jsonNode.has("cookie")) return jsonNode["cookie"].asText() else error(jsonNode["message"].asText()) diff --git a/src/main/kotlin/me/kuku/telegram/logic/OciLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/OciLogic.kt index bfdb9ba..033eb03 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/OciLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/OciLogic.kt @@ -12,8 +12,8 @@ import com.oracle.bmc.identity.model.AvailabilityDomain import com.oracle.bmc.identity.model.Compartment import com.oracle.bmc.identity.requests.ListAvailabilityDomainsRequest import com.oracle.bmc.identity.requests.ListCompartmentsRequest +import io.ktor.util.* import me.kuku.telegram.entity.OciEntity -import me.kuku.utils.base64Encode object OciLogic { @@ -183,7 +183,7 @@ object OciLogic { sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config; sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config; sudo reboot - """.trimIndent().base64Encode())) + """.trimIndent().encodeBase64())) .build() return computeClient(ociEntity).use { val response = it diff --git a/src/main/kotlin/me/kuku/telegram/logic/SmZdmLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/SmZdmLogic.kt index c254e62..dcada1f 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/SmZdmLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/SmZdmLogic.kt @@ -1,11 +1,13 @@ package me.kuku.telegram.logic import io.ktor.client.request.* +import io.ktor.client.request.forms.* import io.ktor.client.statement.* import io.ktor.http.* -import me.kuku.pojo.CommonResult import me.kuku.telegram.entity.SmZdmEntity -import me.kuku.utils.* +import me.kuku.telegram.exception.qrcodeNotScanned +import me.kuku.telegram.exception.qrcodeScanned +import me.kuku.telegram.utils.* import org.springframework.stereotype.Service @Service @@ -14,7 +16,7 @@ class SmZdmLogic( ) { suspend fun login1(phone: String, tgId: Long? = null) { - val geeJsonNode = client.get("https://zhiyou.smzdm.com/user/getgeetest/captcha_init_v3?scene=login&rand=${MyUtils.randomNum(2)}") { + val geeJsonNode = client.get("https://zhiyou.smzdm.com/user/getgeetest/captcha_init_v3?scene=login&rand=${RandomUtils.letter(2)}") { headers { referer("https://zhiyou.smzdm.com/user/login/window/") } @@ -22,36 +24,32 @@ class SmZdmLogic( val gt = geeJsonNode["gt"].asText() val challenge = geeJsonNode["challenge"].asText() val geeTest = geeTest(gt, challenge, "https://zhiyou.smzdm.com/", tgId) - val sendCodeNode = client.post("https://zhiyou.smzdm.com/user/login/ajax_get_mobile_code/") { - setFormDataContent { + val sendCodeNode = client.submitForm("https://zhiyou.smzdm.com/user/login/ajax_get_mobile_code/", + parameters { append("geetest_challenge", geeTest.challenge) append("geetest_validate", geeTest.validate) append("geetest_seccode", geeTest.secCode) append("mobile", phone) - } - }.bodyAsText().toJsonNode() + }).bodyAsText().toJsonNode() if (sendCodeNode["error_code"].asInt() != 0) error(sendCodeNode["error_msg"].asText()) } suspend fun login2(phone: String, code: String): SmZdmEntity { - val response = client.post("https://zhiyou.smzdm.com/user/login/ajax_quick_check") { - setFormDataContent { - append("mobile", phone) + val response = client.submitForm("https://zhiyou.smzdm.com/user/login/ajax_quick_check", + parameters { append("mobile", phone) append("mobile_code", code) append("rememberme", "1") append("captcha", "") - append("redirect_to", "to") - } - } + append("redirect_to", "to") }) val jsonNode = response.bodyAsText().toJsonNode() if (jsonNode["error_code"].asInt() != 0) error(jsonNode["error_msg"].asText()) - var cookie = response.cookie() + var cookie = response.setCookie().renderCookieHeader() val loginResponse = client.get("https://zhiyou.smzdm.com/user/login/jsonp_is_protocol") { headers { referer("https://zhiyou.smzdm.com/user/login/window/") } } - cookie += loginResponse.cookie() + cookie += loginResponse.setCookie().renderCookieHeader() return SmZdmEntity().also { it.cookie = cookie } } @@ -69,11 +67,9 @@ class SmZdmLogic( } @Suppress("DuplicatedCode") - suspend fun wechatQrcode2(smZdmWechatQrcode: SmZdmWechatQrcode): CommonResult { - val response = client.post("https://zhiyou.smzdm.com/user/login/jsonp_weixin_qrcode_check") { - setFormDataContent { - append("scene_str", smZdmWechatQrcode.sceneStr) - } + suspend fun wechatQrcode2(smZdmWechatQrcode: SmZdmWechatQrcode): SmZdmEntity { + val response = client.submitForm("https://zhiyou.smzdm.com/user/login/jsonp_weixin_qrcode_check", + parameters { append("scene_str", smZdmWechatQrcode.sceneStr) }) { headers { referer("https://zhiyou.smzdm.com/user/login/window/") } @@ -81,17 +77,17 @@ class SmZdmLogic( val jsonNode = response.bodyAsText().toJsonNode() if (jsonNode["error_code"].asInt() != 0) error(jsonNode["error_msg"].asText()) return when (jsonNode["data"]["status"].asInt()) { - 1 -> CommonResult.fail("未扫码", code = 0) - 2 -> CommonResult.fail("已扫码", code = 0) + 1 -> qrcodeNotScanned() + 2 -> qrcodeScanned() 3 -> { - val cookie = response.cookie() + val cookie = response.setCookie().renderCookieHeader() client.get("https://www.smzdm.com/") { headers { cookieString(cookie) userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36") } } - CommonResult.success(SmZdmEntity().also { it.cookie = cookie }) + SmZdmEntity().also { it.cookie = cookie } } else -> error("未知错误") } @@ -110,12 +106,10 @@ class SmZdmLogic( } @Suppress("DuplicatedCode") - suspend fun appQrcode2(smZdmAppQrcode: SmZdmAppQrcode): CommonResult { + suspend fun appQrcode2(smZdmAppQrcode: SmZdmAppQrcode): SmZdmEntity { // {"error_code":0,"error_msg":"","data":{"status":"1","redirect_to":""}} - val response = client.post("https://zhiyou.smzdm.com/user/login/jsonp_qrcode_check") { - setFormDataContent { - append("qrcode_token", smZdmAppQrcode.token) - } + val response = client.submitForm("https://zhiyou.smzdm.com/user/login/jsonp_qrcode_check", + parameters { append("qrcode_token", smZdmAppQrcode.token) }) { headers { referer("https://zhiyou.smzdm.com/user/login/window/") } @@ -123,17 +117,17 @@ class SmZdmLogic( val jsonNode = response.bodyAsText().toJsonNode() if (jsonNode["error_code"].asInt() != 0) error(jsonNode["error_msg"].asText()) return when (jsonNode["data"]["status"].asInt()) { - 1 -> CommonResult.fail("未扫码", code = 0) - 2 -> CommonResult.fail("已扫码", code = 0) + 1 -> qrcodeNotScanned() + 2 -> qrcodeScanned() 3 -> { - val cookie = response.cookie() + val cookie = response.setCookie().renderCookieHeader() client.get("https://www.smzdm.com/") { headers { cookieString(cookie) userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36") } } - CommonResult.success(SmZdmEntity().also { it.cookie = cookie }) + SmZdmEntity().also { it.cookie = cookie } } else -> error("未知错误") } @@ -164,16 +158,16 @@ class SmZdmLogic( private fun appSign(smZdmEntity: SmZdmEntity, t: Long): String { val cookie = smZdmEntity.cookie - val sess = OkUtils.cookie(cookie, "sess") + val sess = RegexUtils.extract(cookie, "sess=", ";") val sign = "f=android&sk=1&time=$t&token=$sess&v=10.0&weixin=0&key=apr1\$AwP!wRRT\$gJ/q.X24poeBInlUJC" return sign.md5().uppercase() } suspend fun appSign(smZdmEntity: SmZdmEntity) { val t = System.currentTimeMillis() - val sess = OkUtils.cookie(smZdmEntity.cookie, "sess")!! - val jsonNode = client.post("https://user-api.smzdm.com/checkin") { - setFormDataContent { + val sess = RegexUtils.extract(smZdmEntity.cookie, "sess=", ";")!! + val jsonNode = client.submitForm("https://user-api.smzdm.com/checkin", + parameters { append("touchstone_event", "") append("v", "10.0") append("sign", appSign(smZdmEntity, t)) @@ -183,7 +177,7 @@ class SmZdmLogic( append("token", sess) append("f", "android") append("captcha", "") - } + }) { headers { cookieString(smZdmEntity.cookie) } diff --git a/src/main/kotlin/me/kuku/telegram/logic/StepLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/StepLogic.kt index 1d7ad69..e1b69d7 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/StepLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/StepLogic.kt @@ -1,38 +1,50 @@ package me.kuku.telegram.logic -import me.kuku.pojo.CommonResult +import com.fasterxml.jackson.databind.JsonNode +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.client.request.forms.* +import io.ktor.http.* import me.kuku.telegram.entity.StepEntity -import me.kuku.utils.* +import me.kuku.telegram.utils.* +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.UUID object LeXinStepLogic { - suspend fun login(phone: String, password: String): CommonResult { - val newPassword = if (password.length == 32) password else MD5Utils.toMD5(password) - val response = OkHttpKtUtils.post("https://sports.lifesense.com/sessions_service/login?screenHeight=2267&screenWidth=1080&systemType=2&version=4.5", - OkUtils.json("{\"password\":\"$newPassword\",\"clientId\":\"${MyUtils.randomNum(32)}\",\"appType\":6,\"loginName\":\"$phone\",\"roleType\":0}")) - val jsonNode = OkUtils.json(response) - return if (jsonNode.getInteger("code") == 200) { - val cookie = OkUtils.cookie(response) - CommonResult.success(StepEntity().also { + suspend fun login(phone: String, password: String): StepEntity { + val newPassword = if (password.length == 32) password else password.md5() + val response = client.post("https://sports.lifesense.com/sessions_service/login?screenHeight=2267&screenWidth=1080&systemType=2&version=4.5") { + setBody("{\"password\":\"$newPassword\",\"clientId\":\"${UUID.randomUUID()}\",\"appType\":6,\"loginName\":\"$phone\",\"roleType\":0}") + contentType(ContentType.Application.Json) + } + val jsonNode = response.body() + return if (jsonNode["code"].asInt() == 200) { + val cookie = response.setCookie().renderCookieHeader() + StepEntity().also { it.leXinCookie = cookie - it.leXinUserid = jsonNode.get("data").getString("userId") - it.leXinAccessToken = jsonNode.get("data").getString("accessToken") - }) - } else CommonResult.failure(jsonNode.getString("msg")) + it.leXinUserid = jsonNode.get("data")["userId"].asText() + it.leXinAccessToken = jsonNode.get("data")["accessToken"].asText() + } + } else error(jsonNode["msg"].asText()) } - suspend fun modifyStepCount(stepEntity: StepEntity, step: Int): CommonResult { - if (stepEntity.leXinCookie.isEmpty()) return CommonResult.failure("未绑定乐心运动,操作失败") + suspend fun modifyStepCount(stepEntity: StepEntity, step: Int) { + if (stepEntity.leXinCookie.isEmpty()) error("未绑定乐心运动,操作失败") val dateTimePattern = "yyyy-MM-dd hh:mm:ss" val datePattern = "yyyy-MM-dd" val time = System.currentTimeMillis() val second = System.currentTimeMillis() / 1000 - val jsonNode = OkHttpKtUtils.postJson("https://sports.lifesense.com/sport_service/sport/sport/uploadMobileStepV2?country=%E4%B8%AD%E5%9B%BD&city=%E6%9F%B3%E5%B7%9E&cityCode=450200&timezone=Asia%2FShanghai&latitude=24.368694&os_country=CN&channel=qq&language=zh&openudid=&platform=android&province=%E5%B9%BF%E8%A5%BF%E5%A3%AE%E6%97%8F%E8%87%AA%E6%B2%BB%E5%8C%BA&appType=6&requestId=${MyUtils.randomLetter(32)}&countryCode=&systemType=2&longitude=109.532216&devicemodel=V1914A&area=CN&screenwidth=1080&os_langs=zh&provinceCode=450000&promotion_channel=qq&rnd=3d51742c&version=4.6.7&areaCode=450203&requestToken=${MyUtils.randomLetter(32)}&network_type=wifi&osversion=10&screenheight=2267&ts=$second", - OkUtils.json("{\"list\":[{\"active\":1,\"calories\":${step / 4},\"created\":\"${DateTimeFormatterUtils.format(time, dateTimePattern)}\",\"dataSource\":2,\"dayMeasurementTime\":\"${DateTimeFormatterUtils.format(time, datePattern)}\",\"deviceId\":\"M_NULL\",\"distance\":${step / 3},\"id\":\"${MyUtils.randomLetter(32)}\",\"isUpload\":0,\"measurementTime\":\"${DateTimeFormatterUtils.format(time, dateTimePattern)}\",\"priority\":0,\"step\":$step,\"type\":2,\"updated\":$time,\"userId\":\"${stepEntity.leXinUserid}\",\"DataSource\":2,\"exerciseTime\":0}]}"), - OkUtils.cookie(stepEntity.leXinCookie)) - return if (jsonNode.getInteger("code") == 200) - CommonResult.success() - else CommonResult.failure(jsonNode.getString("msg")) + val now = LocalDateTime.now() + val jsonNode = client.post("https://sports.lifesense.com/sport_service/sport/sport/uploadMobileStepV2?country=%E4%B8%AD%E5%9B%BD&city=%E6%9F%B3%E5%B7%9E&cityCode=450200&timezone=Asia%2FShanghai&latitude=24.368694&os_country=CN&channel=qq&language=zh&openudid=&platform=android&province=%E5%B9%BF%E8%A5%BF%E5%A3%AE%E6%97%8F%E8%87%AA%E6%B2%BB%E5%8C%BA&appType=6&requestId=${UUID.randomUUID()}&countryCode=&systemType=2&longitude=109.532216&devicemodel=V1914A&area=CN&screenwidth=1080&os_langs=zh&provinceCode=450000&promotion_channel=qq&rnd=3d51742c&version=4.6.7&areaCode=450203&requestToken=${UUID.randomUUID()}&network_type=wifi&osversion=10&screenheight=2267&ts=$second") { + setBody("{\"list\":[{\"active\":1,\"calories\":${step / 4},\"created\":\"${now.format(DateTimeFormatter.ofPattern(dateTimePattern))}\",\"dataSource\":2,\"dayMeasurementTime\":\"${now.format(DateTimeFormatter.ofPattern(datePattern))}\",\"deviceId\":\"M_NULL\",\"distance\":${step / 3},\"id\":\"${UUID.randomUUID()}\",\"isUpload\":0,\"measurementTime\":\"${now.format(DateTimeFormatter.ofPattern(dateTimePattern))}\",\"priority\":0,\"step\":$step,\"type\":2,\"updated\":$time,\"userId\":\"${stepEntity.leXinUserid}\",\"DataSource\":2,\"exerciseTime\":0}]}") + contentType(ContentType.Application.Json) + headers { append("Cookie", stepEntity.leXinCookie) } + }.body() + if (jsonNode["code"].asInt() != 200) + error(jsonNode["msg"].asText()) } } @@ -41,53 +53,62 @@ object XiaomiStepLogic { private const val ua = "Dalvik/2.1.0 (Linux; U; Android 10; V1914A Build/QP1A.190711.020)" - suspend fun login(phone: String, password: String): CommonResult { + suspend fun login(phone: String, password: String): StepEntity { val map = mutableMapOf("phone_number" to "+86$phone", "password" to password, "state" to "REDIRECTION", "client_id" to "HuaMi", "country_code" to "CN", "token" to "access", "region" to "cn-northwest-1", "redirect_uri" to "https://s3-us-west-2.amazonaws.com/hm-registration/successsignin.html") - val response = OkHttpKtUtils.post("https://api-user.huami.com/registrations/%2B86$phone/tokens", map, OkUtils.ua(ua)).apply { close() } - val locationUrl = response.header("location") ?: return CommonResult.failure("登录失败") - val access = MyUtils.regex("access=", "&", locationUrl) ?: return CommonResult.failure("账号或者密码错误") + val response = client.submitForm("https://api-user.huami.com/registrations/%2B86$phone/tokens", + formParameters = parameters { map.forEach { (t, u) -> append(t, u) } }) { + headers { userAgent(ua) } + } + val locationUrl = response.headers["location"] ?: error("登录失败") + val access = RegexUtils.extract(locationUrl, "access=", "&") ?: error("账号或者密码错误") val ssMap = mapOf("app_name" to "com.xiaomi.hm.health", "country_code" to "CN", "code" to access, "device_id" to "37:83:85:5a:e8:93", "device_model" to "android_phone", "app_version" to "4.5.0", "grant_type" to "access_token", "allow_registration" to "false", "dn" to "account.huami.com,api-user.huami.com,api-watch.huami.com,api-analytics.huami.com,app-analytics.huami.com,api-mifit.huami.com", "third_name" to "huami_phone", "source" to "com.xiaomi.hm.health:4.5.0:50340") - val jsonNode = OkHttpKtUtils.postJson("https://account.huami.com/v2/client/login", ssMap, OkUtils.ua(ua)) - val token = jsonNode.get("token_info").getString("login_token") - return CommonResult.success(StepEntity().also { + val jsonNode = client.submitForm("https://account.huami.com/v2/client/login", + formParameters = parameters { ssMap.forEach { (t, u) -> append(t, u) } }) { + headers { userAgent(ua) } + }.body() + val token = jsonNode["token_info"]["login_token"].asText() + return StepEntity().also { it.miLoginToken = token - }) + } } - private suspend fun getInfo(token: String): CommonResult { - val jsonNode = OkHttpKtUtils.getJson("https://account-cn.huami.com/v1/client/app_tokens?app_name=com.xiaomi.hm.health&dn=api-user.huami.com%2Capi-mifit.huami.com%2Capp-analytics.huami.com&login_token=$token", - OkUtils.ua(ua)) - return if ("ok" == jsonNode.getString("result")) { + private suspend fun getInfo(token: String): MiInfo { + val jsonNode = client.get("https://account-cn.huami.com/v1/client/app_tokens?app_name=com.xiaomi.hm.health&dn=api-user.huami.com%2Capi-mifit.huami.com%2Capp-analytics.huami.com&login_token=$token") { + userAgent(ua) + }.body() + return if ("ok" == jsonNode["result"].asText()) { val infoJsonNode = jsonNode.get("token_info") - CommonResult.success(MiInfo(infoJsonNode.getString("app_token"), infoJsonNode.getString("user_id"))) - } else CommonResult.failure("登录已失效,请重新登录!!") + MiInfo(infoJsonNode["app_token"].asText(), infoJsonNode["user_id"].asText()) + } else error("登录已失效,请重新登录!!") } - suspend fun modifyStepCount(stepEntity: StepEntity, step: Int): CommonResult { + suspend fun modifyStepCount(stepEntity: StepEntity, step: Int) { val token = stepEntity.miLoginToken - if (token.isEmpty()) return CommonResult.failure("未绑定小米运动,操作失败") - val infoResult = getInfo(token) - if (infoResult.failure()) return CommonResult.failure("步数修改失败,登录已失效") + if (token.isEmpty()) error("未绑定小米运动,操作失败") + val miInfo = getInfo(token) val tenDateStr = System.currentTimeMillis().toString().substring(0, 10) - val miInfo = infoResult.data() val paramsMap = mapOf("userid" to miInfo.userId, "last_sync_data_time" to tenDateStr, "device_type" to "0", "last_deviceid" to "DA932FFFFE8816E7", "data_json" to """ - %5B%7B%22data_hr%22%3A%22%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9L%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FVv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0v%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9e%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0n%5C%2Fa%5C%2F%5C%2F%5C%2FS%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0b%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F1FK%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FR%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9PTFFpaf9L%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FR%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0j%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9K%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FOv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fzf%5C%2F%5C%2F%5C%2F86%5C%2Fzr%5C%2FOv88%5C%2Fzf%5C%2FPf%5C%2F%5C%2F%5C%2F0v%5C%2FS%5C%2F8%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FSf%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fz3%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0r%5C%2FOv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FS%5C%2F9L%5C%2Fzb%5C%2FSf9K%5C%2F0v%5C%2FRf9H%5C%2Fzj%5C%2FSf9K%5C%2F0%5C%2F%5C%2FN%5C%2F%5C%2F%5C%2F%5C%2F0D%5C%2FSf83%5C%2Fzr%5C%2FPf9M%5C%2F0v%5C%2FOv9e%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FS%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fzv%5C%2F%5C%2Fz7%5C%2FO%5C%2F83%5C%2Fzv%5C%2FN%5C%2F83%5C%2Fzr%5C%2FN%5C%2F86%5C%2Fz%5C%2F%5C%2FNv83%5C%2Fzn%5C%2FXv84%5C%2Fzr%5C%2FPP84%5C%2Fzj%5C%2FN%5C%2F9e%5C%2Fzr%5C%2FN%5C%2F89%5C%2F03%5C%2FP%5C%2F89%5C%2Fz3%5C%2FQ%5C%2F9N%5C%2F0v%5C%2FTv9C%5C%2F0H%5C%2FOf9D%5C%2Fzz%5C%2FOf88%5C%2Fz%5C%2F%5C%2FPP9A%5C%2Fzr%5C%2FN%5C%2F86%5C%2Fzz%5C%2FNv87%5C%2F0D%5C%2FOv84%5C%2F0v%5C%2FO%5C%2F84%5C%2Fzf%5C%2FMP83%5C%2FzH%5C%2FNv83%5C%2Fzf%5C%2FN%5C%2F84%5C%2Fzf%5C%2FOf82%5C%2Fzf%5C%2FOP83%5C%2Fzb%5C%2FMv81%5C%2FzX%5C%2FR%5C%2F9L%5C%2F0v%5C%2FO%5C%2F9I%5C%2F0T%5C%2FS%5C%2F9A%5C%2Fzn%5C%2FPf89%5C%2Fzn%5C%2FNf9K%5C%2F07%5C%2FN%5C%2F83%5C%2Fzn%5C%2FNv83%5C%2Fzv%5C%2FO%5C%2F9A%5C%2F0H%5C%2FOf8%5C%2F%5C%2Fzj%5C%2FPP83%5C%2Fzj%5C%2FS%5C%2F87%5C%2Fzj%5C%2FNv84%5C%2Fzf%5C%2FOf83%5C%2Fzf%5C%2FOf83%5C%2Fzb%5C%2FNv9L%5C%2Fzj%5C%2FNv82%5C%2Fzb%5C%2FN%5C%2F85%5C%2Fzf%5C%2FN%5C%2F9J%5C%2Fzf%5C%2FNv83%5C%2Fzj%5C%2FNv84%5C%2F0r%5C%2FSv83%5C%2Fzf%5C%2FMP%5C%2F%5C%2F%5C%2Fzb%5C%2FMv82%5C%2Fzb%5C%2FOf85%5C%2Fz7%5C%2FNv8%5C%2F%5C%2F0r%5C%2FS%5C%2F85%5C%2F0H%5C%2FQP9B%5C%2F0D%5C%2FNf89%5C%2Fzj%5C%2FOv83%5C%2Fzv%5C%2FNv8%5C%2F%5C%2F0f%5C%2FSv9O%5C%2F0ZeXv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F1X%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9B%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FTP%5C%2F%5C%2F%5C%2F1b%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9N%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%22%2C%22date%22%3A%22${DateTimeFormatterUtils.formatNow("yyyy-MM-dd")}%22%2C%22data%22%3A%5B%7B%22start%22%3A0%2C%22stop%22%3A1439%2C%22value%22%3A%22UA8AUBQAUAwAUBoAUAEAYCcAUBkAUB4AUBgAUCAAUAEAUBkAUAwAYAsAYB8AYB0AYBgAYCoAYBgAYB4AUCcAUBsAUB8AUBwAUBIAYBkAYB8AUBoAUBMAUCEAUCIAYBYAUBwAUCAAUBgAUCAAUBcAYBsAYCUAATIPYD0KECQAYDMAYB0AYAsAYCAAYDwAYCIAYB0AYBcAYCQAYB0AYBAAYCMAYAoAYCIAYCEAYCYAYBsAYBUAYAYAYCIAYCMAUB0AUCAAUBYAUCoAUBEAUC8AUB0AUBYAUDMAUDoAUBkAUC0AUBQAUBwAUA0AUBsAUAoAUCEAUBYAUAwAUB4AUAwAUCcAUCYAUCwKYDUAAUUlEC8IYEMAYEgAYDoAYBAAUAMAUBkAWgAAWgAAWgAAWgAAWgAAUAgAWgAAUBAAUAQAUA4AUA8AUAkAUAIAUAYAUAcAUAIAWgAAUAQAUAkAUAEAUBkAUCUAWgAAUAYAUBEAWgAAUBYAWgAAUAYAWgAAWgAAWgAAWgAAUBcAUAcAWgAAUBUAUAoAUAIAWgAAUAQAUAYAUCgAWgAAUAgAWgAAWgAAUAwAWwAAXCMAUBQAWwAAUAIAWgAAWgAAWgAAWgAAWgAAWgAAWgAAWgAAWREAWQIAUAMAWSEAUDoAUDIAUB8AUCEAUC4AXB4AUA4AWgAAUBIAUA8AUBAAUCUAUCIAUAMAUAEAUAsAUAMAUCwAUBYAWgAAWgAAWgAAWgAAWgAAWgAAUAYAWgAAWgAAWgAAUAYAWwAAWgAAUAYAXAQAUAMAUBsAUBcAUCAAWwAAWgAAWgAAWgAAWgAAUBgAUB4AWgAAUAcAUAwAWQIAWQkAUAEAUAIAWgAAUAoAWgAAUAYAUB0AWgAAWgAAUAkAWgAAWSwAUBIAWgAAUC4AWSYAWgAAUAYAUAoAUAkAUAIAUAcAWgAAUAEAUBEAUBgAUBcAWRYAUA0AWSgAUB4AUDQAUBoAXA4AUA8AUBwAUA8AUA4AUA4AWgAAUAIAUCMAWgAAUCwAUBgAUAYAUAAAUAAAUAAAUAAAUAAAUAAAUAAAUAAAUAAAWwAAUAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAeSEAeQ8AcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBcAcAAAcAAAcCYOcBUAUAAAUAAAUAAAUAAAUAUAUAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCgAeQAAcAAAcAAAcAAAcAAAcAAAcAYAcAAAcBgAeQAAcAAAcAAAegAAegAAcAAAcAcAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCkAeQAAcAcAcAAAcAAAcAwAcAAAcAAAcAIAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCIAeQAAcAAAcAAAcAAAcAAAcAAAeRwAeQAAWgAAUAAAUAAAUAAAUAAAUAAAcAAAcAAAcBoAeScAeQAAegAAcBkAeQAAUAAAUAAAUAAAUAAAUAAAUAAAcAAAcAAAcAAAcAAAcAAAcAAAegAAegAAcAAAcAAAcBgAeQAAcAAAcAAAcAAAcAAAcAAAcAkAegAAegAAcAcAcAAAcAcAcAAAcAAAcAAAcAAAcA8AeQAAcAAAcAAAeRQAcAwAUAAAUAAAUAAAUAAAUAAAUAAAcAAAcBEAcA0AcAAAWQsAUAAAUAAAUAAAUAAAUAAAcAAAcAoAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAYAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBYAegAAcAAAcAAAegAAcAcAcAAAcAAAcAAAcAAAcAAAeRkAegAAegAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAEAcAAAcAAAcAAAcAUAcAQAcAAAcBIAeQAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBsAcAAAcAAAcBcAeQAAUAAAUAAAUAAAUAAAUAAAUBQAcBYAUAAAUAAAUAoAWRYAWTQAWQAAUAAAUAAAUAAAcAAAcAAAcAAAcAAAcAAAcAMAcAAAcAQAcAAAcAAAcAAAcDMAeSIAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBQAeQwAcAAAcAAAcAAAcAMAcAAAeSoAcA8AcDMAcAYAeQoAcAwAcFQAcEMAeVIAaTYAbBcNYAsAYBIAYAIAYAIAYBUAYCwAYBMAYDYAYCkAYDcAUCoAUCcAUAUAUBAAWgAAYBoAYBcAYCgAUAMAUAYAUBYAUA4AUBgAUAgAUAgAUAsAUAsAUA4AUAMAUAYAUAQAUBIAASsSUDAAUDAAUBAAYAYAUBAAUAUAUCAAUBoAUCAAUBAAUAoAYAIAUAQAUAgAUCcAUAsAUCIAUCUAUAoAUA4AUB8AUBkAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAA%22%2C%22tz%22%3A32%2C%22did%22%3A%22DA932FFFFE8816E7%22%2C%22src%22%3A24%7D%5D%2C%22summary%22%3A%22%7B%5C%22v%5C%22%3A6%2C%5C%22slp%5C%22%3A%7B%5C%22st%5C%22%3A1628296479%2C%5C%22ed%5C%22%3A1628296479%2C%5C%22dp%5C%22%3A0%2C%5C%22lt%5C%22%3A0%2C%5C%22wk%5C%22%3A0%2C%5C%22usrSt%5C%22%3A-1440%2C%5C%22usrEd%5C%22%3A-1440%2C%5C%22wc%5C%22%3A0%2C%5C%22is%5C%22%3A0%2C%5C%22lb%5C%22%3A0%2C%5C%22to%5C%22%3A0%2C%5C%22dt%5C%22%3A0%2C%5C%22rhr%5C%22%3A0%2C%5C%22ss%5C%22%3A0%7D%2C%5C%22stp%5C%22%3A%7B%5C%22ttl%5C%22%3A${step}%2C%5C%22dis%5C%22%3A10627%2C%5C%22cal%5C%22%3A510%2C%5C%22wk%5C%22%3A41%2C%5C%22rn%5C%22%3A50%2C%5C%22runDist%5C%22%3A7654%2C%5C%22runCal%5C%22%3A397%2C%5C%22stage%5C%22%3A%5B%7B%5C%22start%5C%22%3A327%2C%5C%22stop%5C%22%3A341%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A481%2C%5C%22cal%5C%22%3A13%2C%5C%22step%5C%22%3A680%7D%2C%7B%5C%22start%5C%22%3A342%2C%5C%22stop%5C%22%3A367%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A2295%2C%5C%22cal%5C%22%3A95%2C%5C%22step%5C%22%3A2874%7D%2C%7B%5C%22start%5C%22%3A368%2C%5C%22stop%5C%22%3A377%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1592%2C%5C%22cal%5C%22%3A88%2C%5C%22step%5C%22%3A1664%7D%2C%7B%5C%22start%5C%22%3A378%2C%5C%22stop%5C%22%3A386%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1072%2C%5C%22cal%5C%22%3A51%2C%5C%22step%5C%22%3A1245%7D%2C%7B%5C%22start%5C%22%3A387%2C%5C%22stop%5C%22%3A393%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1036%2C%5C%22cal%5C%22%3A57%2C%5C%22step%5C%22%3A1124%7D%2C%7B%5C%22start%5C%22%3A394%2C%5C%22stop%5C%22%3A398%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A488%2C%5C%22cal%5C%22%3A19%2C%5C%22step%5C%22%3A607%7D%2C%7B%5C%22start%5C%22%3A399%2C%5C%22stop%5C%22%3A414%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A2220%2C%5C%22cal%5C%22%3A120%2C%5C%22step%5C%22%3A2371%7D%2C%7B%5C%22start%5C%22%3A415%2C%5C%22stop%5C%22%3A427%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1268%2C%5C%22cal%5C%22%3A59%2C%5C%22step%5C%22%3A1489%7D%2C%7B%5C%22start%5C%22%3A428%2C%5C%22stop%5C%22%3A433%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A152%2C%5C%22cal%5C%22%3A4%2C%5C%22step%5C%22%3A238%7D%2C%7B%5C%22start%5C%22%3A434%2C%5C%22stop%5C%22%3A444%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A2295%2C%5C%22cal%5C%22%3A95%2C%5C%22step%5C%22%3A2874%7D%2C%7B%5C%22start%5C%22%3A445%2C%5C%22stop%5C%22%3A455%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1592%2C%5C%22cal%5C%22%3A88%2C%5C%22step%5C%22%3A1664%7D%2C%7B%5C%22start%5C%22%3A456%2C%5C%22stop%5C%22%3A466%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1072%2C%5C%22cal%5C%22%3A51%2C%5C%22step%5C%22%3A1245%7D%2C%7B%5C%22start%5C%22%3A467%2C%5C%22stop%5C%22%3A477%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1036%2C%5C%22cal%5C%22%3A57%2C%5C%22step%5C%22%3A1124%7D%2C%7B%5C%22start%5C%22%3A478%2C%5C%22stop%5C%22%3A488%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A488%2C%5C%22cal%5C%22%3A19%2C%5C%22step%5C%22%3A607%7D%2C%7B%5C%22start%5C%22%3A489%2C%5C%22stop%5C%22%3A499%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A2220%2C%5C%22cal%5C%22%3A120%2C%5C%22step%5C%22%3A2371%7D%2C%7B%5C%22start%5C%22%3A500%2C%5C%22stop%5C%22%3A511%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1268%2C%5C%22cal%5C%22%3A59%2C%5C%22step%5C%22%3A1489%7D%2C%7B%5C%22start%5C%22%3A512%2C%5C%22stop%5C%22%3A522%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A152%2C%5C%22cal%5C%22%3A4%2C%5C%22step%5C%22%3A238%7D%5D%7D%2C%5C%22goal%5C%22%3A8000%2C%5C%22tz%5C%22%3A%5C%2228800%5C%22%7D%22%2C%22source%22%3A24%2C%22type%22%3A0%7D%5D + %5B%7B%22data_hr%22%3A%22%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9L%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FVv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0v%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9e%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0n%5C%2Fa%5C%2F%5C%2F%5C%2FS%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0b%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F1FK%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FR%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9PTFFpaf9L%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FR%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0j%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9K%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FOv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fzf%5C%2F%5C%2F%5C%2F86%5C%2Fzr%5C%2FOv88%5C%2Fzf%5C%2FPf%5C%2F%5C%2F%5C%2F0v%5C%2FS%5C%2F8%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FSf%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fz3%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0r%5C%2FOv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FS%5C%2F9L%5C%2Fzb%5C%2FSf9K%5C%2F0v%5C%2FRf9H%5C%2Fzj%5C%2FSf9K%5C%2F0%5C%2F%5C%2FN%5C%2F%5C%2F%5C%2F%5C%2F0D%5C%2FSf83%5C%2Fzr%5C%2FPf9M%5C%2F0v%5C%2FOv9e%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FS%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fzv%5C%2F%5C%2Fz7%5C%2FO%5C%2F83%5C%2Fzv%5C%2FN%5C%2F83%5C%2Fzr%5C%2FN%5C%2F86%5C%2Fz%5C%2F%5C%2FNv83%5C%2Fzn%5C%2FXv84%5C%2Fzr%5C%2FPP84%5C%2Fzj%5C%2FN%5C%2F9e%5C%2Fzr%5C%2FN%5C%2F89%5C%2F03%5C%2FP%5C%2F89%5C%2Fz3%5C%2FQ%5C%2F9N%5C%2F0v%5C%2FTv9C%5C%2F0H%5C%2FOf9D%5C%2Fzz%5C%2FOf88%5C%2Fz%5C%2F%5C%2FPP9A%5C%2Fzr%5C%2FN%5C%2F86%5C%2Fzz%5C%2FNv87%5C%2F0D%5C%2FOv84%5C%2F0v%5C%2FO%5C%2F84%5C%2Fzf%5C%2FMP83%5C%2FzH%5C%2FNv83%5C%2Fzf%5C%2FN%5C%2F84%5C%2Fzf%5C%2FOf82%5C%2Fzf%5C%2FOP83%5C%2Fzb%5C%2FMv81%5C%2FzX%5C%2FR%5C%2F9L%5C%2F0v%5C%2FO%5C%2F9I%5C%2F0T%5C%2FS%5C%2F9A%5C%2Fzn%5C%2FPf89%5C%2Fzn%5C%2FNf9K%5C%2F07%5C%2FN%5C%2F83%5C%2Fzn%5C%2FNv83%5C%2Fzv%5C%2FO%5C%2F9A%5C%2F0H%5C%2FOf8%5C%2F%5C%2Fzj%5C%2FPP83%5C%2Fzj%5C%2FS%5C%2F87%5C%2Fzj%5C%2FNv84%5C%2Fzf%5C%2FOf83%5C%2Fzf%5C%2FOf83%5C%2Fzb%5C%2FNv9L%5C%2Fzj%5C%2FNv82%5C%2Fzb%5C%2FN%5C%2F85%5C%2Fzf%5C%2FN%5C%2F9J%5C%2Fzf%5C%2FNv83%5C%2Fzj%5C%2FNv84%5C%2F0r%5C%2FSv83%5C%2Fzf%5C%2FMP%5C%2F%5C%2F%5C%2Fzb%5C%2FMv82%5C%2Fzb%5C%2FOf85%5C%2Fz7%5C%2FNv8%5C%2F%5C%2F0r%5C%2FS%5C%2F85%5C%2F0H%5C%2FQP9B%5C%2F0D%5C%2FNf89%5C%2Fzj%5C%2FOv83%5C%2Fzv%5C%2FNv8%5C%2F%5C%2F0f%5C%2FSv9O%5C%2F0ZeXv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F1X%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9B%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FTP%5C%2F%5C%2F%5C%2F1b%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9N%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%22%2C%22date%22%3A%22${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))}%22%2C%22data%22%3A%5B%7B%22start%22%3A0%2C%22stop%22%3A1439%2C%22value%22%3A%22UA8AUBQAUAwAUBoAUAEAYCcAUBkAUB4AUBgAUCAAUAEAUBkAUAwAYAsAYB8AYB0AYBgAYCoAYBgAYB4AUCcAUBsAUB8AUBwAUBIAYBkAYB8AUBoAUBMAUCEAUCIAYBYAUBwAUCAAUBgAUCAAUBcAYBsAYCUAATIPYD0KECQAYDMAYB0AYAsAYCAAYDwAYCIAYB0AYBcAYCQAYB0AYBAAYCMAYAoAYCIAYCEAYCYAYBsAYBUAYAYAYCIAYCMAUB0AUCAAUBYAUCoAUBEAUC8AUB0AUBYAUDMAUDoAUBkAUC0AUBQAUBwAUA0AUBsAUAoAUCEAUBYAUAwAUB4AUAwAUCcAUCYAUCwKYDUAAUUlEC8IYEMAYEgAYDoAYBAAUAMAUBkAWgAAWgAAWgAAWgAAWgAAUAgAWgAAUBAAUAQAUA4AUA8AUAkAUAIAUAYAUAcAUAIAWgAAUAQAUAkAUAEAUBkAUCUAWgAAUAYAUBEAWgAAUBYAWgAAUAYAWgAAWgAAWgAAWgAAUBcAUAcAWgAAUBUAUAoAUAIAWgAAUAQAUAYAUCgAWgAAUAgAWgAAWgAAUAwAWwAAXCMAUBQAWwAAUAIAWgAAWgAAWgAAWgAAWgAAWgAAWgAAWgAAWREAWQIAUAMAWSEAUDoAUDIAUB8AUCEAUC4AXB4AUA4AWgAAUBIAUA8AUBAAUCUAUCIAUAMAUAEAUAsAUAMAUCwAUBYAWgAAWgAAWgAAWgAAWgAAWgAAUAYAWgAAWgAAWgAAUAYAWwAAWgAAUAYAXAQAUAMAUBsAUBcAUCAAWwAAWgAAWgAAWgAAWgAAUBgAUB4AWgAAUAcAUAwAWQIAWQkAUAEAUAIAWgAAUAoAWgAAUAYAUB0AWgAAWgAAUAkAWgAAWSwAUBIAWgAAUC4AWSYAWgAAUAYAUAoAUAkAUAIAUAcAWgAAUAEAUBEAUBgAUBcAWRYAUA0AWSgAUB4AUDQAUBoAXA4AUA8AUBwAUA8AUA4AUA4AWgAAUAIAUCMAWgAAUCwAUBgAUAYAUAAAUAAAUAAAUAAAUAAAUAAAUAAAUAAAUAAAWwAAUAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAeSEAeQ8AcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBcAcAAAcAAAcCYOcBUAUAAAUAAAUAAAUAAAUAUAUAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCgAeQAAcAAAcAAAcAAAcAAAcAAAcAYAcAAAcBgAeQAAcAAAcAAAegAAegAAcAAAcAcAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCkAeQAAcAcAcAAAcAAAcAwAcAAAcAAAcAIAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCIAeQAAcAAAcAAAcAAAcAAAcAAAeRwAeQAAWgAAUAAAUAAAUAAAUAAAUAAAcAAAcAAAcBoAeScAeQAAegAAcBkAeQAAUAAAUAAAUAAAUAAAUAAAUAAAcAAAcAAAcAAAcAAAcAAAcAAAegAAegAAcAAAcAAAcBgAeQAAcAAAcAAAcAAAcAAAcAAAcAkAegAAegAAcAcAcAAAcAcAcAAAcAAAcAAAcAAAcA8AeQAAcAAAcAAAeRQAcAwAUAAAUAAAUAAAUAAAUAAAUAAAcAAAcBEAcA0AcAAAWQsAUAAAUAAAUAAAUAAAUAAAcAAAcAoAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAYAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBYAegAAcAAAcAAAegAAcAcAcAAAcAAAcAAAcAAAcAAAeRkAegAAegAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAEAcAAAcAAAcAAAcAUAcAQAcAAAcBIAeQAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBsAcAAAcAAAcBcAeQAAUAAAUAAAUAAAUAAAUAAAUBQAcBYAUAAAUAAAUAoAWRYAWTQAWQAAUAAAUAAAUAAAcAAAcAAAcAAAcAAAcAAAcAMAcAAAcAQAcAAAcAAAcAAAcDMAeSIAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBQAeQwAcAAAcAAAcAAAcAMAcAAAeSoAcA8AcDMAcAYAeQoAcAwAcFQAcEMAeVIAaTYAbBcNYAsAYBIAYAIAYAIAYBUAYCwAYBMAYDYAYCkAYDcAUCoAUCcAUAUAUBAAWgAAYBoAYBcAYCgAUAMAUAYAUBYAUA4AUBgAUAgAUAgAUAsAUAsAUA4AUAMAUAYAUAQAUBIAASsSUDAAUDAAUBAAYAYAUBAAUAUAUCAAUBoAUCAAUBAAUAoAYAIAUAQAUAgAUCcAUAsAUCIAUCUAUAoAUA4AUB8AUBkAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAA%22%2C%22tz%22%3A32%2C%22did%22%3A%22DA932FFFFE8816E7%22%2C%22src%22%3A24%7D%5D%2C%22summary%22%3A%22%7B%5C%22v%5C%22%3A6%2C%5C%22slp%5C%22%3A%7B%5C%22st%5C%22%3A1628296479%2C%5C%22ed%5C%22%3A1628296479%2C%5C%22dp%5C%22%3A0%2C%5C%22lt%5C%22%3A0%2C%5C%22wk%5C%22%3A0%2C%5C%22usrSt%5C%22%3A-1440%2C%5C%22usrEd%5C%22%3A-1440%2C%5C%22wc%5C%22%3A0%2C%5C%22is%5C%22%3A0%2C%5C%22lb%5C%22%3A0%2C%5C%22to%5C%22%3A0%2C%5C%22dt%5C%22%3A0%2C%5C%22rhr%5C%22%3A0%2C%5C%22ss%5C%22%3A0%7D%2C%5C%22stp%5C%22%3A%7B%5C%22ttl%5C%22%3A${step}%2C%5C%22dis%5C%22%3A10627%2C%5C%22cal%5C%22%3A510%2C%5C%22wk%5C%22%3A41%2C%5C%22rn%5C%22%3A50%2C%5C%22runDist%5C%22%3A7654%2C%5C%22runCal%5C%22%3A397%2C%5C%22stage%5C%22%3A%5B%7B%5C%22start%5C%22%3A327%2C%5C%22stop%5C%22%3A341%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A481%2C%5C%22cal%5C%22%3A13%2C%5C%22step%5C%22%3A680%7D%2C%7B%5C%22start%5C%22%3A342%2C%5C%22stop%5C%22%3A367%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A2295%2C%5C%22cal%5C%22%3A95%2C%5C%22step%5C%22%3A2874%7D%2C%7B%5C%22start%5C%22%3A368%2C%5C%22stop%5C%22%3A377%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1592%2C%5C%22cal%5C%22%3A88%2C%5C%22step%5C%22%3A1664%7D%2C%7B%5C%22start%5C%22%3A378%2C%5C%22stop%5C%22%3A386%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1072%2C%5C%22cal%5C%22%3A51%2C%5C%22step%5C%22%3A1245%7D%2C%7B%5C%22start%5C%22%3A387%2C%5C%22stop%5C%22%3A393%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1036%2C%5C%22cal%5C%22%3A57%2C%5C%22step%5C%22%3A1124%7D%2C%7B%5C%22start%5C%22%3A394%2C%5C%22stop%5C%22%3A398%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A488%2C%5C%22cal%5C%22%3A19%2C%5C%22step%5C%22%3A607%7D%2C%7B%5C%22start%5C%22%3A399%2C%5C%22stop%5C%22%3A414%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A2220%2C%5C%22cal%5C%22%3A120%2C%5C%22step%5C%22%3A2371%7D%2C%7B%5C%22start%5C%22%3A415%2C%5C%22stop%5C%22%3A427%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1268%2C%5C%22cal%5C%22%3A59%2C%5C%22step%5C%22%3A1489%7D%2C%7B%5C%22start%5C%22%3A428%2C%5C%22stop%5C%22%3A433%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A152%2C%5C%22cal%5C%22%3A4%2C%5C%22step%5C%22%3A238%7D%2C%7B%5C%22start%5C%22%3A434%2C%5C%22stop%5C%22%3A444%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A2295%2C%5C%22cal%5C%22%3A95%2C%5C%22step%5C%22%3A2874%7D%2C%7B%5C%22start%5C%22%3A445%2C%5C%22stop%5C%22%3A455%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1592%2C%5C%22cal%5C%22%3A88%2C%5C%22step%5C%22%3A1664%7D%2C%7B%5C%22start%5C%22%3A456%2C%5C%22stop%5C%22%3A466%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1072%2C%5C%22cal%5C%22%3A51%2C%5C%22step%5C%22%3A1245%7D%2C%7B%5C%22start%5C%22%3A467%2C%5C%22stop%5C%22%3A477%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1036%2C%5C%22cal%5C%22%3A57%2C%5C%22step%5C%22%3A1124%7D%2C%7B%5C%22start%5C%22%3A478%2C%5C%22stop%5C%22%3A488%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A488%2C%5C%22cal%5C%22%3A19%2C%5C%22step%5C%22%3A607%7D%2C%7B%5C%22start%5C%22%3A489%2C%5C%22stop%5C%22%3A499%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A2220%2C%5C%22cal%5C%22%3A120%2C%5C%22step%5C%22%3A2371%7D%2C%7B%5C%22start%5C%22%3A500%2C%5C%22stop%5C%22%3A511%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1268%2C%5C%22cal%5C%22%3A59%2C%5C%22step%5C%22%3A1489%7D%2C%7B%5C%22start%5C%22%3A512%2C%5C%22stop%5C%22%3A522%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A152%2C%5C%22cal%5C%22%3A4%2C%5C%22step%5C%22%3A238%7D%5D%7D%2C%5C%22goal%5C%22%3A8000%2C%5C%22tz%5C%22%3A%5C%2228800%5C%22%7D%22%2C%22source%22%3A24%2C%22type%22%3A0%7D%5D """.trimIndent().toUrlDecode() ) - val jsonNode = OkHttpKtUtils.postJson("https://api-mifit-cn.huami.com/v1/data/band_data.json?&t=" + System.currentTimeMillis(), - paramsMap, mapOf("apptoken" to miInfo.appToken, "user-agent" to ua) - ) - return when (jsonNode.getInteger("code")) { - 1 -> CommonResult.success() - 0 -> CommonResult.failure("步数修改失败,登录已失效") - else -> CommonResult.failure("步数修改失败,未知错误") + val jsonNode = client.submitForm("https://api-mifit-cn.huami.com/v1/data/band_data.json?&t=" + System.currentTimeMillis(), + parameters { paramsMap.forEach { append(it.key, it.value) } }) { + headers { + append("apptoken", miInfo.appToken) + userAgent(ua) + } + }.body() + when (jsonNode["code"].asInt()) { + 1 -> return + 0 -> error("步数修改失败,登录已失效") + else -> error("步数修改失败,未知错误") } } diff --git a/src/main/kotlin/me/kuku/telegram/logic/ToolLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/ToolLogic.kt index 969fead..9f75204 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/ToolLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/ToolLogic.kt @@ -5,99 +5,23 @@ import io.ktor.client.statement.* import io.ktor.utils.io.jvm.javaio.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import me.kuku.pojo.CommonResult -import me.kuku.telegram.utils.ffmpeg -import me.kuku.utils.* -import okhttp3.MultipartBody +import me.kuku.telegram.utils.* import org.jsoup.Jsoup import org.springframework.stereotype.Service import java.io.File import java.io.FileOutputStream -import java.nio.charset.Charset +import java.nio.file.Files +import java.nio.file.Path @Service class ToolLogic { - suspend fun baiKe(text: String): String { - - suspend fun baiKeByUrl(url: String): CommonResult { - var response = OkHttpKtUtils.get(url) - while (response.code == 302) { - response.close() - val location = response.header("location")!! - if (location.startsWith("//baike.baidu.com/search/none")) return CommonResult.failure("") - val resultUrl = if (location.startsWith("//")) "https:$location" - else "https://baike.baidu.com$location" - response = OkHttpKtUtils.get(resultUrl) - } - val html = OkUtils.str(response) - val doc = Jsoup.parse(html) - val result = doc.select(".lemma-summary .para").first()?.text() - ?: return CommonResult.failure(code = 210, message = "", data = "https://baike.baidu.com" + doc.select("li[class=list-dot list-dot-paddingleft]").first()?.getElementsByTag("a")?.first()?.attr("href")) - return CommonResult.success(result) - } - - val encodeText = text.toUrlEncode() - val url = "https://baike.baidu.com/search/word?word=$encodeText" - val result = baiKeByUrl(url) - return if (result.success()) - """ - ${result.data} - 查看详情: $url - """.trimIndent() - else if (result.code == 210) { - val resultUrl = result.data() - """ - ${baiKeByUrl(resultUrl).data} - 查看详情:$resultUrl - """.trimIndent() - } else """ - 抱歉,没有找到与"$text"相关的百科结果 - """.trimIndent() - } - - suspend fun saucenao(byteArray: ByteArray): List { - val body = MultipartBody.Builder().setType(MultipartBody.FORM) - .addFormDataPart("url", "Paste Image URL") - .addFormDataPart("output_type", "2") - .addFormDataPart("api_key", "WkRRd05qWTFORGhpTTJZd05UaGtaalEwWlRaak1EUXpaamsxTjJOa01HSXpZMlk0TXprMk53PT0=".base64Decode().toString(Charset.defaultCharset()).base64Decode().toString(Charset.defaultCharset())) - .addFormDataPart("file", "demo.jpg", OkUtils.streamBody(byteArray)) - .build() - val urlJsonNode = OkHttpKtUtils.postJson("https://saucenao.com/search.php", body) - if (urlJsonNode.get("header").getInteger("status") != 0) error(urlJsonNode.get("header").getString("message")) - val jsonList = urlJsonNode.get("results") - val list = mutableListOf() - for (jsonNode in jsonList) { - val header = jsonNode.get("header") - val data = jsonNode.get("data") - val similarity = header["similarity"]?.asText() ?: "" - val thumbnail = header["thumbnail"]?.asText() ?: "" - val indexName = header["index_name"]?.asText() ?: "" - val extUrls = data.get("ext_urls")?.let { - val letList = mutableListOf() - it.forEach { k -> letList.add(k.asText()) } - letList - } ?: listOf() - val author = data.get("creator_name")?.asText() ?: data.get("member_name")?.asText() ?: data.get("author_name")?.asText() ?: "" - val title = data.get("title")?.asText() ?: data.get("jp_name")?.asText() ?: "" - val authorUrl = data.get("author_url")?.asText() ?: "" - list.add(SaucenaoResult(similarity, thumbnail, indexName, extUrls, title, author, authorUrl).also { - it.daId = data["da_id"]?.asLong() ?: 0 - it.pixivId = data["pixiv_id"]?.asLong() ?: 0 - it.faId = data["fa_id"]?.asLong() ?: 0 - it.tweetId = data["tweet_id"]?.asLong() ?: 0 - }) - } - return list - } - suspend fun positiveEnergy(date: String): File { - DateTimeFormatterUtils.parseToLocalDate(date, "yyyyMMdd") val html = client.get("http://tv.cctv.com/lm/xwlb/day/$date.shtml").bodyAsText() val url = Jsoup.parse(html).getElementsByTag("li").first()?.getElementsByTag("a")?.last()?.attr("href") ?: error("未找到新闻联播链接") val nextHtml = client.get(url).bodyAsText() - val guid = MyUtils.regex("guid = \"", "\";", nextHtml) ?: error("没有找到guid") + val guid = RegexUtils.extract("guid = \"", "\";", nextHtml) ?: error("没有找到guid") val tsp = System.currentTimeMillis().toString().substring(0, 10) val vc = "${tsp}204947899B86370B879139C08EA3B5E88267BF11E55294143CAE692F250517A4C10C".md5().uppercase() val jsonNode = @@ -107,8 +31,9 @@ class ToolLogic { val list = mutableListOf() for (i in urlList.indices) { client.get(urlList[i]).bodyAsChannel().toInputStream().use { iis -> - val file = IOUtils.writeTmpFile("$date-$i.mp4", iis) - list.add(file) + val path = Path.of("tmp", "$date-$i.mp4") + Files.copy(iis, path) + list.add(path.toFile()) } } val sb = StringBuilder() @@ -120,7 +45,7 @@ class ToolLogic { val txtFos = withContext(Dispatchers.IO) { FileOutputStream(txtFile) } - IOUtils.write(sb.toString().byteInputStream(), txtFos) + sb.toString().byteInputStream().copyTo(txtFos) val newPath = list[0].absolutePath.replace("$date-0.mp4", "$date-output.mp4") ffmpeg("ffmpeg -f concat -safe 0 -i $date.txt -c copy $newPath") list.forEach { it.delete() } diff --git a/src/main/kotlin/me/kuku/telegram/logic/V2exLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/V2exLogic.kt index 3b07c5a..4ff741f 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/V2exLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/V2exLogic.kt @@ -4,8 +4,8 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.JsonNode import io.ktor.client.call.* import io.ktor.client.request.* -import me.kuku.utils.client -import me.kuku.utils.convertValue +import me.kuku.telegram.utils.client +import me.kuku.telegram.utils.convertValue object V2exLogic { diff --git a/src/main/kotlin/me/kuku/telegram/logic/WeiboLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/WeiboLogic.kt index eee947b..2d4a78f 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/WeiboLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/WeiboLogic.kt @@ -6,24 +6,25 @@ import com.fasterxml.jackson.databind.node.ObjectNode import com.fasterxml.jackson.module.kotlin.contains import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.request.forms.* +import io.ktor.client.statement.* import io.ktor.http.* -import me.kuku.pojo.CommonResult -import me.kuku.pojo.UA import me.kuku.telegram.entity.WeiboEntity import me.kuku.telegram.exception.qrcodeExpire import me.kuku.telegram.exception.qrcodeNotScanned import me.kuku.telegram.exception.qrcodeScanned -import me.kuku.utils.* +import me.kuku.telegram.utils.* import org.jsoup.Jsoup object WeiboLogic { - suspend fun getIdByName(name: String, page: Int = 1): CommonResult> { + suspend fun getIdByName(name: String, page: Int = 1): List { val newName = name.toUrlEncode() - val response = OkHttpKtUtils.get("https://m.weibo.cn/api/container/getIndex?containerid=100103type%3D3%26q%3D$newName%26t%3D0&page_type=searchall&page=$page", - OkUtils.referer("https://m.weibo.cn/search?containerid=100103type%3D1%26q%3D$newName")) - return if (response.code == 200) { - val jsonNode = OkUtils.json(response) + val response = client.get("https://m.weibo.cn/api/container/getIndex?containerid=100103type%3D3%26q%3D$newName%26t%3D0&page_type=searchall&page=$page") { + referer("https://m.weibo.cn/search?containerid=100103type%3D1%26q%3D$newName") + } + return if (response.status == HttpStatusCode.OK) { + val jsonNode = response.body() val cardsJsonArray = jsonNode.get("data").get("cards") var jsonArray: JsonNode? = null for (obj in cardsJsonArray) { @@ -33,7 +34,7 @@ object WeiboLogic { break } } - if (jsonArray == null) return CommonResult.failure("没有找到该用户") + if (jsonArray == null) error("没有找到该用户") val list = mutableListOf() for (obj in jsonArray) { if (obj.has("user") || obj.has("users")) { @@ -41,34 +42,34 @@ object WeiboLogic { if (userJsonNode != null) { val username = userJsonNode.get("name")?.asText() ?: userJsonNode.get("screen_name").asText() - list.add(WeiboPojo(name = username, userid = userJsonNode.getString("id"))) + list.add(WeiboPojo(name = username, userid = userJsonNode["id"].asText())) } else { val usersJsonArray = obj.get("users") for (any in usersJsonArray) { val username = any.get("name")?.asText() - ?: any.getString("screen_name") - list.add(WeiboPojo(name = username, userid = any.getString("id"))) + ?: any["screen_name"].asText() + list.add(WeiboPojo(name = username, userid = any["id"].asText())) } } } } - if (list.isEmpty()) CommonResult.failure("未找到该用户") - else CommonResult.success(list) - } else CommonResult.failure("查询失败,请稍后再试!") + if (list.isEmpty()) error("未找到该用户") + else list + } else error("查询失败,请稍后再试!") } private fun convert(jsonNode: JsonNode): WeiboPojo { val weiboPojo = WeiboPojo() val userJsonNode = jsonNode.get("user") - weiboPojo.id = jsonNode.getLong("id") - weiboPojo.name = userJsonNode.getString("screen_name") - weiboPojo.created = jsonNode.getString("created_at") - weiboPojo.text = Jsoup.parse(jsonNode.getString("text")).text() - weiboPojo.bid = jsonNode.getString("bid") - weiboPojo.userid = userJsonNode.getString("id") + weiboPojo.id = jsonNode["id"].asLong() + weiboPojo.name = userJsonNode["screen_name"].asText() + weiboPojo.created = jsonNode["created_at"].asText() + weiboPojo.text = Jsoup.parse(jsonNode["text"].asText()).text() + weiboPojo.bid = jsonNode["bid"].asText() + weiboPojo.userid = userJsonNode["id"].asText() weiboPojo.ipFrom = jsonNode["region_name"]?.asText()?.split(" ")?.get(1) ?: "无" weiboPojo.url = "https://m.weibo.cn/status/${weiboPojo.bid}" - val picNum = jsonNode.getInteger("pic_num") + val picNum = jsonNode["pic_num"].asInt() if (picNum != 0) { val list = weiboPojo.imageUrl val pics = jsonNode["pics"] @@ -90,13 +91,13 @@ object WeiboLogic { if (jsonNode.contains("retweeted_status")) { val forwardJsonNode = jsonNode.get("retweeted_status") weiboPojo.isForward = true - weiboPojo.forwardId = forwardJsonNode.getString("id") - weiboPojo.forwardTime = forwardJsonNode.getString("created_at") + weiboPojo.forwardId = forwardJsonNode["id"].asText() + weiboPojo.forwardTime = forwardJsonNode["created_at"].asText() val forwardUserJsonNode = forwardJsonNode.get("user") val name = forwardUserJsonNode?.get("screen_name")?.asText() ?: "原微博删除" weiboPojo.forwardName = name - weiboPojo.forwardText = Jsoup.parse(forwardJsonNode.getString("text")).text() - weiboPojo.forwardBid = forwardJsonNode.getString("bid") + weiboPojo.forwardText = Jsoup.parse(forwardJsonNode["text"].asText()).text() + weiboPojo.forwardBid = forwardJsonNode["bid"].asText() weiboPojo.forwardIpFrom = forwardJsonNode["region_name"]?.asText()?.split(" ")?.get(1) ?: "无" forwardJsonNode["pic_ids"]?.forEach { val id = it.asText() @@ -128,10 +129,10 @@ object WeiboLogic { return sb.toString() } - suspend fun getWeiboById(id: String): CommonResult> { - val response = OkHttpKtUtils.get("https://m.weibo.cn/api/container/getIndex?type=uid&uid=$id&containerid=107603$id") - return if (response.code == 200) { - val jsonNode = OkUtils.json(response) + suspend fun getWeiboById(id: String): List { + val response = client.get("https://m.weibo.cn/api/container/getIndex?type=uid&uid=$id&containerid=107603$id") + return if (response.status == HttpStatusCode.OK) { + val jsonNode = response.body() val cardJsonArray = jsonNode.get("data").get("cards") val list = mutableListOf() for (any in cardJsonArray) { @@ -139,8 +140,8 @@ object WeiboLogic { if (1 == any.get("isTop")?.asInt()) continue list.add(convert(blogJsonNode)) } - CommonResult.success(list) - } else CommonResult.failure("查询失败,请稍后重试!") + list + } else error("查询失败,请稍后重试!") } suspend fun login1(): WeiboQrcode { @@ -173,28 +174,29 @@ object WeiboLogic { val fourResponse = client.get(fourUrl) { referer("https://passport.weibo.com/") } val fiveUrl = fourResponse.headers["location"]!! val fiveResponse = client.get(fiveUrl) { referer("https://passport.weibo.com/") } - WeiboEntity().also { it.cookie = fiveResponse.cookie() } + WeiboEntity().also { it.cookie = fiveResponse.setCookie().renderCookieHeader() } } else -> qrcodeExpire() } } - suspend fun friendWeibo(weiboEntity: WeiboEntity): CommonResult> { - val str = OkHttpKtUtils.getStr("https://m.weibo.cn/feed/friends?", - OkUtils.cookie(weiboEntity.cookie)) + suspend fun friendWeibo(weiboEntity: WeiboEntity): List { + val str = client.get("https://m.weibo.cn/feed/friends?") { + cookieString(weiboEntity.cookie) + }.bodyAsText() return if ("" != str) { val jsonArray = kotlin.runCatching { - Jackson.parse(str).get("data").get("statuses") + Jackson.readTree(str).get("data").get("statuses") }.onFailure { - return CommonResult.failure("查询微博失败,请稍后再试!!", null) + error("查询微博失败,请稍后再试!!") }.getOrNull()!! val list = mutableListOf() for (any in jsonArray) { list.add(convert(any)) } - CommonResult.success(list) - } else CommonResult.failure("您的cookie已失效,请重新绑定微博") + list + } else error("您的cookie已失效,请重新绑定微博") } private suspend fun group(weiboEntity: WeiboEntity): List { @@ -310,57 +312,63 @@ object WeiboLogic { return jsonNode["data"]?.get("longTextContent")?.asText() ?: "" } - suspend fun myWeibo(weiboEntity: WeiboEntity): CommonResult> { - val jsonNode = OkHttpKtUtils.getJson("https://m.weibo.cn/profile/info", - OkUtils.cookie(weiboEntity.cookie)) - return if (jsonNode.getInteger("ok") == 1) { + suspend fun myWeibo(weiboEntity: WeiboEntity): List { + val jsonNode = client.get("https://m.weibo.cn/profile/info") { + cookieString(weiboEntity.cookie) + }.body() + return if (jsonNode["ok"].asInt() == 1) { val jsonArray = jsonNode.get("data").get("statuses") val list = mutableListOf() for (any in jsonArray) { list.add(convert(any)) } - CommonResult.success(list) - } else CommonResult.failure("您的cookie已失效,请重新绑定微博") + list + } else error("您的cookie已失效,请重新绑定微博") } private suspend fun getToken(weiboEntity: WeiboEntity): WeiboToken { - val response = OkHttpKtUtils.get("https://m.weibo.cn/api/config", - OkUtils.cookie(weiboEntity.cookie)) - val jsonNode = OkUtils.json(response).get("data") - return if (jsonNode.getBoolean("login")) { - val cookie = OkUtils.cookie(response) - WeiboToken(jsonNode.getString("st"), cookie + weiboEntity.cookie) + val response = client.get("https://m.weibo.cn/api/config") { + cookieString(weiboEntity.cookie) + } + val jsonNode = response.body().get("data") + return if (jsonNode["login"].asBoolean()) { + val cookie = response.setCookie().renderCookieHeader() + WeiboToken(jsonNode["st"].asText(), cookie + weiboEntity.cookie) } else error("cookie已失效") } - suspend fun superTalkSign(weiboEntity: WeiboEntity): CommonResult { + suspend fun superTalkSign(weiboEntity: WeiboEntity) { val weiboToken = getToken(weiboEntity) - val response = OkHttpKtUtils.get("https://m.weibo.cn/api/container/getIndex?containerid=100803_-_followsuper&luicode=10000011&lfid=231093_-_chaohua", - mapOf("cookie" to weiboToken.cookie, "x-xsrf-token" to weiboToken.token) - ) - if (response.code != 200) return CommonResult.failure("cookie已失效") - val cookie = OkUtils.cookie(response) - val jsonNode = OkUtils.json(response) - return if (jsonNode.getInteger("ok") == 1) { + val response = client.get("https://m.weibo.cn/api/container/getIndex?containerid=100803_-_followsuper&luicode=10000011&lfid=231093_-_chaohua") { + cookieString(weiboToken.cookie) + headers { append("x-xsrf-token", weiboToken.token) } + } + if (response.status != HttpStatusCode.OK) error("cookie已失效") + val cookie = response.setCookie().renderCookieHeader() + val jsonNode = response.body() + if (jsonNode["ok"].asInt() == 1) { val cardsJsonArray = jsonNode.get("data").get("cards").get(0).get("card_group") for (any in cardsJsonArray) { if (any.contains("buttons")) { val buttonJsonArray = any.get("buttons") for (bu in buttonJsonArray) { - if (bu.getString("name") == "签到") { - val scheme = "https://m.weibo.cn${bu.getString("scheme")}" - OkHttpKtUtils.postJson(scheme, - mapOf("st" to weiboToken.token, "_spr" to "screen:393x851"), - mapOf("x-xsrf-token" to weiboToken.token, "cookie" to weiboToken.cookie + cookie, - "referer" to "https://m.weibo.cn/p/tabbar?containerid=100803_-_followsuper&luicode=10000011&lfid=231093_-_chaohua&page_type=tabbar", - "user-agent" to UA.PC.value, "mweibo-pwa" to "1") - ) + if (bu["name"].asText() == "签到") { + val scheme = "https://m.weibo.cn${bu["scheme"].asText()}" + client.submitForm(scheme, parameters { + append("st", weiboToken.token) + append("_spr", "screen:393x851") + }) { + headers { + mapOf("x-xsrf-token" to weiboToken.token, "cookie" to weiboToken.cookie + cookie, + "referer" to "https://m.weibo.cn/p/tabbar?containerid=100803_-_followsuper&luicode=10000011&lfid=231093_-_chaohua&page_type=tabbar", + "mweibo-pwa" to "1").forEach { append(it.key, it.value) } + } + } } } } } - CommonResult.success() - } else CommonResult.failure("获取关注超话列表失败") + } else error("获取关注超话列表失败") } } diff --git a/src/main/kotlin/me/kuku/telegram/logic/YgoLogic.kt b/src/main/kotlin/me/kuku/telegram/logic/YgoLogic.kt index 7590bac..d0c2c01 100644 --- a/src/main/kotlin/me/kuku/telegram/logic/YgoLogic.kt +++ b/src/main/kotlin/me/kuku/telegram/logic/YgoLogic.kt @@ -1,7 +1,9 @@ package me.kuku.telegram.logic -import me.kuku.utils.OkHttpKtUtils -import me.kuku.utils.toUrlEncode +import io.ktor.client.request.* +import io.ktor.client.statement.* +import me.kuku.telegram.utils.client +import me.kuku.telegram.utils.toUrlEncode import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.TextNode @@ -12,7 +14,7 @@ import org.springframework.stereotype.Service class YgoLogic { suspend fun search(name: String): List { - val str = OkHttpKtUtils.getStr("https://ygocdb.com/?search=${name.toUrlEncode()}") + val str = client.get("https://ygocdb.com/?search=${name.toUrlEncode()}").bodyAsText() val elements = Jsoup.parse(str).select(".card") val list = mutableListOf() for (element in elements) { @@ -35,7 +37,7 @@ class YgoLogic { suspend fun searchDetail(id: Long): Card { val url = "https://ygocdb.com/card/$id" - val str = OkHttpKtUtils.getStr(url) + val str = client.get(url).bodyAsText() val document = Jsoup.parse(str) val imageUrl = document.select(".cardimg img").first()!!.attr("src") val spans = document.select(".detail .names span").filter { it -> it.attributes().isEmpty } diff --git a/src/main/kotlin/me/kuku/telegram/scheduled/BiliBilliScheduled.kt b/src/main/kotlin/me/kuku/telegram/scheduled/BiliBilliScheduled.kt index 720d563..94a349a 100644 --- a/src/main/kotlin/me/kuku/telegram/scheduled/BiliBilliScheduled.kt +++ b/src/main/kotlin/me/kuku/telegram/scheduled/BiliBilliScheduled.kt @@ -14,7 +14,7 @@ import me.kuku.telegram.entity.* import me.kuku.telegram.logic.BiliBiliLogic import me.kuku.telegram.logic.BiliBiliPojo import me.kuku.telegram.context.sendPic -import me.kuku.utils.client +import me.kuku.telegram.utils.client import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component @@ -95,8 +95,7 @@ class BiliBilliScheduled( kotlin.runCatching { val tgId = biliBiliEntity.tgId delay(3000) - val result = BiliBiliLogic.friendDynamic(biliBiliEntity) - val list = result.data ?: return@runCatching + val list = BiliBiliLogic.friendDynamic(biliBiliEntity) val newList = mutableListOf() if (userMap.containsKey(tgId)) { val oldId = userMap[tgId]!! diff --git a/src/main/kotlin/me/kuku/telegram/scheduled/ConfigScheduled.kt b/src/main/kotlin/me/kuku/telegram/scheduled/ConfigScheduled.kt index 22b8430..1ab79c0 100644 --- a/src/main/kotlin/me/kuku/telegram/scheduled/ConfigScheduled.kt +++ b/src/main/kotlin/me/kuku/telegram/scheduled/ConfigScheduled.kt @@ -6,15 +6,17 @@ import com.pengrad.telegrambot.request.SendMessage import com.pengrad.telegrambot.request.SendVideo import io.ktor.client.call.* import io.ktor.client.request.* +import io.ktor.client.statement.* import kotlinx.coroutines.delay import me.kuku.telegram.context.asyncExecute import me.kuku.telegram.entity.ConfigService import me.kuku.telegram.entity.Status import me.kuku.telegram.logic.ToolLogic -import me.kuku.utils.DateTimeFormatterUtils -import me.kuku.utils.client +import me.kuku.telegram.utils.client import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter import java.util.concurrent.TimeUnit @Component @@ -28,7 +30,7 @@ class ConfigScheduled( suspend fun positiveEnergyPush() { val entityList = configService.findByPositiveEnergy(Status.ON) if (entityList.isEmpty()) return - val time = DateTimeFormatterUtils.formatNow("yyyyMMdd") + val time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) val file = toolLogic.positiveEnergy(time) try { for (configEntity in entityList) { diff --git a/src/main/kotlin/me/kuku/telegram/scheduled/DouYuScheduled.kt b/src/main/kotlin/me/kuku/telegram/scheduled/DouYuScheduled.kt index 576f4b6..187af63 100644 --- a/src/main/kotlin/me/kuku/telegram/scheduled/DouYuScheduled.kt +++ b/src/main/kotlin/me/kuku/telegram/scheduled/DouYuScheduled.kt @@ -13,8 +13,7 @@ import me.kuku.telegram.logic.DouYuFish import me.kuku.telegram.logic.DouYuLogic import me.kuku.telegram.context.sendPic import me.kuku.telegram.context.sendTextMessage -import me.kuku.utils.JobManager -import me.kuku.utils.client +import me.kuku.telegram.utils.client import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component @@ -41,10 +40,8 @@ class DouYuScheduled( val list = douYuService.findByLive(Status.ON) for (douYuEntity in list) { kotlin.runCatching { - val baseResult = douYuLogic.room(douYuEntity) delay(3000) - if (baseResult.failure()) return@runCatching - val rooms = baseResult.data() + val rooms = douYuLogic.room(douYuEntity) val tgId = douYuEntity.tgId if (!douYuLiveMap.containsKey(tgId)) douYuLiveMap[tgId] = mutableMapOf() val map = douYuLiveMap[tgId]!! @@ -78,13 +75,12 @@ class DouYuScheduled( val list = douYuService.findByTitleChange(Status.ON) for (douYuEntity in list) { kotlin.runCatching { - val baseResult = douYuLogic.room(douYuEntity) + val data = douYuLogic.room(douYuEntity) delay(3000) - if (baseResult.failure()) return@runCatching val tgId = douYuEntity.tgId if (!douYuTitleMap.containsKey(tgId)) douYuTitleMap[tgId] = mutableMapOf() val map = douYuTitleMap[tgId]!! - for (room in baseResult.data()) { + for (room in data) { val name = room.name val roomId = room.roomId val value = map[roomId] @@ -136,9 +132,9 @@ class DouYuScheduled( for (douYuFish in newList) { val text = "#斗鱼鱼吧动态推送\n#${douYuFish.nickname}\n标题:${douYuFish.title}\n内容:${douYuFish.content}\n链接:${douYuFish.url}" if (douYuFish.image.isNotEmpty()) { - JobManager.delay(1000 * 60) { +// JobManager.delay(1000 * 60) { telegramBot.sendPic(tgId, text, douYuFish.image) - } +// } } else { telegramBot.asyncExecute(SendMessage(tgId, text)) } diff --git a/src/main/kotlin/me/kuku/telegram/scheduled/EpicScheduled.kt b/src/main/kotlin/me/kuku/telegram/scheduled/EpicScheduled.kt index fb202a7..34f79d7 100644 --- a/src/main/kotlin/me/kuku/telegram/scheduled/EpicScheduled.kt +++ b/src/main/kotlin/me/kuku/telegram/scheduled/EpicScheduled.kt @@ -10,14 +10,14 @@ import io.ktor.util.logging.* import me.kuku.telegram.context.sendPic import me.kuku.telegram.entity.ConfigService import me.kuku.telegram.entity.Status -import me.kuku.utils.DateTimeFormatterUtils -import me.kuku.utils.MyUtils -import me.kuku.utils.client -import me.kuku.utils.toJsonNode +import me.kuku.telegram.utils.client +import me.kuku.telegram.utils.toJsonNode import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component +import java.time.LocalDateTime import java.time.ZoneOffset +import java.time.format.DateTimeFormatter import java.util.concurrent.TimeUnit @Component @@ -39,8 +39,7 @@ class EpicScheduled( val promotion = element["promotions"]?.get("promotionalOffers")?.get(0)?.get("promotionalOffers")?.get(0) ?: element["promotions"]?.get("upcomingPromotionalOffers")?.get(0)?.get("promotionalOffers")?.get(0) ?: continue val startDate = promotion["startDate"].asText().replace(".000Z", "") - val startTimeStamp = DateTimeFormatterUtils.parseToLocalDateTime(startDate, "yyyy-MM-dd'T'HH:mm:ss") - .toInstant(ZoneOffset.of("+0")).toEpochMilli() + val startTimeStamp = LocalDateTime.parse(startDate, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")).toInstant(ZoneOffset.ofHours(8)).toEpochMilli() val nowTimeStamp = System.currentTimeMillis() val diff = nowTimeStamp - startTimeStamp if (diff < 1000 * 60 * 60 && diff > 0) { @@ -50,7 +49,7 @@ class EpicScheduled( val html = client.get("https://store.epicgames.com/zh-CN/p/$slug").bodyAsText() val queryJsonNode = - MyUtils.regex("window\\.__REACT_QUERY_INITIAL_QUERIES__\\s*=\\s*(\\{.*\\});", html)?.substring(41)?.dropLast(1)?.toJsonNode() ?: continue + "window\\.__REACT_QUERY_INITIAL_QUERIES__\\s*=\\s*(\\{.*});".toRegex().find(html)?.value?.substring(41)?.dropLast(1)?.toJsonNode() ?: continue val queries = queryJsonNode["queries"] val mappings = queries.filter { it["queryKey"]?.get(0)?.asText() == "getCatalogOffer" } for (mapping in mappings) { diff --git a/src/main/kotlin/me/kuku/telegram/scheduled/HuYaScheduled.kt b/src/main/kotlin/me/kuku/telegram/scheduled/HuYaScheduled.kt index 230b478..f7c6831 100644 --- a/src/main/kotlin/me/kuku/telegram/scheduled/HuYaScheduled.kt +++ b/src/main/kotlin/me/kuku/telegram/scheduled/HuYaScheduled.kt @@ -10,7 +10,7 @@ import me.kuku.telegram.context.asyncExecute import me.kuku.telegram.entity.* import me.kuku.telegram.logic.HuYaLogic import me.kuku.telegram.context.sendTextMessage -import me.kuku.utils.client +import me.kuku.telegram.utils.client import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component @@ -33,9 +33,7 @@ class HuYaScheduled( for (huYaEntity in list) { kotlin.runCatching { delay(3000) - val baseResult = huYaLogic.live(huYaEntity) - if (baseResult.failure()) return@runCatching - val lives = baseResult.data() + val lives = huYaLogic.live(huYaEntity) val tgId = huYaEntity.tgId if (!huYaLiveMap.containsKey(tgId)) huYaLiveMap[tgId] = mutableMapOf() val map = huYaLiveMap[tgId]!! diff --git a/src/main/kotlin/me/kuku/telegram/scheduled/LeiShenScheduled.kt b/src/main/kotlin/me/kuku/telegram/scheduled/LeiShenScheduled.kt index 8f2c777..b91e7d5 100644 --- a/src/main/kotlin/me/kuku/telegram/scheduled/LeiShenScheduled.kt +++ b/src/main/kotlin/me/kuku/telegram/scheduled/LeiShenScheduled.kt @@ -39,6 +39,8 @@ class LeiShenScheduled( leiShenService.save(entity) } } catch (e: Exception) { + entity.status = Status.OFF + leiShenService.save(entity) telegramBot.sendTextMessage(entity.tgId, """ #雷神加速器登录失败提醒 您的雷神加速器cookie已失效,重新登录失败,原因:${e.message} diff --git a/src/main/kotlin/me/kuku/telegram/scheduled/StepScheduled.kt b/src/main/kotlin/me/kuku/telegram/scheduled/StepScheduled.kt index 4f3b85d..8265462 100644 --- a/src/main/kotlin/me/kuku/telegram/scheduled/StepScheduled.kt +++ b/src/main/kotlin/me/kuku/telegram/scheduled/StepScheduled.kt @@ -4,9 +4,9 @@ import kotlinx.coroutines.delay import me.kuku.telegram.entity.* import me.kuku.telegram.logic.LeXinStepLogic import me.kuku.telegram.logic.XiaomiStepLogic -import me.kuku.utils.MyUtils import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component +import kotlin.random.Random @Component class StepScheduled( @@ -20,7 +20,7 @@ class StepScheduled( for (stepEntity in list) { var step = stepEntity.step if (stepEntity.offset == Status.ON) { - step = MyUtils.randomInt(step - 1000, step + 1000) + step = Random.nextInt(step - 1000, step + 1000) } logService.log(stepEntity, LogType.Step) { XiaomiStepLogic.modifyStepCount(stepEntity, step) diff --git a/src/main/kotlin/me/kuku/telegram/scheduled/WeiboScheduled.kt b/src/main/kotlin/me/kuku/telegram/scheduled/WeiboScheduled.kt index f8022f2..2c083b1 100644 --- a/src/main/kotlin/me/kuku/telegram/scheduled/WeiboScheduled.kt +++ b/src/main/kotlin/me/kuku/telegram/scheduled/WeiboScheduled.kt @@ -12,7 +12,7 @@ import me.kuku.telegram.logic.WeiboLogic import me.kuku.telegram.logic.WeiboPojo import me.kuku.telegram.context.sendPic import me.kuku.telegram.context.sendTextMessage -import me.kuku.utils.client +import me.kuku.telegram.utils.client import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component diff --git a/src/main/kotlin/me/kuku/telegram/utils/EncryptUtils.kt b/src/main/kotlin/me/kuku/telegram/utils/EncryptUtils.kt new file mode 100644 index 0000000..c9a9ddf --- /dev/null +++ b/src/main/kotlin/me/kuku/telegram/utils/EncryptUtils.kt @@ -0,0 +1,37 @@ +package me.kuku.telegram.utils + +import java.net.URLDecoder +import java.net.URLEncoder +import java.security.KeyFactory +import java.security.MessageDigest +import java.security.spec.X509EncodedKeySpec +import java.util.* +import javax.crypto.Cipher + +fun String.md5(): String { + val bytes = MessageDigest.getInstance("MD5").digest(this.toByteArray()) + return bytes.joinToString("") { "%02x".format(it) } +} + +fun String.toUrlDecode(): String { + return URLDecoder.decode(this, "UTF-8") +} + +fun String.toUrlEncode(): String { + return URLEncoder.encode(this, "UTF-8") +} + +fun String.rsaEncrypt(publicKeyStr: String): String { + val keyFactory = KeyFactory.getInstance("RSA") + val decodedKey = Base64.getDecoder().decode(publicKeyStr.toByteArray()) + val keySpec = X509EncodedKeySpec(decodedKey) + val publicKey = keyFactory.generatePublic(keySpec) + val cipher = Cipher.getInstance("RSA") + cipher.init(Cipher.ENCRYPT_MODE, publicKey) + val encryptedBytes = cipher.doFinal(this.toByteArray(Charsets.UTF_8)) + return Base64.getEncoder().encodeToString(encryptedBytes) +} + +fun ByteArray.hex(): String { + return this.joinToString("") { "%02x".format(it) } +} \ No newline at end of file diff --git a/src/main/kotlin/me/kuku/telegram/utils/FuntionUtils.kt b/src/main/kotlin/me/kuku/telegram/utils/FuntionUtils.kt index 24ea203..b02532d 100644 --- a/src/main/kotlin/me/kuku/telegram/utils/FuntionUtils.kt +++ b/src/main/kotlin/me/kuku/telegram/utils/FuntionUtils.kt @@ -9,12 +9,11 @@ import io.ktor.client.call.* import io.ktor.client.request.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import me.kuku.utils.DateTimeFormatterUtils -import me.kuku.utils.client import java.io.BufferedReader import java.io.ByteArrayOutputStream import java.io.InputStreamReader import java.time.LocalDateTime +import java.time.format.DateTimeFormatter suspend fun ffmpeg(command: String) { val runtime = Runtime.getRuntime() @@ -59,9 +58,9 @@ suspend fun githubCommit(): List { val message = commit["message"].asText() val dateStr = commit["committer"]["date"].asText() .replace("T", " ").replace("Z", "") - val zero = DateTimeFormatterUtils.parseToLocalDateTime(dateStr, "yyyy-MM-dd HH:mm:ss") + val zero = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) val right = zero.plusHours(8) - val date = DateTimeFormatterUtils.format(right, "yyyy-MM-dd HH:mm:ss") + val date = right.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) list.add(GithubCommit(date, message, right)) } return list diff --git a/src/main/kotlin/me/kuku/telegram/utils/Jackson.kt b/src/main/kotlin/me/kuku/telegram/utils/Jackson.kt new file mode 100644 index 0000000..b63879e --- /dev/null +++ b/src/main/kotlin/me/kuku/telegram/utils/Jackson.kt @@ -0,0 +1,59 @@ +package me.kuku.telegram.utils + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.node.ArrayNode +import com.fasterxml.jackson.databind.node.ObjectNode +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.kotlinModule + +object Jackson { + + var objectMapper: ObjectMapper = ObjectMapper() + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) + .registerModules(JavaTimeModule(), kotlinModule()) + + + fun readTree(json: String): JsonNode { + return objectMapper.readTree(json) + } + + fun createObjectNode(): ObjectNode { + return objectMapper.createObjectNode() + } + + fun createArrayNode(): ArrayNode { + return objectMapper.createArrayNode() + } + + inline fun convertValue(jsonNode: JsonNode): T { + return objectMapper.convertValue(jsonNode, object: TypeReference() {}) + } + + fun writeValueAsString(any: Any): String { + return objectMapper.writeValueAsString(any) + } + +} + +fun String.toJsonNode(): JsonNode { + return Jackson.readTree(this) +} + +fun String.jsonpToJsonNode(): JsonNode { + return """\{(?:[^{}]|\{[^{}]*})*}""".toRegex().find(this)?.value?.toJsonNode() ?: error("json not found") +} + +inline fun JsonNode.convertValue(): T { + return Jackson.convertValue(this) +} + diff --git a/src/main/kotlin/me/kuku/telegram/utils/KtorClient.kt b/src/main/kotlin/me/kuku/telegram/utils/KtorClient.kt new file mode 100644 index 0000000..2c81cab --- /dev/null +++ b/src/main/kotlin/me/kuku/telegram/utils/KtorClient.kt @@ -0,0 +1,43 @@ +package me.kuku.telegram.utils + +import io.ktor.client.* +import io.ktor.client.engine.okhttp.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.logging.* +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.serialization.jackson.* + +val client by lazy { + HttpClient(OkHttp) { + engine { + config { + followRedirects(false) + } + } + + followRedirects = false + + install(ContentNegotiation) { + jackson() + } + + install(Logging) + + } +} + +fun HttpMessageBuilder.cookieString(content: String): Unit = headers.set(HttpHeaders.Cookie, content) + +fun HttpMessageBuilder.origin(content: String): Unit = headers.set(HttpHeaders.Origin, content) + +fun HttpMessageBuilder.referer(content: String): Unit = headers.set(HttpHeaders.Referrer, content) + +fun List.renderCookieHeader(): String { + return this.filterNot { it.value == "deleted" }.joinToString("") { "${it.name}=${it.value}; " } +} + +fun HttpRequestBuilder.setJsonBody(content: Any) { + contentType(ContentType.Application.Json) + setBody(content) +} \ No newline at end of file diff --git a/src/main/kotlin/me/kuku/telegram/utils/RandomUtils.kt b/src/main/kotlin/me/kuku/telegram/utils/RandomUtils.kt new file mode 100644 index 0000000..50af8b2 --- /dev/null +++ b/src/main/kotlin/me/kuku/telegram/utils/RandomUtils.kt @@ -0,0 +1,20 @@ +package me.kuku.telegram.utils + +import kotlin.random.Random + +object RandomUtils { + + fun num(num: Int): String { + return (1..num) + .map { Random.nextInt(0, 10) } // 生成单个数字 + .joinToString("") + } + + fun letter(length: Int): String { + val chars = ('a'..'z') + ('A'..'Z') // 包括大小写字母 + return (1..length) + .map { chars.random() } + .joinToString("") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/me/kuku/telegram/utils/RegexUtils.kt b/src/main/kotlin/me/kuku/telegram/utils/RegexUtils.kt new file mode 100644 index 0000000..90d9f19 --- /dev/null +++ b/src/main/kotlin/me/kuku/telegram/utils/RegexUtils.kt @@ -0,0 +1,10 @@ +package me.kuku.telegram.utils + +object RegexUtils { + + fun extract(text: String, start: String, end: String): String? { + val pattern = "$start(.*?)$end".toRegex() + return pattern.find(text)?.groupValues?.get(1) + } + +} \ No newline at end of file