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)
}
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 {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getInt("port", 5700)
@ -354,6 +365,7 @@ object ShamrockConfig {
"disable_auto_sync_setting" to preferences.getBoolean("disable_auto_sync_setting", false),
"forbid_useless_process" to preferences.getBoolean("forbid_useless_process", 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
}
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(
title = "专业级接口",
@ -445,9 +475,7 @@ private fun InfoItem(
.fillMaxWidth()
.combinedClickable(onDoubleClick = {
doubleClick?.invoke(content)
}) {
true
}
}) { true }
,
verticalAlignment = Alignment.CenterVertically
) {

View File

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

View File

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

View File

@ -1,14 +1,11 @@
package moe.fuqiuluo.qqinterface.servlet.transfile
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever
import androidx.exifinterface.media.ExifInterface
import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.aio.adapter.api.IAIOPttApi
import com.tencent.qqnt.kernel.nativeinterface.CommonFileInfo
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.MsgElement
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.RichMediaFilePathInfo
import com.tencent.qqnt.kernel.nativeinterface.VideoElement
import com.tencent.qqnt.msg.api.IMsgUtilApi
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
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.VideoResource
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.helper.TransfileHelper
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.AudioUtils
import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.utils.MediaType
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
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.TryUpImgReq
import java.io.File
import java.io.FileOutputStream
import kotlin.coroutines.resume
import kotlin.math.roundToInt
import kotlin.random.Random
@ -72,14 +59,30 @@ import kotlin.random.nextULong
import kotlin.time.Duration
internal object NtV2RichMediaSvc: BaseSvc() {
private const val GROUP_PIC_UPLOAD_TO = "100000000"
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,
elementType: Int,
resources: ArrayList<File>,
@ -276,8 +279,9 @@ internal object NtV2RichMediaSvc: BaseSvc() {
}
val contact = when(chatType) {
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>()
withTimeoutOrNull(timeout) {
suspendCancellableCoroutine {
@ -297,10 +301,12 @@ internal object NtV2RichMediaSvc: BaseSvc() {
message = ArrayList(messages),
uniseq = uniseq.qqMsgId
) { _, _ ->
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
msgService.deleteMsg(contact, arrayListOf(uniseq.qqMsgId), null)
if (contact.chatType == MsgConstant.KCHATTYPEGROUP && contact.peerUid == "100000000") {
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
msgService.deleteMsg(contact, arrayListOf(uniseq.qqMsgId), null)
}
}
it.invokeOnCancellation {
RichMediaUploadHandler.removeListener(uniseq.qqMsgId)

View File

@ -24,13 +24,6 @@ fun Routing.testAction() {
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") {
val selfId = fetchOrThrow("selfId").toLong()
val peerId = fetchOrThrow("peerId")