Shamrock: 对QQ9支持,并尝试修复#181

This commit is contained in:
whitechi72 2024-01-03 17:45:32 +08:00
parent d44150ea1a
commit e92c227bba
6 changed files with 167 additions and 45 deletions

View File

@ -62,6 +62,7 @@ android {
println("Full architecture and full compilation.") println("Full architecture and full compilation.")
abiFilters.add("arm64-v8a") abiFilters.add("arm64-v8a")
abiFilters.add("x86_64") abiFilters.add("x86_64")
abiFilters.add("x86")
} }
} }
create("arm64") { create("arm64") {

View File

@ -52,6 +52,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@ -120,7 +121,7 @@ private fun AppMainView() {
val coreVersion = remember { mutableStateOf(getShamrockVersion(context)) } val coreVersion = remember { mutableStateOf(getShamrockVersion(context)) }
val coreName = remember { mutableStateOf("Xposed") } val coreName = remember { mutableStateOf("Xposed") }
val voiceSwitch = remember { mutableStateOf(false) } val voiceSwitch = remember { mutableStateOf(false) }
@Suppress("LocalVariableName") val LocalString = LocalString @Suppress("LocalVariableName") val LocalString = LocalString.init()
if (!AppRuntime.isInit) { if (!AppRuntime.isInit) {
AppRuntime.state = remember { AppRuntime.state = remember {
@ -147,7 +148,7 @@ private fun AppMainView() {
AppRuntime.requestCount = remember { mutableIntStateOf(0) } AppRuntime.requestCount = remember { mutableIntStateOf(0) }
AppRuntime.isInit = false AppRuntime.isInit = true
} }
val ctx = LocalContext.current val ctx = LocalContext.current

View File

@ -6,7 +6,9 @@ package moe.fuqiuluo.shamrock.ui.theme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import moe.fuqiuluo.shamrock.R import moe.fuqiuluo.shamrock.R
private val LocalStringDefault = Default() private val LocalStringDefault = Default()
@ -164,4 +166,14 @@ open class VarString(
var persistentText: String, var persistentText: String,
var persistentTextDesc: String var persistentTextDesc: String
) ) {
private var inited = false
@Composable
fun init(): VarString {
if (inited) return this
inited = true
return this
}
}

View File

@ -55,7 +55,7 @@ internal object ActionManager {
FavAddTextMsg, FavAddImageMsg, FavGetItemContent, FavGetItemList, FavAddTextMsg, FavAddImageMsg, FavGetItemContent, FavGetItemList,
// OTHER // OTHER
GetDeviceBattery, DownloadFile GetDeviceBattery, DownloadFile, QuickOperation
).forEach { ).forEach {
it.alias.forEach { name -> it.alias.forEach { name ->
actionMap[name] = it actionMap[name] = it

View File

@ -0,0 +1,148 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.remote.service.HttpService
import moe.fuqiuluo.shamrock.tools.asBoolean
import moe.fuqiuluo.shamrock.tools.asBooleanOrNull
import moe.fuqiuluo.shamrock.tools.asInt
import moe.fuqiuluo.shamrock.tools.asIntOrNull
import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asLong
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.tools.jsonArray
internal object QuickOperation: IActionHandler() {
val actionMsgTypes = arrayOf(
"record", "voice", "video", "markdown"
)
override suspend fun internalHandle(session: ActionSession): String {
val botId = session.getLong("self_id")
if (botId != TicketSvc.getLongUin()) {
return logic("当前登录账号和输入的`self_id`不一致", session.echo)
}
val context = session.getObject("context")
//val msgType = context["message_type"].asString
val msgHash = context["message_id"].asInt
//val peerId = context[when(msgType) {
// "group" -> "group_id"
// "private" -> "user_id"
// else -> error("unknown message type: $msgType")
//}].asLong
val record = MsgSvc.getMsg(msgHash).getOrNull()
?: return logic("获取源消息失败", session.echo)
val operation = session.getObject("operation")
if (operation.containsKey("reply")) {
LogCenter.log({ "websocket quickly reply successfully" }, Level.DEBUG)
val autoEscape = operation["auto_escape"].asBooleanOrNull ?: false
val atSender = operation["at_sender"].asBooleanOrNull ?: false
val autoReply = operation["auto_reply"].asBooleanOrNull ?: true
val message = operation["reply"]
if (message is JsonPrimitive) {
if (autoEscape) {
val msgList = mutableSetOf<JsonElement>()
msgList.add(mapOf(
"type" to "text",
"data" to mapOf(
"text" to message.asString
)
).json)
quicklyReply(
record,
msgList.jsonArray,
msgHash,
atSender,
autoReply
)
} else {
val messageArray = MessageHelper.decodeCQCode(message.asString)
quicklyReply(
record,
messageArray,
msgHash,
atSender,
autoReply
)
}
} else if (message is JsonArray) {
quicklyReply(
record,
message,
msgHash,
atSender,
autoReply
)
}
}
if (MsgConstant.KCHATTYPEGROUP == record.chatType && operation.containsKey("delete") && operation["delete"].asBoolean) {
MsgSvc.recallMsg(msgHash)
}
if (MsgConstant.KCHATTYPEGROUP == record.chatType && operation.containsKey("kick") && operation["kick"].asBoolean) {
GroupSvc.kickMember(record.peerUin, false, record.senderUin)
}
if (MsgConstant.KCHATTYPEGROUP == record.chatType && operation.containsKey("ban") && operation["ban"].asBoolean) {
val banTime = operation["ban_duration"].asIntOrNull ?: (30 * 60)
if (banTime <= 0) return logic("禁言时间必须大于0", session.echo)
GroupSvc.banMember(record.peerUin, record.senderUin, banTime)
}
return logic("操作成功", session.echo)
}
override fun path(): String = ".handle_quick_operation_async"
override val requiredParams: Array<String> = arrayOf("context", "operation", "self_id")
suspend fun quicklyReply(
record: MsgRecord,
message: JsonArray,
msgHash: Int,
atSender: Boolean,
autoReply: Boolean
) {
val messageList = mutableListOf<JsonElement>()
message.filter {
it.asJsonObject["type"]?.asString in actionMsgTypes
}.let {
if (it.isNotEmpty()) {
it.map { listOf(it) }.forEach {
MsgSvc.sendToAio(record.chatType, record.peerUin.toString(), it.jsonArray)
}
return
}
}
if (autoReply) messageList.add(mapOf(
"type" to "reply",
"data" to mapOf(
"id" to msgHash
)
).json) // 添加回复
if (MsgConstant.KCHATTYPEGROUP == record.chatType && atSender) {
messageList.add(mapOf(
"type" to "at",
"data" to mapOf(
"qq" to record.senderUin
)
).json) // 添加@发送者
}
messageList.addAll(message)
MsgSvc.sendToAio(record.chatType, record.peerUin.toString(), JsonArray(messageList))
}
}

View File

@ -27,16 +27,13 @@ import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.remote.action.ActionManager import moe.fuqiuluo.shamrock.remote.action.ActionManager
import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.handlers.QuickOperation.quicklyReply
import moe.fuqiuluo.shamrock.remote.config.ECHO_KEY 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.Status import moe.fuqiuluo.shamrock.remote.entries.Status
import moe.fuqiuluo.shamrock.remote.service.api.GlobalEventTransmitter import moe.fuqiuluo.shamrock.remote.service.api.GlobalEventTransmitter
internal object HttpService: HttpTransmitServlet() { internal object HttpService: HttpTransmitServlet() {
private val actionMsgTypes = arrayOf(
"record", "voice", "video", "markdown"
)
private val jobList = arrayListOf<Job>() private val jobList = arrayListOf<Job>()
override fun submitFlowJob(job: Job) { override fun submitFlowJob(job: Job) {
@ -154,41 +151,4 @@ internal object HttpService: HttpTransmitServlet() {
handler.handle(ActionSession(params, echo)) handler.handle(ActionSession(params, echo))
} }
} }
private suspend fun quicklyReply(
record: MsgRecord,
message: JsonArray,
msgHash: Int,
atSender: Boolean,
autoReply: Boolean
) {
val messageList = mutableListOf<JsonElement>()
message.filter {
it.asJsonObject["type"]?.asString in actionMsgTypes
}.let {
if (it.isNotEmpty()) {
it.map { listOf(it) }.forEach {
MsgSvc.sendToAio(record.chatType, record.peerUin.toString(), it.jsonArray)
}
return
}
}
if (autoReply) messageList.add(mapOf(
"type" to "reply",
"data" to mapOf(
"id" to msgHash
)
).json) // 添加回复
if (MsgConstant.KCHATTYPEGROUP == record.chatType && atSender) {
messageList.add(mapOf(
"type" to "at",
"data" to mapOf(
"qq" to record.senderUin
)
).json) // 添加@发送者
}
messageList.addAll(message)
MsgSvc.sendToAio(record.chatType, record.peerUin.toString(), JsonArray(messageList))
}
} }