Merge remote-tracking branch 'origin/master'

This commit is contained in:
WhiteChi 2023-12-02 23:55:57 +08:00
commit a0ff4782db
7 changed files with 168 additions and 89 deletions

View File

@ -627,10 +627,16 @@ internal object MessageMaker {
else -> { else -> {
val info = GroupSvc.getTroopMemberInfoByUin(peerId, qq, true).onFailure { val info = GroupSvc.getTroopMemberInfoByUin(peerId, qq, true).onFailure {
LogCenter.log("无法获取群成员信息: $qq", Level.ERROR) LogCenter.log("无法获取群成员信息: $qq", Level.ERROR)
}.getOrThrow() }.getOrNull()
at.content = "@${info.troopnick if (info != null) {
.ifNullOrEmpty(info.friendnick) at.content = "@${
.ifNullOrEmpty(qq)}" info.troopnick
.ifNullOrEmpty(info.friendnick)
.ifNullOrEmpty(qq)
}"
} else {
at.content = "@${data["name"].asStringOrNull.ifNullOrEmpty(qq)}"
}
at.atType = MsgConstant.ATTYPEONE at.atType = MsgConstant.ATTYPEONE
at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong()) at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong())
} }

View File

@ -331,7 +331,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
MsgConstant.GRAYTIPELEMENTSUBTYPEXMLMSG -> { MsgConstant.GRAYTIPELEMENTSUBTYPEXMLMSG -> {
val notify = tip.xmlElement val notify = tip.xmlElement
when(notify.busiId) { when(notify.busiId) {
/* 群戳一戳 */1061L -> {} /* 群戳一戳 */1061L, /* 群打卡 */1068L -> {}
else -> LogCenter.log("不支持的灰条类型(XML): ${notify.busiId}", Level.WARN) else -> LogCenter.log("不支持的灰条类型(XML): ${notify.busiId}", Level.WARN)
} }
} }

View File

@ -2,9 +2,7 @@ package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MultiMsgInfo import com.tencent.qqnt.kernel.nativeinterface.MultiMsgInfo
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.*
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import moe.fuqiuluo.qqinterface.servlet.MsgSvc import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.qqinterface.servlet.TicketSvc import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toSegments import moe.fuqiuluo.qqinterface.servlet.msg.convert.toSegments
@ -18,19 +16,6 @@ import moe.fuqiuluo.shamrock.remote.service.data.ForwardMessageResult
import moe.fuqiuluo.shamrock.tools.* import moe.fuqiuluo.shamrock.tools.*
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
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 SendForwardMessage : IActionHandler() { internal object SendForwardMessage : IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String { override suspend fun internalHandle(session: ActionSession): String {
val detailType = session.getStringOrNull("detail_type") ?: session.getStringOrNull("message_type") val detailType = session.getStringOrNull("detail_type") ?: session.getStringOrNull("message_type")
@ -74,7 +59,7 @@ internal object SendForwardMessage : IActionHandler() {
suspend operator fun invoke( suspend operator fun invoke(
chatType: Int, chatType: Int,
peerId: String, peerId: String,
message: JsonArray, messages: JsonArray,
echo: JsonElement = EmptyJsonString echo: JsonElement = EmptyJsonString
): String { ): String {
kotlin.runCatching { kotlin.runCatching {
@ -83,63 +68,91 @@ internal object SendForwardMessage : IActionHandler() {
val msgService = sessionService.msgService val msgService = sessionService.msgService
val selfUin = TicketSvc.getUin() val selfUin = TicketSvc.getUin()
val nodes = message.map { val multiNodes = messages.map {
if (it.asJsonObject["type"].asStringOrNull != "node") return@map ForwardMsgNode.EmptyNode // 过滤非node类型消息段 if (it.asJsonObject["type"].asStringOrNull != "node") {
it.asJsonObject["data"].asJsonObject.let { data -> LogCenter.log("包含非node类型节点", Level.WARN)
if (data.containsKey("content")) { return@map null
if (data["content"] is JsonArray) {
data["content"].asJsonArray.forEach { msg ->
if (msg.asJsonObject["type"].asStringOrNull == "node") {
LogCenter.log("合并转发消息不支持嵌套", Level.WARN)
return@map ForwardMsgNode.EmptyNode
}
}
}
ForwardMsgNode.MessageNode(
name = data["name"].asStringOrNull ?: "",
content = data["content"]
)
} else ForwardMsgNode.MessageIdNode(data["id"].asInt)
} }
}.map { if (it.asJsonObject["data"] !is JsonObject) {
if (it is ForwardMsgNode.MessageIdNode) { LogCenter.log("data字段错误", Level.WARN)
val recordResult = MsgSvc.getMsg(it.id) return@map null
if (!recordResult.isFailure) { }
ForwardMsgNode.EmptyNode it.asJsonObject["data"].asJsonObject.let { data ->
} else { if (data.containsKey("id")) {
val record = recordResult.getOrThrow() val record = MsgSvc.getMsg(data["id"].asInt).getOrNull()
ForwardMsgNode.MessageNode( if (record == null) {
name = record.peerName, LogCenter.log("合并转发消息节点消息获取失败:${data["id"]}", Level.WARN)
content = record.toSegments().map { segment -> return@map null
} else {
record.peerName to record.toSegments().map { segment ->
segment.toJson() segment.toJson()
}.json }.json
) }
} else if (data.containsKey("content")) {
(data["name"].asStringOrNull ?: "Anno") to when (val raw = data["content"]) {
is JsonObject -> raw.asJsonArray
is JsonArray -> raw.asJsonArray
else -> MessageHelper.decodeCQCode(raw.asString)
}
} else {
LogCenter.log("消息节点缺少id或content字段", Level.WARN)
return@map null
} }
} else { }.let { node ->
it as ForwardMsgNode.MessageNode val content = node.second.map { msg ->
} when (msg.asJsonObject["type"].asStringOrNull ?: "text") {
}.filter { "at" -> {
it.content != null buildJsonObject {
}.map { node -> put("type", "text")
val result = MessageHelper.sendMessageNoCb(MsgConstant.KCHATTYPEC2C, selfUin, node.content.let { msg -> putJsonObject("data") {
when (msg) { put(
is JsonArray -> msg "text", "@${
is JsonObject -> listOf(msg).jsonArray msg.asJsonObject["data"].asJsonObject["name"].asStringOrNull.ifNullOrEmpty(
else -> MessageHelper.decodeCQCode(msg.asString) msg.asJsonObject["data"].asJsonObject["qq"].asString
)
}"
)
}
}
}
"voice" -> {
buildJsonObject {
put("type", "text")
putJsonObject("data") {
put("text", "[语音]")
}
}
}
"node" -> {
LogCenter.log("合并转发消息暂时不支持嵌套", Level.WARN)
buildJsonObject {
put("type", "text")
putJsonObject("data") {
put("text", "[合并转发消息]")
}
}
}
else -> msg
}
}.json
val result = MessageHelper.sendMessageNoCb(MsgConstant.KCHATTYPEC2C, selfUin, content)
if (result.first != 0) {
LogCenter.log("合并转发消息节点消息发送失败", Level.WARN)
} }
}) result.second to node.first
if (result.first != 0) {
LogCenter.log("合并转发消息节点消息发送失败", Level.WARN)
} }
return@map result.second }.filterNotNull()
}
val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin) val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin)
val to = MessageHelper.generateContact(chatType, peerId) val to = MessageHelper.generateContact(chatType, peerId)
val uniseq = MessageHelper.generateMsgId(chatType) val uniseq = MessageHelper.generateMsgId(chatType)
msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply { msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply {
nodes.forEach { add(MultiMsgInfo(it, "Anno")) } multiNodes.forEach { add(MultiMsgInfo(it.first, it.second)) }
}.also { it.reverse() }, from, to, MsgSvc.MessageCallback(peerId, uniseq.first)) }.also { it.reverse() }, from, to, MsgSvc.MessageCallback(peerId, uniseq.first))
return ok( return ok(
@ -154,7 +167,7 @@ internal object SendForwardMessage : IActionHandler() {
return logic("合并转发消息失败(unknown error)", echo) return logic("合并转发消息失败(unknown error)", echo)
} }
override val requiredParams: Array<String> = arrayOf("message") override val requiredParams: Array<String> = arrayOf("messages")
override fun path(): String = "send_forward_msg" override fun path(): String = "send_forward_msg"
} }

View File

@ -29,7 +29,7 @@ import moe.fuqiuluo.shamrock.tools.jsonArray
import moe.fuqiuluo.shamrock.tools.respond import moe.fuqiuluo.shamrock.tools.respond
fun Routing.messageAction() { fun Routing.messageAction() {
route("/send_group_forward_msg") { route("/send_group_forward_(msg|message)".toRegex()) {
post { post {
val groupId = fetchPostOrNull("group_id") val groupId = fetchPostOrNull("group_id")
val messages = fetchPostJsonArray("messages") val messages = fetchPostJsonArray("messages")
@ -40,7 +40,7 @@ fun Routing.messageAction() {
} }
} }
route("/send_private_forward_msg") { route("/send_private_forward_(msg|message)".toRegex()) {
post { post {
val userId = fetchPostOrNull("user_id") val userId = fetchPostOrNull("user_id")
val messages = fetchPostJsonArray("messages") val messages = fetchPostJsonArray("messages")
@ -51,6 +51,18 @@ fun Routing.messageAction() {
} }
} }
route("/send_forward_(msg|message)".toRegex()) {
post {
val userId = fetchPostOrNull("user_id")
val groupId = fetchPostOrNull("group_id")
val messages = fetchPostJsonArray("messages")
call.respondText(SendForwardMessage(MsgConstant.KCHATTYPEC2C, userId ?: groupId?: "", messages), ContentType.Application.Json)
}
get {
respond(false, Status.InternalHandlerError, "Not support GET method")
}
}
getOrPost("/get_forward_msg") { getOrPost("/get_forward_msg") {
val id = fetchOrThrow("id") val id = fetchOrThrow("id")
call.respondText(GetForwardMsg(id), ContentType.Application.Json) call.respondText(GetForwardMsg(id), ContentType.Application.Json)

View File

@ -25,6 +25,7 @@ import moe.fuqiuluo.shamrock.remote.service.data.push.RequestEvent
import moe.fuqiuluo.shamrock.remote.service.data.push.RequestSubType import moe.fuqiuluo.shamrock.remote.service.data.push.RequestSubType
import moe.fuqiuluo.shamrock.remote.service.data.push.RequestType import moe.fuqiuluo.shamrock.remote.service.data.push.RequestType
import moe.fuqiuluo.shamrock.remote.service.data.push.Sender import moe.fuqiuluo.shamrock.remote.service.data.push.Sender
import moe.fuqiuluo.shamrock.remote.service.data.push.SignDetail
import moe.fuqiuluo.shamrock.tools.ShamrockDsl import moe.fuqiuluo.shamrock.tools.ShamrockDsl
import moe.fuqiuluo.shamrock.tools.json import moe.fuqiuluo.shamrock.tools.json
import java.util.ArrayList import java.util.ArrayList
@ -222,6 +223,24 @@ internal object GlobalEventTransmitter: BaseSvc() {
* 群聊通知 通知器 * 群聊通知 通知器
*/ */
object GroupNoticeTransmitter { object GroupNoticeTransmitter {
suspend fun transGroupSign(time: Long, target: Long, action: String?, rankImg: String?, groupCode: Long): Boolean {
pushNotice(NoticeEvent(
time = time,
selfId = app.longAccountUin,
postType = PostType.Notice,
type = NoticeType.Notify,
subType = NoticeSubType.Sign,
userId = target,
groupId = groupCode,
target = target,
signDetail = SignDetail(
rankImg = rankImg,
action = action
)
))
return true
}
suspend fun transGroupPoke(time: Long, operation: Long, target: Long, action: String?, suffix: String?, actionImg: String?, groupCode: Long): Boolean { suspend fun transGroupPoke(time: Long, operation: Long, target: Long, action: String?, suffix: String?, actionImg: String?, groupCode: Long): Boolean {
pushNotice(NoticeEvent( pushNotice(NoticeEvent(
time = time, time = time,

View File

@ -42,6 +42,8 @@ internal enum class NoticeSubType {
@SerialName("kick_me") KickMe, @SerialName("kick_me") KickMe,
@SerialName("poke") Poke, @SerialName("poke") Poke,
@SerialName("sign") Sign,
@SerialName("title") Title, @SerialName("title") Title,
@SerialName("delete") Delete, @SerialName("delete") Delete,
@ -87,6 +89,9 @@ internal data class NoticeEvent(
// 戳一戳 // 戳一戳
@SerialName("poke_detail") val pokeDetail: PokeDetail? = null, @SerialName("poke_detail") val pokeDetail: PokeDetail? = null,
// 群打卡
@SerialName("sign_detail") val signDetail: SignDetail? = null,
) )
/** /**
@ -131,4 +136,11 @@ internal data class PokeDetail (
val suffix: String? = "", val suffix: String? = "",
@SerialName("action_img_url") @SerialName("action_img_url")
val actionImg: String? = "https://tianquan.gtimg.cn/nudgeaction/item/0/expression.jpg", val actionImg: String? = "https://tianquan.gtimg.cn/nudgeaction/item/0/expression.jpg",
)
@Serializable
internal data class SignDetail (
val action: String? = "今日第1个打卡",
@SerialName("rank_img")
val rankImg: String? = "",
) )

View File

@ -82,7 +82,7 @@ internal object PrimitiveListener {
12 -> onGroupBan(msgTime, pb) 12 -> onGroupBan(msgTime, pb)
16 -> onGroupTitleChange(msgTime, pb) 16 -> onGroupTitleChange(msgTime, pb)
17 -> onGroupRecall(msgTime, pb) 17 -> onGroupRecall(msgTime, pb)
20 -> onGroupPoke(msgTime, pb) 20 -> onGroupPokeAndGroupSign(msgTime, pb)
21 -> onEssenceMessage(msgTime, pb) 21 -> onEssenceMessage(msgTime, pb)
} }
} }
@ -188,6 +188,7 @@ internal object PrimitiveListener {
readPacket.discardExact(1) readPacket.discardExact(1)
ProtoUtils.decodeFromByteArray(readPacket.readBytes(readPacket.readShort().toInt())) ProtoUtils.decodeFromByteArray(readPacket.readBytes(readPacket.readShort().toInt()))
} else pb[1, 3, 2] } else pb[1, 3, 2]
readPacket.release()
val targetUin = detail[5, 5].asLong val targetUin = detail[5, 5].asLong
@ -219,6 +220,7 @@ internal object PrimitiveListener {
readPacket.discardExact(1) readPacket.discardExact(1)
ProtoUtils.decodeFromByteArray(readPacket.readBytes(readPacket.readShort().toInt())) ProtoUtils.decodeFromByteArray(readPacket.readBytes(readPacket.readShort().toInt()))
} else pb[1, 3, 2] } else pb[1, 3, 2]
readPacket.release()
val groupId = detail[4].asLong val groupId = detail[4].asLong
val mesSeq = detail[37].asInt val mesSeq = detail[37].asInt
@ -254,24 +256,14 @@ internal object PrimitiveListener {
} }
private suspend fun onGroupPoke(time: Long, pb: ProtoMap) { private suspend fun onGroupPokeAndGroupSign(time: Long, pb: ProtoMap) {
val groupCode1 = pb[1, 1, 1].asULong val groupCode = pb[1, 1, 1].asULong
var groupCode: Long = groupCode1
val readPacket = ByteReadPacket(pb[1, 3, 2].asByteArray) val readPacket = ByteReadPacket(pb[1, 3, 2].asByteArray)
val groupCode2 = readPacket.readBuf32Long() val detail = if (readPacket.readBuf32Long() == groupCode) {
var detail = if (groupCode2 == groupCode1) {
groupCode = groupCode2
readPacket.discardExact(1) readPacket.discardExact(1)
ProtoUtils.decodeFromByteArray(readPacket.readBytes(readPacket.readShort().toInt())) ProtoUtils.decodeFromByteArray(readPacket.readBytes(readPacket.readShort().toInt()))
} else pb[1, 3, 2] } else pb[1, 3, 2]
if (detail !is ProtoMap) {
groupCode = groupCode2
readPacket.discardExact(1)
detail = ProtoUtils.decodeFromByteArray(readPacket.readBytes(readPacket.readShort().toInt()))
}
readPacket.release() readPacket.release()
lateinit var target: String lateinit var target: String
@ -279,6 +271,7 @@ internal object PrimitiveListener {
var action: String? = null var action: String? = null
var suffix: String? = null var suffix: String? = null
var actionImg: String? = null var actionImg: String? = null
var rankImg: String? = null
detail[26][7] detail[26][7]
.asList .asList
.value .value
@ -287,18 +280,42 @@ internal object PrimitiveListener {
when (it[1].asUtf8String) { when (it[1].asUtf8String) {
"uin_str1" -> operation = value "uin_str1" -> operation = value
"uin_str2" -> target = value "uin_str2" -> target = value
// "nick_str1" -> operation_nick = value
// "nick_str2" -> operation_nick = value
"action_str" -> action = value "action_str" -> action = value
"alt_str1" -> action = value "alt_str1" -> action = value
"suffix_str" -> suffix = value "suffix_str" -> suffix = value
"action_img_url" -> actionImg = value "action_img_url" -> actionImg = value
"mqq_uin" -> target = value
// "mqq_nick" -> operation_nick = value
"user_sign" -> action = value
"rank_img" -> rankImg = value
// "sign_word" -> 我也要打卡
}
}
when (detail[26][2].asInt) {
1061 -> {
LogCenter.log("群戳一戳($groupCode): $operation $action $target $suffix")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupPoke(time, operation.toLong(), target.toLong(), action, suffix, actionImg, groupCode)
) {
LogCenter.log("群戳一戳推送失败!", Level.WARN)
} }
} }
LogCenter.log("群戳一戳($groupCode): $operation $action $target $suffix")
if (!GlobalEventTransmitter.GroupNoticeTransmitter 1068 -> {
.transGroupPoke(time, operation.toLong(), target.toLong(), action, suffix, actionImg, groupCode) LogCenter.log("群打卡($groupCode): $action $target")
) { if (!GlobalEventTransmitter.GroupNoticeTransmitter
LogCenter.log("群戳一戳推送失败!", Level.WARN) .transGroupSign(time, target.toLong(), action, rankImg, groupCode)
) {
LogCenter.log("群打卡推送失败!", Level.WARN)
}
}
else -> {
LogCenter.log("onGroupPokeAndGroupSign unknown type ${detail[2].asInt}", Level.WARN)
}
} }
} }