Shamrock: 绕过资源上传检测

Signed-off-by: 白池 <whitechi73@outlook.com>
This commit is contained in:
白池 2024-03-01 20:29:28 +08:00
parent 54b7eb95a8
commit 661680e60b
6 changed files with 77 additions and 37 deletions

View File

@ -162,6 +162,17 @@ object ShamrockConfig {
pushUpdate(ctx) pushUpdate(ctx)
} }
fun getUploadResourceGroup(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("up_res_group", "100000000")!!
}
fun setUploadResourceGroup(ctx: Context, v: String) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("up_res_group", v).apply()
pushUpdate(ctx)
}
fun getHttpPort(ctx: Context): Int { fun getHttpPort(ctx: Context): Int {
val preferences = ctx.getSharedPreferences("config", 0) val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getInt("port", 5700) return preferences.getInt("port", 5700)
@ -354,6 +365,7 @@ object ShamrockConfig {
"disable_auto_sync_setting" to preferences.getBoolean("disable_auto_sync_setting", false), "disable_auto_sync_setting" to preferences.getBoolean("disable_auto_sync_setting", false),
"forbid_useless_process" to preferences.getBoolean("forbid_useless_process", false), "forbid_useless_process" to preferences.getBoolean("forbid_useless_process", false),
"enable_old_bdh" to preferences.getBoolean("enable_old_bdh", false), "enable_old_bdh" to preferences.getBoolean("enable_old_bdh", false),
"up_res_group" to preferences.getString("up_res_group", ""),
) )
} }

View File

@ -356,6 +356,36 @@ private fun FunctionCard(
return@Function true return@Function true
} }
run {
val uploadResourceGroup = remember { mutableStateOf(ShamrockConfig.getUploadResourceGroup(ctx)) }
Column(
modifier = Modifier
.absolutePadding(left = 8.dp, right = 8.dp, top = 12.dp, bottom = 0.dp)
) {
Text(
modifier = Modifier.padding(2.dp),
text = "用来上传资源的群聊,错误的资源上传终点可能导致封禁,请自建一个群聊并填写在下方。",
color = Color.Red,
fontSize = 11.sp
)
}
TextItem(
title = "接受资源群聊",
desc = "用来上传资源的群聊,请自建一个群聊并填写在下方。",
text = uploadResourceGroup,
hint = "请输入群号",
error = "群号不合法",
checker = {
it.isNotBlank() && it.toULongOrNull() != null
},
confirm = {
val groupId = uploadResourceGroup.value
ShamrockConfig.setUploadResourceGroup(ctx, groupId)
AppRuntime.log("设置接受资源群聊为[$groupId]。")
}
)
}
/* /*
Function( Function(
title = "专业级接口", title = "专业级接口",
@ -445,9 +475,7 @@ private fun InfoItem(
.fillMaxWidth() .fillMaxWidth()
.combinedClickable(onDoubleClick = { .combinedClickable(onDoubleClick = {
doubleClick?.invoke(content) doubleClick?.invoke(content)
}) { }) { true }
true
}
, ,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {

View File

@ -338,13 +338,13 @@ internal object MsgSvc : BaseSvc() {
else -> MessageHelper.decodeCQCode(data["content"].asString) else -> MessageHelper.decodeCQCode(data["content"].asString)
}.onEach { element -> }.onEach { element ->
val elementData = element.asJsonObject["data"].asJsonObject val elementData = element.asJsonObject["data"].asJsonObject
if (element.asJsonObject["type"].asString == "forward") if (element.asJsonObject["type"].asString == "forward") {
forwardMsg[elementData["filename"].asString] = forwardMsg[elementData["filename"].asString] =
elementData["id"].asString elementData["id"].asString
}
} }
).getOrElse { throw Exception("消息合成失败: $it") }.let { ).getOrElse { error("消息合成失败: $it") }.let {
desc[++i] = desc[++i] = (data["name"].asStringOrNull ?: data["uin"].asStringOrNull
(data["name"].asStringOrNull ?: data["uin"].asStringOrNull
?: TicketSvc.getNickname()) + ": " + it.first ?: TicketSvc.getNickname()) + ": " + it.first
it.second it.second
} }
@ -353,8 +353,9 @@ internal object MsgSvc : BaseSvc() {
} else { } else {
error("消息节点缺少id或content字段") error("消息节点缺少id或content字段")
} }
}.onFailure {
LogCenter.log("消息节点解析失败:${it.stackTraceToString()}", Level.WARN)
}.getOrElse { }.getOrElse {
LogCenter.log("消息节点解析失败:$it", Level.WARN)
null null
} }
}.ifEmpty { return Result.failure(Exception("消息节点为空")) } }.ifEmpty { return Result.failure(Exception("消息节点为空")) }

View File

@ -261,7 +261,7 @@ internal class ElemMaker {
resources = arrayListOf(file), resources = arrayListOf(file),
timeout = 30.seconds timeout = 30.seconds
).getOrThrow().first() ).getOrThrow().first()
LogCenter.log(uploadRet.toString(), Level.DEBUG) LogCenter.log({ uploadRet.toString() }, Level.DEBUG)
val elem = when (chatType) { val elem = when (chatType) {
MsgConstant.KCHATTYPEGROUP -> Elem( MsgConstant.KCHATTYPEGROUP -> Elem(

View File

@ -1,14 +1,11 @@
package moe.fuqiuluo.qqinterface.servlet.transfile package moe.fuqiuluo.qqinterface.servlet.transfile
import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import com.tencent.mobileqq.qroute.QRoute import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.aio.adapter.api.IAIOPttApi import com.tencent.qqnt.aio.adapter.api.IAIOPttApi
import com.tencent.qqnt.kernel.nativeinterface.CommonFileInfo import com.tencent.qqnt.kernel.nativeinterface.CommonFileInfo
import com.tencent.qqnt.kernel.nativeinterface.Contact import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.FileElement
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.PicElement import com.tencent.qqnt.kernel.nativeinterface.PicElement
@ -16,28 +13,19 @@ import com.tencent.qqnt.kernel.nativeinterface.PttElement
import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo
import com.tencent.qqnt.kernel.nativeinterface.VideoElement import com.tencent.qqnt.kernel.nativeinterface.VideoElement
import com.tencent.qqnt.msg.api.IMsgUtilApi
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.qqinterface.servlet.BaseSvc import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.qqinterface.servlet.TicketSvc import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Private
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Troop
import moe.fuqiuluo.qqinterface.servlet.transfile.data.TryUpPicData import moe.fuqiuluo.qqinterface.servlet.transfile.data.TryUpPicData
import moe.fuqiuluo.qqinterface.servlet.transfile.data.VideoResource
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.helper.TransfileHelper
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.tools.hex2ByteArray import moe.fuqiuluo.shamrock.tools.hex2ByteArray
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.AudioUtils import moe.fuqiuluo.shamrock.utils.AudioUtils
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.utils.MediaType import moe.fuqiuluo.shamrock.utils.MediaType
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.msgService import moe.fuqiuluo.shamrock.xposed.helper.msgService
@ -63,7 +51,6 @@ import protobuf.oidb.cmd0x388.Cmd0x388ReqBody
import protobuf.oidb.cmd0x388.Cmd0x388RspBody import protobuf.oidb.cmd0x388.Cmd0x388RspBody
import protobuf.oidb.cmd0x388.TryUpImgReq import protobuf.oidb.cmd0x388.TryUpImgReq
import java.io.File import java.io.File
import java.io.FileOutputStream
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.random.Random import kotlin.random.Random
@ -72,14 +59,30 @@ import kotlin.random.nextULong
import kotlin.time.Duration import kotlin.time.Duration
internal object NtV2RichMediaSvc: BaseSvc() { internal object NtV2RichMediaSvc: BaseSvc() {
private const val GROUP_PIC_UPLOAD_TO = "100000000"
private val requestIdSeq = atomic(2L) private val requestIdSeq = atomic(2L)
private fun fetchGroupResUploadTo(): String {
return ShamrockConfig.getUpResGroup().ifEmpty { "100000000" }
}
suspend fun tryUploadResourceByNt(
chatType: Int,
elementType: Int,
resources: ArrayList<File>,
timeout: Duration,
retryCnt: Int = 5
): Result<MutableList<CommonFileInfo>> {
return internalTryUploadResourceByNt(chatType, elementType, resources, timeout).onFailure {
if (retryCnt > 0) {
return tryUploadResourceByNt(chatType, elementType, resources, timeout, retryCnt - 1)
}
}
}
/** /**
* 批量上传图片 * 批量上传图片
*/ */
suspend fun tryUploadResourceByNt( private suspend fun internalTryUploadResourceByNt(
chatType: Int, chatType: Int,
elementType: Int, elementType: Int,
resources: ArrayList<File>, resources: ArrayList<File>,
@ -276,8 +279,9 @@ internal object NtV2RichMediaSvc: BaseSvc() {
} }
val contact = when(chatType) { val contact = when(chatType) {
MsgConstant.KCHATTYPEC2C -> MessageHelper.generateContact(chatType, TicketSvc.getUin()) MsgConstant.KCHATTYPEC2C -> MessageHelper.generateContact(chatType, TicketSvc.getUin())
else -> Contact(chatType, GROUP_PIC_UPLOAD_TO, GROUP_PIC_UPLOAD_TO) else -> Contact(chatType, fetchGroupResUploadTo(), null)
} }
LogCenter.log(contact.toString())
val result = mutableListOf<CommonFileInfo>() val result = mutableListOf<CommonFileInfo>()
withTimeoutOrNull(timeout) { withTimeoutOrNull(timeout) {
suspendCancellableCoroutine { suspendCancellableCoroutine {
@ -297,10 +301,12 @@ internal object NtV2RichMediaSvc: BaseSvc() {
message = ArrayList(messages), message = ArrayList(messages),
uniseq = uniseq.qqMsgId uniseq = uniseq.qqMsgId
) { _, _ -> ) { _, _ ->
val kernelService = NTServiceFetcher.kernelService if (contact.chatType == MsgConstant.KCHATTYPEGROUP && contact.peerUid == "100000000") {
val sessionService = kernelService.wrapperSession val kernelService = NTServiceFetcher.kernelService
val msgService = sessionService.msgService val sessionService = kernelService.wrapperSession
msgService.deleteMsg(contact, arrayListOf(uniseq.qqMsgId), null) val msgService = sessionService.msgService
msgService.deleteMsg(contact, arrayListOf(uniseq.qqMsgId), null)
}
} }
it.invokeOnCancellation { it.invokeOnCancellation {
RichMediaUploadHandler.removeListener(uniseq.qqMsgId) RichMediaUploadHandler.removeListener(uniseq.qqMsgId)

View File

@ -24,13 +24,6 @@ fun Routing.testAction() {
return return
} }
getOrPost("/send_msg_by_resid") {
val resId = fetchOrThrow("res_id")
val peerId = fetchOrThrow("peer_Id")
val messageType = fetchOrThrow("message_type")
call.respondText(SendMsgByResid(peerId, resId, messageType))
}
getOrPost("/createUidFromTinyId") { getOrPost("/createUidFromTinyId") {
val selfId = fetchOrThrow("selfId").toLong() val selfId = fetchOrThrow("selfId").toLong()
val peerId = fetchOrThrow("peerId") val peerId = fetchOrThrow("peerId")