diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b5b3d65..8df9d7f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -62,6 +62,7 @@ android { println("Full architecture and full compilation.") abiFilters.add("arm64-v8a") abiFilters.add("x86_64") + abiFilters.add("x86") } } create("arm64") { diff --git a/app/src/main/java/moe/fuqiuluo/shamrock/MainActivity.kt b/app/src/main/java/moe/fuqiuluo/shamrock/MainActivity.kt index f329114..e5d619c 100644 --- a/app/src/main/java/moe/fuqiuluo/shamrock/MainActivity.kt +++ b/app/src/main/java/moe/fuqiuluo/shamrock/MainActivity.kt @@ -52,6 +52,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource @@ -120,7 +121,7 @@ private fun AppMainView() { val coreVersion = remember { mutableStateOf(getShamrockVersion(context)) } val coreName = remember { mutableStateOf("Xposed") } val voiceSwitch = remember { mutableStateOf(false) } - @Suppress("LocalVariableName") val LocalString = LocalString + @Suppress("LocalVariableName") val LocalString = LocalString.init() if (!AppRuntime.isInit) { AppRuntime.state = remember { @@ -147,7 +148,7 @@ private fun AppMainView() { AppRuntime.requestCount = remember { mutableIntStateOf(0) } - AppRuntime.isInit = false + AppRuntime.isInit = true } val ctx = LocalContext.current diff --git a/app/src/main/java/moe/fuqiuluo/shamrock/ui/theme/Strings.kt b/app/src/main/java/moe/fuqiuluo/shamrock/ui/theme/Strings.kt index d3cf301..b974a16 100644 --- a/app/src/main/java/moe/fuqiuluo/shamrock/ui/theme/Strings.kt +++ b/app/src/main/java/moe/fuqiuluo/shamrock/ui/theme/Strings.kt @@ -6,7 +6,9 @@ package moe.fuqiuluo.shamrock.ui.theme import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import moe.fuqiuluo.shamrock.R private val LocalStringDefault = Default() @@ -164,4 +166,14 @@ open class VarString( var persistentText: String, var persistentTextDesc: String -) +) { + private var inited = false + + @Composable + fun init(): VarString { + if (inited) return this + + inited = true + return this + } +} diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt index 18546f2..df1ec9c 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt @@ -55,7 +55,7 @@ internal object ActionManager { FavAddTextMsg, FavAddImageMsg, FavGetItemContent, FavGetItemList, // OTHER - GetDeviceBattery, DownloadFile + GetDeviceBattery, DownloadFile, QuickOperation ).forEach { it.alias.forEach { name -> actionMap[name] = it diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/QuickOperation.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/QuickOperation.kt new file mode 100644 index 0000000..ea7ea95 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/QuickOperation.kt @@ -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() + 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 = arrayOf("context", "operation", "self_id") + + suspend fun quicklyReply( + record: MsgRecord, + message: JsonArray, + msgHash: Int, + atSender: Boolean, + autoReply: Boolean + ) { + val messageList = mutableListOf() + 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)) + } +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/HttpService.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/HttpService.kt index 1710f7b..3de110a 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/HttpService.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/HttpService.kt @@ -27,16 +27,13 @@ import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.remote.action.ActionManager 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.entries.EmptyObject import moe.fuqiuluo.shamrock.remote.entries.Status import moe.fuqiuluo.shamrock.remote.service.api.GlobalEventTransmitter internal object HttpService: HttpTransmitServlet() { - private val actionMsgTypes = arrayOf( - "record", "voice", "video", "markdown" - ) - private val jobList = arrayListOf() override fun submitFlowJob(job: Job) { @@ -154,41 +151,4 @@ internal object HttpService: HttpTransmitServlet() { handler.handle(ActionSession(params, echo)) } } - - private suspend fun quicklyReply( - record: MsgRecord, - message: JsonArray, - msgHash: Int, - atSender: Boolean, - autoReply: Boolean - ) { - val messageList = mutableListOf() - 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)) - } } \ No newline at end of file