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
a5cdd64686
commit
1c65aab673
@ -7,4 +7,5 @@ import kotlinx.serialization.protobuf.ProtoNumber
|
|||||||
data class MessageBody(
|
data class MessageBody(
|
||||||
@ProtoNumber(1) val rich: RichMessage? = null,
|
@ProtoNumber(1) val rich: RichMessage? = null,
|
||||||
@ProtoNumber(2) val rawBuffer: ByteArray? = null,
|
@ProtoNumber(2) val rawBuffer: ByteArray? = null,
|
||||||
|
@ProtoNumber(3) val MsgEncryptContent: ByteArray? = null
|
||||||
)
|
)
|
@ -11,8 +11,20 @@ data class MessageContent(
|
|||||||
@ProtoNumber(5) val msgSeq: Long = Long.MIN_VALUE,
|
@ProtoNumber(5) val msgSeq: Long = Long.MIN_VALUE,
|
||||||
@ProtoNumber(6) val msgTime: Long? = null,
|
@ProtoNumber(6) val msgTime: Long? = null,
|
||||||
@ProtoNumber(7) val u2: Int? = null,
|
@ProtoNumber(7) val u2: Int? = null,
|
||||||
|
@ProtoNumber(8) val u6: Int? = null,
|
||||||
|
@ProtoNumber(9) val u7: Int? = null,
|
||||||
@ProtoNumber(11) val u3: Long? = null,
|
@ProtoNumber(11) val u3: Long? = null,
|
||||||
@ProtoNumber(12) val msgRandom: Long = Long.MIN_VALUE,
|
@ProtoNumber(12) val msgRandom: Long = Long.MIN_VALUE,
|
||||||
@ProtoNumber(14) val u4: Long? = null,
|
@ProtoNumber(14) val u4: Long? = null,
|
||||||
@ProtoNumber(28) val u5: Long? = null,
|
@ProtoNumber(15) val forwardHead: ForwardHead? = null,
|
||||||
|
@ProtoNumber(28) val u5: Long? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ForwardHead(
|
||||||
|
@ProtoNumber(1) val u1: Int? = null,
|
||||||
|
@ProtoNumber(2) val u2: Int? = null,
|
||||||
|
@ProtoNumber(3) val u3: Int? = null,
|
||||||
|
@ProtoNumber(4) val u4: String? = null,
|
||||||
|
@ProtoNumber(5) val Avatar: String? = null
|
||||||
)
|
)
|
@ -7,6 +7,7 @@ import protobuf.message.element.*
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class MessageElement(
|
data class MessageElement(
|
||||||
@ProtoNumber(1) val text: TextElement? = null,
|
@ProtoNumber(1) val text: TextElement? = null,
|
||||||
|
@ProtoNumber(2) val face: FaceElement? = null,
|
||||||
@ProtoNumber(51) val json: JsonElement? = null,
|
@ProtoNumber(51) val json: JsonElement? = null,
|
||||||
@ProtoNumber(53) val commElem: CommonElement? = null,
|
@ProtoNumber(53) val commElem: CommonElement? = null,
|
||||||
)
|
)
|
@ -13,12 +13,19 @@ data class MessageHead(
|
|||||||
@ProtoNumber(4) val appId: Int = Int.MIN_VALUE,
|
@ProtoNumber(4) val appId: Int = Int.MIN_VALUE,
|
||||||
@ProtoNumber(5) val receiver: Long? = null,
|
@ProtoNumber(5) val receiver: Long? = null,
|
||||||
@ProtoNumber(6) val receiverUid: String? = null,
|
@ProtoNumber(6) val receiverUid: String? = null,
|
||||||
|
@ProtoNumber(7) val forward: MessageForward? = null,
|
||||||
@ProtoNumber(8) val groupInfo: GroupInfo? = null,
|
@ProtoNumber(8) val groupInfo: GroupInfo? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class MessageForward(
|
||||||
|
@ProtoNumber(6) val friendName: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class GroupInfo(
|
data class GroupInfo(
|
||||||
@ProtoNumber(1) val groupCode: ULong = ULong.MIN_VALUE,
|
@ProtoNumber(1) val groupCode: ULong = ULong.MIN_VALUE,
|
||||||
@ProtoNumber(4) val memberCard: String? = null,
|
@ProtoNumber(4) val memberCard: String? = null,
|
||||||
|
@ProtoNumber(5) val u1: Int? = null,
|
||||||
@ProtoNumber(7) val groupName: String? = null,
|
@ProtoNumber(7) val groupName: String? = null,
|
||||||
)
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
package protobuf.message.element
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class FaceElement(
|
||||||
|
@ProtoNumber(1) val id: Int? = null,
|
||||||
|
)
|
62
protobuf/src/main/java/protobuf/message/longmsg/LongMsg.kt
Normal file
62
protobuf/src/main/java/protobuf/message/longmsg/LongMsg.kt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
@file:OptIn(ExperimentalSerializationApi::class)
|
||||||
|
package protobuf.message.longmsg
|
||||||
|
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LongMsgSettings(
|
||||||
|
@ProtoNumber(1) val field1: Int? = null,
|
||||||
|
@ProtoNumber(2) val field2: Int? = null,
|
||||||
|
@ProtoNumber(3) val field3: Int? = null,
|
||||||
|
@ProtoNumber(4) val field4: Int? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LongMsgUid(
|
||||||
|
@ProtoNumber(2) val uid: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RecvLongMsgInfo(
|
||||||
|
@ProtoNumber(1) val uid: LongMsgUid? = null,
|
||||||
|
@ProtoNumber(2) val resId: String? = null,
|
||||||
|
@ProtoNumber(3) val acquire: Boolean? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SendLongMsgInfo(
|
||||||
|
@ProtoNumber(1) val type: Int? = null,
|
||||||
|
@ProtoNumber(2) val uid: LongMsgUid? = null,
|
||||||
|
@ProtoNumber(3) val groupUin: Int? = null,
|
||||||
|
@ProtoNumber(4) val payload: ByteArray? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LongMsgReq(
|
||||||
|
@ProtoNumber(1) val recvInfo: RecvLongMsgInfo? = null,
|
||||||
|
@ProtoNumber(2) val sendInfo: SendLongMsgInfo? = null,
|
||||||
|
@ProtoNumber(15) val setting: LongMsgSettings? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LongMsgRsp(
|
||||||
|
@ProtoNumber(1) val recvResult: RecvLongMsgResult? = null,
|
||||||
|
@ProtoNumber(2) val sendResult: SendLongMsgResult? = null,
|
||||||
|
@ProtoNumber(15) val setting: LongMsgSettings? = null
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
@Serializable
|
||||||
|
data class SendLongMsgResult(
|
||||||
|
@ProtoNumber(3) val resId: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RecvLongMsgResult(
|
||||||
|
@ProtoNumber(3) val resId: String? = null,
|
||||||
|
@ProtoNumber(4) val payload: ByteArray? = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
@file:OptIn(ExperimentalSerializationApi::class)
|
||||||
|
|
||||||
|
package protobuf.message.longmsg
|
||||||
|
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
|
import protobuf.message.MessageBody
|
||||||
|
import protobuf.message.MessageContent
|
||||||
|
import protobuf.message.MessageHead
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PushMsgBody(
|
||||||
|
@ProtoNumber(1) val head: MessageHead? = null,
|
||||||
|
@ProtoNumber(2) val content: MessageContent? = null,
|
||||||
|
@ProtoNumber(3) val body: MessageBody? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LongMsgContent(
|
||||||
|
@ProtoNumber(1) val body: List<PushMsgBody>? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LongMsgAction(
|
||||||
|
@ProtoNumber(1) val command: String? = null,
|
||||||
|
@ProtoNumber(2) val data: LongMsgContent? = null
|
||||||
|
)
|
||||||
|
@Serializable
|
||||||
|
data class LongMsgPayload(
|
||||||
|
@ProtoNumber(2) val action: LongMsgAction? = null
|
||||||
|
)
|
@ -5,12 +5,7 @@ package moe.fuqiuluo.qqinterface.servlet
|
|||||||
import com.tencent.mobileqq.qroute.QRoute
|
import com.tencent.mobileqq.qroute.QRoute
|
||||||
import com.tencent.mobileqq.troop.api.ITroopMemberNameService
|
import com.tencent.mobileqq.troop.api.ITroopMemberNameService
|
||||||
import com.tencent.qqnt.kernel.api.IKernelService
|
import com.tencent.qqnt.kernel.api.IKernelService
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback
|
import com.tencent.qqnt.kernel.nativeinterface.*
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatGameSession
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatInfo
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatPrepareInfo
|
|
||||||
import com.tencent.qqnt.msg.api.IMsgService
|
import com.tencent.qqnt.msg.api.IMsgService
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@ -18,21 +13,33 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
|
import kotlinx.serialization.decodeFromByteArray
|
||||||
|
import kotlinx.serialization.encodeToByteArray
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toSegments
|
||||||
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
||||||
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.helper.SendMsgException
|
import moe.fuqiuluo.shamrock.helper.MessageHelper.messageArrayToMessageElements
|
||||||
import moe.fuqiuluo.shamrock.remote.structures.SendMsgResult
|
import moe.fuqiuluo.shamrock.remote.structures.SendMsgResult
|
||||||
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
|
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||||
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
||||||
import moe.fuqiuluo.shamrock.xposed.helper.msgService
|
import moe.fuqiuluo.shamrock.xposed.helper.msgService
|
||||||
|
import protobuf.message.*
|
||||||
|
import protobuf.message.longmsg.*
|
||||||
|
import tencent.mobileim.structmsg.structmsg.SystemMsg
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import kotlin.collections.slice
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
import kotlin.random.Random
|
||||||
|
import kotlin.random.nextLong
|
||||||
|
|
||||||
internal object MsgSvc: BaseSvc() {
|
internal object MsgSvc : BaseSvc() {
|
||||||
suspend fun prepareTempChatFromGroup(
|
suspend fun prepareTempChatFromGroup(
|
||||||
groupId: String,
|
groupId: String,
|
||||||
peerId: String
|
peerId: String
|
||||||
@ -40,16 +47,19 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
LogCenter.log("主动临时消息,创建临时会话。", Level.INFO)
|
LogCenter.log("主动临时消息,创建临时会话。", Level.INFO)
|
||||||
val msgService = app.getRuntimeService(IKernelService::class.java, "all").msgService
|
val msgService = app.getRuntimeService(IKernelService::class.java, "all").msgService
|
||||||
?: return Result.failure(Exception("获取消息服务失败"))
|
?: return Result.failure(Exception("获取消息服务失败"))
|
||||||
msgService.prepareTempChat(TempChatPrepareInfo(
|
msgService.prepareTempChat(
|
||||||
|
TempChatPrepareInfo(
|
||||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP,
|
MsgConstant.KCHATTYPETEMPC2CFROMGROUP,
|
||||||
ContactHelper.getUidByUinAsync(peerId = peerId.toLong()),
|
ContactHelper.getUidByUinAsync(peerId = peerId.toLong()),
|
||||||
app.getRuntimeService(ITroopMemberNameService::class.java, "all").getTroopMemberNameRemarkFirst(groupId, peerId),
|
app.getRuntimeService(ITroopMemberNameService::class.java, "all")
|
||||||
|
.getTroopMemberNameRemarkFirst(groupId, peerId),
|
||||||
groupId,
|
groupId,
|
||||||
EMPTY_BYTE_ARRAY,
|
EMPTY_BYTE_ARRAY,
|
||||||
app.currentUid,
|
app.currentUid,
|
||||||
"",
|
"",
|
||||||
TempChatGameSession()
|
TempChatGameSession()
|
||||||
)) { code, reason ->
|
)
|
||||||
|
) { code, reason ->
|
||||||
if (code != 0) {
|
if (code != 0) {
|
||||||
LogCenter.log("临时会话创建失败: $code, $reason", Level.ERROR)
|
LogCenter.log("临时会话创建失败: $code, $reason", Level.ERROR)
|
||||||
}
|
}
|
||||||
@ -83,7 +93,7 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
?: return Result.failure(Exception("没有对应消息映射,消息获取失败"))
|
?: return Result.failure(Exception("没有对应消息映射,消息获取失败"))
|
||||||
|
|
||||||
val peerId = mapping.peerId
|
val peerId = mapping.peerId
|
||||||
val contact = MessageHelper.generateContact(mapping.chatType, peerId, mapping.subPeerId ?: "")
|
val contact = MessageHelper.generateContact(mapping.chatType, peerId, mapping.subPeerId)
|
||||||
|
|
||||||
val msg = withTimeoutOrNull(5000) {
|
val msg = withTimeoutOrNull(5000) {
|
||||||
val service = QRoute.api(IMsgService::class.java)
|
val service = QRoute.api(IMsgService::class.java)
|
||||||
@ -174,7 +184,7 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
||||||
?: return -1 to "无法找到消息映射"
|
?: return -1 to "无法找到消息映射"
|
||||||
|
|
||||||
val contact = MessageHelper.generateContact(mapping.chatType, mapping.peerId, mapping.subPeerId ?: "")
|
val contact = MessageHelper.generateContact(mapping.chatType, mapping.peerId, mapping.subPeerId)
|
||||||
|
|
||||||
return suspendCancellableCoroutine { continuation ->
|
return suspendCancellableCoroutine { continuation ->
|
||||||
msgService.recallMsg(contact, arrayListOf(mapping.qqMsgId)) { code, why ->
|
msgService.recallMsg(contact, arrayListOf(mapping.qqMsgId)) { code, why ->
|
||||||
@ -204,7 +214,8 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val result = MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, fromId, MessageCallback(peedId, 0))
|
val result =
|
||||||
|
MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, fromId, MessageCallback(peedId, 0))
|
||||||
if (result.isFailure) {
|
if (result.isFailure) {
|
||||||
LogCenter.log("sendToAio: " + result.exceptionOrNull()?.stackTraceToString(), Level.ERROR)
|
LogCenter.log("sendToAio: " + result.exceptionOrNull()?.stackTraceToString(), Level.ERROR)
|
||||||
return result
|
return result
|
||||||
@ -220,13 +231,54 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun sendMultiMsg(
|
||||||
|
uid: String,
|
||||||
|
groupUin: String?,
|
||||||
|
messages: List<PushMsgBody>,
|
||||||
|
): Result<String> {
|
||||||
|
val payload = LongMsgPayload(
|
||||||
|
action = LongMsgAction(
|
||||||
|
command = "MultiMsg",
|
||||||
|
data = LongMsgContent(
|
||||||
|
messages
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val req = LongMsgReq(
|
||||||
|
sendInfo = SendLongMsgInfo(
|
||||||
|
type = if (groupUin == null) 1 else 3,
|
||||||
|
uid = LongMsgUid(groupUin ?: uid),
|
||||||
|
groupUin = groupUin?.toInt(),
|
||||||
|
payload = DeflateTools.gzip(ProtoBuf.encodeToByteArray(payload))
|
||||||
|
),
|
||||||
|
setting = LongMsgSettings(
|
||||||
|
field1 = 4,
|
||||||
|
field2 = 2,
|
||||||
|
field3 = 9,
|
||||||
|
field4 = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val buffer = sendBufferAW(
|
||||||
|
"trpc.group.long_msg_interface.MsgService.SsoSendLongMsg",
|
||||||
|
true,
|
||||||
|
ProtoBuf.encodeToByteArray(req)
|
||||||
|
) ?: return Result.failure(Exception("unable to upload multi message"))
|
||||||
|
val rsp = ProtoBuf.decodeFromByteArray<LongMsgRsp>(buffer.slice(4))
|
||||||
|
return rsp.sendResult?.resId?.let { Result.success(it) } ?: Result.failure(Exception("unable to upload multi message"))
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getMultiMsg(resId: String): Result<List<MsgRecord>> {
|
suspend fun getMultiMsg(resId: String): Result<List<MsgRecord>> {
|
||||||
|
// trpc.group.long_msg_interface.MsgService.SsoRecvLongMsg
|
||||||
|
// 00 00 00 70 0A 60 0A 1A 12 18 75 5F 35 5A 5A 53 6F 38 63 4D 71 70 49 79 63 75 57 5F 78 43 4C 48 6E 77 12 40 4D 6F 61 44 38 77 2B 55 74 43 42 55 45 4C 4F 66 7A 61 72 69 43 7A 4F 5A 44 57 4B 43 6D 68 45 74 4F 65 54 6C 46 66 44 70 2F 73 61 56 77 50 2F 44 52 37 72 4A 2B 4B 4B 47 30 65 71 2B 6C 4B 58 34 18 03 7A 08 08 02 10 02 18 09 20 00
|
||||||
|
|
||||||
val kernelService = NTServiceFetcher.kernelService
|
val kernelService = NTServiceFetcher.kernelService
|
||||||
val sessionService = kernelService.wrapperSession
|
val sessionService = kernelService.wrapperSession
|
||||||
val msgService = sessionService.msgService
|
val msgService = sessionService.msgService
|
||||||
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, TicketSvc.getUin())
|
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, TicketSvc.getUin())
|
||||||
|
|
||||||
val content = "{\"app\":\"com.tencent.multimsg\",\"config\":{\"autosize\":1,\"forward\":1,\"round\":1,\"type\":\"normal\",\"width\":300},\"desc\":\"[聊天记录]\",\"extra\":\"\",\"meta\":{\"detail\":{\"news\":[{\"text\":\"Shamrock: 这是条假消息!\"}],\"resid\":\"$resId\",\"source\":\"聊天记录\",\"summary\":\"转发消息\",\"uniseq\":\"${UUID.randomUUID()}\"}},\"prompt\":\"[聊天记录]\",\"ver\":\"0.0.0.5\",\"view\":\"contact\"}"
|
val content =
|
||||||
|
"{\"app\":\"com.tencent.multimsg\",\"config\":{\"autosize\":1,\"forward\":1,\"round\":1,\"type\":\"normal\",\"width\":300},\"desc\":\"[聊天记录]\",\"extra\":\"\",\"meta\":{\"detail\":{\"news\":[{\"text\":\"Shamrock: 这是条假消息!\"}],\"resid\":\"$resId\",\"source\":\"聊天记录\",\"summary\":\"转发消息\",\"uniseq\":\"${UUID.randomUUID()}\"}},\"prompt\":\"[聊天记录]\",\"ver\":\"0.0.0.5\",\"view\":\"contact\"}"
|
||||||
val msgId = PacketSvc.fakeSelfRecvJsonMsg(msgService, content)
|
val msgId = PacketSvc.fakeSelfRecvJsonMsg(msgService, content)
|
||||||
if (msgId < 0) {
|
if (msgId < 0) {
|
||||||
return Result.failure(Exception("获取合并转发消息ID失败"))
|
return Result.failure(Exception("获取合并转发消息ID失败"))
|
||||||
@ -261,7 +313,7 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
class MessageCallback(
|
class MessageCallback(
|
||||||
private val peerId: String,
|
private val peerId: String,
|
||||||
var msgHash: Int
|
var msgHash: Int
|
||||||
): IOperateCallback {
|
) : IOperateCallback {
|
||||||
override fun onResult(code: Int, reason: String?) {
|
override fun onResult(code: Int, reason: String?) {
|
||||||
if (code != 0 && msgHash != 0) {
|
if (code != 0 && msgHash != 0) {
|
||||||
MessageHelper.removeMsgByHashCode(msgHash)
|
MessageHelper.removeMsgByHashCode(msgHash)
|
||||||
|
@ -50,6 +50,14 @@ internal object TicketSvc: BaseSvc() {
|
|||||||
return app.longAccountUin
|
return app.longAccountUin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getUid(): String {
|
||||||
|
return app.currentUid.ifBlank { "u_" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNickname(): String {
|
||||||
|
return app.currentNickname
|
||||||
|
}
|
||||||
|
|
||||||
fun getCookie(): String {
|
fun getCookie(): String {
|
||||||
val uin = getUin()
|
val uin = getUin()
|
||||||
val skey = getRealSkey(uin)
|
val skey = getRealSkey(uin)
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
package moe.fuqiuluo.qqinterface.servlet.msg
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import moe.fuqiuluo.shamrock.helper.ParamsException
|
||||||
|
import moe.fuqiuluo.shamrock.tools.asInt
|
||||||
|
import moe.fuqiuluo.shamrock.tools.asString
|
||||||
|
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||||
|
import protobuf.message.MessageElement
|
||||||
|
import protobuf.message.element.FaceElement
|
||||||
|
import protobuf.message.element.TextElement
|
||||||
|
|
||||||
|
|
||||||
|
internal typealias IMessageMaker = suspend (Int, Long, String, JsonObject) -> Result<MessageElement>
|
||||||
|
|
||||||
|
internal object MessageElementMaker {
|
||||||
|
private val makerArray = hashMapOf(
|
||||||
|
"text" to MessageElementMaker::createTextElem,
|
||||||
|
"face" to MessageElementMaker::createFaceElem,
|
||||||
|
// "pic" to MessageElementMaker::createImageElem,
|
||||||
|
// "image" to MessageElementMaker::createImageElem,
|
||||||
|
// "voice" to MessageElementMaker::createRecordElem,
|
||||||
|
// "record" to MessageElementMaker::createRecordElem,
|
||||||
|
// "at" to MessageElementMaker::createAtElem,
|
||||||
|
// "video" to MessageElementMaker::createVideoElem,
|
||||||
|
// "markdown" to MessageElementMaker::createMarkdownElem,
|
||||||
|
// "dice" to MessageElementMaker::createDiceElem,
|
||||||
|
// "rps" to MessageElementMaker::createRpsElem,
|
||||||
|
// "poke" to MessageElementMaker::createPokeElem,
|
||||||
|
// "anonymous" to MessageElementMaker::createAnonymousElem,
|
||||||
|
// "share" to MessageElementMaker::createShareElem,
|
||||||
|
// "contact" to MessageElementMaker::createContactElem,
|
||||||
|
// "location" to MessageElementMaker::createLocationElem,
|
||||||
|
// "music" to MessageElementMaker::createMusicElem,
|
||||||
|
// "reply" to MessageElementMaker::createReplyElem,
|
||||||
|
// "touch" to MessageElementMaker::createTouchElem,
|
||||||
|
// "weather" to MessageElementMaker::createWeatherElem,
|
||||||
|
"json" to MessageElementMaker::createJsonElem,
|
||||||
|
//"new_dice" to MessageElementMaker::createNewDiceElem,
|
||||||
|
//"new_rps" to MessageElementMaker::createNewRpsElem,
|
||||||
|
//"basketball" to MessageElementMaker::createBasketballElem,
|
||||||
|
//"node" to MessageMaker::createNodeElem,
|
||||||
|
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
||||||
|
//"bubble_face" to MessageElementMaker::createBubbleFaceElem,
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun createTextElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MessageElement> {
|
||||||
|
data.checkAndThrow("text")
|
||||||
|
val elem = MessageElement(
|
||||||
|
text = TextElement(data["text"].asString)
|
||||||
|
)
|
||||||
|
return Result.success(elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun createFaceElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MessageElement> {
|
||||||
|
data.checkAndThrow("id")
|
||||||
|
val elem = MessageElement(
|
||||||
|
face = FaceElement(data["id"].asInt)
|
||||||
|
)
|
||||||
|
return Result.success(elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun createJsonElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MessageElement> {
|
||||||
|
data.checkAndThrow("data")
|
||||||
|
|
||||||
|
val elem = MessageElement(
|
||||||
|
json = protobuf.message.element.JsonElement(
|
||||||
|
data = DeflateTools.compress(data.toString().toByteArray())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return Result.success(elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JsonObject.checkAndThrow(vararg key: String) {
|
||||||
|
key.forEach {
|
||||||
|
if (!containsKey(it)) throw ParamsException(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(type: String): IMessageMaker? = makerArray[type]
|
||||||
|
|
||||||
|
}
|
@ -82,38 +82,38 @@ import kotlin.math.roundToInt
|
|||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.random.nextInt
|
import kotlin.random.nextInt
|
||||||
|
|
||||||
internal typealias IMaker = suspend (Int, Long, String, JsonObject) -> Result<MsgElement>
|
internal typealias IMsgMaker = suspend (Int, Long, String, JsonObject) -> Result<MsgElement>
|
||||||
|
|
||||||
internal object MessageMaker {
|
internal object MsgElementMaker {
|
||||||
private val makerArray = mutableMapOf(
|
private val makerArray = hashMapOf(
|
||||||
"text" to MessageMaker::createTextElem,
|
"text" to MsgElementMaker::createTextElem,
|
||||||
"face" to MessageMaker::createFaceElem,
|
"face" to MsgElementMaker::createFaceElem,
|
||||||
"pic" to MessageMaker::createImageElem,
|
"pic" to MsgElementMaker::createImageElem,
|
||||||
"image" to MessageMaker::createImageElem,
|
"image" to MsgElementMaker::createImageElem,
|
||||||
"voice" to MessageMaker::createRecordElem,
|
"voice" to MsgElementMaker::createRecordElem,
|
||||||
"record" to MessageMaker::createRecordElem,
|
"record" to MsgElementMaker::createRecordElem,
|
||||||
"at" to MessageMaker::createAtElem,
|
"at" to MsgElementMaker::createAtElem,
|
||||||
"video" to MessageMaker::createVideoElem,
|
"video" to MsgElementMaker::createVideoElem,
|
||||||
"markdown" to MessageMaker::createMarkdownElem,
|
"markdown" to MsgElementMaker::createMarkdownElem,
|
||||||
"dice" to MessageMaker::createDiceElem,
|
"dice" to MsgElementMaker::createDiceElem,
|
||||||
"rps" to MessageMaker::createRpsElem,
|
"rps" to MsgElementMaker::createRpsElem,
|
||||||
"poke" to MessageMaker::createPokeElem,
|
"poke" to MsgElementMaker::createPokeElem,
|
||||||
"anonymous" to MessageMaker::createAnonymousElem,
|
"anonymous" to MsgElementMaker::createAnonymousElem,
|
||||||
"share" to MessageMaker::createShareElem,
|
"share" to MsgElementMaker::createShareElem,
|
||||||
"contact" to MessageMaker::createContactElem,
|
"contact" to MsgElementMaker::createContactElem,
|
||||||
"location" to MessageMaker::createLocationElem,
|
"location" to MsgElementMaker::createLocationElem,
|
||||||
"music" to MessageMaker::createMusicElem,
|
"music" to MsgElementMaker::createMusicElem,
|
||||||
"reply" to MessageMaker::createReplyElem,
|
"reply" to MsgElementMaker::createReplyElem,
|
||||||
"touch" to MessageMaker::createTouchElem,
|
"touch" to MsgElementMaker::createTouchElem,
|
||||||
"weather" to MessageMaker::createWeatherElem,
|
"weather" to MsgElementMaker::createWeatherElem,
|
||||||
"json" to MessageMaker::createJsonElem,
|
"json" to MsgElementMaker::createJsonElem,
|
||||||
"new_dice" to MessageMaker::createNewDiceElem,
|
"new_dice" to MsgElementMaker::createNewDiceElem,
|
||||||
"new_rps" to MessageMaker::createNewRpsElem,
|
"new_rps" to MsgElementMaker::createNewRpsElem,
|
||||||
"basketball" to MessageMaker::createBasketballElem,
|
"basketball" to MsgElementMaker::createBasketballElem,
|
||||||
//"node" to MessageMaker::createNodeElem,
|
//"node" to MessageMaker::createNodeElem,
|
||||||
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
||||||
"bubble_face" to MessageMaker::createBubbleFaceElem,
|
"bubble_face" to MsgElementMaker::createBubbleFaceElem,
|
||||||
"inline_keyboard" to MessageMaker::createInlineKeywordElem
|
"inline_keyboard" to MsgElementMaker::createInlineKeywordElem
|
||||||
)
|
)
|
||||||
|
|
||||||
private suspend fun createInlineKeywordElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createInlineKeywordElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
||||||
@ -169,7 +169,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createBubbleFaceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createBubbleFaceElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("id", "count")
|
data.checkAndThrow("id", "count")
|
||||||
val faceId = data["id"].asInt
|
val faceId = data["id"].asInt
|
||||||
val local = QQSysFaceUtil.convertToLocal(faceId)
|
val local = QQSysFaceUtil.convertToLocal(faceId)
|
||||||
@ -202,21 +207,13 @@ internal object MessageMaker {
|
|||||||
// SendForwardMessage(MsgConstant.KCHATTYPEC2C, TicketSvc.getUin(), data["content"].asJsonArray)
|
// SendForwardMessage(MsgConstant.KCHATTYPEC2C, TicketSvc.getUin(), data["content"].asJsonArray)
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
/**\
|
|
||||||
* msgElement.setFaceElement(new FaceElement());
|
private suspend fun createBasketballElem(
|
||||||
* msgElement.getFaceElement().setFaceIndex(114);
|
chatType: Int,
|
||||||
* msgElement.getFaceElement().setFaceText("/篮球");
|
msgId: Long,
|
||||||
* msgElement.getFaceElement().setFaceType(3);
|
peerId: String,
|
||||||
* msgElement.getFaceElement().setPackId("1");
|
data: JsonObject
|
||||||
* msgElement.getFaceElement().setStickerId("13");
|
): Result<MsgElement> {
|
||||||
* msgElement.getFaceElement().setRandomType(1);
|
|
||||||
* msgElement.getFaceElement().setImageType(1);
|
|
||||||
* msgElement.getFaceElement().setStickerType(2);
|
|
||||||
* msgElement.getFaceElement().setSourceType(1);
|
|
||||||
* msgElement.getFaceElement().setSurpriseId("");
|
|
||||||
* msgElement.getFaceElement().setResultId(String.valueOf(new Random().nextInt(5) + 1));
|
|
||||||
*/
|
|
||||||
private suspend fun createBasketballElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
|
||||||
val elem = MsgElement()
|
val elem = MsgElement()
|
||||||
elem.elementType = MsgConstant.KELEMTYPEFACE
|
elem.elementType = MsgConstant.KELEMTYPEFACE
|
||||||
val face = FaceElement()
|
val face = FaceElement()
|
||||||
@ -227,14 +224,19 @@ internal object MessageMaker {
|
|||||||
face.stickerId = "13"
|
face.stickerId = "13"
|
||||||
face.sourceType = 1
|
face.sourceType = 1
|
||||||
face.stickerType = 2
|
face.stickerType = 2
|
||||||
face.resultId = Random.nextInt(1 .. 5).toString()
|
face.resultId = Random.nextInt(1..5).toString()
|
||||||
face.surpriseId = ""
|
face.surpriseId = ""
|
||||||
face.randomType = 1
|
face.randomType = 1
|
||||||
elem.faceElement = face
|
elem.faceElement = face
|
||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createNewRpsElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createNewRpsElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
val elem = MsgElement()
|
val elem = MsgElement()
|
||||||
elem.elementType = MsgConstant.KELEMTYPEFACE
|
elem.elementType = MsgConstant.KELEMTYPEFACE
|
||||||
val face = FaceElement()
|
val face = FaceElement()
|
||||||
@ -252,7 +254,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createNewDiceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createNewDiceElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
val elem = MsgElement()
|
val elem = MsgElement()
|
||||||
elem.elementType = MsgConstant.KELEMTYPEFACE
|
elem.elementType = MsgConstant.KELEMTYPEFACE
|
||||||
val face = FaceElement()
|
val face = FaceElement()
|
||||||
@ -370,7 +377,7 @@ internal object MessageMaker {
|
|||||||
LogCenter.log("无法获取被回复消息", Level.ERROR)
|
LogCenter.log("无法获取被回复消息", Level.ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.containsKey("text")) {
|
if (data.containsKey("text")) {
|
||||||
data.checkAndThrow("qq", "time", "seq")
|
data.checkAndThrow("qq", "time", "seq")
|
||||||
reply.replayMsgSeq = data["seq"].asLong
|
reply.replayMsgSeq = data["seq"].asLong
|
||||||
reply.sourceMsgText = data["text"].asString
|
reply.sourceMsgText = data["text"].asString
|
||||||
@ -389,21 +396,23 @@ internal object MessageMaker {
|
|||||||
): Result<MsgElement> {
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("type")
|
data.checkAndThrow("type")
|
||||||
|
|
||||||
when(val type = data["type"].asString) {
|
when (val type = data["type"].asString) {
|
||||||
"qq" -> {
|
"qq" -> {
|
||||||
data.checkAndThrow("id")
|
data.checkAndThrow("id")
|
||||||
val id = data["id"].asString
|
val id = data["id"].asString
|
||||||
if(!MusicHelper.tryShareQQMusicById(chatType, peerId.toLong(), msgId, id)) {
|
if (!MusicHelper.tryShareQQMusicById(chatType, peerId.toLong(), msgId, id)) {
|
||||||
LogCenter.log("无法发送QQ音乐分享", Level.ERROR)
|
LogCenter.log("无法发送QQ音乐分享", Level.ERROR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"163" -> {
|
"163" -> {
|
||||||
data.checkAndThrow("id")
|
data.checkAndThrow("id")
|
||||||
val id = data["id"].asString
|
val id = data["id"].asString
|
||||||
if(!MusicHelper.tryShare163MusicById(chatType, peerId.toLong(), msgId, id)) {
|
if (!MusicHelper.tryShare163MusicById(chatType, peerId.toLong(), msgId, id)) {
|
||||||
LogCenter.log("无法发送网易云音乐分享", Level.ERROR)
|
LogCenter.log("无法发送网易云音乐分享", Level.ERROR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"custom" -> {
|
"custom" -> {
|
||||||
data.checkAndThrow("url", "audio", "title")
|
data.checkAndThrow("url", "audio", "title")
|
||||||
ArkMsgSvc.tryShareMusic(
|
ArkMsgSvc.tryShareMusic(
|
||||||
@ -418,13 +427,19 @@ internal object MessageMaker {
|
|||||||
data["audio"].asString
|
data["audio"].asString
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> LogCenter.log("不支持的音乐分享类型: $type", Level.ERROR)
|
else -> LogCenter.log("不支持的音乐分享类型: $type", Level.ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.failure(ActionMsgException)
|
return Result.failure(ActionMsgException)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createLocationElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createLocationElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("lat", "lon")
|
data.checkAndThrow("lat", "lon")
|
||||||
|
|
||||||
val lat = data["lat"].asString.toDouble()
|
val lat = data["lat"].asString.toDouble()
|
||||||
@ -437,7 +452,12 @@ internal object MessageMaker {
|
|||||||
return Result.failure(ActionMsgException)
|
return Result.failure(ActionMsgException)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createContactElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createContactElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("id")
|
data.checkAndThrow("id")
|
||||||
val type = data["type"].asStringOrNull ?: data["kind"].asStringOrNull
|
val type = data["type"].asStringOrNull ?: data["kind"].asStringOrNull
|
||||||
val id = data["id"].asString
|
val id = data["id"].asString
|
||||||
@ -448,10 +468,12 @@ internal object MessageMaker {
|
|||||||
val ark = ArkElement(CardSvc.getSharePrivateArkMsg(id.toLong()), null, null)
|
val ark = ArkElement(CardSvc.getSharePrivateArkMsg(id.toLong()), null, null)
|
||||||
elem.arkElement = ark
|
elem.arkElement = ark
|
||||||
}
|
}
|
||||||
|
|
||||||
"group" -> {
|
"group" -> {
|
||||||
val ark = ArkElement(GroupSvc.getShareTroopArkMsg(id.toLong()), null, null)
|
val ark = ArkElement(GroupSvc.getShareTroopArkMsg(id.toLong()), null, null)
|
||||||
elem.arkElement = ark
|
elem.arkElement = ark
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw IllegalParamsException("type")
|
else -> throw IllegalParamsException("type")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,7 +482,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createShareElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createShareElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("title", "url")
|
data.checkAndThrow("title", "url")
|
||||||
|
|
||||||
val url = data["url"].asString
|
val url = data["url"].asString
|
||||||
@ -525,11 +552,21 @@ internal object MessageMaker {
|
|||||||
return Result.failure(ActionMsgException)
|
return Result.failure(ActionMsgException)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createAnonymousElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createAnonymousElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
return Result.failure(ActionMsgException)
|
return Result.failure(ActionMsgException)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createPokeElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createPokeElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("type", "id")
|
data.checkAndThrow("type", "id")
|
||||||
val elem = MsgElement()
|
val elem = MsgElement()
|
||||||
val face = FaceElement()
|
val face = FaceElement()
|
||||||
@ -545,7 +582,7 @@ internal object MessageMaker {
|
|||||||
face.vaspokeMinver = ""
|
face.vaspokeMinver = ""
|
||||||
face.pokeStrength = (data["strength"].asIntOrNull ?: data["cnt"].asIntOrNull
|
face.pokeStrength = (data["strength"].asIntOrNull ?: data["cnt"].asIntOrNull
|
||||||
?: data["count"].asIntOrNull ?: data["time"].asIntOrNull ?: 0).also {
|
?: data["count"].asIntOrNull ?: data["time"].asIntOrNull ?: 0).also {
|
||||||
if(it < 0 || it > 3) throw IllegalParamsException("strength")
|
if (it < 0 || it > 3) throw IllegalParamsException("strength")
|
||||||
}
|
}
|
||||||
face.msgType = 0
|
face.msgType = 0
|
||||||
face.faceBubbleCount = 0
|
face.faceBubbleCount = 0
|
||||||
@ -556,7 +593,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createFaceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createFaceElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("id")
|
data.checkAndThrow("id")
|
||||||
|
|
||||||
val serverId = data["id"].asInt
|
val serverId = data["id"].asInt
|
||||||
@ -575,15 +617,15 @@ internal object MessageMaker {
|
|||||||
face.faceIndex = serverId
|
face.faceIndex = serverId
|
||||||
face.faceText = QQSysFaceUtil.getFaceDescription(QQSysFaceUtil.convertToLocal(serverId))
|
face.faceText = QQSysFaceUtil.getFaceDescription(QQSysFaceUtil.convertToLocal(serverId))
|
||||||
if (serverId == 394) {
|
if (serverId == 394) {
|
||||||
face.stickerId = 40.toString()
|
face.stickerId = "40"
|
||||||
face.packId = "1"
|
face.packId = "1"
|
||||||
face.sourceType = 1
|
face.sourceType = 1
|
||||||
face.stickerType = 3
|
face.stickerType = 3
|
||||||
face.randomType = 1
|
face.randomType = 1
|
||||||
face.resultId = data["result"].asStringOrNull ?: Random.nextInt(1 .. 5).toString()
|
face.resultId = data["result"].asStringOrNull ?: Random.nextInt(1..5).toString()
|
||||||
} else if (big) {
|
} else if (big) {
|
||||||
face.imageType = 0
|
face.imageType = 0
|
||||||
face.stickerId = 30.toString()
|
face.stickerId = "30"
|
||||||
face.packId = "1"
|
face.packId = "1"
|
||||||
face.sourceType = 1
|
face.sourceType = 1
|
||||||
face.stickerType = 1
|
face.stickerType = 1
|
||||||
@ -597,7 +639,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createRpsElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createRpsElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
val elem = MsgElement()
|
val elem = MsgElement()
|
||||||
elem.elementType = MsgConstant.KELEMTYPEMARKETFACE
|
elem.elementType = MsgConstant.KELEMTYPEMARKETFACE
|
||||||
val market = MarketFaceElement(
|
val market = MarketFaceElement(
|
||||||
@ -612,7 +659,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createDiceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createDiceElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
val elem = MsgElement()
|
val elem = MsgElement()
|
||||||
elem.elementType = MsgConstant.KELEMTYPEMARKETFACE
|
elem.elementType = MsgConstant.KELEMTYPEMARKETFACE
|
||||||
val market = MarketFaceElement(
|
val market = MarketFaceElement(
|
||||||
@ -627,7 +679,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createMarkdownElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createMarkdownElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("content")
|
data.checkAndThrow("content")
|
||||||
val elem = MsgElement()
|
val elem = MsgElement()
|
||||||
elem.elementType = MsgConstant.KELEMTYPEMARKDOWN
|
elem.elementType = MsgConstant.KELEMTYPEMARKDOWN
|
||||||
@ -636,7 +693,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createVideoElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createVideoElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("file")
|
data.checkAndThrow("file")
|
||||||
|
|
||||||
val file = data["file"].asString.let {
|
val file = data["file"].asString.let {
|
||||||
@ -672,7 +734,8 @@ internal object MessageMaker {
|
|||||||
)
|
)
|
||||||
if (!QQNTWrapperUtil.CppProxy.fileIsExist(originalPath) || QQNTWrapperUtil.CppProxy.getFileSize(
|
if (!QQNTWrapperUtil.CppProxy.fileIsExist(originalPath) || QQNTWrapperUtil.CppProxy.getFileSize(
|
||||||
originalPath
|
originalPath
|
||||||
) != file.length()) {
|
) != file.length()
|
||||||
|
) {
|
||||||
QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
|
QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
|
||||||
AudioUtils.obtainVideoCover(file.absolutePath, thumbPath!!)
|
AudioUtils.obtainVideoCover(file.absolutePath, thumbPath!!)
|
||||||
}
|
}
|
||||||
@ -711,23 +774,26 @@ internal object MessageMaker {
|
|||||||
val qq = data["qq"].asString
|
val qq = data["qq"].asString
|
||||||
|
|
||||||
val at = TextElement()
|
val at = TextElement()
|
||||||
when(qq) {
|
when (qq) {
|
||||||
"0", "all" -> {
|
"0", "all" -> {
|
||||||
at.content = "@全体成员"
|
at.content = "@全体成员"
|
||||||
at.atType = MsgConstant.ATTYPEALL
|
at.atType = MsgConstant.ATTYPEALL
|
||||||
at.atNtUid = "0"
|
at.atNtUid = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
"online" -> {
|
"online" -> {
|
||||||
at.content = "@在线成员"
|
at.content = "@在线成员"
|
||||||
at.atType = MsgConstant.ATTYPEONLINE
|
at.atType = MsgConstant.ATTYPEONLINE
|
||||||
at.atNtUid = "0"
|
at.atNtUid = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
"admin" -> {
|
"admin" -> {
|
||||||
at.content = "@管理员"
|
at.content = "@管理员"
|
||||||
at.atRoleId = 1
|
at.atRoleId = 1
|
||||||
at.atType = MsgConstant.ATTYPEROLE
|
at.atType = MsgConstant.ATTYPEROLE
|
||||||
at.atNtUid = "0"
|
at.atNtUid = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val name = data["name"].asStringOrNull
|
val name = data["name"].asStringOrNull
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
@ -757,7 +823,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createRecordElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createRecordElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
var file = data["file"].asStringOrNull?.let {
|
var file = data["file"].asStringOrNull?.let {
|
||||||
val md5 = it.replace(regex = "[{}\\-]".toRegex(), replacement = "")
|
val md5 = it.replace(regex = "[{}\\-]".toRegex(), replacement = "")
|
||||||
.replace(" ", "")
|
.replace(" ", "")
|
||||||
@ -785,11 +856,13 @@ internal object MessageMaker {
|
|||||||
ptt.duration = QRoute.api(IAIOPttApi::class.java)
|
ptt.duration = QRoute.api(IAIOPttApi::class.java)
|
||||||
.getPttFileDuration(file.absolutePath)
|
.getPttFileDuration(file.absolutePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaType.Amr -> {
|
MediaType.Amr -> {
|
||||||
LogCenter.log({ "Amr: $file" }, Level.DEBUG)
|
LogCenter.log({ "Amr: $file" }, Level.DEBUG)
|
||||||
ptt.duration = AudioUtils.getDurationSec(file)
|
ptt.duration = AudioUtils.getDurationSec(file)
|
||||||
ptt.formatType = MsgConstant.KPTTFORMATTYPEAMR
|
ptt.formatType = MsgConstant.KPTTFORMATTYPEAMR
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaType.Pcm -> {
|
MediaType.Pcm -> {
|
||||||
LogCenter.log({ "Pcm To Silk: $file" }, Level.DEBUG)
|
LogCenter.log({ "Pcm To Silk: $file" }, Level.DEBUG)
|
||||||
val result = AudioUtils.pcmToSilk(file)
|
val result = AudioUtils.pcmToSilk(file)
|
||||||
@ -797,6 +870,7 @@ internal object MessageMaker {
|
|||||||
file = result.first
|
file = result.first
|
||||||
ptt.formatType = MsgConstant.KPTTFORMATTYPESILK
|
ptt.formatType = MsgConstant.KPTTFORMATTYPESILK
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
LogCenter.log({ "Audio To SILK: $file" }, Level.DEBUG)
|
LogCenter.log({ "Audio To SILK: $file" }, Level.DEBUG)
|
||||||
val result = AudioUtils.audioToSilk(file)
|
val result = AudioUtils.audioToSilk(file)
|
||||||
@ -813,12 +887,13 @@ internal object MessageMaker {
|
|||||||
// QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
|
// QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
if(!(Transfer with when (chatType) {
|
if (!(Transfer with when (chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> Troop(peerId)
|
MsgConstant.KCHATTYPEGROUP -> Troop(peerId)
|
||||||
MsgConstant.KCHATTYPEC2C -> Private(peerId)
|
MsgConstant.KCHATTYPEC2C -> Private(peerId)
|
||||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> Private(peerId)
|
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> Private(peerId)
|
||||||
else -> error("Not supported chatType($chatType) for RecordMsg")
|
else -> error("Not supported chatType($chatType) for RecordMsg")
|
||||||
} trans VoiceResource(file))) {
|
} trans VoiceResource(file))
|
||||||
|
) {
|
||||||
return Result.failure(RuntimeException("上传语音失败: $file"))
|
return Result.failure(RuntimeException("上传语音失败: $file"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,7 +923,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createImageElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createImageElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
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
|
||||||
@ -889,7 +969,8 @@ internal object MessageMaker {
|
|||||||
)
|
)
|
||||||
if (!QQNTWrapperUtil.CppProxy.fileIsExist(originalPath) || QQNTWrapperUtil.CppProxy.getFileSize(
|
if (!QQNTWrapperUtil.CppProxy.fileIsExist(originalPath) || QQNTWrapperUtil.CppProxy.getFileSize(
|
||||||
originalPath
|
originalPath
|
||||||
) != file.length()) {
|
) != file.length()
|
||||||
|
) {
|
||||||
val thumbPath = msgService.getRichMediaFilePathForMobileQQSend(
|
val thumbPath = msgService.getRichMediaFilePathForMobileQQSend(
|
||||||
RichMediaFilePathInfo(
|
RichMediaFilePathInfo(
|
||||||
2, 0, pic.md5HexStr, file.name, 2, 720, null, "", true
|
2, 0, pic.md5HexStr, file.name, 2, 720, null, "", true
|
||||||
@ -927,7 +1008,12 @@ internal object MessageMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createTextElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
private suspend fun createTextElem(
|
||||||
|
chatType: Int,
|
||||||
|
msgId: Long,
|
||||||
|
peerId: String,
|
||||||
|
data: JsonObject
|
||||||
|
): Result<MsgElement> {
|
||||||
data.checkAndThrow("text")
|
data.checkAndThrow("text")
|
||||||
val elem = MsgElement()
|
val elem = MsgElement()
|
||||||
elem.elementType = MsgConstant.KELEMTYPETEXT
|
elem.elementType = MsgConstant.KELEMTYPETEXT
|
||||||
@ -943,5 +1029,5 @@ internal object MessageMaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(type: String): IMaker? = makerArray[type]
|
operator fun get(type: String): IMsgMaker? = makerArray[type]
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.msg.convert
|
package moe.fuqiuluo.qqinterface.servlet.msg.convert
|
||||||
|
|
||||||
import com.tencent.mobileqq.qmmkv.QMMKV
|
|
||||||
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.add
|
||||||
@ -19,8 +18,6 @@ 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
|
||||||
import moe.fuqiuluo.shamrock.tools.json
|
import moe.fuqiuluo.shamrock.tools.json
|
||||||
import mqq.app.MobileQQ
|
|
||||||
import kotlin.jvm.internal.Intrinsics
|
|
||||||
|
|
||||||
internal sealed class MessageElemConverter: IMessageConvert {
|
internal sealed class MessageElemConverter: IMessageConvert {
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,8 @@ import kotlinx.serialization.json.JsonElement
|
|||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.MessageMaker
|
import moe.fuqiuluo.qqinterface.servlet.msg.MessageElementMaker
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.msg.MsgElementMaker
|
||||||
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
|
||||||
@ -26,8 +27,8 @@ import moe.fuqiuluo.shamrock.tools.asJsonObjectOrNull
|
|||||||
import moe.fuqiuluo.shamrock.tools.asString
|
import moe.fuqiuluo.shamrock.tools.asString
|
||||||
import moe.fuqiuluo.shamrock.tools.json
|
import moe.fuqiuluo.shamrock.tools.json
|
||||||
import moe.fuqiuluo.shamrock.tools.jsonArray
|
import moe.fuqiuluo.shamrock.tools.jsonArray
|
||||||
|
import protobuf.message.MessageElement
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
internal object MessageHelper {
|
internal object MessageHelper {
|
||||||
@ -39,7 +40,7 @@ internal object MessageHelper {
|
|||||||
fromId: String = peerId
|
fromId: String = peerId
|
||||||
): SendMsgResult {
|
): SendMsgResult {
|
||||||
val uniseq = generateMsgId(chatType)
|
val uniseq = generateMsgId(chatType)
|
||||||
val msg = messageArrayToMessageElements(chatType, uniseq.qqMsgId, peerId, decodeCQCode(message)).also {
|
val msg = messageArrayToMsgElements(chatType, uniseq.qqMsgId, peerId, decodeCQCode(message)).also {
|
||||||
if (it.second.isEmpty() && !it.first) {
|
if (it.second.isEmpty() && !it.first) {
|
||||||
error("消息合成失败,请查看日志或者检查输入。")
|
error("消息合成失败,请查看日志或者检查输入。")
|
||||||
} else if (it.second.isEmpty()) {
|
} else if (it.second.isEmpty()) {
|
||||||
@ -82,7 +83,7 @@ internal object MessageHelper {
|
|||||||
callback: IOperateCallback
|
callback: IOperateCallback
|
||||||
): Result<SendMsgResult> {
|
): Result<SendMsgResult> {
|
||||||
val uniseq = generateMsgId(chatType)
|
val uniseq = generateMsgId(chatType)
|
||||||
val msg = messageArrayToMessageElements(chatType, uniseq.qqMsgId, peerId, message).also {
|
val msg = messageArrayToMsgElements(chatType, uniseq.qqMsgId, peerId, message).also {
|
||||||
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||||
}.second.filter {
|
}.second.filter {
|
||||||
it.elementType != -1
|
it.elementType != -1
|
||||||
@ -166,7 +167,7 @@ internal object MessageHelper {
|
|||||||
fromId: String = peerId
|
fromId: String = peerId
|
||||||
): SendMsgResult {
|
): SendMsgResult {
|
||||||
val uniseq = generateMsgId(chatType)
|
val uniseq = generateMsgId(chatType)
|
||||||
val msg = messageArrayToMessageElements(chatType, uniseq.qqMsgId, peerId, message).also {
|
val msg = messageArrayToMsgElements(chatType, uniseq.qqMsgId, peerId, message).also {
|
||||||
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||||
}.second.filter {
|
}.second.filter {
|
||||||
it.elementType != -1
|
it.elementType != -1
|
||||||
@ -224,7 +225,7 @@ internal object MessageHelper {
|
|||||||
fromId: String = peerId
|
fromId: String = peerId
|
||||||
): SendMsgResult {
|
): SendMsgResult {
|
||||||
val uniseq = generateMsgId(chatType)
|
val uniseq = generateMsgId(chatType)
|
||||||
val msg = messageArrayToMessageElements(chatType, uniseq.qqMsgId, peerId, message).also {
|
val msg = messageArrayToMsgElements(chatType, uniseq.qqMsgId, peerId, message).also {
|
||||||
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||||
}.second.filter {
|
}.second.filter {
|
||||||
it.elementType != -1
|
it.elementType != -1
|
||||||
@ -276,12 +277,41 @@ internal object MessageHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun messageArrayToMessageElements(chatType: Int, msgId: Long, targetUin: String, messageList: JsonArray): Pair<Boolean, ArrayList<MsgElement>> {
|
suspend fun messageArrayToMsgElements(chatType: Int, msgId: Long, targetUin: String, messageList: JsonArray): Pair<Boolean, ArrayList<MsgElement>> {
|
||||||
val msgList = arrayListOf<MsgElement>()
|
val msgList = arrayListOf<MsgElement>()
|
||||||
var hasActionMsg = false
|
var hasActionMsg = false
|
||||||
messageList.forEach {
|
messageList.forEach {
|
||||||
val msg = it.jsonObject
|
val msg = it.jsonObject
|
||||||
val maker = MessageMaker[msg["type"].asString]
|
val maker = MsgElementMaker[msg["type"].asString]
|
||||||
|
if (maker != null) {
|
||||||
|
try {
|
||||||
|
val data = msg["data"].asJsonObjectOrNull ?: EmptyJsonObject
|
||||||
|
maker(chatType, msgId, targetUin, data).onSuccess { msgElem ->
|
||||||
|
msgList.add(msgElem)
|
||||||
|
}.onFailure {
|
||||||
|
if (it.javaClass != ActionMsgException::class.java) {
|
||||||
|
throw it
|
||||||
|
} else {
|
||||||
|
hasActionMsg = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
LogCenter.log(e.stackTraceToString(), Level.ERROR)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogCenter.log("不支持的消息类型: ${msg["type"].asString}", Level.ERROR)
|
||||||
|
return false to arrayListOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasActionMsg to msgList
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun messageArrayToMessageElements(chatType: Int, msgId: Long, targetUin: String, messageList: JsonArray): Pair<Boolean, ArrayList<MessageElement>> {
|
||||||
|
val msgList = arrayListOf<MessageElement>()
|
||||||
|
var hasActionMsg = false
|
||||||
|
messageList.forEach {
|
||||||
|
val msg = it.jsonObject
|
||||||
|
val maker = MessageElementMaker[msg["type"].asString]
|
||||||
if (maker != null) {
|
if (maker != null) {
|
||||||
try {
|
try {
|
||||||
val data = msg["data"].asJsonObjectOrNull ?: EmptyJsonObject
|
val data = msg["data"].asJsonObjectOrNull ?: EmptyJsonObject
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MultiMsgInfo
|
|
||||||
import 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.TicketSvc
|
||||||
@ -14,8 +13,11 @@ 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.ForwardMessageResult
|
||||||
import moe.fuqiuluo.shamrock.tools.*
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
|
||||||
import moe.fuqiuluo.symbols.OneBotHandler
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
|
import protobuf.message.*
|
||||||
|
import protobuf.message.longmsg.PushMsgBody
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
@OneBotHandler("send_forward_msg")
|
@OneBotHandler("send_forward_msg")
|
||||||
internal object SendForwardMessage : IActionHandler() {
|
internal object SendForwardMessage : IActionHandler() {
|
||||||
@ -37,14 +39,26 @@ internal object SendForwardMessage : 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.getStringOrNull("group_id") ?: return noParam("group_id", session.echo)
|
MsgConstant.KCHATTYPEGROUP -> session.getStringOrNull("group_id") ?: return noParam(
|
||||||
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getStringOrNull("user_id") ?: return noParam("user_id", session.echo)
|
"group_id",
|
||||||
|
session.echo
|
||||||
|
)
|
||||||
|
|
||||||
|
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getStringOrNull("user_id")
|
||||||
|
?: return noParam("user_id", session.echo)
|
||||||
|
|
||||||
else -> error("unknown chat type: $chatType")
|
else -> error("unknown chat type: $chatType")
|
||||||
}
|
}
|
||||||
val fromId = when(chatType) {
|
val fromId = when (chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getStringOrNull("group_id") ?: return noParam("group_id", session.echo)
|
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> session.getStringOrNull("group_id")
|
||||||
MsgConstant.KCHATTYPEC2C -> session.getStringOrNull("user_id") ?: return noParam("user_id", session.echo)
|
?: return noParam("group_id", session.echo)
|
||||||
|
|
||||||
|
MsgConstant.KCHATTYPEC2C -> session.getStringOrNull("user_id") ?: return noParam(
|
||||||
|
"user_id",
|
||||||
|
session.echo
|
||||||
|
)
|
||||||
|
|
||||||
else -> error("unknown chat type: $chatType")
|
else -> error("unknown chat type: $chatType")
|
||||||
}
|
}
|
||||||
return if (session.isArray("messages")) {
|
return if (session.isArray("messages")) {
|
||||||
@ -68,103 +82,184 @@ internal object SendForwardMessage : IActionHandler() {
|
|||||||
echo: JsonElement = EmptyJsonString
|
echo: JsonElement = EmptyJsonString
|
||||||
): String {
|
): String {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val kernelService = NTServiceFetcher.kernelService
|
var uid: String? = null
|
||||||
val sessionService = kernelService.wrapperSession
|
var groupUin: String? = null
|
||||||
val msgService = sessionService.msgService
|
|
||||||
val selfUin = TicketSvc.getUin()
|
|
||||||
|
|
||||||
val multiNodes = messages.map {
|
var i = -1
|
||||||
if (it.asJsonObject["type"].asStringOrNull != "node") {
|
val desc = MutableList(messages.size) { "" }
|
||||||
LogCenter.log("包含非node类型节点", Level.WARN)
|
|
||||||
return@map null
|
val msgs = messages.map { msg ->
|
||||||
}
|
val data = msg.asJsonObject["data"].asJsonObject
|
||||||
if (it.asJsonObject["data"] !is JsonObject) {
|
|
||||||
LogCenter.log("data字段错误", Level.WARN)
|
|
||||||
return@map null
|
|
||||||
}
|
|
||||||
it.asJsonObject["data"].asJsonObject.let { data ->
|
|
||||||
if (data.containsKey("id")) {
|
if (data.containsKey("id")) {
|
||||||
val record = MsgSvc.getMsg(data["id"].asInt).getOrNull()
|
val record = MsgSvc.getMsg(data["id"].asInt).getOrElse {
|
||||||
if (record == null) {
|
LogCenter.log("合并转发消息节点消息(id = ${data["id"].asInt})获取失败:$it", Level.WARN)
|
||||||
LogCenter.log("合并转发消息节点消息获取失败:${data["id"]}", Level.WARN)
|
|
||||||
return@map null
|
return@map null
|
||||||
} else {
|
}
|
||||||
record.peerName to record.toSegments().map { segment ->
|
uid = record.peerUid
|
||||||
segment.toJson()
|
if (record.chatType == MsgConstant.KCHATTYPEGROUP) groupUin = record.peerUin.toString()
|
||||||
|
PushMsgBody(
|
||||||
|
head = MessageHead(
|
||||||
|
peerUid = record.senderUid,
|
||||||
|
groupInfo = if (record.chatType == MsgConstant.KCHATTYPEGROUP) GroupInfo(
|
||||||
|
groupCode = record.peerUin.toULong(),
|
||||||
|
memberCard = record.sendMemberName,
|
||||||
|
u1 = 2
|
||||||
|
) else null
|
||||||
|
),
|
||||||
|
content = MessageContent(
|
||||||
|
msgType = record.msgType,
|
||||||
|
msgViaRandom = record.msgId,
|
||||||
|
msgSeq = record.msgSeq,
|
||||||
|
msgTime = record.msgTime,
|
||||||
|
u2 = 1,
|
||||||
|
u6 = 0,
|
||||||
|
u7 = 0,
|
||||||
|
forwardHead = ForwardHead(
|
||||||
|
u1 = 0,
|
||||||
|
u2 = 0,
|
||||||
|
u3 = if (record.chatType == MsgConstant.KCHATTYPEGROUP) 0 else 2,
|
||||||
|
u4 = "",
|
||||||
|
Avatar = ""
|
||||||
|
)
|
||||||
|
),
|
||||||
|
body = MessageBody(
|
||||||
|
rich = RichMessage(
|
||||||
|
elements = MessageHelper.messageArrayToMessageElements(
|
||||||
|
record.chatType,
|
||||||
|
record.msgId,
|
||||||
|
record.peerUin.toString(),
|
||||||
|
record.elements.toSegments(record.chatType, record.peerUin.toString(), "0").also {
|
||||||
|
desc[++i] = record.peerName + ": "
|
||||||
|
}.map {
|
||||||
|
when (it.type) {
|
||||||
|
"text" -> desc[i] += it.data["text"] as String
|
||||||
|
|
||||||
|
"at" -> desc[i] += "@${it.data["name"] as String? ?: it.data["qq"] as String}"
|
||||||
|
|
||||||
|
"face" -> desc[i] += "[表情]"
|
||||||
|
|
||||||
|
"voice" -> desc[i] += "[语音]"
|
||||||
|
|
||||||
|
"node" -> desc[i] += "[合并转发消息]"
|
||||||
|
}
|
||||||
|
|
||||||
|
it.toJson()
|
||||||
}.json
|
}.json
|
||||||
}
|
).also {
|
||||||
|
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||||
|
}.second
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
} else if (data.containsKey("content")) {
|
} else if (data.containsKey("content")) {
|
||||||
(data["name"].asStringOrNull ?: "Anno") to when (val raw = data["content"]) {
|
PushMsgBody(
|
||||||
is JsonObject -> raw.asJsonArray
|
head = MessageHead(
|
||||||
is JsonArray -> raw.asJsonArray
|
peerUid = data["uid"]?.asString ?: TicketSvc.getUid()
|
||||||
else -> MessageHelper.decodeCQCode(raw.asString)
|
),
|
||||||
|
content = MessageContent(
|
||||||
|
msgType = 529,
|
||||||
|
msgViaRandom = 4,
|
||||||
|
msgSeq = data["seq"]?.asLong ?: 0,
|
||||||
|
msgTime = System.currentTimeMillis() / 1000,
|
||||||
|
u2 = 1,
|
||||||
|
u6 = 0,
|
||||||
|
u7 = 0,
|
||||||
|
forwardHead = ForwardHead(
|
||||||
|
u1 = 0,
|
||||||
|
u2 = 0,
|
||||||
|
u3 = 2,
|
||||||
|
u4 = "",
|
||||||
|
Avatar = ""
|
||||||
|
)
|
||||||
|
),
|
||||||
|
body = MessageBody(
|
||||||
|
rich = RichMessage(
|
||||||
|
elements = MessageHelper.messageArrayToMessageElements(
|
||||||
|
1,
|
||||||
|
Random.nextLong(),
|
||||||
|
data["uin"]?.asString ?: TicketSvc.getUin(),
|
||||||
|
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 {
|
||||||
|
when (it.asJsonObject["type"].asString) {
|
||||||
|
"text" -> desc[i] += it.asJsonObject["data"].asJsonObject["text"].asString
|
||||||
|
|
||||||
|
"at" -> desc[i] += "@${it.asJsonObject["data"].asJsonObject["name"].asStringOrNull ?: it.asJsonObject["data"].asJsonObject["qq"].asString}"
|
||||||
|
|
||||||
|
"face" -> desc[i] += "[表情]"
|
||||||
|
|
||||||
|
"voice" -> desc[i] += "[语音]"
|
||||||
|
|
||||||
|
"node" -> desc[i] += "[合并转发消息]"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
).also {
|
||||||
|
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||||
|
}.second
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
LogCenter.log("消息节点缺少id或content字段", Level.WARN)
|
LogCenter.log("消息节点缺少id或content字段", Level.WARN)
|
||||||
return@map null
|
null
|
||||||
}
|
|
||||||
}.let { node ->
|
|
||||||
val content = node.second.map { msg ->
|
|
||||||
when (msg.asJsonObject["type"].asStringOrNull ?: "text") {
|
|
||||||
"at" -> {
|
|
||||||
buildJsonObject {
|
|
||||||
put("type", "text")
|
|
||||||
putJsonObject("data") {
|
|
||||||
put(
|
|
||||||
"text", "@${
|
|
||||||
msg.asJsonObject["data"].asJsonObject["name"].asStringOrNull.ifNullOrEmpty(
|
|
||||||
msg.asJsonObject["data"].asJsonObject["qq"].asString
|
|
||||||
)
|
|
||||||
}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}.filterNotNull().ifEmpty { return logic("消息节点为空", echo) }
|
||||||
|
|
||||||
"voice" -> {
|
|
||||||
buildJsonObject {
|
|
||||||
put("type", "text")
|
|
||||||
putJsonObject("data") {
|
|
||||||
put("text", "[语音]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"node" -> {
|
val resid = MsgSvc.sendMultiMsg(uid ?: TicketSvc.getUid(), groupUin, msgs)
|
||||||
LogCenter.log("合并转发消息暂时不支持嵌套", Level.WARN)
|
.getOrElse { return logic(it.message ?: "", echo) }
|
||||||
buildJsonObject {
|
val uniseq = UUID.randomUUID().toString().uppercase()
|
||||||
put("type", "text")
|
|
||||||
putJsonObject("data") {
|
|
||||||
put("text", "[合并转发消息]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> msg
|
val result = MsgSvc.sendToAio(
|
||||||
}
|
chatType, peerId,
|
||||||
}.json
|
listOf(
|
||||||
|
hashMapOf(
|
||||||
val result = MessageHelper.sendMessageNoCb(MsgConstant.KCHATTYPEC2C, selfUin, content)
|
"type" to "json", "data" to hashMapOf(
|
||||||
if (result.qqMsgId == 0L) {
|
"data" to hashMapOf(
|
||||||
LogCenter.log("合并转发消息节点消息发送失败", Level.WARN)
|
"app" to "com.tencent.multimsg",
|
||||||
return@map null
|
"config" to hashMapOf(
|
||||||
}
|
"autosize" to 1,
|
||||||
result.qqMsgId to node.first
|
"forward" to 1,
|
||||||
}
|
"round" to 1,
|
||||||
}.filterNotNull()
|
"type" to "normal",
|
||||||
|
"width" to 300
|
||||||
val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin)
|
).json,
|
||||||
val to = MessageHelper.generateContact(chatType, peerId, fromId)
|
"desc" to "[聊天记录]",
|
||||||
|
"extra" to hashMapOf(
|
||||||
val uniseq = MessageHelper.generateMsgId(chatType)
|
"filename" to uniseq,
|
||||||
msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply {
|
"tsum" to 2
|
||||||
multiNodes.forEach { add(MultiMsgInfo(it.first, it.second)) }
|
).json.toString() + "\n",
|
||||||
}.also { it.reverse() }, from, to, MsgSvc.MessageCallback(peerId, uniseq.msgHashId))
|
"meta" to hashMapOf(
|
||||||
|
"detail" to hashMapOf(
|
||||||
|
"news" to desc.slice(0..if (i < 3) i else 3)
|
||||||
|
.map { hashMapOf("text" to it).json }.json,
|
||||||
|
"resid" to resid,
|
||||||
|
"source" to "群聊的聊天记录",
|
||||||
|
"summary" to "查看${msgs.size}条转发消息",
|
||||||
|
"uniseq" to uniseq
|
||||||
|
).json
|
||||||
|
).json,
|
||||||
|
"prompt" to "[聊天记录]",
|
||||||
|
"ver" to "0.0.0.5",
|
||||||
|
"view" to "contact"
|
||||||
|
).json,
|
||||||
|
"resid" to resid
|
||||||
|
).json
|
||||||
|
).json
|
||||||
|
).json, fromId, 3
|
||||||
|
).getOrElse { return logic(it.message ?: "", echo) }
|
||||||
|
|
||||||
return ok(
|
return ok(
|
||||||
ForwardMessageResult(
|
ForwardMessageResult(
|
||||||
msgId = uniseq.msgHashId,
|
msgId = result.msgHashId,
|
||||||
forwardId = ""
|
forwardId = resid
|
||||||
), echo = echo
|
), echo = echo
|
||||||
)
|
)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
|
@ -120,18 +120,14 @@ internal object SendMessage: IActionHandler() {
|
|||||||
//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)
|
val result = MsgSvc.sendToAio(chatType, peerId, message, 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) }
|
recallDuration?.let { autoRecall(result.msgHashId, it) }
|
||||||
return ok(MessageResult(
|
return ok(MessageResult(
|
||||||
msgId = sendMsgResult.msgHashId,
|
msgId = result.msgHashId,
|
||||||
time = (sendMsgResult.msgTime * 0.001).toLong()
|
time = (result.msgTime * 0.001).toLong()
|
||||||
), echo)
|
), echo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEGUILD -> {
|
MsgConstant.KCHATTYPEGUILD -> {
|
||||||
LogCenter.log("频道消息(guildId = ${record.guildId}, sender=${record.senderUid}, id = [$msgHash | ${record.msgId}], msg = $rawMsg)")
|
LogCenter.log("频道消息(guildId = ${record.guildId}, sender = ${record.senderUid}, id = [$msgHash | ${record.msgId}], msg = $rawMsg)")
|
||||||
if (!GlobalEventTransmitter.MessageTransmitter
|
if (!GlobalEventTransmitter.MessageTransmitter
|
||||||
.transGuildMessage(record, record.elements, rawMsg, msgHash, postType = postType)
|
.transGuildMessage(record, record.elements, rawMsg, msgHash, postType = postType)
|
||||||
) {
|
) {
|
||||||
@ -169,7 +169,6 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
try {
|
try {
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
|
|
||||||
val peerId = when (record.chatType) {
|
val peerId = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
else -> record.peerUin.toString()
|
else -> record.peerUin.toString()
|
||||||
|
@ -65,7 +65,6 @@ val Map<String, Any>.json: JsonObject
|
|||||||
get() {
|
get() {
|
||||||
val map = hashMapOf<String, JsonElement>()
|
val map = hashMapOf<String, JsonElement>()
|
||||||
forEach { (key, any) ->
|
forEach { (key, any) ->
|
||||||
if (any != null) {
|
|
||||||
when (any) {
|
when (any) {
|
||||||
is JsonElement -> map[key] = any
|
is JsonElement -> map[key] = any
|
||||||
is Number -> map[key] = any.json
|
is Number -> map[key] = any.json
|
||||||
@ -76,7 +75,6 @@ val Map<String, Any>.json: JsonObject
|
|||||||
else -> error("unknown object type: ${any::class.java}")
|
else -> error("unknown object type: ${any::class.java}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return map.jsonObject
|
return map.jsonObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user