Shamrock: fix #33

This commit is contained in:
WhiteChi 2023-11-17 20:20:33 +08:00
parent 472b17f744
commit 58d93b8f56
5 changed files with 246 additions and 254 deletions

View File

@ -53,7 +53,6 @@ internal object GetTroopMemberList : IActionHandler() {
role = when {
GroupSvc.getOwner(groupId)
.toString() == info.memberuin -> MemberRole.Owner
info.memberuin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
else -> MemberRole.Member
},

View File

@ -0,0 +1,233 @@
@file:OptIn(DelicateCoroutinesApi::class)
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MultiMsgInfo
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toSegments
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.tools.asInt
import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.asStringOrNull
import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
/**
* 合并转发消息节点数据类
*/
sealed interface ForwardMsgNode {
class MessageIdNode(
val id: Int
): ForwardMsgNode
open class MessageNode(
val name: String,
val content: JsonElement?
): ForwardMsgNode
object EmptyNode: MessageNode("", null)
}
/**
* 私聊合并转发
*/
internal object SendPrivateForwardMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupId = session.getString("user_id")
if (session.isArray("messages")) {
val messages = session.getArray("messages")
return invoke(messages, groupId, session.echo)
}
return logic("未知格式合并转发消息", session.echo)
}
suspend operator fun invoke(
message: JsonArray,
userId: String,
echo: JsonElement = EmptyJsonString
): String {
kotlin.runCatching {
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
val selfUin = TicketSvc.getUin()
val msgs = message.map {
if (it.asJsonObject["type"].asStringOrNull != "node") return@map ForwardMsgNode.EmptyNode // 过滤非node类型消息段
it.asJsonObject["data"].asJsonObject.let { data ->
if (data.containsKey("content"))
ForwardMsgNode.MessageNode(
name = data["name"].asStringOrNull ?: "",
content = data["content"]
)
else ForwardMsgNode.MessageIdNode(data["id"].asInt)
}
}.map {
if (it is ForwardMsgNode.MessageIdNode) {
val recordResult = MsgSvc.getMsg(it.id)
if (recordResult.isFailure) {
ForwardMsgNode.EmptyNode
} else {
val record = recordResult.getOrThrow()
ForwardMsgNode.MessageNode(
name = record.sendMemberName
.ifBlank { record.sendNickName }
.ifBlank { record.sendRemarkName }
.ifBlank { record.peerName },
content = record.toSegments().map { segment ->
segment.toJson()
}.json
)
}
} else {
it as ForwardMsgNode.MessageNode
}
}.filter {
it.content != null
}
val multiNodes = msgs.map { node ->
suspendCoroutine {
GlobalScope.launch {
var msgId: Long = 0
msgId = MessageHelper.sendMessageWithMsgId(MsgConstant.KCHATTYPEC2C, selfUin, node.content!!.let { msg ->
if (msg is JsonArray) msg else MessageHelper.decodeCQCode(msg.asString)
}, { code, why ->
if (code != 0) {
LogCenter.log("合并转发消息节点消息发送失败:$code($why)", Level.WARN)
}
it.resume(node.name to msgId)
}).first
}
}
}
val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin)
val to = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, userId)
msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply {
multiNodes.forEach { add(MultiMsgInfo(it.second, it.first)) }
}.also { it.reverse() }, from, to) { code, why ->
if (code != 0)
LogCenter.log("合并转发消息:$code($why)", Level.WARN)
}
return ok(data = EmptyJsonObject, echo = echo)
}.onFailure {
return error("error: $it", echo)
}
return logic("合并转发消息失败(unknown error)", echo)
}
override val requiredParams: Array<String> = arrayOf("user_id")
override fun path(): String = "send_private_forward_msg"
}
/**
* 群聊合并转发
*/
internal object SendGroupForwardMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupId = session.getString("group_id")
if (session.isArray("messages")) {
val messages = session.getArray("messages")
return invoke(messages, groupId, session.echo)
}
return logic("未知格式合并转发消息", session.echo)
}
suspend operator fun invoke(
message: JsonArray,
groupId: String,
echo: JsonElement = EmptyJsonString
): String {
kotlin.runCatching {
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
val selfUin = TicketSvc.getUin()
val msgs = message.map {
if (it.asJsonObject["type"].asStringOrNull != "node") return@map ForwardMsgNode.EmptyNode // 过滤非node类型消息段
it.asJsonObject["data"].asJsonObject.let { data ->
if (data.containsKey("content"))
ForwardMsgNode.MessageNode(
name = data["name"].asStringOrNull ?: "",
content = data["content"]
)
else ForwardMsgNode.MessageIdNode(data["id"].asInt)
}
}.map {
if (it is ForwardMsgNode.MessageIdNode) {
val recordResult = MsgSvc.getMsg(it.id)
if (recordResult.isFailure) {
ForwardMsgNode.EmptyNode
} else {
val record = recordResult.getOrThrow()
ForwardMsgNode.MessageNode(
name = record.sendMemberName
.ifBlank { record.sendNickName }
.ifBlank { record.sendRemarkName }
.ifBlank { record.peerName },
content = record.toSegments().map { segment ->
segment.toJson()
}.json
)
}
} else {
it as ForwardMsgNode.MessageNode
}
}.filter {
it.content != null
}
val multiNodes = msgs.map { node ->
suspendCoroutine {
GlobalScope.launch {
var msgId: Long = 0
msgId = MessageHelper.sendMessageWithMsgId(MsgConstant.KCHATTYPEC2C, selfUin, node.content!!.let { msg ->
if (msg is JsonArray) msg else MessageHelper.decodeCQCode(msg.asString)
}, { code, why ->
if (code != 0) {
LogCenter.log("合并转发消息节点消息发送失败:$code($why)", Level.WARN)
}
it.resume(node.name to msgId)
}).first
}
}
}
val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin)
val to = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId)
msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply {
multiNodes.forEach { add(MultiMsgInfo(it.second, it.first)) }
}.also { it.reverse() }, from, to) { code, why ->
if (code != 0)
LogCenter.log("合并转发消息:$code($why)", Level.WARN)
}
return ok(data = EmptyJsonObject, echo = echo)
}.onFailure {
return error("error: $it", echo)
}
return logic("合并转发消息失败(unknown error)", echo)
}
override val requiredParams: Array<String> = arrayOf("group_id")
override fun path(): String = "send_group_forward_msg"
}

View File

@ -1,125 +0,0 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MultiMsgInfo
import kotlinx.atomicfu.atomic
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toSegments
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.tools.asInt
import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.asStringOrNull
import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
internal object SendGroupForwardMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupId = session.getString("group_id")
if (session.isArray("messages")) {
val messages = session.getArray("messages")
return invoke(messages, groupId, session.echo)
}
return logic("未知格式合并转发消息", session.echo)
}
suspend operator fun invoke(
message: JsonArray,
groupId: String,
echo: JsonElement = EmptyJsonString
): String {
kotlin.runCatching {
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
val selfUin = TicketSvc.getUin()
val msgs = message.map {
it.asJsonObject["data"].asJsonObject.let { data ->
if (data.containsKey("content"))
MessageNode(
name = data["name"].asStringOrNull ?: "",
content = data["content"]
)
else MessageIdNode(data["id"].asInt)
}
}.map {
if (it is MessageIdNode) {
val recordResult = MsgSvc.getMsg(it.id)
if (recordResult.isFailure) {
EmptyNode
} else {
val record = recordResult.getOrThrow()
MessageNode(
name = record.sendMemberName
.ifBlank { record.sendNickName }
.ifBlank { record.sendRemarkName }
.ifBlank { record.peerName },
content = record.toSegments().map { segment ->
segment.toJson()
}.json
)
}
} else {
it as MessageNode
}
}.filter {
it.content != null
}
lateinit var forwardMsgCallback: (() -> Unit)
val availableMsgSize = atomic(0)
val msgIds = msgs.map {
it.name to MessageHelper.sendMessageWithMsgId(MsgConstant.KCHATTYPEC2C, selfUin, it.content!!.let { msg ->
if (msg is JsonArray) msg else MessageHelper.decodeCQCode(msg.asString)
}, { code, why ->
if (code != 0) {
availableMsgSize.incrementAndGet()
LogCenter.log("合并转发消息节点消息发送失败:$code($why)", Level.WARN)
}
if (availableMsgSize.incrementAndGet() == msgs.size) {
forwardMsgCallback.invoke()
}
}).first
}
val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin)
val to = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId)
forwardMsgCallback = {
msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply {
msgIds.forEach { add(MultiMsgInfo(it.second, it.first)) }
}.also { it.reverse() }, from, to) { code, why ->
if (code != 0)
LogCenter.log("合并转发消息:$code($why)", Level.WARN)
}
}
return ok(data = EmptyJsonObject, echo = echo)
}.onFailure {
return error("error: $it", echo)
}
return logic("合并转发消息失败(unknown error)", echo)
}
override val requiredParams: Array<String> = arrayOf("group_id")
override fun path(): String = "send_group_forward_msg"
class MessageIdNode(
val id: Int
): Node
open class MessageNode(
val name: String,
val content: JsonElement?
): Node
object EmptyNode: MessageNode("", null)
interface Node
}

View File

@ -1,125 +0,0 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MultiMsgInfo
import kotlinx.atomicfu.atomic
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toSegments
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.tools.asInt
import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.asStringOrNull
import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
internal object SendPrivateForwardMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupId = session.getString("user_id")
if (session.isArray("messages")) {
val messages = session.getArray("messages")
return invoke(messages, groupId, session.echo)
}
return logic("未知格式合并转发消息", session.echo)
}
suspend operator fun invoke(
message: JsonArray,
userId: String,
echo: JsonElement = EmptyJsonString
): String {
kotlin.runCatching {
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
val selfUin = TicketSvc.getUin()
val msgs = message.map {
it.asJsonObject["data"].asJsonObject.let { data ->
if (data.containsKey("content"))
MessageNode(
name = data["name"].asStringOrNull ?: "",
content = data["content"]
)
else MessageIdNode(data["id"].asInt)
}
}.map {
if (it is MessageIdNode) {
val recordResult = MsgSvc.getMsg(it.id)
if (recordResult.isFailure) {
EmptyNode
} else {
val record = recordResult.getOrThrow()
MessageNode(
name = record.sendMemberName
.ifBlank { record.sendNickName }
.ifBlank { record.sendRemarkName }
.ifBlank { record.peerName },
content = record.toSegments().map { segment ->
segment.toJson()
}.json
)
}
} else {
it as MessageNode
}
}.filter {
it.content != null
}
lateinit var forwardMsgCallback: (() -> Unit)
val availableMsgSize = atomic(0)
val msgIds = msgs.map {
it.name to MessageHelper.sendMessageWithMsgId(MsgConstant.KCHATTYPEC2C, selfUin, it.content!!.let { msg ->
if (msg is JsonArray) msg else MessageHelper.decodeCQCode(msg.asString)
}, { code, why ->
if (code != 0) {
availableMsgSize.incrementAndGet()
LogCenter.log("合并转发消息节点消息发送失败:$code($why)", Level.WARN)
}
if (availableMsgSize.incrementAndGet() == msgs.size) {
forwardMsgCallback.invoke()
}
}).first
}
val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin)
val to = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, userId)
forwardMsgCallback = {
msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply {
msgIds.forEach { add(MultiMsgInfo(it.second, it.first)) }
}.also { it.reverse() }, from, to) { code, why ->
if (code != 0)
LogCenter.log("合并转发消息:$code($why)", Level.WARN)
}
}
return ok(data = EmptyJsonObject, echo = echo)
}.onFailure {
return error("error: $it", echo)
}
return logic("合并转发消息失败(unknown error)", echo)
}
override val requiredParams: Array<String> = arrayOf("user_id")
override fun path(): String = "send_private_forward_msg"
class MessageIdNode(
val id: Int
): Node
open class MessageNode(
val name: String,
val content: JsonElement?
): Node
object EmptyNode: MessageNode("", null)
interface Node
}

View File

@ -11,6 +11,7 @@ import io.ktor.server.routing.post
import io.ktor.server.routing.route
import moe.fuqiuluo.shamrock.helper.db.MessageDB
import moe.fuqiuluo.shamrock.remote.action.handlers.*
import moe.fuqiuluo.shamrock.remote.entries.Status
import moe.fuqiuluo.shamrock.tools.fetchGetOrNull
import moe.fuqiuluo.shamrock.tools.fetchGetOrThrow
import moe.fuqiuluo.shamrock.tools.fetchOrNull
@ -22,12 +23,21 @@ import moe.fuqiuluo.shamrock.tools.fetchPostOrThrow
import moe.fuqiuluo.shamrock.tools.getOrPost
import moe.fuqiuluo.shamrock.tools.isJsonData
import moe.fuqiuluo.shamrock.tools.isJsonString
import moe.fuqiuluo.shamrock.tools.respond
fun Routing.messageAction() {
route("/send_group_forward_msg") {
post {
val groupId = fetchPostOrNull("group_id")
val messages = fetchPostJsonArray("messages")
call.respondText(SendGroupForwardMsg(messages, groupId ?: ""), ContentType.Application.Json)
}
get {
respond(false, Status.InternalHandlerError, "Not support GET method")
}
}
post("/send_group_forward_msg") {
val groupId = fetchPostOrNull("group_id")
val messages = fetchPostJsonArray("messages")
call.respondText(SendGroupForwardMsg(messages, groupId ?: ""), ContentType.Application.Json)
}
post("/send_private_forward_msg") {