Merge branch 'master' of github.com:whitechi73/OpenShamrock

This commit is contained in:
ikechan8370 2023-11-21 23:39:26 +08:00
commit 1593d973a0
15 changed files with 148 additions and 67 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: shamrock320 # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -24,7 +24,7 @@ android {
minSdk = 24 minSdk = 24
targetSdk = 33 targetSdk = 33
versionCode = (System.currentTimeMillis() / 1000).toInt() versionCode = (System.currentTimeMillis() / 1000).toInt()
versionName = "1.0.5-dev" + gitCommitHash() versionName = "1.0.6-dev" + gitCommitHash()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
@ -66,6 +66,7 @@ android {
create("app") { create("app") {
dimension = "mode" dimension = "mode"
ndk { ndk {
println("Full architecture and full compilation.")
abiFilters.add("arm64-v8a") abiFilters.add("arm64-v8a")
abiFilters.add("x86_64") abiFilters.add("x86_64")
} }
@ -73,12 +74,14 @@ android {
create("arm64") { create("arm64") {
dimension = "mode" dimension = "mode"
ndk { ndk {
println("Full compilation of arm64 architecture")
abiFilters.add("arm64-v8a") abiFilters.add("arm64-v8a")
} }
} }
create("x64") { create("x64") {
dimension = "mode" dimension = "mode"
ndk { ndk {
println("Full compilation of x64 architecture")
abiFilters.add("x86_64") abiFilters.add("x86_64")
} }
} }

View File

@ -1,7 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
/* compiled from: P */
/* loaded from: classes2.dex */
public final class GroupFileCommonResult { public final class GroupFileCommonResult {
String clientWording; String clientWording;
int retCode; int retCode;
@ -29,8 +27,6 @@ public final class GroupFileCommonResult {
} }
public GroupFileCommonResult(int i2, String str, String str2) { public GroupFileCommonResult(int i2, String str, String str2) {
this.retMsg = "";
this.clientWording = "";
this.retCode = i2; this.retCode = i2;
this.retMsg = str; this.retMsg = str;
this.clientWording = str2; this.clientWording = str2;

View File

@ -1,5 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
public interface IDeleteGroupFileCallback { public interface IDeleteGroupFileCallback {
void onResult(int i2, String str, DeleteGroupFileResult deleteGroupFileResult); void onResult(int code, String why, DeleteGroupFileResult result);
} }

View File

@ -9,7 +9,7 @@ public interface IKernelRichMediaService {
void cancelTransferTask(Contact contact, ArrayList<Long> arrayList, ArrayList<Integer> arrayList2, IOperateTransferInfoCallback iOperateTransferInfoCallback); void cancelTransferTask(Contact contact, ArrayList<Long> arrayList, ArrayList<Integer> arrayList2, IOperateTransferInfoCallback iOperateTransferInfoCallback);
void deleteGroupFile(long j2, String str, int i2, IDeleteGroupFileCallback iDeleteGroupFileCallback); void deleteGroupFile(long groupCode, String fileUid, int bizId, IDeleteGroupFileCallback cb);
void deleteTransferInfo(Contact contact, ArrayList<Long> arrayList, IOperateTransferInfoCallback iOperateTransferInfoCallback); void deleteTransferInfo(Contact contact, ArrayList<Long> arrayList, IOperateTransferInfoCallback iOperateTransferInfoCallback);

View File

@ -11,6 +11,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.proto.protobufOf import moe.fuqiuluo.proto.protobufOf
import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.utils.PlatformUtils
@ -37,9 +38,9 @@ internal abstract class BaseSvc {
} }
suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false, timeout: Long = 5000L): ByteArray? { suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false, timeout: Long = 5000L): ByteArray? {
return withTimeoutOrNull(timeout) {
suspendCoroutine { continuation ->
val seq = MsfCore.getNextSeq() val seq = MsfCore.getNextSeq()
return withTimeoutOrNull(timeout) {
suspendCancellableCoroutine { continuation ->
GlobalScope.launch(Dispatchers.Default) { GlobalScope.launch(Dispatchers.Default) {
DynamicReceiver.register(IPCRequest(cmd, seq) { DynamicReceiver.register(IPCRequest(cmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!! val buffer = it.getByteArrayExtra("buffer")!!
@ -49,13 +50,16 @@ internal abstract class BaseSvc {
if (trpc) sendTrpcOidb(cmd, cmdId, serviceId, data, seq) if (trpc) sendTrpcOidb(cmd, cmdId, serviceId, data, seq)
else sendOidb(cmd, cmdId, serviceId, data, seq) else sendOidb(cmd, cmdId, serviceId, data, seq)
} }
}.also {
if (it == null)
DynamicReceiver.unregister(seq)
}?.copyOf() }?.copyOf()
} }
suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray, timeout: Long = 5000L): ByteArray? { suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray, timeout: Long = 5000L): ByteArray? {
return withTimeoutOrNull<ByteArray?>(timeout) {
suspendCoroutine { continuation ->
val seq = MsfCore.getNextSeq() val seq = MsfCore.getNextSeq()
return withTimeoutOrNull<ByteArray?>(timeout) {
suspendCancellableCoroutine { continuation ->
GlobalScope.launch(Dispatchers.Default) { GlobalScope.launch(Dispatchers.Default) {
DynamicReceiver.register(IPCRequest(cmd, seq) { DynamicReceiver.register(IPCRequest(cmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!! val buffer = it.getByteArrayExtra("buffer")!!
@ -64,6 +68,9 @@ internal abstract class BaseSvc {
sendBuffer(cmd, isPb, data, seq) sendBuffer(cmd, isPb, data, seq)
} }
} }
}.also {
if (it == null)
DynamicReceiver.unregister(seq)
}?.copyOf() }?.copyOf()
} }

View File

@ -1,7 +1,12 @@
package moe.fuqiuluo.qqinterface.servlet package moe.fuqiuluo.qqinterface.servlet
import com.tencent.mobileqq.pb.ByteStringMicro import com.tencent.mobileqq.pb.ByteStringMicro
import com.tencent.qqnt.kernel.nativeinterface.DeleteGroupFileResult
import com.tencent.qqnt.kernel.nativeinterface.GroupFileCommonResult
import com.tencent.qqnt.kernel.nativeinterface.IDeleteGroupFileCallback
import io.ktor.util.Deflate import io.ktor.util.Deflate
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.fuqiuluo.proto.protobufOf import moe.fuqiuluo.proto.protobufOf
@ -12,8 +17,11 @@ import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.tools.toHexString import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.DeflateTools import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import tencent.im.oidb.cmd0x6d8.oidb_0x6d8 import tencent.im.oidb.cmd0x6d8.oidb_0x6d8
import tencent.im.oidb.oidb_sso import tencent.im.oidb.oidb_sso
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
internal object FileSvc: BaseSvc() { internal object FileSvc: BaseSvc() {
fun createFileFolder(groupId: String, folderName: String) { fun createFileFolder(groupId: String, folderName: String) {
@ -38,6 +46,21 @@ internal object FileSvc: BaseSvc() {
} }
fun deleteGroupFile(groupId: String, bizId: Int, fileUid: String) { fun deleteGroupFile(groupId: String, bizId: Int, fileUid: String) {
/*
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val richMediaService = sessionService.richMediaService
val result = withTimeoutOrNull(3000L) {
suspendCancellableCoroutine {
richMediaService.deleteGroupFile(groupId.toLong(), fileUid, bizId) { code, _, result ->
it.resume(code to result.result)
}
}
}
return if (result == null) Result.failure(RuntimeException("delete group file timeout")) else Result.success(result)*/
// 调用QQ内部实现会导致闪退
sendOidb("OidbSvc.0x6d6_3", 1750, 3, protobufOf( sendOidb("OidbSvc.0x6d6_3", 1750, 3, protobufOf(
4 to mapOf( 4 to mapOf(
1 to groupId.toLong(), 1 to groupId.toLong(),

View File

@ -143,13 +143,15 @@ internal sealed class MessageElemConverter: IMessageConvert {
element: MsgElement element: MsgElement
): MessageSegment { ): MessageSegment {
val video = element.videoElement val video = element.videoElement
val md5 = video.fileName.split(".")[0]
return MessageSegment( return MessageSegment(
type = "video", type = "video",
data = hashMapOf( data = hashMapOf(
"file" to video.fileName, "file" to video.fileName,
"url" to when(chatType) { "url" to when(chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", video.fileName, video.fileUuid) MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", video.fileName, video.fileUuid) MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
else -> unknownChatType(chatType) else -> unknownChatType(chatType)
} }
).also { ).also {

View File

@ -63,24 +63,30 @@ internal object RichProtoSvc: BaseSvc() {
suspend fun getC2CFileDownUrl( suspend fun getC2CFileDownUrl(
fileId: String, fileId: String,
subId: String, subId: String,
retryCnt: Int = 0
): String { ): String {
val uid = ContactHelper.getUidByUinAsync(app.currentUin.toLong()) val buffer = sendOidbAW("OidbSvc.0xe37_1200", 3639, 1200, protobufOf(
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xe37_1200", 3639, 1200, protobufOf(
1 to 1200, 1 to 1200,
2 to 1 /* QRoute.api(IAudioHelperApi::class.java).genDebugSeq().toInt() */, /* seq */ 2 to 1 /* QRoute.api(IAudioHelperApi::class.java).genDebugSeq().toInt() */, /* seq */
14 to mapOf( 14 to mapOf(
10 to uid, 10 to app.longAccountUin,
20 to fileId, 20 to fileId,
30 to 2, /* ver */ 30 to 2, /* ver */
60 to subId, 60 to subId,
601 to 0 601 to 0
), ),
101 to 3, 101 to 3, // uint32_business_id
102 to 104, /* client_type */ 102 to 104, /* client_type */
200 to 1, /* url_type */ 200 to 1, /* uint32_flag_support_mediaplatform */
99999 to 90200 to 1 99999 to mapOf(
).toByteArray(), trpc = true) 90200 to 1 // uint32_download_url_type
)
).toByteArray())
if (buffer == null) { if (buffer == null) {
if (retryCnt < 3) {
return getC2CFileDownUrl(fileId, subId, retryCnt + 1)
}
return "" return ""
} else { } else {
val body = oidb_sso.OIDBSSOPkg() val body = oidb_sso.OIDBSSOPkg()

View File

@ -14,10 +14,30 @@ internal object DeleteGroupFile: IActionHandler() {
return invoke(groupId, fileId, busid, session.echo) return invoke(groupId, fileId, busid, session.echo)
} }
/*
suspend operator fun invoke(
groupId: String,
fileId: String,
bizId: Int,
echo: JsonElement = EmptyJsonString
): String {
val result = FileSvc.deleteGroupFile(groupId, bizId, fileId)
if(result.isFailure) {
return error(result.exceptionOrNull()?.message ?: "删除群文件失败", echo)
}
val commonResult = result.getOrThrow()
if (commonResult.first != 0 || commonResult.second.retCode != 0) {
return error(commonResult.second.clientWording, echo)
}
return ok("成功", echo)
}
*/
operator fun invoke(groupId: String, fileId: String, bizId: Int, echo: JsonElement = EmptyJsonString): String { operator fun invoke(groupId: String, fileId: String, bizId: Int, echo: JsonElement = EmptyJsonString): String {
FileSvc.deleteGroupFile(groupId, bizId, fileId) FileSvc.deleteGroupFile(groupId, bizId, fileId)
return ok("成功", echo) return ok("成功", echo)
} }
override val requiredParams: Array<String> = arrayOf("group_id", "file_id", "busid")
override fun path(): String = "delete_group_file" override fun path(): String = "delete_group_file"
} }

View File

@ -17,10 +17,12 @@ import moe.fuqiuluo.shamrock.remote.config.ECHO_KEY
import moe.fuqiuluo.shamrock.remote.entries.EmptyObject import moe.fuqiuluo.shamrock.remote.entries.EmptyObject
import moe.fuqiuluo.shamrock.remote.entries.IndexData import moe.fuqiuluo.shamrock.remote.entries.IndexData
import moe.fuqiuluo.shamrock.remote.entries.Status import moe.fuqiuluo.shamrock.remote.entries.Status
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
import moe.fuqiuluo.shamrock.tools.fetchOrNull import moe.fuqiuluo.shamrock.tools.fetchOrNull
import moe.fuqiuluo.shamrock.tools.fetchOrThrow import moe.fuqiuluo.shamrock.tools.fetchOrThrow
import moe.fuqiuluo.shamrock.tools.fetchPostJsonElement import moe.fuqiuluo.shamrock.tools.fetchPostJsonElement
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObjectOrNull
import moe.fuqiuluo.shamrock.tools.isJsonArray import moe.fuqiuluo.shamrock.tools.isJsonArray
import moe.fuqiuluo.shamrock.tools.isJsonObject import moe.fuqiuluo.shamrock.tools.isJsonObject
import moe.fuqiuluo.shamrock.tools.isJsonString import moe.fuqiuluo.shamrock.tools.isJsonString
@ -55,7 +57,7 @@ fun Routing.echoVersion() {
} }
call.attributes.put(ECHO_KEY, echo) call.attributes.put(ECHO_KEY, echo)
val params = fetchPostJsonObject("params") val params = fetchPostJsonObjectOrNull("params") ?: EmptyJsonObject
val handler = ActionManager[action] val handler = ActionManager[action]
if (handler == null) { if (handler == null) {

View File

@ -69,8 +69,8 @@ internal class WebSocketService(host: String, port: Int): WebSocketTransmitServl
} }
val path = URI.create(handshake.resourceDescriptor).path val path = URI.create(handshake.resourceDescriptor).path
if (path != "/api") { if (path != "/api") {
pushMetaLifecycle()
eventReceivers.add(conn) eventReceivers.add(conn)
pushMetaLifecycle()
} }
LogCenter.log({ "WSServer连接(${conn.remoteSocketAddress.address.hostAddress}:${conn.remoteSocketAddress.port}$path)" }, Level.WARN) LogCenter.log({ "WSServer连接(${conn.remoteSocketAddress.address.hostAddress}:${conn.remoteSocketAddress.port}$path)" }, Level.WARN)
} }

View File

@ -97,7 +97,7 @@ internal abstract class WebSocketTransmitServlet(
} }
val action = actionObject["action"].asString val action = actionObject["action"].asString
val echo = actionObject["echo"] ?: EmptyJsonString val echo = actionObject["echo"] ?: EmptyJsonString
val params = actionObject["params"].asJsonObject val params = actionObject["params"].asJsonObjectOrNull ?: EmptyJsonObject
val handler = ActionManager[action] val handler = ActionManager[action]
handler?.handle(ActionSession(params, echo)) handler?.handle(ActionSession(params, echo))

View File

@ -55,7 +55,7 @@ internal object PrimitiveListener {
) return ) return
val msgType = pb[1, 2, 1].asInt val msgType = pb[1, 2, 1].asInt
var subType = 0 var subType = 0
if (pb.has(1, 2, 3)) { if (pb.has(1, 2, 3) && pb.has(1, 2, 2)) {
subType = pb[1, 2, 2].asInt subType = pb[1, 2, 2].asInt
} }
val msgTime = pb[1, 2, 6].asLong val msgTime = pb[1, 2, 6].asLong

View File

@ -20,13 +20,10 @@ import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import moe.fuqiuluo.shamrock.helper.ParamsException import moe.fuqiuluo.shamrock.helper.ParamsException
import io.ktor.http.HttpMethod import io.ktor.http.HttpMethod
import io.ktor.http.decodeURLPart
import io.ktor.http.parseUrlEncodedParameters import io.ktor.http.parseUrlEncodedParameters
import io.ktor.server.request.httpMethod import io.ktor.server.request.httpMethod
import io.ktor.server.routing.route import io.ktor.server.routing.route
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.remote.entries.CommonResult import moe.fuqiuluo.shamrock.remote.entries.CommonResult
import moe.fuqiuluo.shamrock.remote.entries.EmptyObject import moe.fuqiuluo.shamrock.remote.entries.EmptyObject
import moe.fuqiuluo.shamrock.remote.entries.Status import moe.fuqiuluo.shamrock.remote.entries.Status
@ -38,9 +35,9 @@ import moe.fuqiuluo.shamrock.remote.entries.Status
annotation class ShamrockDsl annotation class ShamrockDsl
private val isJsonKey = AttributeKey<Boolean>("isJson") private val keyIsJson = AttributeKey<Boolean>("isJson")
private val jsonKey = AttributeKey<JsonObject>("paramsJson") private val keyJsonObject = AttributeKey<JsonObject>("paramsJson")
private val partsKey = AttributeKey<Parameters>("paramsParts") private val keyParts = AttributeKey<Parameters>("paramsParts")
suspend fun ApplicationCall.fetch(key: String): String { suspend fun ApplicationCall.fetch(key: String): String {
val isPost = request.httpMethod == HttpMethod.Post val isPost = request.httpMethod == HttpMethod.Post
@ -94,23 +91,23 @@ fun ApplicationCall.isJsonData(): Boolean {
} }
suspend fun ApplicationCall.fetchPostOrNull(key: String): String? { suspend fun ApplicationCall.fetchPostOrNull(key: String): String? {
if (attributes.contains(jsonKey)) { if (attributes.contains(keyJsonObject)) {
return attributes[jsonKey][key].asStringOrNull return attributes[keyJsonObject][key].asStringOrNull
} }
if (attributes.contains(partsKey)) { if (attributes.contains(keyParts)) {
return attributes[partsKey][key] return attributes[keyParts][key]
} }
return kotlin.runCatching { return kotlin.runCatching {
if (isJsonData()) { if (isJsonData()) {
Json.parseToJsonElement(receiveText()).jsonObject.also { Json.parseToJsonElement(receiveText()).jsonObject.also {
attributes.put(jsonKey, it) attributes.put(keyJsonObject, it)
attributes.put(isJsonKey, true) attributes.put(keyIsJson, true)
}[key].asStringOrNull }[key].asStringOrNull
} else if ( } else if (
ContentType.Application.FormUrlEncoded == request.contentType() ContentType.Application.FormUrlEncoded == request.contentType()
) { ) {
receiveParameters().also { receiveParameters().also {
attributes.put(partsKey, it) attributes.put(keyParts, it)
}[key] }[key]
} else { } else {
receiveTextAsUnknown(key) receiveTextAsUnknown(key)
@ -124,13 +121,13 @@ private suspend fun ApplicationCall.receiveTextAsUnknown(key: String): String? {
return receiveText().let { text -> return receiveText().let { text ->
if (text.startsWith("{") && text.endsWith("}")) { if (text.startsWith("{") && text.endsWith("}")) {
Json.parseToJsonElement(text).jsonObject.also { Json.parseToJsonElement(text).jsonObject.also {
attributes.put(jsonKey, it) attributes.put(keyJsonObject, it)
attributes.put(isJsonKey, true) attributes.put(keyIsJson, true)
}[key].asStringOrNull }[key].asStringOrNull
} else { } else {
text.parseUrlEncodedParameters().also { text.parseUrlEncodedParameters().also {
attributes.put(partsKey, it) attributes.put(keyParts, it)
attributes.put(isJsonKey, false) attributes.put(keyIsJson, false)
}[key] }[key]
} }
} // receiveText } // receiveText
@ -170,17 +167,17 @@ suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostOrThrow(key: String)
} }
fun PipelineContext<Unit, ApplicationCall>.isJsonData(): Boolean { fun PipelineContext<Unit, ApplicationCall>.isJsonData(): Boolean {
return ContentType.Application.Json == call.request.contentType() || call.attributes[isJsonKey] return ContentType.Application.Json == call.request.contentType() || (keyIsJson in call.attributes && call.attributes[keyIsJson])
} }
suspend fun PipelineContext<Unit, ApplicationCall>.isJsonString(key: String): Boolean { suspend fun PipelineContext<Unit, ApplicationCall>.isJsonString(key: String): Boolean {
if (!isJsonData()) return true if (!isJsonData()) return true
val data = if (call.attributes.contains(jsonKey)) { val data = if (keyJsonObject in call.attributes) {
call.attributes[jsonKey] call.attributes[keyJsonObject]
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(keyJsonObject, it)
call.attributes.put(isJsonKey, true) call.attributes.put(keyIsJson, true)
} }
} }
return data[key] is JsonPrimitive return data[key] is JsonPrimitive
@ -188,11 +185,11 @@ suspend fun PipelineContext<Unit, ApplicationCall>.isJsonString(key: String): Bo
suspend fun PipelineContext<Unit, ApplicationCall>.isJsonObject(key: String): Boolean { suspend fun PipelineContext<Unit, ApplicationCall>.isJsonObject(key: String): Boolean {
if (!isJsonData()) return false if (!isJsonData()) return false
val data = if (call.attributes.contains(jsonKey)) { val data = if (call.attributes.contains(keyJsonObject)) {
call.attributes[jsonKey] call.attributes[keyJsonObject]
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(keyJsonObject, it)
} }
} }
return data[key] is JsonObject return data[key] is JsonObject
@ -200,56 +197,68 @@ suspend fun PipelineContext<Unit, ApplicationCall>.isJsonObject(key: String): Bo
suspend fun PipelineContext<Unit, ApplicationCall>.isJsonArray(key: String): Boolean { suspend fun PipelineContext<Unit, ApplicationCall>.isJsonArray(key: String): Boolean {
if (!isJsonData()) return false if (!isJsonData()) return false
val data = if (call.attributes.contains(jsonKey)) { val data = if (call.attributes.contains(keyJsonObject)) {
call.attributes[jsonKey] call.attributes[keyJsonObject]
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(keyJsonObject, it)
} }
} }
return data[key] is JsonArray return data[key] is JsonArray
} }
suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonString(key: String): String { suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonString(key: String): String {
val data = if (call.attributes.contains(jsonKey)) { val data = if (call.attributes.contains(keyJsonObject)) {
call.attributes[jsonKey] call.attributes[keyJsonObject]
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(keyJsonObject, it)
} }
} }
return data[key].asString return data[key].asString
} }
suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonElement(key: String): JsonElement { suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonElement(key: String): JsonElement {
val data = if (call.attributes.contains(jsonKey)) { val data = if (call.attributes.contains(keyJsonObject)) {
call.attributes[jsonKey] call.attributes[keyJsonObject]
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(keyJsonObject, it)
} }
} }
return data[key]!! return data[key]!!
} }
suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonObject(key: String): JsonObject { suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonObject(key: String): JsonObject {
val data = if (call.attributes.contains(jsonKey)) { val data = if (call.attributes.contains(keyJsonObject)) {
call.attributes[jsonKey] call.attributes[keyJsonObject]
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(keyJsonObject, it)
} }
} }
return data[key].asJsonObject return data[key].asJsonObject
} }
suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonArray(key: String): JsonArray { suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonObjectOrNull(key: String): JsonObject? {
val data = if (call.attributes.contains(jsonKey)) { val data = if (call.attributes.contains(keyJsonObject)) {
call.attributes[jsonKey] call.attributes[keyJsonObject]
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(keyJsonObject, it)
call.attributes.put(isJsonKey, true) }
}
return data[key].asJsonObjectOrNull
}
suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonArray(key: String): JsonArray {
val data = if (call.attributes.contains(keyJsonObject)) {
call.attributes[keyJsonObject]
} else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(keyJsonObject, it)
call.attributes.put(keyIsJson, true)
} }
} }
return data[key].asJsonArray return data[key].asJsonArray