mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 13:12:17 +08:00
refactor send_forward_msg
This commit is contained in:
parent
4dc83fdeba
commit
ec56e32be1
@ -7,9 +7,9 @@ import kotlinx.serialization.protobuf.ProtoNumber
|
|||||||
data class Ptt(
|
data class Ptt(
|
||||||
@ProtoNumber(1) var fileType: UInt?=null,
|
@ProtoNumber(1) var fileType: UInt?=null,
|
||||||
@ProtoNumber(2) var srcUin: ULong?=null,
|
@ProtoNumber(2) var srcUin: ULong?=null,
|
||||||
@ProtoNumber(3) var fileUuid: ByteArray?=null,
|
@ProtoNumber(3) var fileUuid: String?=null,
|
||||||
@ProtoNumber(4) var fileMd5: ByteArray?=null,
|
@ProtoNumber(4) var fileMd5: ByteArray?=null,
|
||||||
@ProtoNumber(5) var fileName: ByteArray?=null,
|
@ProtoNumber(5) var fileName: String?=null,
|
||||||
@ProtoNumber(6) var fileSize: UInt?=null,
|
@ProtoNumber(6) var fileSize: UInt?=null,
|
||||||
@ProtoNumber(7) var reserve: ByteArray?=null,
|
@ProtoNumber(7) var reserve: ByteArray?=null,
|
||||||
@ProtoNumber(8) var fileId: UInt?=null,
|
@ProtoNumber(8) var fileId: UInt?=null,
|
||||||
@ -22,11 +22,19 @@ data class Ptt(
|
|||||||
@ProtoNumber(15) var magicPttIndex: UInt?=null,
|
@ProtoNumber(15) var magicPttIndex: UInt?=null,
|
||||||
@ProtoNumber(16) var voiceSwitch: UInt?=null,
|
@ProtoNumber(16) var voiceSwitch: UInt?=null,
|
||||||
@ProtoNumber(17) var pttUrl: ByteArray?=null,
|
@ProtoNumber(17) var pttUrl: ByteArray?=null,
|
||||||
@ProtoNumber(18) var groupFileKey: ByteArray?=null,
|
@ProtoNumber(18) var groupFileKey: String?=null,
|
||||||
@ProtoNumber(19) var time: UInt?=null,
|
@ProtoNumber(19) var time: UInt?=null,
|
||||||
@ProtoNumber(20) var downPara: ByteArray?=null,
|
@ProtoNumber(20) var downPara: ByteArray?=null,
|
||||||
@ProtoNumber(29) var format: UInt?=null,
|
@ProtoNumber(29) var format: UInt?=null,
|
||||||
@ProtoNumber(30) var pbReserve: ByteArray?=null,
|
@ProtoNumber(30) var pbReserve: PbReserve?=null,
|
||||||
@ProtoNumber(31) var rptPttUrls: List<String>? = null,
|
@ProtoNumber(31) var rptPttUrls: List<String>? = null,
|
||||||
@ProtoNumber(32) var downloadFlag: UInt?=null,
|
@ProtoNumber(32) var downloadFlag: UInt?=null,
|
||||||
)
|
){
|
||||||
|
companion object{
|
||||||
|
@Serializable
|
||||||
|
data class PbReserve(
|
||||||
|
@ProtoNumber(2) var magic: Int?=null,
|
||||||
|
@ProtoNumber(7) var reserve: Int?=null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ import kotlinx.serialization.protobuf.ProtoNumber
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class RichText(
|
data class RichText(
|
||||||
@ProtoNumber(1) val attr: Attr? = null,
|
@ProtoNumber(1) val attr: Attr? = null,
|
||||||
@ProtoNumber(2) val elements: List<Elem>? = null,
|
@ProtoNumber(2) var elements: List<Elem>? = null,
|
||||||
@ProtoNumber(3) val not_online_file: NotOnlineFile? = null,
|
@ProtoNumber(3) val not_online_file: NotOnlineFile? = null,
|
||||||
@ProtoNumber(4) val ptt: Ptt? = null,
|
@ProtoNumber(4) val ptt: Ptt? = null,
|
||||||
@ProtoNumber(5) val tmp_ptt: TmpPtt? = null,
|
@ProtoNumber(5) val tmp_ptt: TmpPtt? = null,
|
||||||
|
@ -9,7 +9,9 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.msg.MessageSegment
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.msg.toJson
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.toListMap
|
import moe.fuqiuluo.qqinterface.servlet.msg.toListMap
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
|
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
|
||||||
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
||||||
@ -25,10 +27,12 @@ import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
|||||||
import moe.fuqiuluo.shamrock.xposed.helper.msgService
|
import moe.fuqiuluo.shamrock.xposed.helper.msgService
|
||||||
import moe.fuqiuluo.symbols.decodeProtobuf
|
import moe.fuqiuluo.symbols.decodeProtobuf
|
||||||
import protobuf.auto.toByteArray
|
import protobuf.auto.toByteArray
|
||||||
import protobuf.message.PushMsgBody
|
import protobuf.message.*
|
||||||
import protobuf.message.longmsg.*
|
import protobuf.message.longmsg.*
|
||||||
|
import java.util.*
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal object MsgSvc : BaseSvc() {
|
internal object MsgSvc : BaseSvc() {
|
||||||
private suspend fun prepareTempChatFromGroup(
|
private suspend fun prepareTempChatFromGroup(
|
||||||
@ -207,45 +211,195 @@ internal object MsgSvc : BaseSvc() {
|
|||||||
}
|
}
|
||||||
val result =
|
val result =
|
||||||
MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, fromId, MessageCallback(peedId, 0))
|
MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, fromId, MessageCallback(peedId, 0))
|
||||||
if (result.isFailure) {
|
.getOrElse { return Result.failure(it) }
|
||||||
LogCenter.log("sendToAio: " + result.exceptionOrNull()?.stackTraceToString(), Level.ERROR)
|
return if (result.isTimeout) {
|
||||||
return result
|
|
||||||
}
|
|
||||||
val sendResult = result.getOrThrow()
|
|
||||||
return if (sendResult.isTimeout) {
|
|
||||||
// 发送失败,可能网络问题出现红色感叹号,重试
|
// 发送失败,可能网络问题出现红色感叹号,重试
|
||||||
// 例如 rich media transfer failed
|
// 例如 rich media transfer failed
|
||||||
delay(100)
|
delay(100)
|
||||||
MessageHelper.resendMsg(chatType, peedId, fromId, sendResult.qqMsgId, retryCnt, sendResult.msgHashId)
|
MessageHelper.resendMsg(chatType, peedId, fromId, result.qqMsgId, retryCnt, result.msgHashId)
|
||||||
} else {
|
} else {
|
||||||
result
|
Result.success(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun uploadMultiMsg(
|
suspend fun uploadMultiMsg(
|
||||||
uid: String,
|
chatType: Int,
|
||||||
groupUin: String?,
|
peerId: String,
|
||||||
messages: List<PushMsgBody>,
|
fromId: String,
|
||||||
): Result<String> {
|
messages: JsonArray,
|
||||||
|
retryCnt: Int,
|
||||||
|
): Result<MessageSegment> {
|
||||||
|
var i = -1
|
||||||
|
val desc = MutableList(messages.size) { "" }
|
||||||
|
val forwardMsg = mutableMapOf<String, String>()
|
||||||
|
|
||||||
|
val msgs = messages.mapNotNull { msg ->
|
||||||
|
kotlin.runCatching {
|
||||||
|
val data = msg.asJsonObject["data"].asJsonObject
|
||||||
|
if (data.containsKey("id")) {
|
||||||
|
val record = getMsg(data["id"].asInt).getOrElse {
|
||||||
|
error("合并转发消息节点消息(id = ${data["id"].asInt})获取失败:$it")
|
||||||
|
}
|
||||||
|
PushMsgBody(
|
||||||
|
msgHead = ResponseHead(
|
||||||
|
peerUid = record.senderUid,
|
||||||
|
receiverUid = record.peerUid,
|
||||||
|
forward = ResponseForward(
|
||||||
|
friendName = record.sendNickName
|
||||||
|
),
|
||||||
|
responseGrp = if (record.chatType == MsgConstant.KCHATTYPEGROUP) ResponseGrp(
|
||||||
|
groupCode = record.peerUin.toULong(),
|
||||||
|
memberCard = record.sendMemberName,
|
||||||
|
u1 = 2
|
||||||
|
) else null
|
||||||
|
),
|
||||||
|
contentHead = ContentHead(
|
||||||
|
msgType = when (record.chatType) {
|
||||||
|
MsgConstant.KCHATTYPEC2C -> 9
|
||||||
|
MsgConstant.KCHATTYPEGROUP -> 82
|
||||||
|
else -> throw UnsupportedOperationException(
|
||||||
|
"Unsupported chatType: $chatType"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
msgSubType = if (record.chatType == MsgConstant.KCHATTYPEC2C) 175 else null,
|
||||||
|
divSeq = if (record.chatType == MsgConstant.KCHATTYPEC2C) 175 else null,
|
||||||
|
msgViaRandom = record.msgId,
|
||||||
|
sequence = record.msgSeq, // idk what this is(i++)
|
||||||
|
msgTime = record.msgTime,
|
||||||
|
u2 = 1,
|
||||||
|
u6 = 0,
|
||||||
|
u7 = 0,
|
||||||
|
msgSeq = if (record.chatType == MsgConstant.KCHATTYPEC2C) record.msgSeq else null, // seq for dm
|
||||||
|
forwardHead = ForwardHead(
|
||||||
|
u1 = 0,
|
||||||
|
u2 = 0,
|
||||||
|
u3 = 0,
|
||||||
|
ub641 = "",
|
||||||
|
avatar = ""
|
||||||
|
)
|
||||||
|
),
|
||||||
|
body = MsgBody(
|
||||||
|
richText = MessageHelper.messageArrayToRichText(
|
||||||
|
record.chatType,
|
||||||
|
record.msgId,
|
||||||
|
record.peerUin.toString(),
|
||||||
|
record.elements.toSegments(
|
||||||
|
record.chatType,
|
||||||
|
record.peerUin.toString(),
|
||||||
|
"0"
|
||||||
|
).onEach { segment ->
|
||||||
|
if (segment.type == "forward")
|
||||||
|
forwardMsg[segment.data["filename"] as String] =
|
||||||
|
segment.data["id"] as String
|
||||||
|
}.toJson()
|
||||||
|
).getOrElse { throw Exception("消息合成失败: $it") }.let {
|
||||||
|
desc[++i] = record.sendMemberName.ifEmpty { record.sendNickName } + ": " + it.first
|
||||||
|
it.second
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else if (data.containsKey("content")) {
|
||||||
|
PushMsgBody(
|
||||||
|
msgHead = ResponseHead(
|
||||||
|
peer = data["uin"]?.asLong ?: TicketSvc.getUin().toLong(),
|
||||||
|
peerUid = data["uid"]?.asString ?: TicketSvc.getUid(),
|
||||||
|
receiverUid = TicketSvc.getUid(),
|
||||||
|
forward = ResponseForward(
|
||||||
|
friendName = data["name"]?.asStringOrNull ?: TicketSvc.getNickname()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
contentHead = ContentHead(
|
||||||
|
msgType = 9,
|
||||||
|
msgSubType = 175,
|
||||||
|
divSeq = 175,
|
||||||
|
msgViaRandom = Random.nextLong(),
|
||||||
|
sequence = data["seq"]?.asLong ?: Random.nextLong(),
|
||||||
|
msgTime = data["time"]?.asLong ?: (System.currentTimeMillis() / 1000),
|
||||||
|
u2 = 1,
|
||||||
|
u6 = 0,
|
||||||
|
u7 = 0,
|
||||||
|
msgSeq = data["seq"]?.asLong ?: Random.nextLong(),
|
||||||
|
forwardHead = ForwardHead(
|
||||||
|
u1 = 0,
|
||||||
|
u2 = 0,
|
||||||
|
u3 = 2,
|
||||||
|
ub641 = "",
|
||||||
|
avatar = ""
|
||||||
|
)
|
||||||
|
),
|
||||||
|
body = MsgBody(
|
||||||
|
richText = MessageHelper.messageArrayToRichText(
|
||||||
|
chatType = chatType,
|
||||||
|
msgId = Random.nextLong(),
|
||||||
|
peerId = data["uin"]?.asString ?: TicketSvc.getUin(),
|
||||||
|
messageList = when (data["content"]) {
|
||||||
|
is JsonObject -> listOf(data["content"] as JsonObject).json
|
||||||
|
is JsonArray -> data["content"] as JsonArray
|
||||||
|
else -> MessageHelper.decodeCQCode(data["content"].asString)
|
||||||
|
}.onEach { element ->
|
||||||
|
val elementData = element.asJsonObject["data"].asJsonObject
|
||||||
|
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
|
||||||
|
?: TicketSvc.getNickname()) + ": " + it.first
|
||||||
|
it.second
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
error("消息节点缺少id或content字段")
|
||||||
|
}
|
||||||
|
}.getOrElse {
|
||||||
|
LogCenter.log("消息节点解析失败:$it", Level.WARN)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}.ifEmpty { return Result.failure(Exception("消息节点为空")) }
|
||||||
|
|
||||||
val payload = LongMsgPayload(
|
val payload = LongMsgPayload(
|
||||||
action = listOf(
|
action = mutableListOf(
|
||||||
LongMsgAction(
|
LongMsgAction(
|
||||||
command = "MultiMsg",
|
command = "MultiMsg",
|
||||||
data = LongMsgContent(
|
data = LongMsgContent(
|
||||||
body = messages
|
body = msgs
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
).apply {
|
||||||
|
forwardMsg.map { msg ->
|
||||||
|
addAll(getMultiMsg(msg.value).getOrElse { return Result.failure(Exception("无法获取嵌套转发消息: $it")) }
|
||||||
|
.map { action ->
|
||||||
|
if (action.command == "MultiMsg") LongMsgAction(
|
||||||
|
command = msg.key,
|
||||||
|
data = action.data
|
||||||
|
) else action
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
LogCenter.log(payload.toByteArray().toHexString(), Level.DEBUG)
|
LogCenter.log(payload.toByteArray().toHexString(), Level.DEBUG)
|
||||||
|
|
||||||
val req = LongMsgReq(
|
val req = LongMsgReq(
|
||||||
sendInfo = SendLongMsgInfo(
|
sendInfo = when (chatType) {
|
||||||
type = if (groupUin == null) 1 else 3,
|
MsgConstant.KCHATTYPEC2C -> SendLongMsgInfo(
|
||||||
uid = LongMsgUid(groupUin ?: uid),
|
type = 1,
|
||||||
groupUin = groupUin?.toInt(),
|
uid = LongMsgUid(peerId),
|
||||||
payload = DeflateTools.gzip(payload.toByteArray())
|
payload = DeflateTools.gzip(payload.toByteArray())
|
||||||
),
|
)
|
||||||
|
|
||||||
|
MsgConstant.KCHATTYPEGROUP -> SendLongMsgInfo(
|
||||||
|
type = 3,
|
||||||
|
uid = LongMsgUid(fromId),
|
||||||
|
groupUin = fromId.toInt(),
|
||||||
|
payload = DeflateTools.gzip(payload.toByteArray())
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> throw UnsupportedOperationException(
|
||||||
|
"Unsupported chatType: $chatType"
|
||||||
|
)
|
||||||
|
},
|
||||||
setting = LongMsgSettings(
|
setting = LongMsgSettings(
|
||||||
field1 = 4,
|
field1 = 4,
|
||||||
field2 = 2,
|
field2 = 2,
|
||||||
@ -253,17 +407,29 @@ internal object MsgSvc : BaseSvc() {
|
|||||||
field4 = 0
|
field4 = 0
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val buffer = sendBufferAW(
|
val buffer = sendBufferAW(
|
||||||
"trpc.group.long_msg_interface.MsgService.SsoSendLongMsg",
|
"trpc.group.long_msg_interface.MsgService.SsoSendLongMsg",
|
||||||
true,
|
true,
|
||||||
req.toByteArray()
|
req.toByteArray()
|
||||||
) ?: return Result.failure(Exception("unable to upload multi message"))
|
) ?: return Result.failure(Exception("unable to upload multi message"))
|
||||||
val rsp = buffer.slice(4).decodeProtobuf<LongMsgRsp>()
|
val rsp = buffer.slice(4).decodeProtobuf<LongMsgRsp>()
|
||||||
return rsp.sendResult?.resId?.let { Result.success(it) }
|
val resId = rsp.sendResult?.resId ?: return Result.failure(Exception("unable to upload multi message"))
|
||||||
?: Result.failure(Exception("unable to upload multi message"))
|
val filename = UUID.randomUUID().toString().uppercase()
|
||||||
|
return Result.success(
|
||||||
|
MessageSegment(
|
||||||
|
"forward",
|
||||||
|
mapOf(
|
||||||
|
"id" to resId,
|
||||||
|
"filename" to filename,
|
||||||
|
"summary" to "查看${desc.size}条转发消息",
|
||||||
|
"desc" to desc.slice(0..if (i < 3) i else 3).joinToString("\n")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getMultiMsg(resId: String): Result<List<MessageDetail>> {
|
suspend fun getMultiMsg(resId: String): Result<List<LongMsgAction>> {
|
||||||
val req = LongMsgReq(
|
val req = LongMsgReq(
|
||||||
recvInfo = RecvLongMsgInfo(
|
recvInfo = RecvLongMsgInfo(
|
||||||
uid = LongMsgUid(TicketSvc.getUid()),
|
uid = LongMsgUid(TicketSvc.getUid()),
|
||||||
@ -284,11 +450,18 @@ internal object MsgSvc : BaseSvc() {
|
|||||||
) ?: return Result.failure(Exception("unable to get multi message"))
|
) ?: return Result.failure(Exception("unable to get multi message"))
|
||||||
val rsp = buffer.slice(4).decodeProtobuf<LongMsgRsp>()
|
val rsp = buffer.slice(4).decodeProtobuf<LongMsgRsp>()
|
||||||
val zippedPayload = DeflateTools.ungzip(
|
val zippedPayload = DeflateTools.ungzip(
|
||||||
rsp.recvResult?.payload ?: return Result.failure(Exception("unable to get multi message"))
|
rsp.recvResult?.payload ?: return Result.failure(Exception("payload is empty"))
|
||||||
)
|
)
|
||||||
LogCenter.log(zippedPayload.toHexString(), Level.DEBUG)
|
LogCenter.log(zippedPayload.toHexString(), Level.DEBUG)
|
||||||
val payload = zippedPayload.decodeProtobuf<LongMsgPayload>()
|
return Result.success(
|
||||||
payload.action?.forEach {
|
zippedPayload.decodeProtobuf<LongMsgPayload>().action
|
||||||
|
?: return Result.failure(Exception("action is empty"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getForwardMsg(resId: String): Result<List<MessageDetail>> {
|
||||||
|
val result = getMultiMsg(resId).getOrElse { return Result.failure(it) }
|
||||||
|
result.forEach {
|
||||||
if (it.command == "MultiMsg") {
|
if (it.command == "MultiMsg") {
|
||||||
return Result.success(it.data?.body?.map { msg ->
|
return Result.success(it.data?.body?.map { msg ->
|
||||||
val chatType =
|
val chatType =
|
||||||
@ -296,27 +469,29 @@ internal object MsgSvc : BaseSvc() {
|
|||||||
MessageDetail(
|
MessageDetail(
|
||||||
time = msg.contentHead?.msgTime?.toInt() ?: 0,
|
time = msg.contentHead?.msgTime?.toInt() ?: 0,
|
||||||
msgType = MessageHelper.obtainDetailTypeByMsgType(chatType),
|
msgType = MessageHelper.obtainDetailTypeByMsgType(chatType),
|
||||||
msgId = 0, // MessageHelper.generateMsgIdHash(chatType, msg.content!!.msgViaRandom), msgViaRandom 为空
|
msgId = 0, // msgViaRandom为空 tx不给
|
||||||
|
qqMsgId = 0,
|
||||||
msgSeq = msg.contentHead!!.msgSeq ?: 0,
|
msgSeq = msg.contentHead!!.msgSeq ?: 0,
|
||||||
realId = msg.contentHead!!.msgSeq ?: 0,
|
realId = msg.contentHead!!.msgSeq ?: 0,
|
||||||
sender = MessageSender(
|
sender = MessageSender(
|
||||||
msg.msgHead?.peer ?: 0,
|
msg.msgHead?.peer ?: 0,
|
||||||
msg.msgHead?.responseGrp?.memberCard?.ifEmpty { msg.msgHead?.forward?.friendName }
|
msg.msgHead?.responseGrp?.memberCard ?: msg.msgHead?.forward?.friendName ?: "",
|
||||||
?: msg.msgHead?.forward?.friendName ?: "",
|
|
||||||
"unknown",
|
"unknown",
|
||||||
0,
|
0,
|
||||||
msg.msgHead?.peerUid ?: "",
|
msg.msgHead?.peerUid ?: "",
|
||||||
msg.msgHead?.peerUid ?: ""
|
msg.msgHead?.peerUid ?: ""
|
||||||
),
|
),
|
||||||
message = msg.body?.richText?.elements?.toSegments(chatType, msg.msgHead?.peer.toString(), "0")
|
message = msg.body?.richText?.toSegments(
|
||||||
?.toListMap() ?: emptyList(),
|
chatType,
|
||||||
|
msg.msgHead?.peer.toString(),
|
||||||
|
"0"
|
||||||
|
)?.toListMap() ?: emptyList(),
|
||||||
peerId = msg.msgHead?.peer ?: 0,
|
peerId = msg.msgHead?.peer ?: 0,
|
||||||
groupId = if (chatType == MsgConstant.KCHATTYPEGROUP) msg.msgHead?.responseGrp?.groupCode?.toLong()
|
groupId = if (chatType == MsgConstant.KCHATTYPEGROUP) msg.msgHead?.responseGrp?.groupCode?.toLong()
|
||||||
?: 0 else 0,
|
?: 0 else 0,
|
||||||
targetId = if (chatType != MsgConstant.KCHATTYPEGROUP) msg.msgHead?.peer ?: 0 else 0
|
targetId = if (chatType != MsgConstant.KCHATTYPEGROUP) msg.msgHead?.peer ?: 0 else 0
|
||||||
)
|
)
|
||||||
}
|
} ?: return Result.failure(Exception("Msg is empty")))
|
||||||
?: return Result.failure(Exception("Msg is empty")))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.failure(Exception("Can't find msg"))
|
return Result.failure(Exception("Can't find msg"))
|
||||||
|
@ -10,24 +10,18 @@ import io.ktor.utils.io.core.writeInt
|
|||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.MessageTempHandler
|
import moe.fuqiuluo.qqinterface.servlet.msg.MessageTempHandler
|
||||||
|
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
|
import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
|
||||||
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
|
|
||||||
import moe.fuqiuluo.shamrock.tools.broadcast
|
import moe.fuqiuluo.shamrock.tools.broadcast
|
||||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||||
import protobuf.message.element.LightAppElem
|
|
||||||
import protobuf.message.PushMsgBody
|
|
||||||
import protobuf.message.ContentHead
|
|
||||||
import protobuf.message.Elem
|
|
||||||
import protobuf.message.RichText
|
|
||||||
import protobuf.message.ResponseHead
|
|
||||||
import protobuf.message.MsgBody
|
|
||||||
import protobuf.push.MessagePush
|
|
||||||
import mqq.app.MobileQQ
|
import mqq.app.MobileQQ
|
||||||
import protobuf.auto.toByteArray
|
import protobuf.auto.toByteArray
|
||||||
|
import protobuf.message.*
|
||||||
|
import protobuf.message.element.LightAppElem
|
||||||
|
import protobuf.push.MessagePush
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.text.toByteArray
|
||||||
|
|
||||||
internal object PacketSvc: BaseSvc() {
|
internal object PacketSvc : BaseSvc() {
|
||||||
/**
|
/**
|
||||||
* 伪造收到Json卡片消息
|
* 伪造收到Json卡片消息
|
||||||
*/
|
*/
|
||||||
@ -36,7 +30,7 @@ internal object PacketSvc: BaseSvc() {
|
|||||||
listOf(
|
listOf(
|
||||||
Elem(
|
Elem(
|
||||||
lightApp = LightAppElem((byteArrayOf(1) + DeflateTools.compress(content.toByteArray())))
|
lightApp = LightAppElem((byteArrayOf(1) + DeflateTools.compress(content.toByteArray())))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,7 +38,12 @@ internal object PacketSvc: BaseSvc() {
|
|||||||
private suspend fun fakeReceiveSelfMsg(msgService: IKernelMsgService, builder: () -> List<Elem>): Long {
|
private suspend fun fakeReceiveSelfMsg(msgService: IKernelMsgService, builder: () -> List<Elem>): Long {
|
||||||
val latestMsg = withTimeoutOrNull(3000) {
|
val latestMsg = withTimeoutOrNull(3000) {
|
||||||
suspendCancellableCoroutine {
|
suspendCancellableCoroutine {
|
||||||
msgService.getMsgs(Contact(MsgConstant.KCHATTYPEC2C, app.currentUid, ""), 0L, 1, true) { code, why, msgs ->
|
msgService.getMsgs(
|
||||||
|
Contact(MsgConstant.KCHATTYPEC2C, app.currentUid, ""),
|
||||||
|
0L,
|
||||||
|
1,
|
||||||
|
true
|
||||||
|
) { code, why, msgs ->
|
||||||
it.resume(GetHistoryMsg.GetMsgResult(code, why, msgs))
|
it.resume(GetHistoryMsg.GetMsgResult(code, why, msgs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,9 +71,11 @@ internal object PacketSvc: BaseSvc() {
|
|||||||
u4 = msgSeq - 2,
|
u4 = msgSeq - 2,
|
||||||
u5 = msgSeq
|
u5 = msgSeq
|
||||||
),
|
),
|
||||||
body = MsgBody(RichText(
|
body = MsgBody(
|
||||||
elements = builder()
|
RichText(
|
||||||
))
|
elements = builder()
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,21 +1,46 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.msg
|
package moe.fuqiuluo.qqinterface.servlet.msg
|
||||||
|
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.converter.ElemConverter
|
import moe.fuqiuluo.qqinterface.servlet.msg.converter.ElemConverter
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.converter.NtMsgElementConverter
|
import moe.fuqiuluo.qqinterface.servlet.msg.converter.NtMsgElementConverter
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
||||||
import moe.fuqiuluo.shamrock.helper.Level
|
import moe.fuqiuluo.shamrock.helper.Level
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||||
|
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||||
import protobuf.message.Elem
|
import protobuf.message.Elem
|
||||||
|
import protobuf.message.RichText
|
||||||
|
|
||||||
@JvmName("elemListToSegments")
|
@JvmName("richTextToSegments")
|
||||||
internal suspend fun List<Elem>.toSegments(
|
internal suspend fun RichText.toSegments(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
subPeer: String
|
subPeer: String
|
||||||
): List<MessageSegment> {
|
): List<MessageSegment> {
|
||||||
val messageData = arrayListOf<MessageSegment>()
|
val messageData = arrayListOf<MessageSegment>()
|
||||||
this.forEach { msg ->
|
if (ptt != null) {
|
||||||
|
val md5 = ptt!!.fileMd5!!
|
||||||
|
messageData.add(
|
||||||
|
MessageSegment(
|
||||||
|
"record", mapOf(
|
||||||
|
"file" to md5.toHexString(),
|
||||||
|
"url" to when (chatType) {
|
||||||
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt!!.fileUuid!!)
|
||||||
|
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl(
|
||||||
|
"0",
|
||||||
|
md5,
|
||||||
|
ptt!!.groupFileKey!!
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> throw UnsupportedOperationException("Not supported chat type: $chatType")
|
||||||
|
},
|
||||||
|
"magic" to ptt!!.pbReserve?.magic,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
elements?.forEach { msg ->
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val elementType = if (msg.text != null) {
|
val elementType = if (msg.text != null) {
|
||||||
1
|
1
|
||||||
@ -29,7 +54,7 @@ internal suspend fun List<Elem>.toSegments(
|
|||||||
37
|
37
|
||||||
} else if (msg.srcMsg != null) {
|
} else if (msg.srcMsg != null) {
|
||||||
45
|
45
|
||||||
} else if (msg.lightApp != null) {
|
} else if (msg.lightApp != null) {
|
||||||
51
|
51
|
||||||
} else if (msg.commonElem != null) {
|
} else if (msg.commonElem != null) {
|
||||||
53
|
53
|
||||||
|
@ -12,8 +12,8 @@ internal data class MessageSegment(
|
|||||||
) {
|
) {
|
||||||
fun toJson(): JsonObject {
|
fun toJson(): JsonObject {
|
||||||
return mapOf(
|
return mapOf(
|
||||||
"type" to type.json,
|
"type" to type,
|
||||||
"data" to data.json
|
"data" to data
|
||||||
).json
|
).json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,6 +29,6 @@ internal fun List<MessageSegment>.toListMap(): List<Map<String, JsonElement>> {
|
|||||||
mapOf(
|
mapOf(
|
||||||
"type" to it.type.json,
|
"type" to it.type.json,
|
||||||
"data" to it.data.json
|
"data" to it.data.json
|
||||||
).json
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,11 +13,13 @@ import moe.fuqiuluo.shamrock.helper.LogCenter
|
|||||||
import moe.fuqiuluo.shamrock.helper.db.ImageDB
|
import moe.fuqiuluo.shamrock.helper.db.ImageDB
|
||||||
import moe.fuqiuluo.shamrock.helper.db.ImageMapping
|
import moe.fuqiuluo.shamrock.helper.db.ImageMapping
|
||||||
import moe.fuqiuluo.shamrock.helper.db.MessageDB
|
import moe.fuqiuluo.shamrock.helper.db.MessageDB
|
||||||
|
import moe.fuqiuluo.shamrock.tools.asJsonArray
|
||||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||||
import moe.fuqiuluo.shamrock.tools.asJsonObject
|
import moe.fuqiuluo.shamrock.tools.asJsonObject
|
||||||
import moe.fuqiuluo.shamrock.tools.asString
|
import moe.fuqiuluo.shamrock.tools.asString
|
||||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||||
import moe.fuqiuluo.symbols.decodeProtobuf
|
import moe.fuqiuluo.symbols.decodeProtobuf
|
||||||
|
import moe.fuqiuluo.shamrock.tools.slice
|
||||||
import protobuf.message.Elem
|
import protobuf.message.Elem
|
||||||
import protobuf.message.element.commelem.ButtonExtra
|
import protobuf.message.element.commelem.ButtonExtra
|
||||||
import protobuf.message.element.commelem.MarkdownExtra
|
import protobuf.message.element.commelem.MarkdownExtra
|
||||||
@ -335,10 +337,8 @@ internal object ElemConverter {
|
|||||||
element: Elem
|
element: Elem
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val data = element.lightApp!!.data!!
|
val data = element.lightApp!!.data!!
|
||||||
val jsonStr =
|
val jsonStr = String(if (data[0].toInt() == 1) DeflateTools.uncompress(data.slice(1)) else data.slice(1))
|
||||||
(if (data[0].toInt() == 1) DeflateTools.uncompress(data.sliceArray(1 until data.size)) else data.sliceArray(
|
LogCenter.log(jsonStr, Level.DEBUG)
|
||||||
1 until data.size
|
|
||||||
)).toString()
|
|
||||||
val json = jsonStr.asJsonObject
|
val json = jsonStr.asJsonObject
|
||||||
return when (json["app"].asString) {
|
return when (json["app"].asString) {
|
||||||
"com.tencent.multimsg" -> {
|
"com.tencent.multimsg" -> {
|
||||||
@ -346,7 +346,10 @@ internal object ElemConverter {
|
|||||||
MessageSegment(
|
MessageSegment(
|
||||||
type = "forward",
|
type = "forward",
|
||||||
data = mapOf(
|
data = mapOf(
|
||||||
"id" to info["resid"].asString
|
"id" to info["resid"].asString,
|
||||||
|
"filename" to info["uniseq"].asString,
|
||||||
|
"summary" to info["summary"].asString,
|
||||||
|
"desc" to info["news"].asJsonArray.joinToString("\n") { it.asJsonObject["text"].asString }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,6 @@ package moe.fuqiuluo.qqinterface.servlet.msg.converter
|
|||||||
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||||
import kotlinx.serialization.json.add
|
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
|
||||||
import kotlinx.serialization.json.put
|
|
||||||
import kotlinx.serialization.json.putJsonArray
|
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.MessageSegment
|
import moe.fuqiuluo.qqinterface.servlet.msg.MessageSegment
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
||||||
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
||||||
@ -15,6 +11,7 @@ import moe.fuqiuluo.shamrock.helper.MessageHelper
|
|||||||
import moe.fuqiuluo.shamrock.helper.db.ImageDB
|
import moe.fuqiuluo.shamrock.helper.db.ImageDB
|
||||||
import moe.fuqiuluo.shamrock.helper.db.ImageMapping
|
import moe.fuqiuluo.shamrock.helper.db.ImageMapping
|
||||||
import moe.fuqiuluo.shamrock.helper.db.MessageDB
|
import moe.fuqiuluo.shamrock.helper.db.MessageDB
|
||||||
|
import moe.fuqiuluo.shamrock.tools.asJsonArray
|
||||||
import moe.fuqiuluo.shamrock.tools.asJsonObject
|
import moe.fuqiuluo.shamrock.tools.asJsonObject
|
||||||
import moe.fuqiuluo.shamrock.tools.asString
|
import moe.fuqiuluo.shamrock.tools.asString
|
||||||
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
|
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
|
||||||
@ -242,16 +239,10 @@ internal object NtMsgElementConverter {
|
|||||||
data = hashMapOf(
|
data = hashMapOf(
|
||||||
"file" to md5,
|
"file" to md5,
|
||||||
"url" to when (chatType) {
|
"url" to when (chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPttDownUrl(
|
|
||||||
"0",
|
|
||||||
record.md5HexStr,
|
|
||||||
record.fileUuid
|
|
||||||
)
|
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", record.fileUuid)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", record.fileUuid)
|
||||||
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl(
|
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl(
|
||||||
"0",
|
"0",
|
||||||
record.md5HexStr,
|
md5.hex2ByteArray(),
|
||||||
record.fileUuid
|
record.fileUuid
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -343,7 +334,10 @@ internal object NtMsgElementConverter {
|
|||||||
MessageSegment(
|
MessageSegment(
|
||||||
type = "forward",
|
type = "forward",
|
||||||
data = mapOf(
|
data = mapOf(
|
||||||
"id" to info["resid"].asString
|
"id" to info["resid"].asString,
|
||||||
|
"filename" to info["uniseq"].asString,
|
||||||
|
"summary" to info["summary"].asString,
|
||||||
|
"desc" to info["news"].asJsonArray.joinToString("\n") { it.asJsonObject["text"].asString }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -13,64 +13,84 @@ import moe.fuqiuluo.qqinterface.servlet.msg.toJson
|
|||||||
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
|
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc
|
import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc
|
||||||
import moe.fuqiuluo.shamrock.helper.*
|
import moe.fuqiuluo.shamrock.helper.*
|
||||||
import moe.fuqiuluo.shamrock.helper.MessageHelper.messageArrayToMessageElements
|
import moe.fuqiuluo.shamrock.helper.MessageHelper.messageArrayToRichText
|
||||||
|
import moe.fuqiuluo.shamrock.helper.MessageHelper.obtainMessageTypeByDetailType
|
||||||
import moe.fuqiuluo.shamrock.tools.*
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||||
import moe.fuqiuluo.shamrock.utils.FileUtils
|
import moe.fuqiuluo.shamrock.utils.FileUtils
|
||||||
import protobuf.auto.toByteArray
|
import protobuf.auto.toByteArray
|
||||||
import protobuf.message.Elem
|
import protobuf.message.Elem
|
||||||
|
import protobuf.message.RichText
|
||||||
import protobuf.message.element.*
|
import protobuf.message.element.*
|
||||||
import protobuf.message.element.commelem.*
|
import protobuf.message.element.commelem.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
import java.util.*
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.random.nextULong
|
import kotlin.random.nextULong
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
internal typealias IElemMaker = suspend (Int, Long, String, JsonObject) -> Result<Elem>
|
internal typealias IElemMaker = suspend (ElemMaker, Int, Long, String, JsonObject) -> Unit
|
||||||
|
|
||||||
internal object ElemMaker {
|
internal class ElemMaker {
|
||||||
private val makerArray = hashMapOf(
|
companion object {
|
||||||
"text" to ElemMaker::createTextElem,
|
private val makerArray = hashMapOf(
|
||||||
"at" to ElemMaker::createAtElem,
|
"text" to ElemMaker::createTextElem,
|
||||||
"face" to ElemMaker::createFaceElem,
|
"at" to ElemMaker::createAtElem,
|
||||||
"pic" to ElemMaker::createImageElem,
|
"face" to ElemMaker::createFaceElem,
|
||||||
"image" to ElemMaker::createImageElem,
|
"pic" to ElemMaker::createImageElem,
|
||||||
|
"image" to ElemMaker::createImageElem,
|
||||||
// "voice" to ElemMaker::createRecordElem,
|
// "voice" to ElemMaker::createRecordElem,
|
||||||
// "record" to ElemMaker::createRecordElem,
|
// "record" to ElemMaker::createRecordElem,
|
||||||
// "video" to ElemMaker::createVideoElem,
|
// "video" to ElemMaker::createVideoElem,
|
||||||
"markdown" to ElemMaker::createMarkdownElem,
|
"forward" to ElemMaker::createForwardStruct,
|
||||||
"button" to ElemMaker::createButtonElem,
|
"json" to ElemMaker::createJsonElem,
|
||||||
"dice" to ElemMaker::createNewDiceElem,
|
"poke" to ElemMaker::createPokeElem,
|
||||||
"rps" to ElemMaker::createNewRpsElem,
|
"dice" to ElemMaker::createNewDiceElem,
|
||||||
"poke" to ElemMaker::createPokeElem,
|
"rps" to ElemMaker::createNewRpsElem,
|
||||||
|
"markdown" to ElemMaker::createMarkdownElem,
|
||||||
|
"button" to ElemMaker::createButtonElem,
|
||||||
// "anonymous" to ElemMaker::createAnonymousElem,
|
// "anonymous" to ElemMaker::createAnonymousElem,
|
||||||
// "share" to ElemMaker::createShareElem,
|
// "share" to ElemMaker::createShareElem,
|
||||||
// "contact" to ElemMaker::createContactElem,
|
// "contact" to ElemMaker::createContactElem,
|
||||||
// "location" to ElemMaker::createLocationElem,
|
// "location" to ElemMaker::createLocationElem,
|
||||||
// "music" to ElemMaker::createMusicElem,
|
// "music" to ElemMaker::createMusicElem,
|
||||||
"reply" to ElemMaker::createReplyElem,
|
"reply" to ElemMaker::createReplyElem,
|
||||||
// "touch" to ElemMaker::createTouchElem,
|
// "touch" to ElemMaker::createTouchElem,
|
||||||
"weather" to ElemMaker::createWeatherElem,
|
"weather" to ElemMaker::createWeatherElem,
|
||||||
"json" to ElemMaker::createJsonElem,
|
//"forward" to MessageMaker::createForwardElem,
|
||||||
//"forward" to MessageMaker::createForwardElem,
|
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
||||||
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
//"bubble_face" to ElemMaker::createBubbleFaceElem,
|
||||||
//"bubble_face" to ElemMaker::createBubbleFaceElem,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
operator fun get(type: String): IElemMaker? = makerArray[type]
|
operator fun get(type: String): IElemMaker? = makerArray[type]
|
||||||
|
}
|
||||||
|
|
||||||
|
private var rich = RichText()
|
||||||
|
private val elems = mutableListOf<Elem>()
|
||||||
|
private var desc = ""
|
||||||
|
|
||||||
|
fun getRich(): RichText {
|
||||||
|
rich.elements = elems
|
||||||
|
return rich
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDesc(): String {
|
||||||
|
return desc
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun createTextElem(
|
private suspend fun createTextElem(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
data.checkAndThrow("text")
|
data.checkAndThrow("text")
|
||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
text = TextMsg(data["text"].asString)
|
text = TextMsg(data["text"].asString)
|
||||||
)
|
)
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += data["text"].asString
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createAtElem(
|
private suspend fun createAtElem(
|
||||||
@ -78,8 +98,8 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
return when (chatType) {
|
when (chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> {
|
MsgConstant.KCHATTYPEGROUP -> {
|
||||||
data.checkAndThrow("qq")
|
data.checkAndThrow("qq")
|
||||||
|
|
||||||
@ -105,15 +125,14 @@ internal object ElemMaker {
|
|||||||
peerId.toLong(),
|
peerId.toLong(),
|
||||||
qq,
|
qq,
|
||||||
true
|
true
|
||||||
)
|
).let {
|
||||||
.let {
|
val info = it.getOrNull()
|
||||||
val info = it.getOrNull()
|
if (info == null)
|
||||||
if (info == null)
|
LogCenter.log("无法获取群成员信息: $qqStr", Level.ERROR)
|
||||||
LogCenter.log("无法获取群成员信息: $qqStr", Level.ERROR)
|
else info.troopnick
|
||||||
info?.troopnick
|
.ifNullOrEmpty(info.friendnick)
|
||||||
.ifNullOrEmpty(info?.friendnick)
|
.ifNullOrEmpty(qqStr)
|
||||||
.ifNullOrEmpty(qqStr)
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +145,8 @@ internal object ElemMaker {
|
|||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
text = TextMsg(str = display, attr6Buf = attr6.array())
|
text = TextMsg(str = display, attr6Buf = attr6.array())
|
||||||
)
|
)
|
||||||
Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += display
|
||||||
}
|
}
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEC2C -> {
|
MsgConstant.KCHATTYPEC2C -> {
|
||||||
@ -144,10 +164,11 @@ internal object ElemMaker {
|
|||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
text = TextMsg(str = display)
|
text = TextMsg(str = display)
|
||||||
)
|
)
|
||||||
Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += display
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> Result.failure(ActionMsgException)
|
else -> throw UnsupportedOperationException("Unsupported chatType($chatType) for AtMsg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +177,7 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
data.checkAndThrow("id")
|
data.checkAndThrow("id")
|
||||||
val faceId = data["id"].asInt
|
val faceId = data["id"].asInt
|
||||||
val elem = if (data["big"].asBooleanOrNull == true) {
|
val elem = if (data["big"].asBooleanOrNull == true) {
|
||||||
@ -183,7 +204,8 @@ internal object ElemMaker {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[表情]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createImageElem(
|
private suspend fun createImageElem(
|
||||||
@ -191,7 +213,7 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
val isOriginal = data["original"].asBooleanOrNull ?: true
|
val isOriginal = data["original"].asBooleanOrNull ?: true
|
||||||
val isFlash = data["flash"].asBooleanOrNull ?: false
|
val isFlash = data["flash"].asBooleanOrNull ?: false
|
||||||
val filePath = data["file"].asStringOrNull
|
val filePath = data["file"].asStringOrNull
|
||||||
@ -307,7 +329,8 @@ internal object ElemMaker {
|
|||||||
|
|
||||||
else -> throw LogicException("Not supported chatType($chatType) for PictureMsg")
|
else -> throw LogicException("Not supported chatType($chatType) for PictureMsg")
|
||||||
}
|
}
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[图片]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createReplyElem(
|
private suspend fun createReplyElem(
|
||||||
@ -315,16 +338,15 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
data.checkAndThrow("id")
|
data.checkAndThrow("id")
|
||||||
val msgHash = data["id"].asInt
|
val msgHash = data["id"].asInt
|
||||||
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
||||||
?: return Result.failure(Exception("不存在该消息映射,无法回复消息"))
|
?: throw Exception("不存在该消息映射,无法回复消息")
|
||||||
|
|
||||||
if (mapping.qqMsgId == 0L) {
|
if (mapping.qqMsgId == 0L) {
|
||||||
// 貌似获取失败了,555
|
// 貌似获取失败了,555
|
||||||
LogCenter.log("无法获取被回复消息", Level.ERROR)
|
throw Exception("无法获取被回复消息")
|
||||||
return Result.failure(Exception("无法获取被回复消息"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val elem = if (data.containsKey("text")) {
|
val elem = if (data.containsKey("text")) {
|
||||||
@ -351,16 +373,15 @@ internal object ElemMaker {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val msg =
|
val msg =
|
||||||
MsgSvc.getMsgByQMsgId(chatType, mapping.peerId, mapping.qqMsgId).getOrNull() ?: return Result.failure(
|
MsgSvc.getMsgByQMsgId(chatType, mapping.peerId, mapping.qqMsgId).getOrNull()
|
||||||
Exception("无法获取被回复消息")
|
?: throw Exception("无法获取被回复消息")
|
||||||
)
|
|
||||||
Elem(
|
Elem(
|
||||||
srcMsg = SourceMsg(
|
srcMsg = SourceMsg(
|
||||||
origSeqs = listOf(msg.msgSeq.toInt()),
|
origSeqs = listOf(msg.msgSeq.toInt()),
|
||||||
senderUin = msg.senderUin.toULong(),
|
senderUin = msg.senderUin.toULong(),
|
||||||
time = msg.msgTime.toULong(),
|
time = msg.msgTime.toULong(),
|
||||||
flag = 1u,
|
flag = 1u,
|
||||||
elems = messageArrayToMessageElements(
|
elems = messageArrayToRichText(
|
||||||
msg.chatType,
|
msg.chatType,
|
||||||
msg.msgId,
|
msg.msgId,
|
||||||
msg.peerUin.toString(),
|
msg.peerUin.toString(),
|
||||||
@ -369,7 +390,7 @@ internal object ElemMaker {
|
|||||||
if (msg.chatType == MsgConstant.KCHATTYPEGUILD) msg.guildId else msg.peerUin.toString(),
|
if (msg.chatType == MsgConstant.KCHATTYPEGUILD) msg.guildId else msg.peerUin.toString(),
|
||||||
msg.channelId ?: msg.peerUin.toString()
|
msg.channelId ?: msg.peerUin.toString()
|
||||||
).toJson()
|
).toJson()
|
||||||
).second,
|
).getOrElse { throw Exception("解析回复消息失败: $it") }.second.elements,
|
||||||
type = 0u,
|
type = 0u,
|
||||||
pbReserve = SourceMsg.Companion.PbReserve(
|
pbReserve = SourceMsg.Companion.PbReserve(
|
||||||
msgRand = Random.nextULong(),
|
msgRand = Random.nextULong(),
|
||||||
@ -380,7 +401,8 @@ internal object ElemMaker {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[回复消息]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createJsonElem(
|
private suspend fun createJsonElem(
|
||||||
@ -388,15 +410,82 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
data.checkAndThrow("data")
|
data.checkAndThrow("data")
|
||||||
|
|
||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
lightApp = LightAppElem(
|
lightApp = LightAppElem(
|
||||||
data = DeflateTools.compress(data.toString().toByteArray())
|
data = byteArrayOf(1) + DeflateTools.compress(data.toString().toByteArray())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[Json消息]"
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun createForwardStruct(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
) {
|
||||||
|
data.checkAndThrow("id")
|
||||||
|
val resId = data["id"].asString
|
||||||
|
val filename = data["filename"].asStringOrNull ?: UUID.randomUUID().toString().uppercase()
|
||||||
|
var summary = data["summary"].asStringOrNull
|
||||||
|
val descriptions = data["desc"].asStringOrNull
|
||||||
|
var news = descriptions?.split("\n")?.map { "text" to it }
|
||||||
|
|
||||||
|
if (news == null || summary == null) {
|
||||||
|
val forwardMsg = MsgSvc.getForwardMsg(resId).getOrThrow()
|
||||||
|
if (news == null) {
|
||||||
|
news = forwardMsg.map {
|
||||||
|
"text" to it.sender.nickName + ": " + messageArrayToRichText(
|
||||||
|
obtainMessageTypeByDetailType(it.msgType),
|
||||||
|
it.qqMsgId,
|
||||||
|
it.peerId.toString(),
|
||||||
|
it.message.json
|
||||||
|
).getOrThrow().first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (summary == null) {
|
||||||
|
summary = "查看${forwardMsg.size}条转发消息"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val json = mapOf(
|
||||||
|
"app" to "com.tencent.multimsg",
|
||||||
|
"config" to mapOf(
|
||||||
|
"autosize" to 1,
|
||||||
|
"forward" to 1,
|
||||||
|
"round" to 1,
|
||||||
|
"type" to "normal",
|
||||||
|
"width" to 300
|
||||||
|
),
|
||||||
|
"desc" to "[聊天记录]",
|
||||||
|
"extra" to mapOf(
|
||||||
|
"filename" to filename,
|
||||||
|
"tsum" to 2
|
||||||
|
).json.toString(),
|
||||||
|
"meta" to mapOf(
|
||||||
|
"detail" to mapOf(
|
||||||
|
"news" to news,
|
||||||
|
"resid" to resId,
|
||||||
|
"source" to "群聊的聊天记录",
|
||||||
|
"summary" to summary,
|
||||||
|
"uniseq" to filename
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"prompt" to "[聊天记录]",
|
||||||
|
"ver" to "0.0.0.5",
|
||||||
|
"view" to "contact"
|
||||||
|
)
|
||||||
|
val elem = Elem(
|
||||||
|
lightApp = LightAppElem(
|
||||||
|
data = byteArrayOf(1) + DeflateTools.compress(json.json.toString().toByteArray())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elems.add(elem)
|
||||||
|
desc += "[聊天记录]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createWeatherElem(
|
private suspend fun createWeatherElem(
|
||||||
@ -404,7 +493,7 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
var code = data["code"].asIntOrNull
|
var code = data["code"].asIntOrNull
|
||||||
|
|
||||||
if (code == null) {
|
if (code == null) {
|
||||||
@ -416,19 +505,22 @@ internal object ElemMaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (code != null) {
|
if (code != null) {
|
||||||
WeatherSvc.fetchWeatherCard(code).onSuccess {
|
val weatherCard = WeatherSvc.fetchWeatherCard(code).getOrThrow()
|
||||||
// OidbSvc.0xdc2_34
|
// OidbSvc.0xdc2_34
|
||||||
// 00 00 00 DF 08 C2 1B 10 22 22 C4 01 0A B7 01 08 A2 E0 F2 2F 10 01 18 00 2A 02 08 01 58 FB 91 F6 AE 02 62 A1 01 08 01 52 08 E5 8C 97 E4 BA AC 20 20 5A 19 2D 33 C2 B0 2F 33 C2 B0 0A E7 A9 BA E6 B0 94 E8 B4 A8 E9 87 8F 3A E8 89 AF 62 11 5B E5 88 86 E4 BA AB 5D 20 E5 8C 97 E4 BA AC 20 20 6A 25 68 74 74 70 73 3A 2F 2F 77 65 61 74 68 65 72 2E 6D 70 2E 71 71 2E 63 6F 6D 2F 3F 73 74 3D 30 26 5F 77 76 3D 31 72 3E 68 74 74 70 73 3A 2F 2F 69 6D 67 63 61 63 68 65 2E 71 71 2E 63 6F 6D 2F 61 63 2F 71 71 77 65 61 74 68 65 72 2F 69 6D 61 67 65 2F 73 68 61 72 65 5F 69 63 6F 6E 2F 66 69 6E 65 2E 70 6E 67 12 08 08 01 10 FB 91 F6 AE 02 32 0D 61 6E 64 72 6F 69 64 20 39 2E 30 2E 38
|
// 00 00 00 DF 08 C2 1B 10 22 22 C4 01 0A B7 01 08 A2 E0 F2 2F 10 01 18 00 2A 02 08 01 58 FB 91 F6 AE 02 62 A1 01 08 01 52 08 E5 8C 97 E4 BA AC 20 20 5A 19 2D 33 C2 B0 2F 33 C2 B0 0A E7 A9 BA E6 B0 94 E8 B4 A8 E9 87 8F 3A E8 89 AF 62 11 5B E5 88 86 E4 BA AB 5D 20 E5 8C 97 E4 BA AC 20 20 6A 25 68 74 74 70 73 3A 2F 2F 77 65 61 74 68 65 72 2E 6D 70 2E 71 71 2E 63 6F 6D 2F 3F 73 74 3D 30 26 5F 77 76 3D 31 72 3E 68 74 74 70 73 3A 2F 2F 69 6D 67 63 61 63 68 65 2E 71 71 2E 63 6F 6D 2F 61 63 2F 71 71 77 65 61 74 68 65 72 2F 69 6D 61 67 65 2F 73 68 61 72 65 5F 69 63 6F 6E 2F 66 69 6E 65 2E 70 6E 67 12 08 08 01 10 FB 91 F6 AE 02 32 0D 61 6E 64 72 6F 69 64 20 39 2E 30 2E 38
|
||||||
return createJsonElem(
|
val elem = Elem(
|
||||||
chatType, msgId, peerId, it["weekStore"]
|
lightApp = LightAppElem(
|
||||||
.asJsonObject["share"].asJsonObject
|
data = byteArrayOf(1) + DeflateTools.compress(
|
||||||
|
weatherCard["weekStore"]
|
||||||
|
.asJsonObject["share"].asString.toByteArray()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}.onFailure {
|
)
|
||||||
LogCenter.log("无法发送天气分享", Level.ERROR)
|
elems.add(elem)
|
||||||
}
|
desc += "[天气卡片]"
|
||||||
|
} else {
|
||||||
|
throw LogicException("无法获取城市天气")
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.failure(ActionMsgException)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createPokeElem(
|
private suspend fun createPokeElem(
|
||||||
@ -436,7 +528,7 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
data.checkAndThrow("type", "id")
|
data.checkAndThrow("type", "id")
|
||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
commonElem = CommonElem(
|
commonElem = CommonElem(
|
||||||
@ -449,7 +541,8 @@ internal object ElemMaker {
|
|||||||
businessType = data["id"].asInt
|
businessType = data["id"].asInt
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[戳一戳]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createNewDiceElem(
|
private suspend fun createNewDiceElem(
|
||||||
@ -457,7 +550,7 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
commonElem = CommonElem(
|
commonElem = CommonElem(
|
||||||
serviceType = 37,
|
serviceType = 37,
|
||||||
@ -474,7 +567,8 @@ internal object ElemMaker {
|
|||||||
businessType = 2
|
businessType = 2
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[骰子]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createNewRpsElem(
|
private suspend fun createNewRpsElem(
|
||||||
@ -482,7 +576,7 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
commonElem = CommonElem(
|
commonElem = CommonElem(
|
||||||
serviceType = 37,
|
serviceType = 37,
|
||||||
@ -499,7 +593,8 @@ internal object ElemMaker {
|
|||||||
businessType = 1
|
businessType = 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[包剪锤]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createMarkdownElem(
|
private suspend fun createMarkdownElem(
|
||||||
@ -507,7 +602,7 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
data.checkAndThrow("content")
|
data.checkAndThrow("content")
|
||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
commonElem = CommonElem(
|
commonElem = CommonElem(
|
||||||
@ -516,7 +611,8 @@ internal object ElemMaker {
|
|||||||
businessType = 1
|
businessType = 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[Markdown消息]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createButtonElem(
|
private suspend fun createButtonElem(
|
||||||
@ -524,7 +620,7 @@ internal object ElemMaker {
|
|||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
data: JsonObject
|
data: JsonObject
|
||||||
): Result<Elem> {
|
) {
|
||||||
data.checkAndThrow("buttons")
|
data.checkAndThrow("buttons")
|
||||||
val elem = Elem(
|
val elem = Elem(
|
||||||
commonElem = CommonElem(
|
commonElem = CommonElem(
|
||||||
@ -565,7 +661,8 @@ internal object ElemMaker {
|
|||||||
businessType = 1
|
businessType = 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return Result.success(elem)
|
elems.add(elem)
|
||||||
|
desc += "[Button消息]"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun JsonObject.checkAndThrow(vararg key: String) {
|
private fun JsonObject.checkAndThrow(vararg key: String) {
|
||||||
|
@ -16,6 +16,7 @@ import kotlinx.serialization.json.JsonPrimitive
|
|||||||
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.LbsSvc
|
import moe.fuqiuluo.qqinterface.servlet.LbsSvc
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo
|
import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo
|
||||||
import moe.fuqiuluo.qqinterface.servlet.ark.ArkMsgSvc
|
import moe.fuqiuluo.qqinterface.servlet.ark.ArkMsgSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.ark.WeatherSvc
|
import moe.fuqiuluo.qqinterface.servlet.ark.WeatherSvc
|
||||||
@ -51,6 +52,8 @@ import tencent.im.oidb.cmd0xb77.oidb_cmd0xb77
|
|||||||
import tencent.im.oidb.cmd0xdc2.oidb_cmd0xdc2
|
import tencent.im.oidb.cmd0xdc2.oidb_cmd0xdc2
|
||||||
import tencent.im.oidb.oidb_sso
|
import tencent.im.oidb.oidb_sso
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.random.nextInt
|
import kotlin.random.nextInt
|
||||||
@ -80,10 +83,11 @@ internal object NtMsgElementMaker {
|
|||||||
"touch" to NtMsgElementMaker::createTouchElem,
|
"touch" to NtMsgElementMaker::createTouchElem,
|
||||||
"weather" to NtMsgElementMaker::createWeatherElem,
|
"weather" to NtMsgElementMaker::createWeatherElem,
|
||||||
"json" to NtMsgElementMaker::createJsonElem,
|
"json" to NtMsgElementMaker::createJsonElem,
|
||||||
|
"forward" to NtMsgElementMaker::createForwardStruct,
|
||||||
"new_dice" to NtMsgElementMaker::createNewDiceElem,
|
"new_dice" to NtMsgElementMaker::createNewDiceElem,
|
||||||
"new_rps" to NtMsgElementMaker::createNewRpsElem,
|
"new_rps" to NtMsgElementMaker::createNewRpsElem,
|
||||||
"basketball" to NtMsgElementMaker::createBasketballElem,
|
"basketball" to NtMsgElementMaker::createBasketballElem,
|
||||||
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
//"multi_msg" to NtMsgElementMaker::createLongMsgStruct,
|
||||||
"bubble_face" to NtMsgElementMaker::createBubbleFaceElem,
|
"bubble_face" to NtMsgElementMaker::createBubbleFaceElem,
|
||||||
"button" to NtMsgElementMaker::createInlineKeywordElem,
|
"button" to NtMsgElementMaker::createInlineKeywordElem,
|
||||||
"inline_keyboard" to NtMsgElementMaker::createInlineKeywordElem
|
"inline_keyboard" to NtMsgElementMaker::createInlineKeywordElem
|
||||||
@ -91,6 +95,70 @@ internal object NtMsgElementMaker {
|
|||||||
|
|
||||||
operator fun get(type: String): IMsgElementMaker? = makerMap[type]
|
operator fun get(type: String): IMsgElementMaker? = makerMap[type]
|
||||||
|
|
||||||
|
private suspend fun createForwardStruct(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
|
data.checkAndThrow("id")
|
||||||
|
val resId = data["id"].asString
|
||||||
|
val filename = data["filename"].asStringOrNull ?: UUID.randomUUID().toString().uppercase()
|
||||||
|
var summary = data["summary"].asStringOrNull
|
||||||
|
val descriptions = data["desc"].asStringOrNull
|
||||||
|
var news = descriptions?.split("\n")?.map { "text" to it }
|
||||||
|
|
||||||
|
if (news == null || summary == null) {
|
||||||
|
val forwardMsg = MsgSvc.getForwardMsg(resId).getOrElse { return Result.failure(it) }
|
||||||
|
if (news == null) {
|
||||||
|
news = forwardMsg.map {
|
||||||
|
"text" to it.sender.nickName + ": " + MessageHelper.messageArrayToRichText(
|
||||||
|
MessageHelper.obtainMessageTypeByDetailType(it.msgType),
|
||||||
|
it.qqMsgId,
|
||||||
|
it.peerId.toString(),
|
||||||
|
it.message.json
|
||||||
|
).getOrThrow().first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (summary == null) {
|
||||||
|
summary = "查看${forwardMsg.size}条转发消息"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val json = mapOf(
|
||||||
|
"app" to "com.tencent.multimsg",
|
||||||
|
"config" to mapOf(
|
||||||
|
"autosize" to 1,
|
||||||
|
"forward" to 1,
|
||||||
|
"round" to 1,
|
||||||
|
"type" to "normal",
|
||||||
|
"width" to 300
|
||||||
|
),
|
||||||
|
"desc" to "[聊天记录]",
|
||||||
|
"extra" to mapOf(
|
||||||
|
"filename" to filename,
|
||||||
|
"tsum" to 2
|
||||||
|
).json.toString(),
|
||||||
|
"meta" to mapOf(
|
||||||
|
"detail" to mapOf(
|
||||||
|
"news" to news,
|
||||||
|
"resid" to resId,
|
||||||
|
"source" to "群聊的聊天记录",
|
||||||
|
"summary" to summary,
|
||||||
|
"uniseq" to filename
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"prompt" to "[聊天记录]",
|
||||||
|
"ver" to "0.0.0.5",
|
||||||
|
"view" to "contact"
|
||||||
|
)
|
||||||
|
return createJsonElem(
|
||||||
|
chatType, msgId, peerId, mapOf(
|
||||||
|
"data" to json
|
||||||
|
).json
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun createInlineKeywordElem(
|
private suspend fun createInlineKeywordElem(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
msgId: Long,
|
msgId: Long,
|
||||||
@ -262,19 +330,21 @@ internal object NtMsgElementMaker {
|
|||||||
): Result<MsgElement> {
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("data")
|
data.checkAndThrow("data")
|
||||||
val jsonStr = data["data"].let {
|
val jsonStr = data["data"].let {
|
||||||
if (it is JsonObject) it.asJsonObject.toString() else {
|
if (it is JsonObject) {
|
||||||
val str = it.asStringOrNull ?: ""
|
it.asJsonObject.toString()
|
||||||
|
} else {
|
||||||
// 检查字符串是否是合法json,不然qq会闪退
|
// 检查字符串是否是合法json,不然qq会闪退
|
||||||
try {
|
try {
|
||||||
|
val str = it.asString
|
||||||
val element = Json.decodeFromString<JsonElement>(str)
|
val element = Json.decodeFromString<JsonElement>(str)
|
||||||
if (element !is JsonObject) {
|
if (element !is JsonObject) {
|
||||||
return Result.failure(Exception("不合法的JSON字符串"))
|
return Result.failure(Exception("不合法的JSON字符串"))
|
||||||
}
|
}
|
||||||
|
str
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
LogCenter.log(err.stackTraceToString(), Level.ERROR)
|
LogCenter.log(err.stackTraceToString(), Level.ERROR)
|
||||||
return Result.failure(Exception("不合法的JSON字符串"))
|
return Result.failure(Exception("不合法的JSON字符串"))
|
||||||
}
|
}
|
||||||
str
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val element = MsgElement()
|
val element = MsgElement()
|
||||||
|
@ -6,7 +6,6 @@ import com.tencent.mobileqq.transfile.FileMsg
|
|||||||
import com.tencent.mobileqq.transfile.api.IProtoReqManager
|
import com.tencent.mobileqq.transfile.api.IProtoReqManager
|
||||||
import com.tencent.mobileqq.transfile.protohandler.RichProto
|
import com.tencent.mobileqq.transfile.protohandler.RichProto
|
||||||
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
|
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
|
||||||
import kotlinx.atomicfu.atomic
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||||
@ -22,23 +21,9 @@ import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
|||||||
import moe.fuqiuluo.symbols.decodeProtobuf
|
import moe.fuqiuluo.symbols.decodeProtobuf
|
||||||
import mqq.app.MobileQQ
|
import mqq.app.MobileQQ
|
||||||
import protobuf.auto.toByteArray
|
import protobuf.auto.toByteArray
|
||||||
import protobuf.oidb.TrpcOidb
|
|
||||||
import protobuf.oidb.cmd0x11c5.C2CUserInfo
|
import protobuf.oidb.cmd0x11c5.C2CUserInfo
|
||||||
import protobuf.oidb.cmd0x11c5.ChannelUserInfo
|
import protobuf.oidb.cmd0x11c5.ChannelUserInfo
|
||||||
import protobuf.oidb.cmd0x11c5.ClientMeta
|
|
||||||
import protobuf.oidb.cmd0x11c5.CodecConfigReq
|
|
||||||
import protobuf.oidb.cmd0x11c5.CommonHead
|
|
||||||
import protobuf.oidb.cmd0x11c5.DownloadExt
|
|
||||||
import protobuf.oidb.cmd0x11c5.DownloadReq
|
|
||||||
import protobuf.oidb.cmd0x11c5.FileInfo
|
|
||||||
import protobuf.oidb.cmd0x11c5.FileType
|
|
||||||
import protobuf.oidb.cmd0x11c5.GroupUserInfo
|
import protobuf.oidb.cmd0x11c5.GroupUserInfo
|
||||||
import protobuf.oidb.cmd0x11c5.IndexNode
|
|
||||||
import protobuf.oidb.cmd0x11c5.MultiMediaReqHead
|
|
||||||
import protobuf.oidb.cmd0x11c5.NtV2RichMediaReq
|
|
||||||
import protobuf.oidb.cmd0x11c5.NtV2RichMediaRsp
|
|
||||||
import protobuf.oidb.cmd0x11c5.SceneInfo
|
|
||||||
import protobuf.oidb.cmd0x11c5.VideoDownloadExt
|
|
||||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
|
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
|
||||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
|
import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
|
||||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
|
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
|
||||||
@ -405,8 +390,8 @@ internal object RichProtoSvc: BaseSvc() {
|
|||||||
|
|
||||||
suspend fun getGroupPttDownUrl(
|
suspend fun getGroupPttDownUrl(
|
||||||
peerId: String,
|
peerId: String,
|
||||||
md5Hex: String,
|
md5: ByteArray,
|
||||||
fileUUId: String
|
groupFileKey: String
|
||||||
): String {
|
): String {
|
||||||
return suspendCancellableCoroutine {
|
return suspendCancellableCoroutine {
|
||||||
val runtime = AppRuntimeFetcher.appRuntime
|
val runtime = AppRuntimeFetcher.appRuntime
|
||||||
@ -417,8 +402,8 @@ internal object RichProtoSvc: BaseSvc() {
|
|||||||
groupPttDownReq.secondUin = peerId
|
groupPttDownReq.secondUin = peerId
|
||||||
groupPttDownReq.uinType = FileMsg.UIN_TROOP
|
groupPttDownReq.uinType = FileMsg.UIN_TROOP
|
||||||
groupPttDownReq.groupFileID = 0
|
groupPttDownReq.groupFileID = 0
|
||||||
groupPttDownReq.groupFileKey = fileUUId
|
groupPttDownReq.groupFileKey = groupFileKey
|
||||||
groupPttDownReq.md5 = md5Hex.hex2ByteArray()
|
groupPttDownReq.md5 = md5
|
||||||
groupPttDownReq.voiceType = 1
|
groupPttDownReq.voiceType = 1
|
||||||
groupPttDownReq.downType = 1
|
groupPttDownReq.downType = 1
|
||||||
richProtoReq.callback = RichProtoProc.RichProtoCallback { _, resp ->
|
richProtoReq.callback = RichProtoProc.RichProtoCallback { _, resp ->
|
||||||
|
@ -21,12 +21,9 @@ import moe.fuqiuluo.qqinterface.servlet.msg.maker.NtMsgElementMaker
|
|||||||
import moe.fuqiuluo.shamrock.helper.db.MessageDB
|
import moe.fuqiuluo.shamrock.helper.db.MessageDB
|
||||||
import moe.fuqiuluo.shamrock.helper.db.MessageMapping
|
import moe.fuqiuluo.shamrock.helper.db.MessageMapping
|
||||||
import moe.fuqiuluo.shamrock.remote.structures.SendMsgResult
|
import moe.fuqiuluo.shamrock.remote.structures.SendMsgResult
|
||||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
import moe.fuqiuluo.shamrock.tools.asJsonObjectOrNull
|
|
||||||
import moe.fuqiuluo.shamrock.tools.asString
|
|
||||||
import moe.fuqiuluo.shamrock.tools.json
|
|
||||||
import moe.fuqiuluo.shamrock.tools.jsonArray
|
|
||||||
import protobuf.message.Elem
|
import protobuf.message.Elem
|
||||||
|
import protobuf.message.RichText
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
@ -75,7 +72,7 @@ internal object MessageHelper {
|
|||||||
}
|
}
|
||||||
if (resendRet != 0 &&
|
if (resendRet != 0 &&
|
||||||
resendRet != 4 // 使用OldBDH 100%触发
|
resendRet != 4 // 使用OldBDH 100%触发
|
||||||
) {
|
) {
|
||||||
resendMsg(contact, msgId, retryCnt - 1, msgHashId)
|
resendMsg(contact, msgId, retryCnt - 1, msgHashId)
|
||||||
} else {
|
} else {
|
||||||
Result.success(SendMsgResult(msgHashId, msgId, System.currentTimeMillis()))
|
Result.success(SendMsgResult(msgHashId, msgId, System.currentTimeMillis()))
|
||||||
@ -319,38 +316,30 @@ internal object MessageHelper {
|
|||||||
return hasActionMsg to msgList
|
return hasActionMsg to msgList
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun messageArrayToMessageElements(
|
suspend fun messageArrayToRichText(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
msgId: Long,
|
msgId: Long,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
messageList: JsonArray
|
messageList: JsonArray
|
||||||
): Pair<Boolean, ArrayList<Elem>> {
|
): Result<Pair<String, RichText>> {
|
||||||
val msgList = arrayListOf<Elem>()
|
val elemMaker = ElemMaker()
|
||||||
var hasActionMsg = false
|
messageList.forEach { element ->
|
||||||
messageList.forEach {
|
val msg = element.asJsonObject
|
||||||
val msg = it.jsonObject
|
|
||||||
val maker = ElemMaker[msg["type"].asString]
|
val maker = ElemMaker[msg["type"].asString]
|
||||||
if (maker != null) {
|
if (maker != null) {
|
||||||
try {
|
try {
|
||||||
val data = msg["data"].asJsonObjectOrNull ?: EmptyJsonObject
|
val data = msg["data"].asJsonObjectOrNull ?: EmptyJsonObject
|
||||||
maker(chatType, msgId, peerId, data).onSuccess { msgElem ->
|
maker(elemMaker, chatType, msgId, peerId, data)
|
||||||
msgList.add(msgElem)
|
|
||||||
}.onFailure {
|
|
||||||
if (it.javaClass != ActionMsgException::class.java) {
|
|
||||||
throw it
|
|
||||||
} else {
|
|
||||||
hasActionMsg = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
LogCenter.log(e.stackTraceToString(), Level.ERROR)
|
if (e.javaClass != ActionMsgException::class.java) {
|
||||||
|
LogCenter.log(e.stackTraceToString(), Level.ERROR)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogCenter.log("不支持的消息类型: ${msg["type"].asString}", Level.ERROR)
|
LogCenter.log("不支持的消息类型: ${msg["type"].asString}", Level.ERROR)
|
||||||
return false to arrayListOf()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hasActionMsg to msgList
|
return Result.success(elemMaker.getDesc() to elemMaker.getRich())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateMsgIdHash(chatType: Int, msgId: Long): Int {
|
fun generateMsgIdHash(chatType: Int, msgId: Long): Int {
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
import moe.fuqiuluo.shamrock.remote.service.data.MessageDetail
|
import moe.fuqiuluo.shamrock.remote.service.data.GetForwardMsgResult
|
||||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||||
import moe.fuqiuluo.symbols.OneBotHandler
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
|
|
||||||
@ -21,14 +19,12 @@ internal object GetForwardMsg : IActionHandler() {
|
|||||||
resId: String,
|
resId: String,
|
||||||
echo: JsonElement = EmptyJsonString
|
echo: JsonElement = EmptyJsonString
|
||||||
): String {
|
): String {
|
||||||
val result = MsgSvc.getMultiMsg(resId).getOrElse { return logic(it.toString(), echo) }
|
return ok(
|
||||||
return ok(data = GetForwardMsgResult(result), echo = echo)
|
data = GetForwardMsgResult(
|
||||||
|
msgs = MsgSvc.getForwardMsg(resId).getOrElse { return logic(it.toString(), echo = echo) }),
|
||||||
|
echo = echo
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class GetForwardMsgResult(
|
|
||||||
@SerialName("messages") val msgs: List<MessageDetail>
|
|
||||||
)
|
|
||||||
|
|
||||||
override val requiredParams: Array<String> = arrayOf("id")
|
override val requiredParams: Array<String> = arrayOf("id")
|
||||||
}
|
}
|
@ -69,6 +69,7 @@ internal object GetHistoryMsg : IActionHandler() {
|
|||||||
time = msg.msgTime.toInt(),
|
time = msg.msgTime.toInt(),
|
||||||
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
||||||
msgId = msgHash,
|
msgId = msgHash,
|
||||||
|
qqMsgId = msg.msgId,
|
||||||
msgSeq = msg.msgSeq,
|
msgSeq = msg.msgSeq,
|
||||||
realId = msg.msgSeq,
|
realId = msg.msgSeq,
|
||||||
sender = MessageSender(
|
sender = MessageSender(
|
||||||
@ -92,6 +93,7 @@ internal object GetHistoryMsg : IActionHandler() {
|
|||||||
time = msg.msgTime.toInt(),
|
time = msg.msgTime.toInt(),
|
||||||
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
||||||
msgId = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId),
|
msgId = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId),
|
||||||
|
qqMsgId = msg.msgId,
|
||||||
msgSeq = msg.msgSeq,
|
msgSeq = msg.msgSeq,
|
||||||
realId = msg.msgSeq,
|
realId = msg.msgSeq,
|
||||||
sender = MessageSender(
|
sender = MessageSender(
|
||||||
|
@ -29,6 +29,7 @@ internal object GetMsg: IActionHandler() {
|
|||||||
time = msg.msgTime.toInt(),
|
time = msg.msgTime.toInt(),
|
||||||
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
||||||
msgId = msgHash,
|
msgId = msgHash,
|
||||||
|
qqMsgId = msg.msgId,
|
||||||
msgSeq = msg.msgSeq,
|
msgSeq = msg.msgSeq,
|
||||||
realId = msg.msgSeq,
|
realId = msg.msgSeq,
|
||||||
sender = MessageSender(
|
sender = MessageSender(
|
||||||
|
@ -3,20 +3,14 @@ package moe.fuqiuluo.shamrock.remote.action.handlers
|
|||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
import moe.fuqiuluo.qqinterface.servlet.msg.toJson
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
|
|
||||||
import moe.fuqiuluo.shamrock.helper.Level
|
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
|
||||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||||
import moe.fuqiuluo.shamrock.helper.ParamsException
|
import moe.fuqiuluo.shamrock.helper.ParamsException
|
||||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
import moe.fuqiuluo.shamrock.remote.service.data.ForwardMessageResult
|
import moe.fuqiuluo.shamrock.remote.service.data.SendForwardMessageResult
|
||||||
import moe.fuqiuluo.shamrock.tools.*
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
import moe.fuqiuluo.symbols.OneBotHandler
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
import protobuf.message.*
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
@OneBotHandler("send_forward_msg")
|
@OneBotHandler("send_forward_msg")
|
||||||
internal object SendForwardMessage : IActionHandler() {
|
internal object SendForwardMessage : IActionHandler() {
|
||||||
@ -60,9 +54,10 @@ internal object SendForwardMessage : IActionHandler() {
|
|||||||
|
|
||||||
else -> error("unknown chat type: $chatType")
|
else -> error("unknown chat type: $chatType")
|
||||||
}.toString()
|
}.toString()
|
||||||
|
val retryCnt = session.getIntOrNull("retry_cnt") ?: 5
|
||||||
return if (session.isArray("messages")) {
|
return if (session.isArray("messages")) {
|
||||||
val messages = session.getArray("messages")
|
val messages = session.getArray("messages")
|
||||||
invoke(chatType, peerId, messages, fromId, echo = session.echo)
|
invoke(chatType, peerId, fromId, messages, retryCnt, session.echo)
|
||||||
} else {
|
} else {
|
||||||
logic("未知格式合并转发消息", session.echo)
|
logic("未知格式合并转发消息", session.echo)
|
||||||
}
|
}
|
||||||
@ -76,222 +71,22 @@ internal object SendForwardMessage : IActionHandler() {
|
|||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
messages: JsonArray,
|
|
||||||
fromId: String = peerId,
|
fromId: String = peerId,
|
||||||
|
messages: JsonArray,
|
||||||
|
retryCnt: Int,
|
||||||
echo: JsonElement = EmptyJsonString
|
echo: JsonElement = EmptyJsonString
|
||||||
): String {
|
): String {
|
||||||
var uid: String? = null
|
|
||||||
var groupUin: String? = null
|
|
||||||
|
|
||||||
var i = -1
|
|
||||||
val desc = MutableList(messages.size) { "" }
|
|
||||||
|
|
||||||
val msgs = messages.map { msg ->
|
|
||||||
kotlin.runCatching {
|
|
||||||
val data = msg.asJsonObject["data"].asJsonObject
|
|
||||||
if (data.containsKey("id")) {
|
|
||||||
val record = MsgSvc.getMsg(data["id"].asInt).getOrElse {
|
|
||||||
error("合并转发消息节点消息(id = ${data["id"].asInt})获取失败:$it")
|
|
||||||
}
|
|
||||||
if (record.chatType == MsgConstant.KCHATTYPEGROUP) groupUin = record.peerUin.toString()
|
|
||||||
if (record.chatType == MsgConstant.KCHATTYPEC2C) uid = record.peerUid
|
|
||||||
PushMsgBody(
|
|
||||||
msgHead = ResponseHead(
|
|
||||||
peerUid = record.senderUid,
|
|
||||||
receiverUid = record.peerUid,
|
|
||||||
forward = ResponseForward(
|
|
||||||
friendName = record.sendNickName
|
|
||||||
),
|
|
||||||
responseGrp = if (record.chatType == MsgConstant.KCHATTYPEGROUP) ResponseGrp(
|
|
||||||
groupCode = record.peerUin.toULong(),
|
|
||||||
memberCard = record.sendMemberName,
|
|
||||||
u1 = 2
|
|
||||||
) else null
|
|
||||||
),
|
|
||||||
contentHead = ContentHead(
|
|
||||||
msgType = when (record.chatType) {
|
|
||||||
MsgConstant.KCHATTYPEC2C -> 9
|
|
||||||
MsgConstant.KCHATTYPEGROUP -> 82
|
|
||||||
else -> throw UnsupportedOperationException(
|
|
||||||
"Unsupported chatType: $chatType"
|
|
||||||
)
|
|
||||||
},
|
|
||||||
msgSubType = if (record.chatType == MsgConstant.KCHATTYPEC2C) 175 else null,
|
|
||||||
divSeq = if (record.chatType == MsgConstant.KCHATTYPEC2C) 175 else null,
|
|
||||||
msgViaRandom = record.msgId,
|
|
||||||
sequence = record.msgSeq, // idk what this is(i++)
|
|
||||||
msgTime = record.msgTime,
|
|
||||||
u2 = 1,
|
|
||||||
u6 = 0,
|
|
||||||
u7 = 0,
|
|
||||||
msgSeq = if (record.chatType == MsgConstant.KCHATTYPEC2C) record.msgSeq else null, // seq for dm
|
|
||||||
forwardHead = ForwardHead(
|
|
||||||
u1 = 0,
|
|
||||||
u2 = 0,
|
|
||||||
u3 = 0,
|
|
||||||
ub641 = "",
|
|
||||||
avatar = ""
|
|
||||||
)
|
|
||||||
),
|
|
||||||
body = MsgBody(
|
|
||||||
richText = RichText(
|
|
||||||
elements = MessageHelper.messageArrayToMessageElements(
|
|
||||||
record.chatType,
|
|
||||||
record.msgId,
|
|
||||||
record.peerUin.toString(),
|
|
||||||
record.elements.toSegments(
|
|
||||||
record.chatType,
|
|
||||||
record.peerUin.toString(),
|
|
||||||
"0"
|
|
||||||
).also {
|
|
||||||
desc[++i] = record.sendMemberName.ifEmpty { record.sendNickName } + ": "
|
|
||||||
}.map {
|
|
||||||
desc[i] += when (it.type) {
|
|
||||||
"text" -> it.data["text"] as String
|
|
||||||
"at" -> "@${it.data["name"] as String? ?: it.data["qq"] as String}"
|
|
||||||
"face" -> "[表情]"
|
|
||||||
"pic", "image" -> "[图片]"
|
|
||||||
"voice", "record" -> "[语音]"
|
|
||||||
"video" -> "[视频]"
|
|
||||||
"node" -> "[合并转发消息]"
|
|
||||||
"markdown" -> "[Markdown消息]"
|
|
||||||
"button" -> "[Button类型]"
|
|
||||||
else -> "[未知消息类型]"
|
|
||||||
}
|
|
||||||
it.toJson()
|
|
||||||
}.json
|
|
||||||
).also {
|
|
||||||
if (it.second.isEmpty() && !it.first)
|
|
||||||
error("消息合成失败,请查看日志或者检查输入。")
|
|
||||||
}.second
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else if (data.containsKey("content")) {
|
|
||||||
PushMsgBody(
|
|
||||||
msgHead = ResponseHead(
|
|
||||||
peer = data["uin"]?.asLong ?: TicketSvc.getUin().toLong(),
|
|
||||||
peerUid = data["uid"]?.asString ?: TicketSvc.getUid(),
|
|
||||||
receiverUid = TicketSvc.getUid(),
|
|
||||||
forward = ResponseForward(
|
|
||||||
friendName = data["name"]?.asStringOrNull ?: TicketSvc.getNickname()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
contentHead = ContentHead(
|
|
||||||
msgType = 9,
|
|
||||||
msgSubType = 175,
|
|
||||||
divSeq = 175,
|
|
||||||
msgViaRandom = Random.nextLong(),
|
|
||||||
sequence = data["seq"]?.asLong ?: Random.nextLong(),
|
|
||||||
msgTime = data["time"]?.asLong ?: (System.currentTimeMillis() / 1000),
|
|
||||||
u2 = 1,
|
|
||||||
u6 = 0,
|
|
||||||
u7 = 0,
|
|
||||||
msgSeq = data["seq"]?.asLong ?: Random.nextLong(),
|
|
||||||
forwardHead = ForwardHead(
|
|
||||||
u1 = 0,
|
|
||||||
u2 = 0,
|
|
||||||
u3 = 2,
|
|
||||||
ub641 = "",
|
|
||||||
avatar = ""
|
|
||||||
)
|
|
||||||
),
|
|
||||||
body = MsgBody(
|
|
||||||
richText = RichText(
|
|
||||||
elements = MessageHelper.messageArrayToMessageElements(
|
|
||||||
chatType = chatType,
|
|
||||||
msgId = Random.nextLong(),
|
|
||||||
peerId = data["uin"]?.asString ?: TicketSvc.getUin(),
|
|
||||||
messageList = when (data["content"]) {
|
|
||||||
is JsonObject -> listOf(data["content"] as JsonObject).json
|
|
||||||
is JsonArray -> data["content"] as JsonArray
|
|
||||||
else -> MessageHelper.decodeCQCode(data["content"].asString)
|
|
||||||
}.also {
|
|
||||||
desc[++i] =
|
|
||||||
(data["name"].asStringOrNull ?: data["uin"].asStringOrNull
|
|
||||||
?: TicketSvc.getNickname()) + ": "
|
|
||||||
}.onEach {
|
|
||||||
val type = it.asJsonObject["type"].asString
|
|
||||||
val itData = it.asJsonObject["data"].asJsonObject
|
|
||||||
desc[i] += when (type) {
|
|
||||||
"text" -> itData["text"].asString
|
|
||||||
"at" -> "@${itData["name"].asStringOrNull ?: itData["qq"].asString}"
|
|
||||||
"face" -> "[表情]"
|
|
||||||
"pic", "image" -> "[图片]"
|
|
||||||
"voice", "record" -> "[语音]"
|
|
||||||
"video" -> "[视频]"
|
|
||||||
"node" -> "[合并转发消息]"
|
|
||||||
"markdown" -> "[Markdown消息]"
|
|
||||||
"button" -> "[Button类型]"
|
|
||||||
else -> "[未知消息类型]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).also {
|
|
||||||
if (it.second.isEmpty() && !it.first)
|
|
||||||
error("消息合成失败,请查看日志或者检查输入。")
|
|
||||||
}.second
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
error("消息节点缺少id或content字段")
|
|
||||||
}
|
|
||||||
}.getOrElse {
|
|
||||||
LogCenter.log("消息节点解析失败:$it", Level.WARN)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}.filterNotNull().ifEmpty { return logic("消息节点为空", echo) }
|
|
||||||
|
|
||||||
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val resid = MsgSvc.uploadMultiMsg(uid ?: TicketSvc.getUid(), groupUin, msgs)
|
val message = MsgSvc.uploadMultiMsg(chatType, peerId, fromId, messages, retryCnt)
|
||||||
.getOrElse { return logic(it.message ?: "", echo) }
|
.getOrElse { return logic(it.message ?: "", echo) }
|
||||||
val uniseq = UUID.randomUUID().toString().uppercase()
|
|
||||||
|
|
||||||
val result = MsgSvc.sendToAio(
|
val result = MsgSvc.sendToAio(chatType, peerId, listOf(message).toJson(), fromId, retryCnt)
|
||||||
chatType, peerId,
|
.getOrElse { return logic(it.message ?: "", echo) }
|
||||||
listOf(
|
|
||||||
mapOf(
|
|
||||||
"type" to "json",
|
|
||||||
"data" to mapOf(
|
|
||||||
"data" to mapOf(
|
|
||||||
"app" to "com.tencent.multimsg",
|
|
||||||
"config" to mapOf(
|
|
||||||
"autosize" to 1,
|
|
||||||
"forward" to 1,
|
|
||||||
"round" to 1,
|
|
||||||
"type" to "normal",
|
|
||||||
"width" to 300
|
|
||||||
),
|
|
||||||
"desc" to "[聊天记录]",
|
|
||||||
"extra" to mapOf(
|
|
||||||
"filename" to uniseq,
|
|
||||||
"tsum" to 2
|
|
||||||
).json.toString(),
|
|
||||||
"meta" to mapOf(
|
|
||||||
"detail" to mapOf(
|
|
||||||
"news" to desc.slice(0..if (i < 3) i else 3)
|
|
||||||
.map { mapOf("text" to it) },
|
|
||||||
"resid" to resid,
|
|
||||||
"source" to "群聊的聊天记录",
|
|
||||||
"summary" to "查看${msgs.size}条转发消息",
|
|
||||||
"uniseq" to uniseq
|
|
||||||
)
|
|
||||||
),
|
|
||||||
"prompt" to "[聊天记录]",
|
|
||||||
"ver" to "0.0.0.5",
|
|
||||||
"view" to "contact"
|
|
||||||
),
|
|
||||||
"resid" to resid
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).json, fromId, 3
|
|
||||||
).getOrElse { return logic(it.message ?: "", echo) }
|
|
||||||
|
|
||||||
return ok(
|
return ok(
|
||||||
ForwardMessageResult(
|
SendForwardMessageResult(
|
||||||
msgId = result.msgHashId,
|
msgId = result.msgHashId,
|
||||||
forwardId = resid
|
resId = message.data["id"] as String
|
||||||
), echo = echo
|
), echo = echo
|
||||||
)
|
)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
|
@ -6,12 +6,19 @@ import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
|||||||
import moe.fuqiuluo.symbols.OneBotHandler
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
|
|
||||||
@OneBotHandler("send_group_forward_msg")
|
@OneBotHandler("send_group_forward_msg")
|
||||||
internal object SendGroupForwardMessage: IActionHandler() {
|
internal object SendGroupForwardMessage : IActionHandler() {
|
||||||
override suspend fun internalHandle(session: ActionSession): String {
|
override suspend fun internalHandle(session: ActionSession): String {
|
||||||
val groupId = session.getLong("group_id")
|
val groupId = session.getLong("group_id")
|
||||||
|
val retryCnt = session.getIntOrNull("retry_cnt") ?: 5
|
||||||
return if (session.isArray("messages")) {
|
return if (session.isArray("messages")) {
|
||||||
val messages = session.getArray("messages")
|
val messages = session.getArray("messages")
|
||||||
SendForwardMessage(MsgConstant.KCHATTYPEGROUP, groupId.toString(), messages, echo = session.echo)
|
SendForwardMessage(
|
||||||
|
MsgConstant.KCHATTYPEGROUP,
|
||||||
|
groupId.toString(),
|
||||||
|
messages = messages,
|
||||||
|
retryCnt = retryCnt,
|
||||||
|
echo = session.echo
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
logic("未知格式合并转发消息", session.echo)
|
logic("未知格式合并转发消息", session.echo)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import moe.fuqiuluo.shamrock.tools.jsonArray
|
|||||||
import moe.fuqiuluo.symbols.OneBotHandler
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
|
|
||||||
@OneBotHandler("send_msg", ["send_message"])
|
@OneBotHandler("send_msg", ["send_message"])
|
||||||
internal object SendMessage: IActionHandler() {
|
internal object SendMessage : 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")
|
||||||
try {
|
try {
|
||||||
@ -40,28 +40,61 @@ internal object SendMessage: IActionHandler() {
|
|||||||
return noParam("detail_type/message_type", session.echo)
|
return noParam("detail_type/message_type", session.echo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val peerId = when(chatType) {
|
val peerId = when (chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> session.getLongOrNull("group_id") ?: return noParam("group_id", session.echo)
|
MsgConstant.KCHATTYPEGROUP -> session.getLongOrNull("group_id") ?: return noParam(
|
||||||
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getLongOrNull("user_id") ?: return noParam("user_id", session.echo)
|
"group_id",
|
||||||
|
session.echo
|
||||||
|
)
|
||||||
|
|
||||||
|
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getLongOrNull("user_id")
|
||||||
|
?: return noParam("user_id", session.echo)
|
||||||
|
|
||||||
else -> error("unknown chat type: $chatType")
|
else -> error("unknown chat type: $chatType")
|
||||||
}.toString()
|
}.toString()
|
||||||
val fromId = when(chatType) {
|
val fromId = when (chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getLongOrNull("group_id") ?: return noParam("group_id", session.echo)
|
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getLongOrNull("group_id")
|
||||||
|
?: return noParam("group_id", session.echo)
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEC2C -> session.getLongOrNull("user_id") ?: return noParam("user_id", session.echo)
|
MsgConstant.KCHATTYPEC2C -> session.getLongOrNull("user_id") ?: return noParam("user_id", session.echo)
|
||||||
else -> error("unknown chat type: $chatType")
|
else -> error("unknown chat type: $chatType")
|
||||||
}.toString()
|
}.toString()
|
||||||
val retryCnt = session.getIntOrNull("retry_cnt")
|
val retryCnt = session.getIntOrNull("retry_cnt") ?: 5
|
||||||
val recallDuration = session.getLongOrNull("recall_duration")
|
val recallDuration = session.getLongOrNull("recall_duration")
|
||||||
return if (session.isString("message")) {
|
return if (session.isString("message")) {
|
||||||
val autoEscape = session.getBooleanOrDefault("auto_escape", false)
|
val autoEscape = session.getBooleanOrDefault("auto_escape", false)
|
||||||
val message = session.getString("message")
|
val message = session.getString("message")
|
||||||
invoke(chatType, peerId, message, autoEscape, echo = session.echo, fromId = fromId, retryCnt = retryCnt ?: 5, recallDuration = recallDuration)
|
invoke(
|
||||||
|
chatType,
|
||||||
|
peerId,
|
||||||
|
message,
|
||||||
|
autoEscape,
|
||||||
|
echo = session.echo,
|
||||||
|
fromId = fromId,
|
||||||
|
retryCnt = retryCnt,
|
||||||
|
recallDuration = recallDuration
|
||||||
|
)
|
||||||
} else if (session.isArray("message")) {
|
} else if (session.isArray("message")) {
|
||||||
val message = session.getArray("message")
|
val message = session.getArray("message")
|
||||||
invoke(chatType, peerId, message, session.echo, fromId = fromId, retryCnt ?: 5, recallDuration = recallDuration)
|
invoke(
|
||||||
|
chatType,
|
||||||
|
peerId,
|
||||||
|
message,
|
||||||
|
session.echo,
|
||||||
|
fromId = fromId,
|
||||||
|
retryCnt = retryCnt,
|
||||||
|
recallDuration = recallDuration
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
val message = session.getObject("message")
|
val message = session.getObject("message")
|
||||||
invoke(chatType, peerId, listOf( message ).jsonArray, session.echo, fromId = fromId, retryCnt ?: 5, recallDuration = recallDuration)
|
invoke(
|
||||||
|
chatType,
|
||||||
|
peerId,
|
||||||
|
listOf(message).jsonArray,
|
||||||
|
session.echo,
|
||||||
|
fromId = fromId,
|
||||||
|
retryCnt = retryCnt,
|
||||||
|
recallDuration = recallDuration
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (e: ParamsException) {
|
} catch (e: ParamsException) {
|
||||||
return noParam(e.message!!, session.echo)
|
return noParam(e.message!!, session.echo)
|
||||||
@ -82,14 +115,16 @@ internal object SendMessage: IActionHandler() {
|
|||||||
echo: JsonElement = EmptyJsonString
|
echo: JsonElement = EmptyJsonString
|
||||||
): String {
|
): String {
|
||||||
val result = if (autoEscape) {
|
val result = if (autoEscape) {
|
||||||
MsgSvc.sendToAio(chatType, peerId, listOf(
|
MsgSvc.sendToAio(
|
||||||
mapOf(
|
chatType, peerId, listOf(
|
||||||
"type" to "text",
|
mapOf(
|
||||||
"data" to mapOf(
|
"type" to "text",
|
||||||
"text" to message
|
"data" to mapOf(
|
||||||
|
"text" to message
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
).json, fromId = fromId, retryCnt
|
||||||
).json, fromId = fromId, retryCnt)
|
)
|
||||||
} else {
|
} else {
|
||||||
val msg = MessageHelper.decodeCQCode(message)
|
val msg = MessageHelper.decodeCQCode(message)
|
||||||
if (msg.isEmpty()) {
|
if (msg.isEmpty()) {
|
||||||
@ -98,44 +133,54 @@ internal object SendMessage: IActionHandler() {
|
|||||||
} else {
|
} else {
|
||||||
MsgSvc.sendToAio(chatType, peerId, msg, fromId = fromId, retryCnt)
|
MsgSvc.sendToAio(chatType, peerId, msg, fromId = fromId, retryCnt)
|
||||||
}
|
}
|
||||||
}
|
}.getOrElse{ return logic(it.message ?: "", echo)}
|
||||||
if (result.isFailure) {
|
if (result.msgHashId <= 0) {
|
||||||
return logic(result.exceptionOrNull()?.message ?: "", echo)
|
|
||||||
}
|
|
||||||
val sendMsgResult = result.getOrThrow()
|
|
||||||
if (sendMsgResult.msgHashId <= 0) {
|
|
||||||
return logic("send message failed", echo = echo)
|
return logic("send message failed", echo = echo)
|
||||||
}
|
}
|
||||||
recallDuration?.let { autoRecall(sendMsgResult.msgHashId, it) }
|
if (recallDuration != null) {
|
||||||
return ok(MessageResult(
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
msgId = sendMsgResult.msgHashId,
|
delay(recallDuration)
|
||||||
time = (sendMsgResult.msgTime * 0.001).toLong()
|
MsgSvc.recallMsg(result.msgHashId)
|
||||||
), echo = echo)
|
}
|
||||||
|
}
|
||||||
|
return ok(
|
||||||
|
MessageResult(
|
||||||
|
msgId = result.msgHashId,
|
||||||
|
time = (result.msgTime * 0.001).toLong()
|
||||||
|
), echo = echo
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 消息段格式消息
|
// 消息段格式消息
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
chatType: Int, peerId: String, message: JsonArray, echo: JsonElement = EmptyJsonString, fromId: String = peerId, retryCnt: Int, recallDuration: Long?,
|
chatType: Int,
|
||||||
|
peerId: String,
|
||||||
|
message: JsonArray,
|
||||||
|
echo: JsonElement = EmptyJsonString,
|
||||||
|
fromId: String = peerId,
|
||||||
|
retryCnt: Int,
|
||||||
|
recallDuration: Long?,
|
||||||
): String {
|
): String {
|
||||||
//if (!ContactHelper.checkContactAvailable(chatType, peerId)) {
|
//if (!ContactHelper.checkContactAvailable(chatType, peerId)) {
|
||||||
// return logic("contact is not found", echo = echo)
|
// return logic("contact is not found", echo = echo)
|
||||||
//}
|
//}
|
||||||
val result = MsgSvc.sendToAio(chatType, peerId, message, fromId = fromId, retryCnt).getOrElse { return logic(it.message ?: "", echo) }
|
val result = MsgSvc.sendToAio(chatType, peerId, message, fromId, retryCnt)
|
||||||
|
.getOrElse { return logic(it.message ?: "", echo) }
|
||||||
if (result.msgHashId <= 0) {
|
if (result.msgHashId <= 0) {
|
||||||
return logic("send message failed", echo = echo)
|
return logic("send message failed", echo = echo)
|
||||||
}
|
}
|
||||||
recallDuration?.let { autoRecall(result.msgHashId, it) }
|
if (recallDuration != null) {
|
||||||
return ok(MessageResult(
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
msgId = result.msgHashId,
|
delay(recallDuration)
|
||||||
time = (result.msgTime * 0.001).toLong()
|
MsgSvc.recallMsg(result.msgHashId)
|
||||||
), echo)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun autoRecall(msgHash: Int, duration: Long) {
|
|
||||||
GlobalScope.launch(Dispatchers.Default) {
|
|
||||||
delay(duration)
|
|
||||||
MsgSvc.recallMsg(msgHash)
|
|
||||||
}
|
}
|
||||||
|
return ok(
|
||||||
|
MessageResult(
|
||||||
|
msgId = result.msgHashId,
|
||||||
|
time = (result.msgTime * 0.001).toLong()
|
||||||
|
), echo
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val requiredParams: Array<String> = arrayOf("message")
|
override val requiredParams: Array<String> = arrayOf("message")
|
||||||
|
@ -9,9 +9,18 @@ import moe.fuqiuluo.symbols.OneBotHandler
|
|||||||
internal object SendPrivateForwardMessage : IActionHandler() {
|
internal object SendPrivateForwardMessage : IActionHandler() {
|
||||||
override suspend fun internalHandle(session: ActionSession): String {
|
override suspend fun internalHandle(session: ActionSession): String {
|
||||||
val userId = session.getLong("user_id")
|
val userId = session.getLong("user_id")
|
||||||
|
val groupId = session.getLongOrNull("group_id")
|
||||||
|
val retryCnt = session.getIntOrNull("retry_cnt") ?: 5
|
||||||
return if (session.isArray("messages")) {
|
return if (session.isArray("messages")) {
|
||||||
val messages = session.getArray("messages")
|
val messages = session.getArray("messages")
|
||||||
SendForwardMessage(MsgConstant.KCHATTYPEC2C, userId.toString(), messages, echo = session.echo)
|
SendForwardMessage(
|
||||||
|
MsgConstant.KCHATTYPEC2C,
|
||||||
|
userId.toString(),
|
||||||
|
groupId?.toString() ?: userId.toString(),
|
||||||
|
messages,
|
||||||
|
retryCnt,
|
||||||
|
echo = session.echo
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
logic("未知格式合并转发消息", session.echo)
|
logic("未知格式合并转发消息", session.echo)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||||
|
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||||
|
import moe.fuqiuluo.shamrock.helper.ParamsException
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
|
import moe.fuqiuluo.shamrock.remote.service.data.UploadForwardMessageResult
|
||||||
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
|
|
||||||
|
@OneBotHandler("upload_multi_message")
|
||||||
|
internal object UploadMultiMessage : IActionHandler() {
|
||||||
|
override suspend fun internalHandle(session: ActionSession): String {
|
||||||
|
val detailType = session.getStringOrNull("detail_type") ?: session.getStringOrNull("message_type")
|
||||||
|
try {
|
||||||
|
val chatType = detailType?.let {
|
||||||
|
MessageHelper.obtainMessageTypeByDetailType(it)
|
||||||
|
} ?: run {
|
||||||
|
if (session.has("user_id")) {
|
||||||
|
if (session.has("group_id")) {
|
||||||
|
MsgConstant.KCHATTYPETEMPC2CFROMGROUP
|
||||||
|
} else {
|
||||||
|
MsgConstant.KCHATTYPEC2C
|
||||||
|
}
|
||||||
|
} else if (session.has("group_id")) {
|
||||||
|
MsgConstant.KCHATTYPEGROUP
|
||||||
|
} else {
|
||||||
|
return noParam("detail_type/message_type", session.echo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val peerId = when (chatType) {
|
||||||
|
MsgConstant.KCHATTYPEGROUP -> session.getLongOrNull("group_id") ?: return noParam(
|
||||||
|
"group_id",
|
||||||
|
session.echo
|
||||||
|
)
|
||||||
|
|
||||||
|
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getLongOrNull("user_id")
|
||||||
|
?: return noParam("user_id", session.echo)
|
||||||
|
|
||||||
|
else -> error("unknown chat type: $chatType")
|
||||||
|
}.toString()
|
||||||
|
val fromId = when (chatType) {
|
||||||
|
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getLongOrNull("group_id")
|
||||||
|
?: return noParam("group_id", session.echo)
|
||||||
|
|
||||||
|
MsgConstant.KCHATTYPEC2C -> session.getLongOrNull("user_id") ?: return noParam(
|
||||||
|
"user_id",
|
||||||
|
session.echo
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> error("unknown chat type: $chatType")
|
||||||
|
}.toString()
|
||||||
|
val retryCnt = session.getIntOrNull("retry_cnt") ?: 5
|
||||||
|
return if (session.isArray("messages")) {
|
||||||
|
val messages = session.getArray("messages")
|
||||||
|
invoke(chatType, peerId, fromId, messages, retryCnt, echo = session.echo)
|
||||||
|
} else {
|
||||||
|
logic("未知格式合并转发消息", session.echo)
|
||||||
|
}
|
||||||
|
} catch (e: ParamsException) {
|
||||||
|
return noParam(e.message!!, session.echo)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
return logic(e.message ?: e.toString(), session.echo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend operator fun invoke(
|
||||||
|
chatType: Int,
|
||||||
|
peerId: String,
|
||||||
|
fromId: String = peerId,
|
||||||
|
messages: JsonArray,
|
||||||
|
retryCnt: Int,
|
||||||
|
echo: JsonElement = EmptyJsonString
|
||||||
|
): String {
|
||||||
|
kotlin.runCatching {
|
||||||
|
val message = MsgSvc.uploadMultiMsg(chatType, peerId, fromId, messages, retryCnt)
|
||||||
|
.getOrElse { return logic(it.message ?: "", echo) }
|
||||||
|
|
||||||
|
return ok(
|
||||||
|
UploadForwardMessageResult(
|
||||||
|
resId = message.data["id"] as String,
|
||||||
|
filename = message.data["filename"] as String,
|
||||||
|
summary = message.data["summary"] as String,
|
||||||
|
desc = message.data["desc"] as String
|
||||||
|
), echo = echo
|
||||||
|
)
|
||||||
|
}.onFailure {
|
||||||
|
return error("合并转发消息失败: $it", echo)
|
||||||
|
}
|
||||||
|
return logic("合并转发消息失败(unknown error)", echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val requiredParams: Array<String> = arrayOf("messages")
|
||||||
|
}
|
@ -44,49 +44,6 @@ fun Routing.messageAction() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
route("/send_group_forward_(msg|message)".toRegex()) {
|
|
||||||
post {
|
|
||||||
val groupId = fetchPostOrNull("group_id")
|
|
||||||
val messages = fetchPostJsonArray("messages")
|
|
||||||
call.respondText(
|
|
||||||
SendForwardMessage(MsgConstant.KCHATTYPEGROUP, groupId ?: "", messages),
|
|
||||||
ContentType.Application.Json
|
|
||||||
)
|
|
||||||
}
|
|
||||||
get {
|
|
||||||
respond(false, Status.InternalHandlerError, "Not support GET method")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
route("/send_private_forward_(msg|message)".toRegex()) {
|
|
||||||
post {
|
|
||||||
val userId = fetchPostOrNull("user_id")
|
|
||||||
val messages = fetchPostJsonArray("messages")
|
|
||||||
call.respondText(
|
|
||||||
SendForwardMessage(MsgConstant.KCHATTYPEC2C, userId ?: "", messages),
|
|
||||||
ContentType.Application.Json
|
|
||||||
)
|
|
||||||
}
|
|
||||||
get {
|
|
||||||
respond(false, Status.InternalHandlerError, "Not support GET method")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
@ -144,7 +101,7 @@ fun Routing.messageAction() {
|
|||||||
get {
|
get {
|
||||||
val msgType = fetchGetOrThrow("message_type")
|
val msgType = fetchGetOrThrow("message_type")
|
||||||
val message = fetchGetOrThrow("message")
|
val message = fetchGetOrThrow("message")
|
||||||
val retryCnt = fetchGetOrNull("retry_cnt")?.toInt() ?: 3
|
val retryCnt = fetchGetOrNull("retry_cnt")?.toInt() ?: 5
|
||||||
val autoEscape = fetchGetOrNull("auto_escape")?.toBooleanStrict() ?: false
|
val autoEscape = fetchGetOrNull("auto_escape")?.toBooleanStrict() ?: false
|
||||||
val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType)
|
val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType)
|
||||||
|
|
||||||
@ -288,11 +245,11 @@ fun Routing.messageAction() {
|
|||||||
post {
|
post {
|
||||||
val userId = fetchPostOrThrow("user_id")
|
val userId = fetchPostOrThrow("user_id")
|
||||||
val groupId = fetchPostOrNull("group_id")
|
val groupId = fetchPostOrNull("group_id")
|
||||||
val retryCnt = fetchPostOrNull("retry_cnt")?.toInt() ?: 3
|
|
||||||
val autoEscape = fetchPostOrNull("auto_escape")?.toBooleanStrict() ?: false
|
|
||||||
|
|
||||||
val chatType = if (groupId == null) MsgConstant.KCHATTYPEC2C else MsgConstant.KCHATTYPETEMPC2CFROMGROUP
|
val chatType = if (groupId == null) MsgConstant.KCHATTYPEC2C else MsgConstant.KCHATTYPETEMPC2CFROMGROUP
|
||||||
val fromId = groupId ?: userId
|
|
||||||
|
val retryCnt = fetchPostOrNull("retry_cnt")?.toInt() ?: 3
|
||||||
|
val autoEscape = fetchPostOrNull("auto_escape")?.toBooleanStrict() ?: false
|
||||||
val recallDuration = fetchPostOrNull("recall_duration")?.toLongOrNull()
|
val recallDuration = fetchPostOrNull("recall_duration")?.toLongOrNull()
|
||||||
|
|
||||||
val result = if (isJsonData()) {
|
val result = if (isJsonData()) {
|
||||||
@ -302,8 +259,9 @@ fun Routing.messageAction() {
|
|||||||
peerId = userId,
|
peerId = userId,
|
||||||
message = fetchPostJsonString("message"),
|
message = fetchPostJsonString("message"),
|
||||||
autoEscape = autoEscape,
|
autoEscape = autoEscape,
|
||||||
fromId = fromId,
|
fromId = groupId ?: userId,
|
||||||
retryCnt = retryCnt, recallDuration = recallDuration
|
retryCnt = retryCnt,
|
||||||
|
recallDuration = recallDuration
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
SendMessage(
|
SendMessage(
|
||||||
@ -312,7 +270,7 @@ fun Routing.messageAction() {
|
|||||||
message = if (isJsonObject("message")) listOf(fetchPostJsonObject("message")).jsonArray else fetchPostJsonArray(
|
message = if (isJsonObject("message")) listOf(fetchPostJsonObject("message")).jsonArray else fetchPostJsonArray(
|
||||||
"message"
|
"message"
|
||||||
),
|
),
|
||||||
fromId = groupId ?: userId ?: "",
|
fromId = groupId ?: userId,
|
||||||
retryCnt = retryCnt,
|
retryCnt = retryCnt,
|
||||||
recallDuration = recallDuration
|
recallDuration = recallDuration
|
||||||
)
|
)
|
||||||
@ -323,12 +281,96 @@ fun Routing.messageAction() {
|
|||||||
peerId = userId,
|
peerId = userId,
|
||||||
message = fetchPostOrThrow("message"),
|
message = fetchPostOrThrow("message"),
|
||||||
autoEscape = autoEscape,
|
autoEscape = autoEscape,
|
||||||
fromId = fromId,
|
fromId = groupId ?: userId,
|
||||||
retryCnt = retryCnt, recallDuration = recallDuration
|
retryCnt = retryCnt,
|
||||||
|
recallDuration = recallDuration
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
call.respondText(result, ContentType.Application.Json)
|
call.respondText(result, ContentType.Application.Json)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
route("/upload_multi_(msg|message)".toRegex()) {
|
||||||
|
post {
|
||||||
|
val msgType = fetchPostOrThrow("message_type")
|
||||||
|
val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType)
|
||||||
|
val retryCnt = fetchPostOrNull("retry_cnt")?.toInt() ?: 5
|
||||||
|
|
||||||
|
val userId = fetchPostOrNull("user_id")
|
||||||
|
val groupId = fetchPostOrNull("group_id")
|
||||||
|
val messages = fetchPostJsonArray("messages")
|
||||||
|
call.respondText(
|
||||||
|
UploadMultiMessage(
|
||||||
|
chatType,
|
||||||
|
if (chatType == MsgConstant.KCHATTYPEC2C) userId!! else groupId!!,
|
||||||
|
groupId ?: userId ?: "",
|
||||||
|
messages,
|
||||||
|
retryCnt
|
||||||
|
),
|
||||||
|
ContentType.Application.Json
|
||||||
|
)
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
respond(false, Status.InternalHandlerError, "Not support GET method")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
route("/send_forward_(msg|message)".toRegex()) {
|
||||||
|
post {
|
||||||
|
val msgType = fetchPostOrThrow("message_type")
|
||||||
|
val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType)
|
||||||
|
val retryCnt = fetchPostOrNull("retry_cnt")?.toInt() ?: 5
|
||||||
|
|
||||||
|
val userId = fetchPostOrNull("user_id")
|
||||||
|
val groupId = fetchPostOrNull("group_id")
|
||||||
|
val messages = fetchPostJsonArray("messages")
|
||||||
|
call.respondText(
|
||||||
|
SendForwardMessage(
|
||||||
|
chatType,
|
||||||
|
if (chatType == MsgConstant.KCHATTYPEC2C) userId!! else groupId!!,
|
||||||
|
groupId ?: userId ?: "",
|
||||||
|
messages,
|
||||||
|
retryCnt
|
||||||
|
),
|
||||||
|
ContentType.Application.Json
|
||||||
|
)
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
respond(false, Status.InternalHandlerError, "Not support GET method")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
route("/send_private_forward_(msg|message)".toRegex()) {
|
||||||
|
post {
|
||||||
|
val userId = fetchPostOrThrow("user_id")
|
||||||
|
val groupId = fetchPostOrNull("group_id")
|
||||||
|
|
||||||
|
val retryCnt = fetchPostOrNull("retry_cnt")?.toInt() ?: 5
|
||||||
|
val messages = fetchPostJsonArray("messages")
|
||||||
|
call.respondText(
|
||||||
|
SendForwardMessage(MsgConstant.KCHATTYPEC2C, userId, groupId ?: userId, messages, retryCnt),
|
||||||
|
ContentType.Application.Json
|
||||||
|
)
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
respond(false, Status.InternalHandlerError, "Not support GET method")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
route("/send_group_forward_(msg|message)".toRegex()) {
|
||||||
|
post {
|
||||||
|
val groupId = fetchPostOrThrow("group_id")
|
||||||
|
|
||||||
|
val retryCnt = fetchPostOrNull("retry_cnt")?.toInt() ?: 5
|
||||||
|
val messages = fetchPostJsonArray("messages")
|
||||||
|
call.respondText(
|
||||||
|
SendForwardMessage(MsgConstant.KCHATTYPEGROUP, groupId, messages = messages, retryCnt = retryCnt),
|
||||||
|
ContentType.Application.Json
|
||||||
|
)
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
respond(false, Status.InternalHandlerError, "Not support GET method")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,10 +9,20 @@ internal data class MessageResult(
|
|||||||
@SerialName("message_id") val msgId: Int,
|
@SerialName("message_id") val msgId: Int,
|
||||||
@SerialName("time") val time: Long
|
@SerialName("time") val time: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class ForwardMessageResult(
|
internal data class UploadForwardMessageResult(
|
||||||
|
@SerialName("res_id") val resId: String,
|
||||||
|
@SerialName("filename") val filename: String,
|
||||||
|
@SerialName("summary") val summary: String,
|
||||||
|
@SerialName("desc") val desc: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
internal data class SendForwardMessageResult(
|
||||||
@SerialName("message_id") val msgId: Int,
|
@SerialName("message_id") val msgId: Int,
|
||||||
@SerialName("forward_id") val forwardId: String
|
@SerialName("res_id") val resId: String,
|
||||||
|
@SerialName("forward_id") val forwardId: String = resId
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -20,6 +30,7 @@ internal data class MessageDetail(
|
|||||||
@SerialName("time") val time: Int,
|
@SerialName("time") val time: Int,
|
||||||
@SerialName("message_type") val msgType: String,
|
@SerialName("message_type") val msgType: String,
|
||||||
@SerialName("message_id") val msgId: Int,
|
@SerialName("message_id") val msgId: Int,
|
||||||
|
@SerialName("message_id_qq") val qqMsgId: Long,
|
||||||
@SerialName("message_seq") val msgSeq: Long,
|
@SerialName("message_seq") val msgSeq: Long,
|
||||||
@SerialName("real_id") val realId: Long,
|
@SerialName("real_id") val realId: Long,
|
||||||
@SerialName("sender") val sender: MessageSender,
|
@SerialName("sender") val sender: MessageSender,
|
||||||
@ -29,6 +40,11 @@ internal data class MessageDetail(
|
|||||||
@SerialName("target_id") val targetId: Long = 0,
|
@SerialName("target_id") val targetId: Long = 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
internal data class GetForwardMsgResult(
|
||||||
|
@SerialName("messages") val msgs: List<MessageDetail>
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class MessageSender(
|
internal data class MessageSender(
|
||||||
@SerialName("user_id") val userId: Long,
|
@SerialName("user_id") val userId: Long,
|
||||||
|
@ -69,7 +69,6 @@ internal object PrimitiveListener {
|
|||||||
528 -> when (subType) {
|
528 -> when (subType) {
|
||||||
35 -> onFriendApply(msgTime, push.clientInfo!!, body)
|
35 -> onFriendApply(msgTime, push.clientInfo!!, body)
|
||||||
39 -> onCardChange(msgTime, body)
|
39 -> onCardChange(msgTime, body)
|
||||||
// invite
|
|
||||||
68 -> onGroupApply(msgTime, contentHead, body)
|
68 -> onGroupApply(msgTime, contentHead, body)
|
||||||
138 -> onC2CRecall(msgTime, body)
|
138 -> onC2CRecall(msgTime, body)
|
||||||
290 -> onC2CPoke(msgTime, body)
|
290 -> onC2CPoke(msgTime, body)
|
||||||
@ -79,7 +78,7 @@ internal object PrimitiveListener {
|
|||||||
12 -> onGroupBan(msgTime, body)
|
12 -> onGroupBan(msgTime, body)
|
||||||
16 -> onGroupUniqueTitleChange(msgTime, body)
|
16 -> onGroupUniqueTitleChange(msgTime, body)
|
||||||
17 -> onGroupRecall(msgTime, body)
|
17 -> onGroupRecall(msgTime, body)
|
||||||
20 -> onGroupPokeAndGroupSign(msgTime, body)
|
20 -> onGroupCommonTips(msgTime, body)
|
||||||
21 -> onEssenceMessage(msgTime, push.clientInfo, body)
|
21 -> onEssenceMessage(msgTime, push.clientInfo, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,7 +280,7 @@ internal object PrimitiveListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private suspend fun onGroupPokeAndGroupSign(time: Long, body: MsgBody) {
|
private suspend fun onGroupCommonTips(time: Long, body: MsgBody) {
|
||||||
val event = runCatching {
|
val event = runCatching {
|
||||||
body.msgContent!!.decodeProtobuf<GroupCommonTipsEvent>()
|
body.msgContent!!.decodeProtobuf<GroupCommonTipsEvent>()
|
||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
|
@ -52,6 +52,7 @@ val Collection<Any?>.json: JsonArray
|
|||||||
is Number -> arrayList.add(it.json)
|
is Number -> arrayList.add(it.json)
|
||||||
is String -> arrayList.add(it.json)
|
is String -> arrayList.add(it.json)
|
||||||
is Boolean -> arrayList.add(it.json)
|
is Boolean -> arrayList.add(it.json)
|
||||||
|
is Pair<*, *> -> arrayList.add(mapOf(it as Pair<String, Any?>).json)
|
||||||
is Map<*, *> -> arrayList.add((it as Map<String, Any?>).json)
|
is Map<*, *> -> arrayList.add((it as Map<String, Any?>).json)
|
||||||
is Collection<*> -> arrayList.add((it as Collection<Any?>).json)
|
is Collection<*> -> arrayList.add((it as Collection<Any?>).json)
|
||||||
else -> error("unknown array type: ${it::class.java}")
|
else -> error("unknown array type: ${it::class.java}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user