Shamrock: 实现事件推送

Signed-off-by: 白池 <whitechi73@outlook.com>
This commit is contained in:
白池 2024-03-15 01:37:28 +08:00
parent a95d8d85e8
commit 3a07116093
10 changed files with 1217 additions and 236 deletions

View File

@ -1,28 +1,41 @@
package kritor.service package kritor.service
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.event.EventRequest import io.kritor.event.EventRequest
import io.kritor.event.EventServiceGrpcKt import io.kritor.event.EventServiceGrpcKt
import io.kritor.event.EventStructure import io.kritor.event.EventStructure
import io.kritor.event.EventType import io.kritor.event.EventType
import io.kritor.event.RequestPushEvent
import io.kritor.event.eventStructure import io.kritor.event.eventStructure
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import moe.fuqiuluo.shamrock.internals.GlobalEventTransmitter import moe.fuqiuluo.shamrock.internals.GlobalEventTransmitter
object EventService: EventServiceGrpcKt.EventServiceCoroutineImplBase() { object EventService: EventServiceGrpcKt.EventServiceCoroutineImplBase() {
override fun registerActiveListener(request: EventRequest): Flow<EventStructure> { override fun registerActiveListener(request: RequestPushEvent): Flow<EventStructure> {
return channelFlow { return channelFlow {
when(request.type!!) { when(request.type!!) {
EventType.CORE_EVENT -> TODO() EventType.EVENT_TYPE_CORE_EVENT -> {}
EventType.MESSAGE -> GlobalEventTransmitter.onMessageEvent { EventType.EVENT_TYPE_MESSAGE -> GlobalEventTransmitter.onMessageEvent {
send(eventStructure { send(eventStructure {
this.type = EventType.MESSAGE this.type = EventType.EVENT_TYPE_MESSAGE
this.message = it.second this.message = it.second
}) })
} }
EventType.NOTICE -> TODO() EventType.EVENT_TYPE_NOTICE -> GlobalEventTransmitter.onRequestEvent {
EventType.REQUEST -> TODO() send(eventStructure {
EventType.UNRECOGNIZED -> TODO() this.type = EventType.EVENT_TYPE_NOTICE
this.request = it
})
}
EventType.EVENT_TYPE_REQUEST -> GlobalEventTransmitter.onNoticeEvent {
send(eventStructure {
this.type = EventType.EVENT_TYPE_NOTICE
this.notice = it
})
}
EventType.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT)
} }
} }
} }

View File

@ -4,17 +4,42 @@ package moe.fuqiuluo.shamrock.internals
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import io.kritor.event.GroupApplyType
import io.kritor.event.GroupMemberBanType
import io.kritor.event.GroupMemberDecreasedType
import io.kritor.event.GroupMemberIncreasedType
import io.kritor.event.MessageEvent import io.kritor.event.MessageEvent
import io.kritor.event.NoticeEvent
import io.kritor.event.NoticeType
import io.kritor.event.RequestType
import io.kritor.event.RequestsEvent
import io.kritor.event.Scene import io.kritor.event.Scene
import io.kritor.event.contact import io.kritor.event.contact
import io.kritor.event.essenceMessageNotice
import io.kritor.event.friendApplyRequest
import io.kritor.event.friendFileComeNotice
import io.kritor.event.friendPokeNotice
import io.kritor.event.friendRecallNotice
import io.kritor.event.groupAdminChangedNotice
import io.kritor.event.groupApplyRequest
import io.kritor.event.groupFileComeNotice
import io.kritor.event.groupMemberBannedNotice
import io.kritor.event.groupMemberDecreasedNotice
import io.kritor.event.groupMemberIncreasedNotice
import io.kritor.event.groupPokeNotice
import io.kritor.event.groupRecallNotice
import io.kritor.event.groupSignNotice
import io.kritor.event.groupUniqueTitleChangedNotice
import io.kritor.event.groupWholeBanNotice
import io.kritor.event.messageEvent import io.kritor.event.messageEvent
import io.kritor.event.noticeEvent
import io.kritor.event.requestsEvent
import io.kritor.event.sender import io.kritor.event.sender
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.io.core.BytePacketBuilder
import qq.service.QQInterfaces import qq.service.QQInterfaces
import qq.service.msg.toKritorMessages import qq.service.msg.toKritorMessages
@ -22,16 +47,16 @@ internal object GlobalEventTransmitter: QQInterfaces() {
private val messageEventFlow by lazy { private val messageEventFlow by lazy {
MutableSharedFlow<Pair<MsgRecord, MessageEvent>>() MutableSharedFlow<Pair<MsgRecord, MessageEvent>>()
} }
//private val noticeEventFlow by lazy { private val noticeEventFlow by lazy {
// MutableSharedFlow<NoticeEvent>() MutableSharedFlow<NoticeEvent>()
//} }
//private val requestEventFlow by lazy { private val requestEventFlow by lazy {
// MutableSharedFlow<RequestEvent>() MutableSharedFlow<RequestsEvent>()
//} }
//private suspend fun pushNotice(noticeEvent: NoticeEvent) = noticeEventFlow.emit(noticeEvent) private suspend fun pushNotice(noticeEvent: NoticeEvent) = noticeEventFlow.emit(noticeEvent)
//private suspend fun pushRequest(requestEvent: RequestEvent) = requestEventFlow.emit(requestEvent) private suspend fun pushRequest(requestEvent: RequestsEvent) = requestEventFlow.emit(requestEvent)
private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message) private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message)
@ -135,10 +160,9 @@ internal object GlobalEventTransmitter: QQInterfaces() {
} }
} }
/*
/** /**
* 文件通知 通知器 * 文件通知 通知器
*/ **/
object FileNoticeTransmitter { object FileNoticeTransmitter {
/** /**
* 推送私聊文件事件 * 推送私聊文件事件
@ -153,23 +177,19 @@ internal object GlobalEventTransmitter: QQInterfaces() {
expireTime: Long, expireTime: Long,
url: String url: String
): Boolean { ): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = msgTime, this.type = NoticeType.FRIEND_FILE_COME
selfId = app.longAccountUin, this.time = msgTime.toInt()
postType = PostType.Notice, this.friendFileCome = friendFileComeNotice {
type = NoticeType.PrivateUpload, this.fileId = fileId
operatorId = userId, this.fileName = fileName
userId = userId, this.operator = userId
senderId = userId, this.fileSize = fileSize
privateFile = PrivateFileMsg( this.expireTime = expireTime.toInt()
id = fileId, this.fileSubId = fileSubId
name = fileName, this.url = url
size = fileSize, }
url = url, })
subId = fileSubId,
expire = expireTime
)
))
return true return true
} }
@ -186,22 +206,19 @@ internal object GlobalEventTransmitter: QQInterfaces() {
bizId: Int, bizId: Int,
url: String url: String
): Boolean { ): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = msgTime, this.type = NoticeType.GROUP_FILE_COME
selfId = app.longAccountUin, this.time = msgTime.toInt()
postType = PostType.Notice, this.groupFileCome = groupFileComeNotice {
type = NoticeType.GroupUpload, this.groupId = groupId
operatorId = userId, this.operator = userId
userId = userId, this.fileId = uuid
groupId = groupId, this.fileName = fileName
file = GroupFileMsg( this.fileSize = fileSize
id = uuid, this.biz = bizId
name = fileName, this.url = url
size = fileSize, }
busid = bizId.toLong(), })
url = url
)
))
return true return true
} }
} }
@ -211,68 +228,80 @@ internal object GlobalEventTransmitter: QQInterfaces() {
*/ */
object GroupNoticeTransmitter { object GroupNoticeTransmitter {
suspend fun transGroupSign(time: Long, target: Long, action: String?, rankImg: String?, groupCode: Long): Boolean { suspend fun transGroupSign(time: Long, target: Long, action: String?, rankImg: String?, groupCode: Long): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = time, this.type = NoticeType.GROUP_SIGN
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Notice, this.groupSign = groupSignNotice {
type = NoticeType.Notify, this.groupId = groupCode
subType = NoticeSubType.Sign, this.targetUin = target
userId = target, this.action = action ?: ""
groupId = groupCode, this.suffix = ""
target = target, this.rankImage = rankImg ?: ""
signDetail = SignDetail( }
rankImg = rankImg, })
action = action
)
))
return true return true
} }
suspend fun transGroupPoke(time: Long, operation: Long, target: Long, action: String?, suffix: String?, actionImg: String?, groupCode: Long): Boolean { suspend fun transGroupPoke(time: Long, operator: Long, target: Long, action: String?, suffix: String?, actionImg: String?, groupCode: Long): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = time, this.type = NoticeType.GROUP_POKE
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Notice, this.groupPoke = groupPokeNotice {
type = NoticeType.Notify, this.action = action ?: ""
subType = NoticeSubType.Poke, this.target = target
operatorId = operation, this.operator = operator
userId = operation, this.suffix = suffix ?: ""
groupId = groupCode, this.actionImage = actionImg ?: ""
target = target, }
pokeDetail = PokeDetail( })
action = action,
suffix = suffix,
actionImg = actionImg
)
))
return true return true
} }
suspend fun transGroupMemberNumChanged( suspend fun transGroupMemberNumIncreased(
time: Long, time: Long,
target: Long, target: Long,
targetUid: String, targetUid: String,
groupCode: Long, groupCode: Long,
operator: Long, operator: Long,
operatorUid: String, operatorUid: String,
noticeType: NoticeType, type: GroupMemberIncreasedType
noticeSubType: NoticeSubType
): Boolean { ): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = time, this.type = NoticeType.GROUP_MEMBER_INCREASE
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Notice, this.groupMemberIncrease = groupMemberIncreasedNotice {
type = noticeType, this.groupId = groupCode
subType = noticeSubType, this.operatorUid = operatorUid
operatorId = operator, this.operatorUin = operator
userId = target, this.targetUid = targetUid
senderId = operator, this.targetUin = target
target = target, this.type = type
groupId = groupCode, }
targetUid = targetUid, })
operatorUid = operatorUid, return true
userUid = targetUid }
))
suspend fun transGroupMemberNumDecreased(
time: Long,
target: Long,
targetUid: String,
groupCode: Long,
operator: Long,
operatorUid: String,
type: GroupMemberDecreasedType
): Boolean {
pushNotice(noticeEvent {
this.type = NoticeType.GROUP_MEMBER_INCREASE
this.time = time.toInt()
this.groupMemberDecrease = groupMemberDecreasedNotice {
this.groupId = groupCode
this.operatorUid = operatorUid
this.operatorUin = operator
this.targetUid = targetUid
this.targetUin = target
this.type = type
}
})
return true return true
} }
@ -283,25 +312,39 @@ internal object GlobalEventTransmitter: QQInterfaces() {
groupCode: Long, groupCode: Long,
setAdmin: Boolean setAdmin: Boolean
): Boolean { ): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = msgTime, this.type = NoticeType.GROUP_ADMIN_CHANGED
selfId = app.longAccountUin, this.time = msgTime.toInt()
postType = PostType.Notice, this.groupAdminChanged = groupAdminChangedNotice {
type = NoticeType.GroupAdminChange, this.groupId = groupCode
subType = if (setAdmin) NoticeSubType.Set else NoticeSubType.UnSet, this.targetUid = targetUid
operatorId = 0, this.targetUin = target
userId = target, this.isAdmin = setAdmin
userUid = targetUid, }
target = target, })
targetUid = targetUid, return true
groupId = groupCode }
))
suspend fun transGroupWholeBan(
msgTime: Long,
operator: Long,
groupCode: Long,
isOpen: Boolean
): Boolean {
pushNotice(noticeEvent {
this.type = NoticeType.GROUP_WHOLE_BAN
this.time = msgTime.toInt()
this.groupWholeBan = groupWholeBanNotice {
this.groupId = groupCode
this.isWholeBan = isOpen
this.operator = operator
}
})
return true return true
} }
suspend fun transGroupBan( suspend fun transGroupBan(
msgTime: Long, msgTime: Long,
subType: NoticeSubType,
operator: Long, operator: Long,
operatorUid: String, operatorUid: String,
target: Long, target: Long,
@ -309,43 +352,46 @@ internal object GlobalEventTransmitter: QQInterfaces() {
groupCode: Long, groupCode: Long,
duration: Int duration: Int
): Boolean { ): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = msgTime, this.type = NoticeType.GROUP_MEMBER_BANNED
selfId = app.longAccountUin, this.time = msgTime.toInt()
postType = PostType.Notice, this.groupMemberBanned = groupMemberBannedNotice {
type = NoticeType.GroupBan, this.groupId = groupCode
subType = subType, this.operatorUid = operatorUid
operatorId = operator, this.operatorUin = operator
userId = target, this.targetUid = targetUid
senderId = operator, this.targetUin = target
target = target, this.duration = duration
groupId = groupCode, this.type = if (duration > 0) GroupMemberBanType.BAN
duration = duration, else GroupMemberBanType.LIFT_BAN
operatorUid = operatorUid, }
targetUid = targetUid })
))
return true return true
} }
suspend fun transGroupMsgRecall( suspend fun transGroupMsgRecall(
time: Long, time: Long,
operator: Long, operator: Long,
operatorUid: String,
target: Long, target: Long,
targetUid: String,
groupCode: Long, groupCode: Long,
msgHash: Int, msgId: Long,
tipText: String tipText: String
): Boolean { ): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = time, this.type = NoticeType.GROUP_RECALL
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Notice, this.groupRecall = groupRecallNotice {
type = NoticeType.GroupRecall, this.groupId = groupCode
operatorId = operator, this.operatorUid = operatorUid
userId = target, this.operatorUin = operator
msgId = msgHash, this.targetUid = targetUid
tip = tipText, this.targetUin = target
groupId = groupCode this.messageId = msgId
)) this.tipText = tipText
}
})
return true return true
} }
@ -356,16 +402,7 @@ internal object GlobalEventTransmitter: QQInterfaces() {
newCard: String, newCard: String,
groupId: Long groupId: Long
): Boolean { ): Boolean {
pushNotice(NoticeEvent(
time = time,
selfId = app.longAccountUin,
postType = PostType.Notice,
type = NoticeType.GroupCard,
userId = targetId,
cardNew = newCard,
cardOld = oldCard,
groupId = groupId
))
return true return true
} }
@ -375,16 +412,15 @@ internal object GlobalEventTransmitter: QQInterfaces() {
title: String, title: String,
groupId: Long groupId: Long
): Boolean { ): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = time, this.type = NoticeType.GROUP_MEMBER_UNIQUE_TITLE_CHANGED
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Notice, this.groupMemberUniqueTitleChanged = groupUniqueTitleChangedNotice {
type = NoticeType.Notify, this.groupId = groupId
userId = targetId, this.target = targetId
groupId = groupId, this.title = title
title = title, }
subType = NoticeSubType.Title })
))
return true return true
} }
@ -392,21 +428,21 @@ internal object GlobalEventTransmitter: QQInterfaces() {
time: Long, time: Long,
senderUin: Long, senderUin: Long,
operatorUin: Long, operatorUin: Long,
msgId: Int, msgId: Long,
groupId: Long, groupId: Long,
subType: NoticeSubType subType: UInt
): Boolean { ): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = time, this.type = NoticeType.GROUP_ESSENCE_CHANGED
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Notice, this.groupEssenceChanged = essenceMessageNotice {
type = NoticeType.Essence, this.groupId = groupId
senderId = senderUin, this.messageId = msgId
groupId = groupId, this.sender = senderUin
operatorId = operatorUin, this.operator = operatorUin
msgId = msgId, this.subType = subType.toInt()
subType = subType }
)) })
return true return true
} }
} }
@ -415,37 +451,31 @@ internal object GlobalEventTransmitter: QQInterfaces() {
* 私聊通知 通知器 * 私聊通知 通知器
*/ */
object PrivateNoticeTransmitter { object PrivateNoticeTransmitter {
suspend fun transPrivatePoke(msgTime: Long, operation: Long, target: Long, action: String?, suffix: String?, actionImg: String?): Boolean { suspend fun transPrivatePoke(msgTime: Long, operator: Long, target: Long, action: String?, suffix: String?, actionImg: String?): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = msgTime, this.type = NoticeType.FRIEND_POKE
selfId = app.longAccountUin, this.time = msgTime.toInt()
postType = PostType.Notice, this.friendPoke = friendPokeNotice {
type = NoticeType.Notify, this.action = action ?: ""
subType = NoticeSubType.Poke, this.target = target
operatorId = operation, this.operator = operator
userId = operation, this.suffix = suffix ?: ""
senderId = operation, this.actionImage = actionImg ?: ""
target = target, }
pokeDetail = PokeDetail( })
actionImg = actionImg,
action = action,
suffix = suffix
)
))
return true return true
} }
suspend fun transPrivateRecall(time: Long, operation: Long, msgHashId: Int, tipText: String): Boolean { suspend fun transPrivateRecall(time: Long, operator: Long, msgId: Long, tipText: String): Boolean {
pushNotice(NoticeEvent( pushNotice(noticeEvent {
time = time, this.type = NoticeType.FRIEND_RECALL
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Notice, this.friendRecall = friendRecallNotice {
type = NoticeType.FriendRecall, this.operator = operator
operatorId = operation, this.messageId = msgId
userId = operation, this.tipText = tipText
msgId = msgHashId, }
tip = tipText })
))
return true return true
} }
@ -455,16 +485,16 @@ internal object GlobalEventTransmitter: QQInterfaces() {
* 请求 通知器 * 请求 通知器
*/ */
object RequestTransmitter { object RequestTransmitter {
suspend fun transFriendApp(time: Long, operation: Long, tipText: String, flag: String): Boolean { suspend fun transFriendApp(time: Long, operator: Long, tipText: String, flag: String): Boolean {
pushRequest(RequestEvent( pushRequest(requestsEvent {
time = time, this.type = RequestType.FRIEND_APPLY
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Request, this.friendApply = friendApplyRequest {
type = RequestType.Friend, this.applierUin = operator
userId = operation, this.message = tipText
comment = tipText, this.flag = flag
flag = flag }
)) })
return true return true
} }
@ -475,23 +505,23 @@ internal object GlobalEventTransmitter: QQInterfaces() {
reason: String, reason: String,
groupCode: Long, groupCode: Long,
flag: String, flag: String,
subType: RequestSubType type: GroupApplyType
): Boolean { ): Boolean {
pushRequest(RequestEvent( pushRequest(requestsEvent {
time = time, this.type = RequestType.GROUP_APPLY
selfId = app.longAccountUin, this.time = time.toInt()
postType = PostType.Request, this.groupApply = groupApplyRequest {
type = RequestType.Group, this.applierUid = applierUid
userId = applier, this.applierUin = applier
userUid = applierUid, this.groupId = groupCode
comment = reason, this.reason = reason
groupId = groupCode, this.flag = flag
subType = subType, this.type = type
flag = flag }
)) })
return true return true
} }
}*/ }
suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) { suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) {
messageEventFlow.collect { messageEventFlow.collect {
@ -501,7 +531,6 @@ internal object GlobalEventTransmitter: QQInterfaces() {
} }
} }
/*
suspend inline fun onNoticeEvent(collector: FlowCollector<NoticeEvent>) { suspend inline fun onNoticeEvent(collector: FlowCollector<NoticeEvent>) {
noticeEventFlow.collect { noticeEventFlow.collect {
GlobalScope.launch { GlobalScope.launch {
@ -510,11 +539,11 @@ internal object GlobalEventTransmitter: QQInterfaces() {
} }
} }
suspend inline fun onRequestEvent(collector: FlowCollector<RequestEvent>) { suspend inline fun onRequestEvent(collector: FlowCollector<RequestsEvent>) {
requestEventFlow.collect { requestEventFlow.collect {
GlobalScope.launch { GlobalScope.launch {
collector.emit(it) collector.emit(it)
} }
} }
}*/ }
} }

View File

@ -3,11 +3,15 @@ package qq.service.friend
import com.tencent.mobileqq.data.Friends import com.tencent.mobileqq.data.Friends
import com.tencent.mobileqq.friend.api.IFriendDataService import com.tencent.mobileqq.friend.api.IFriendDataService
import com.tencent.mobileqq.friend.api.IFriendHandlerService import com.tencent.mobileqq.friend.api.IFriendHandlerService
import com.tencent.mobileqq.qroute.QRoute
import com.tencent.mobileqq.relation.api.IAddFriendTempApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import moe.fuqiuluo.shamrock.tools.slice
import qq.service.QQInterfaces import qq.service.QQInterfaces
import tencent.mobileim.structmsg.structmsg
import kotlin.coroutines.resume import kotlin.coroutines.resume
internal object FriendHelper: QQInterfaces() { internal object FriendHelper: QQInterfaces() {
@ -21,6 +25,69 @@ internal object FriendHelper: QQInterfaces() {
return Result.success(service.allFriends) return Result.success(service.allFriends)
} }
// ProfileService.Pb.ReqSystemMsgAction.Friend
fun requestFriendRequest(msgSeq: Long, uin: Long, remark: String = "", approve: Boolean? = true, notSee: Boolean? = false) {
val service = QRoute.api(IAddFriendTempApi::class.java)
val action = structmsg.SystemMsgActionInfo()
action.type.set(if (approve != false) 2 else 3)
action.group_id.set(0)
action.remark.set(remark)
val snInfo = structmsg.AddFrdSNInfo()
snInfo.uint32_not_see_dynamic.set(if (notSee != false) 1 else 0)
snInfo.uint32_set_sn.set(0)
action.addFrdSNInfo.set(snInfo)
service.sendFriendSystemMsgAction(1, msgSeq, uin, 1, 2004, 11, 0, action, 0, structmsg.StructMsg(), false, app)
}
suspend fun requestFriendSystemMsgNew(msgNum: Int, latestFriendSeq: Long = 0, latestGroupSeq: Long = 0, retryCnt: Int = 3): List<structmsg.StructMsg>? {
if (retryCnt < 0) {
return ArrayList()
}
val req = structmsg.ReqSystemMsgNew()
req.msg_num.set(msgNum)
req.latest_friend_seq.set(latestFriendSeq)
req.latest_group_seq.set(latestGroupSeq)
req.version.set(1000)
req.checktype.set(2)
val flag = structmsg.FlagInfo()
// flag.GrpMsg_Kick_Admin.set(1)
// flag.GrpMsg_HiddenGrp.set(1)
// flag.GrpMsg_WordingDown.set(1)
flag.FrdMsg_GetBusiCard.set(1)
// flag.GrpMsg_GetOfficialAccount.set(1)
// flag.GrpMsg_GetPayInGroup.set(1)
flag.FrdMsg_Discuss2ManyChat.set(1)
// flag.GrpMsg_NotAllowJoinGrp_InviteNotFrd.set(1)
flag.FrdMsg_NeedWaitingMsg.set(1)
flag.FrdMsg_uint32_need_all_unread_msg.set(1)
// flag.GrpMsg_NeedAutoAdminWording.set(1)
// flag.GrpMsg_get_transfer_group_msg_flag.set(1)
// flag.GrpMsg_get_quit_pay_group_msg_flag.set(1)
// flag.GrpMsg_support_invite_auto_join.set(1)
// flag.GrpMsg_mask_invite_auto_join.set(1)
// flag.GrpMsg_GetDisbandedByAdmin.set(1)
flag.GrpMsg_GetC2cInviteJoinGroup.set(1)
req.flag.set(flag)
req.is_get_frd_ribbon.set(false)
req.is_get_grp_ribbon.set(false)
req.friend_msg_type_flag.set(1)
req.uint32_req_msg_type.set(1)
req.uint32_need_uid.set(1)
val fromServiceMsg = sendBufferAW("ProfileService.Pb.ReqSystemMsgNew.Friend", true, req.toByteArray())
return if (fromServiceMsg == null || fromServiceMsg.wupBuffer == null) {
ArrayList()
} else {
try {
val msg = structmsg.RspSystemMsgNew()
msg.mergeFrom(fromServiceMsg.wupBuffer.slice(4))
return msg.friendmsgs.get()
} catch (err: Throwable) {
requestFriendSystemMsgNew(msgNum, latestFriendSeq, latestGroupSeq, retryCnt - 1)
}
}
}
private suspend fun requestFriendList(dataService: IFriendDataService): Boolean { private suspend fun requestFriendList(dataService: IFriendDataService): Boolean {
val service = app.getRuntimeService(IFriendHandlerService::class.java, "all") val service = app.getRuntimeService(IFriendHandlerService::class.java, "all")
service.requestFriendList(true, 0) service.requestFriendList(true, 0)

View File

@ -37,6 +37,7 @@ import tencent.im.oidb.cmd0x8fc.Oidb_0x8fc
import tencent.im.oidb.cmd0xed3.oidb_cmd0xed3 import tencent.im.oidb.cmd0xed3.oidb_cmd0xed3
import tencent.im.oidb.oidb_sso import tencent.im.oidb.oidb_sso
import tencent.im.troop.honor.troop_honor import tencent.im.troop.honor.troop_honor
import tencent.mobileim.structmsg.structmsg
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.nio.ByteBuffer import java.nio.ByteBuffer
@ -217,6 +218,106 @@ internal object GroupHelper: QQInterfaces() {
sendOidb("OidbSvc.0x55c_1", 1372, 1, array) sendOidb("OidbSvc.0x55c_1", 1372, 1, array)
} }
// ProfileService.Pb.ReqSystemMsgAction.Group
suspend fun requestGroupRequest(
msgSeq: Long,
uin: Long,
gid: Long,
msg: String? = "",
approve: Boolean? = true,
notSee: Boolean? = false,
subType: String
): Result<String>{
val req = structmsg.ReqSystemMsgAction().apply {
if (subType == "invite") {
msg_type.set(1)
src_id.set(3)
sub_src_id.set(10016)
group_msg_type.set(2)
} else {
msg_type.set(2)
src_id.set(2)
sub_src_id.set(30024)
group_msg_type.set(1)
}
msg_seq.set(msgSeq)
req_uin.set(uin)
sub_type.set(1)
action_info.set(structmsg.SystemMsgActionInfo().apply {
type.set(if (approve != false) 11 else 12)
group_code.set(gid)
if (subType == "add") {
this.msg.set(msg)
this.blacklist.set(notSee != false)
}
})
language.set(1000)
}
val fromServiceMsg = sendBufferAW("ProfileService.Pb.ReqSystemMsgAction.Group", true, req.toByteArray())
?: return Result.failure(Exception("ReqSystemMsgAction.Group: No Response"))
if (fromServiceMsg.wupBuffer == null) {
return Result.failure(Exception("ReqSystemMsgAction.Group: No WupBuffer"))
}
val rsp = structmsg.RspSystemMsgAction().mergeFrom(fromServiceMsg.wupBuffer.slice(4))
return if (rsp.head.result.has()) {
if (rsp.head.result.get() == 0) {
Result.success(rsp.msg_detail.get())
} else {
Result.failure(Exception(rsp.head.msg_fail.get()))
}
} else {
Result.failure(Exception("操作失败"))
}
}
suspend fun requestGroupSystemMsgNew(msgNum: Int, reqMsgType: Int = 1, latestFriendSeq: Long = 0, latestGroupSeq: Long = 0, retryCnt: Int = 5): List<structmsg.StructMsg> {
if (retryCnt < 0) {
return ArrayList()
}
val req = structmsg.ReqSystemMsgNew()
req.msg_num.set(msgNum)
req.latest_friend_seq.set(latestFriendSeq)
req.latest_group_seq.set(latestGroupSeq)
req.version.set(1000)
req.checktype.set(3)
val flag = structmsg.FlagInfo()
flag.GrpMsg_Kick_Admin.set(1)
flag.GrpMsg_HiddenGrp.set(1)
flag.GrpMsg_WordingDown.set(1)
// flag.FrdMsg_GetBusiCard.set(1)
flag.GrpMsg_GetOfficialAccount.set(1)
flag.GrpMsg_GetPayInGroup.set(1)
flag.FrdMsg_Discuss2ManyChat.set(1)
flag.GrpMsg_NotAllowJoinGrp_InviteNotFrd.set(1)
flag.FrdMsg_NeedWaitingMsg.set(1)
// flag.FrdMsg_uint32_need_all_unread_msg.set(1)
flag.GrpMsg_NeedAutoAdminWording.set(1)
flag.GrpMsg_get_transfer_group_msg_flag.set(1)
flag.GrpMsg_get_quit_pay_group_msg_flag.set(1)
flag.GrpMsg_support_invite_auto_join.set(1)
flag.GrpMsg_mask_invite_auto_join.set(1)
flag.GrpMsg_GetDisbandedByAdmin.set(1)
flag.GrpMsg_GetC2cInviteJoinGroup.set(1)
req.flag.set(flag)
req.is_get_frd_ribbon.set(false)
req.is_get_grp_ribbon.set(false)
req.friend_msg_type_flag.set(1)
req.uint32_req_msg_type.set(reqMsgType)
req.uint32_need_uid.set(1)
val fromServiceMsg = sendBufferAW("ProfileService.Pb.ReqSystemMsgNew.Group", true, req.toByteArray())
return if (fromServiceMsg == null || fromServiceMsg.wupBuffer == null) {
ArrayList()
} else {
try {
val msg = structmsg.RspSystemMsgNew()
msg.mergeFrom(fromServiceMsg.wupBuffer.slice(4))
return msg.groupmsgs.get().orEmpty()
} catch (err: Throwable) {
requestGroupSystemMsgNew(msgNum, reqMsgType, latestFriendSeq, latestGroupSeq, retryCnt - 1)
}
}
}
suspend fun setGroupUniqueTitle(groupId: Long, userId: Long, title: String) { suspend fun setGroupUniqueTitle(groupId: Long, userId: Long, title: String) {
val localMemberInfo = getTroopMemberInfoByUin(groupId, userId, true).getOrThrow() val localMemberInfo = getTroopMemberInfoByUin(groupId, userId, true).getOrThrow()
val req = Oidb_0x8fc.ReqBody() val req = Oidb_0x8fc.ReqBody()

View File

@ -10,6 +10,7 @@ import kotlinx.coroutines.launch
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.internals.GlobalEventTransmitter import moe.fuqiuluo.shamrock.internals.GlobalEventTransmitter
import qq.service.bdh.RichProtoSvc
import qq.service.kernel.SimpleKernelMsgListener import qq.service.kernel.SimpleKernelMsgListener
import qq.service.msg.MessageHelper import qq.service.msg.MessageHelper
@ -29,6 +30,8 @@ object AioListener: SimpleKernelMsgListener() {
private suspend fun onMsg(record: MsgRecord) { private suspend fun onMsg(record: MsgRecord) {
when (record.chatType) { when (record.chatType) {
MsgConstant.KCHATTYPEGROUP -> { MsgConstant.KCHATTYPEGROUP -> {
if (record.senderUin == 0L) return
LogCenter.log("群消息(group = ${record.peerName}(${record.peerUin}), uin = ${record.senderUin}, id = ${record.msgId})") LogCenter.log("群消息(group = ${record.peerName}(${record.peerUin}), uin = ${record.senderUin}, id = ${record.msgId})")
if (!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(record, record.elements)) { if (!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(record, record.elements)) {
@ -75,4 +78,63 @@ object AioListener: SimpleKernelMsgListener() {
else -> LogCenter.log("不支持PUSH事件: ${record.chatType}") else -> LogCenter.log("不支持PUSH事件: ${record.chatType}")
} }
} }
override fun onFileMsgCome(arrayList: ArrayList<MsgRecord>?) {
arrayList?.forEach { record ->
GlobalScope.launch {
when (record.chatType) {
MsgConstant.KCHATTYPEGROUP -> onGroupFileMsg(record)
MsgConstant.KCHATTYPEC2C -> onC2CFileMsg(record)
else -> LogCenter.log("不支持该来源的文件上传事件:${record}", Level.WARN)
}
}
}
}
private suspend fun onC2CFileMsg(record: MsgRecord) {
val userId = record.senderUin
val fileMsg = record.elements.firstOrNull {
it.elementType == MsgConstant.KELEMTYPEFILE
}?.fileElement ?: kotlin.run {
LogCenter.log("消息为私聊文件消息但不包含文件消息,来自:${record.peerUin}", Level.WARN)
return
}
val fileName = fileMsg.fileName
val fileSize = fileMsg.fileSize
val expireTime = fileMsg.expireTime ?: 0
val fileId = fileMsg.fileUuid
val fileSubId = fileMsg.fileSubId ?: ""
val url = RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId)
if (!GlobalEventTransmitter.FileNoticeTransmitter
.transPrivateFileEvent(record.msgTime, userId, fileId, fileSubId, fileName, fileSize, expireTime, url)
) {
LogCenter.log("私聊文件消息推送失败 -> FileNoticeTransmitter", Level.WARN)
}
}
private suspend fun onGroupFileMsg(record: MsgRecord) {
val groupId = record.peerUin
val userId = record.senderUin
val fileMsg = record.elements.firstOrNull {
it.elementType == MsgConstant.KELEMTYPEFILE
}?.fileElement ?: kotlin.run {
LogCenter.log("消息为群聊文件消息但不包含文件消息,来自:${record.peerUin}", Level.WARN)
return
}
//val fileMd5 = fileMsg.fileMd5
val fileName = fileMsg.fileName
val fileSize = fileMsg.fileSize
val uuid = fileMsg.fileUuid
val bizId = fileMsg.fileBizId
val url = RichProtoSvc.getGroupFileDownUrl(record.peerUin, uuid, bizId)
if (!GlobalEventTransmitter.FileNoticeTransmitter
.transGroupFileEvent(record.msgTime, userId, groupId, uuid, fileName, fileSize, bizId, url)
) {
LogCenter.log("群聊文件消息推送失败 -> FileNoticeTransmitter", Level.WARN)
}
}
} }

View File

@ -52,8 +52,12 @@ internal object MSFHandler {
fun onPush(fromServiceMsg: FromServiceMsg) { fun onPush(fromServiceMsg: FromServiceMsg) {
val cmd = fromServiceMsg.serviceCmd val cmd = fromServiceMsg.serviceCmd
val push = mPushHandlers[cmd] if (cmd == "trpc.msg.olpush.OlPushService.MsgPush") {
push?.invoke(fromServiceMsg) PrimitiveListener.onPush(fromServiceMsg)
} else {
val push = mPushHandlers[cmd]
push?.invoke(fromServiceMsg)
}
} }
fun onResp(toServiceMsg: ToServiceMsg, fromServiceMsg: FromServiceMsg) { fun onResp(toServiceMsg: ToServiceMsg, fromServiceMsg: FromServiceMsg) {

View File

@ -0,0 +1,668 @@
@file:OptIn(DelicateCoroutinesApi::class)
package qq.service.internals
import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qphone.base.remote.FromServiceMsg
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.event.GroupApplyType
import io.kritor.event.GroupMemberDecreasedType
import io.kritor.event.GroupMemberIncreasedType
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.internals.GlobalEventTransmitter
import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.readBuf32Long
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.message.ContentHead
import protobuf.message.MsgBody
import protobuf.message.ResponseHead
import protobuf.push.C2CCommonTipsEvent
import protobuf.push.C2CRecallEvent
import protobuf.push.FriendApplyEvent
import protobuf.push.GroupAdminChangeEvent
import protobuf.push.GroupApplyEvent
import protobuf.push.GroupBanEvent
import protobuf.push.GroupCommonTipsEvent
import protobuf.push.GroupInviteEvent
import protobuf.push.GroupInvitedApplyEvent
import protobuf.push.GroupListChangeEvent
import protobuf.push.MessagePush
import protobuf.push.MessagePushClientInfo
import qq.service.QQInterfaces
import qq.service.contact.ContactHelper
import qq.service.friend.FriendHelper.requestFriendSystemMsgNew
import qq.service.group.GroupHelper
import qq.service.group.GroupHelper.requestGroupSystemMsgNew
import qq.service.msg.MessageHelper
import kotlin.coroutines.resume
internal object PrimitiveListener {
fun onPush(fromServiceMsg: FromServiceMsg) {
if (fromServiceMsg.wupBuffer == null) return
try {
val push = fromServiceMsg.wupBuffer.slice(4)
.decodeProtobuf<MessagePush>()
GlobalScope.launch {
onMsgPush(push)
}
} catch (e: Exception) {
LogCenter.log(e.stackTraceToString(), Level.WARN)
}
}
private suspend fun onMsgPush(push: MessagePush) {
if (
push.msgBody == null ||
push.msgBody!!.contentHead == null ||
push.msgBody!!.body == null ||
push.msgBody!!.contentHead!!.msgTime == null
) return
val msgBody = push.msgBody!!
val contentHead = msgBody.contentHead!!
val msgType = contentHead.msgType
val subType = contentHead.msgSubType
val msgTime = contentHead.msgTime!!
val body = msgBody.body!!
try {
when (msgType) {
33 -> onGroupMemIncreased(msgTime, body)
34 -> onGroupMemberDecreased(msgTime, body)
44 -> onGroupAdminChange(msgTime, body)
82 -> onGroupMessage(msgTime, body)
84 -> onGroupApply(msgTime, contentHead, body)
87 -> onInviteGroup(msgTime, msgBody.msgHead!!, body)
528 -> when (subType) {
35 -> onFriendApply(msgTime, push.clientInfo!!, body)
39 -> onCardChange(msgTime, body)
68 -> onGroupApply(msgTime, contentHead, body)
138 -> onC2CRecall(msgTime, body)
290 -> onC2CPoke(msgTime, body)
}
732 -> when (subType) {
12 -> onGroupBan(msgTime, body)
16 -> onGroupUniqueTitleChange(msgTime, body)
17 -> onGroupRecall(msgTime, body)
20 -> onGroupCommonTips(msgTime, body)
21 -> onEssenceMessage(msgTime, push.clientInfo, body)
}
}
} catch (e: Exception) {
LogCenter.log("onMsgPush(msgType: $msgType, subType: $subType): " + e.stackTraceToString(), Level.WARN)
}
}
private fun onGroupMessage(msgTime: Long, body: MsgBody) {
}
private suspend fun onC2CPoke(msgTime: Long, body: MsgBody) {
val event = body.msgContent!!.decodeProtobuf<C2CCommonTipsEvent>()
if (event.params == null) return
val params = event.params!!.associate {
it.key to it.value
}
val target = params["uin_str2"] ?: return
val operation = params["uin_str1"] ?: return
val suffix = params["suffix_str"] ?: ""
val actionImg = params["action_img_url"] ?: ""
val action = params["alt_str1"] ?: ""
LogCenter.log("私聊戳一戳: $operation $action $target $suffix")
if (!GlobalEventTransmitter.PrivateNoticeTransmitter
.transPrivatePoke(msgTime, operation.toLong(), target.toLong(), action, suffix, actionImg)
) {
LogCenter.log("私聊戳一戳推送失败!", Level.WARN)
}
}
private suspend fun onFriendApply(
msgTime: Long,
clientInfo: MessagePushClientInfo,
body: MsgBody
) {
val event = body.msgContent!!.decodeProtobuf<FriendApplyEvent>()
if (event.head == null) return
val head = event.head!!
val applierUid = head.applierUid
val msg = head.applyMsg ?: ""
val source = head.source ?: ""
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
if (applier == 0L) {
applier = clientInfo.liteHead?.sender?.toLong() ?: 0
}
val src = head.srcId
val subSrc = head.subSrc
val flag: String = try {
val reqs = requestFriendSystemMsgNew(20, 0, 0)
val req = reqs?.first {
it.msg_time.get() == msgTime
}
val seq = req?.msg_seq?.get()
"$seq;$src;$subSrc;$applier"
} catch (err: Throwable) {
"$msgTime;$src;$subSrc;$applier"
}
LogCenter.log("来自$applier 的好友申请:$msg ($source)")
if (!GlobalEventTransmitter.RequestTransmitter
.transFriendApp(msgTime, applier, msg, flag)
) {
LogCenter.log("好友申请推送失败!", Level.WARN)
}
}
private suspend fun onCardChange(msgTime: Long, body: MsgBody) {
// val event = runCatching {
// body.msgContent!!.decodeProtobuf<GroupCardChangeEvent>()
// }.getOrElse {
// val readPacket = ByteReadPacket(body.msgContent!!)
// readPacket.readBuf32Long()
// readPacket.discardExact(1)
//
// readPacket.readBytes(readPacket.readShort().toInt()).also {
// readPacket.release()
// }.decodeProtobuf<GroupCommonTipsEvent>()
// }
//
// val targetId = detail[1, 13, 2].asUtf8String
// val newCardList = detail[1, 13, 3].asList
// var newCard = ""
// newCardList
// .value
// .forEach {
// if (it[1].asInt == 1) {
// newCard = it[2].asUtf8String
// }
// }
// val groupId = detail[1, 13, 4].asLong
// var oldCard = ""
// val targetQQ = ContactHelper.getUinByUidAsync(targetId).toLong()
// LogCenter.log("群组[$groupId]成员$targetQQ 群名片变动 -> $newCard")
// // oldCard暂时获取不到
// if (!GlobalEventTransmitter.GroupNoticeTransmitter
// .transCardChange(msgTime, targetQQ, oldCard, newCard, groupId)
// ) {
// LogCenter.log("群名片变动推送失败!", Level.WARN)
// }
}
private suspend fun onGroupUniqueTitleChange(msgTime: Long, body: MsgBody) {
val event = runCatching {
body.msgContent!!.decodeProtobuf<GroupCommonTipsEvent>()
}.getOrElse {
val readPacket = ByteReadPacket(body.msgContent!!)
readPacket.readBuf32Long()
readPacket.discardExact(1)
readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.release()
}.decodeProtobuf<GroupCommonTipsEvent>()
}
val groupId = event.groupCode.toLong()
val detail = event.uniqueTitleChangeDetail!!.first()
//detail = if (detail[5] is ProtoList) {
// (detail[5] as ProtoList).value[0]
//} else {
// detail[5]
// }
val targetUin = detail.targetUin.toLong()
// 恭喜<{\"cmd\":5,\"data\":\"qq\",\"text}\":\"nickname\"}>获得群主授予的<{\"cmd\":1,\"data\":\"https://qun.qq.com/qqweb/m/qun/medal/detail.html?_wv=16777223&bid=2504&gc=gid&isnew=1&medal=302&uin=uin\",\"text\":\"title\",\"url\":\"https://qun.qq.com/qqweb/m/qun/medal/detail.html?_wv=16777223&bid=2504&gc=gid&isnew=1&medal=302&uin=uin\"}>头衔
val titleChangeInfo = detail.wording
if (titleChangeInfo.indexOf("群主授予") == -1) {
return
}
val titleJson = titleChangeInfo.split("获得群主授予的<")[1].replace(">头衔", "")
val titleJsonObj = Json.decodeFromString<JsonElement>(titleJson).asJsonObject
val title = titleJsonObj["text"].asString
LogCenter.log("群组[$groupId]成员$targetUin 获得群头衔 -> $title")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transTitleChange(msgTime, targetUin, title, groupId)
) {
LogCenter.log("群头衔变动推送失败!", Level.WARN)
}
}
private suspend fun onEssenceMessage(
msgTime: Long,
clientInfo: MessagePushClientInfo?,
body: MsgBody
) {
if (clientInfo == null) return
val event = runCatching {
body.msgContent!!.decodeProtobuf<GroupCommonTipsEvent>()
}.getOrElse {
val readPacket = ByteReadPacket(body.msgContent!!)
readPacket.readBuf32Long()
readPacket.discardExact(1)
readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.release()
}.decodeProtobuf<GroupCommonTipsEvent>()
}
val groupId = event.groupCode.toLong()
val detail = event.essenceMsgInfo!!.first()
val msgSeq = event.msgSeq.toLong()
val senderUin = detail.sender.toLong()
val operatorUin = detail.operator.toLong()
when (val type = detail.type) {
1u -> {
LogCenter.log("群设精消息(groupId=$groupId, sender=$senderUin, msgSeq=$msgSeq, operator=$operatorUin)")
}
2u -> {
LogCenter.log("群撤精消息(groupId=$groupId, sender=$senderUin, msgId=$msgSeq, operator=$operatorUin)")
}
else -> error("onEssenceMessage unknown type: $type")
}
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId.toString())
val sourceRecord = withTimeoutOrNull(3000) {
suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records ->
it.resume(records)
}
}
}?.firstOrNull()
if (sourceRecord == null) {
LogCenter.log("无法获取源消息记录,无法推送精华消息变动!", Level.WARN)
return
}
val msgId = sourceRecord.msgId
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transEssenceChange(msgTime, senderUin, operatorUin, msgId, groupId, detail.type)
) {
LogCenter.log("精华消息变动推送失败!", Level.WARN)
}
}
private suspend fun onGroupCommonTips(time: Long, body: MsgBody) {
val event = runCatching {
body.msgContent!!.decodeProtobuf<GroupCommonTipsEvent>()
}.getOrElse {
val readPacket = ByteReadPacket(body.msgContent!!)
readPacket.discardExact(4)
readPacket.discardExact(1)
readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.release()
}.decodeProtobuf<GroupCommonTipsEvent>()
}
val groupId = event.groupCode.toLong()
val detail = event.baseTips!!.first()
val params = detail.params!!.associate {
it.key to it.value
}
val target = params["uin_str2"] ?: params["mqq_uin"] ?: return
val operation = params["uin_str1"] ?: return
val suffix = params["suffix_str"] ?: ""
val actionImg = params["action_img_url"] ?: ""
val action = params["alt_str1"]
?: params["action_str"]
?: params["user_sign"]
?: ""
val rankImg = params["rank_img"] ?: ""
when (detail.type) {
1061u -> {
LogCenter.log("群戳一戳($groupId): $operation $action $target $suffix")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupPoke(time, operation.toLong(), target.toLong(), action, suffix, actionImg, groupId)
) {
LogCenter.log("群戳一戳推送失败!", Level.WARN)
}
}
1068u -> {
LogCenter.log("群打卡($groupId): $action $target")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupSign(time, target.toLong(), action, rankImg, groupId)
) {
LogCenter.log("群打卡推送失败!", Level.WARN)
}
}
else -> {
LogCenter.log("onGroupPokeAndGroupSign unknown type ${detail.type}", Level.WARN)
}
}
}
private suspend fun onC2CRecall(time: Long, body: MsgBody) {
val event = body.msgContent!!.decodeProtobuf<C2CRecallEvent>()
val head = event.head!!
val operationUid = head.operator!!
val operator = ContactHelper.getUinByUidAsync(operationUid).toLong()
val msgSeq = head.msgSeq
val tipText = head.wording?.wording ?: ""
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, operationUid)
val sourceRecord = withTimeoutOrNull(3000) {
suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records ->
it.resume(records)
}
}
}?.firstOrNull()
if (sourceRecord == null) {
LogCenter.log("无法获取源消息记录,无法推送撤回消息!", Level.WARN)
return
}
val msgId = sourceRecord.msgId
LogCenter.log("私聊消息撤回: $operator, seq = $msgSeq, msgId = ${msgId}, tip = $tipText")
if (!GlobalEventTransmitter.PrivateNoticeTransmitter
.transPrivateRecall(time, operator, msgId, tipText)
) {
LogCenter.log("私聊消息撤回推送失败!", Level.WARN)
}
}
private suspend fun onGroupMemIncreased(time: Long, body: MsgBody) {
val event = body.msgContent!!.decodeProtobuf<GroupListChangeEvent>()
val groupCode = event.groupCode
val targetUid = event.memberUid
val type = event.type
GroupHelper.getGroupMemberList(groupCode.toString(), true).onFailure {
LogCenter.log("新成员加入刷新群成员列表失败: $groupCode", Level.WARN)
}.onSuccess {
LogCenter.log("新成员加入刷新群成员列表成功,群成员数量: ${it.size}", Level.INFO)
}
val operatorUid = event.operatorUid
val operator = ContactHelper.getUinByUidAsync(operatorUid).toLong()
val target = ContactHelper.getUinByUidAsync(targetUid).toLong()
LogCenter.log("群成员增加($groupCode): $target, type = $type")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupMemberNumIncreased(
time,
target,
targetUid,
groupCode,
operator,
operatorUid,
when (type) {
130 -> GroupMemberIncreasedType.APPROVE
131 -> GroupMemberIncreasedType.INVITE
else -> GroupMemberIncreasedType.APPROVE
}
)
) {
LogCenter.log("群成员增加推送失败!", Level.WARN)
}
}
private suspend fun onGroupMemberDecreased(time: Long, body: MsgBody) {
val event = body.msgContent!!.decodeProtobuf<GroupListChangeEvent>()
val groupCode = event.groupCode
val targetUid = event.memberUid
val type = event.type
val operatorUid = event.operatorUid
GroupHelper.getGroupMemberList(groupCode.toString(), true).onFailure {
LogCenter.log("新成员加入刷新群成员列表失败: $groupCode", Level.WARN)
}.onSuccess {
LogCenter.log("新成员加入刷新群成员列表成功,群成员数量: ${it.size}", Level.INFO)
}
val operator = ContactHelper.getUinByUidAsync(operatorUid).toLong()
val target = ContactHelper.getUinByUidAsync(targetUid).toLong()
val subtype = when (type) {
130 -> GroupMemberDecreasedType.LEAVE
131 -> GroupMemberDecreasedType.KICK
3 -> GroupMemberDecreasedType.KICK_ME
else -> GroupMemberDecreasedType.KICK
}
LogCenter.log("群成员减少($groupCode): $target, type = $subtype ($type)")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupMemberNumDecreased(
time,
target,
targetUid,
groupCode,
operator,
operatorUid,
subtype
)
) {
LogCenter.log("群成员减少推送失败!", Level.WARN)
}
}
private suspend fun onGroupAdminChange(msgTime: Long, body: MsgBody) {
val event = body.msgContent!!.decodeProtobuf<GroupAdminChangeEvent>()
val groupCode = event.groupCode
if (event.operation == null) return
val operation = event.operation!!
if (operation.setInfo == null && operation.unsetInfo == null) return
val isSetAdmin: Boolean
val targetUid: String
if (operation.setInfo == null) {
isSetAdmin = false
targetUid = operation.unsetInfo!!.targetUid!!
} else {
isSetAdmin = true
targetUid = operation.setInfo!!.targetUid!!
}
val target = ContactHelper.getUinByUidAsync(targetUid).toLong()
LogCenter.log("群管理员变动($groupCode): $target, isSetAdmin = $isSetAdmin")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupAdminChanged(msgTime, target, targetUid, groupCode, isSetAdmin)
) {
LogCenter.log("群管理员变动推送失败!", Level.WARN)
}
}
private suspend fun onGroupBan(msgTime: Long, body: MsgBody) {
val event = body.msgContent!!.decodeProtobuf<GroupBanEvent>()
val groupCode = event.groupCode.toLong()
val operatorUid = event.operatorUid
val wholeBan = event.target?.target?.targetUid == null
val targetUid = event.target?.target?.targetUid ?: ""
val rawDuration = event.target?.target?.rawDuration?.toInt() ?: 0
val operator = ContactHelper.getUinByUidAsync(operatorUid).toLong()
val duration = if (wholeBan) -1 else rawDuration
val target = if (wholeBan) 0 else ContactHelper.getUinByUidAsync(targetUid).toLong()
if (wholeBan) {
LogCenter.log("群全员禁言($groupCode): $operator -> ${if (rawDuration != 0) "开启" else "关闭"}")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupWholeBan(msgTime, groupCode, operator, rawDuration != 0)
) {
LogCenter.log("群禁言推送失败!", Level.WARN)
}
} else {
LogCenter.log("群禁言($groupCode): $operator -> $target, 时长 = ${duration}s")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupBan(msgTime, operator, operatorUid, target, targetUid, groupCode, duration)
) {
LogCenter.log("群禁言推送失败!", Level.WARN)
}
}
}
private suspend fun onGroupRecall(time: Long, body: MsgBody) {
val event = runCatching {
body.msgContent!!.decodeProtobuf<GroupCommonTipsEvent>()
}.getOrElse {
val readPacket = ByteReadPacket(body.msgContent!!)
readPacket.discardExact(4)
readPacket.discardExact(1)
readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.release()
}.decodeProtobuf<GroupCommonTipsEvent>()
}
val groupCode = event.groupCode.toLong()
val detail = event.recallDetails!!
val operatorUid = detail.operatorUid
val targetUid = detail.msgInfo!!.senderUid
val msgSeq = detail.msgInfo!!.msgSeq.toLong()
val tipText = detail.wording?.wording ?: ""
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupCode.toString())
val sourceRecord = withTimeoutOrNull(3000) {
suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records ->
it.resume(records)
}
}
}?.firstOrNull()
if (sourceRecord == null) {
LogCenter.log("无法获取源消息记录,无法推送撤回消息!", Level.WARN)
return
}
val msgId = sourceRecord.msgId
val operator = ContactHelper.getUinByUidAsync(operatorUid).toLong()
val target = ContactHelper.getUinByUidAsync(targetUid).toLong()
LogCenter.log("群消息撤回($groupCode): $operator -> $target, seq = $msgSeq, id = $msgId, tip = $tipText")
if (!GlobalEventTransmitter.GroupNoticeTransmitter
.transGroupMsgRecall(time, operator, operatorUid, target, targetUid, groupCode, msgId, tipText)
) {
LogCenter.log("群消息撤回推送失败!", Level.WARN)
}
}
private suspend fun onGroupApply(time: Long, contentHead: ContentHead, body: MsgBody) {
when (contentHead.msgType) {
84 -> {
val event = body.msgContent!!.decodeProtobuf<GroupApplyEvent>()
val groupCode = event.groupCode
val applierUid = event.applierUid
val reason = event.applyMsg ?: ""
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
if (applier == QQInterfaces.app.longAccountUin) {
return
}
val msgSeq = contentHead.msgSeq
val flag = try {
var reqs = requestGroupSystemMsgNew(10, 1)
val riskReqs = requestGroupSystemMsgNew(5, 2)
reqs = reqs + riskReqs
val req = reqs.firstOrNull {
it.msg_time.get() == time && it.msg?.group_code?.get() == groupCode
}
val seq = req?.msg_seq?.get() ?: time
if (applier == 0L) {
applier = req?.req_uin?.get() ?: 0L
}
"$seq;$groupCode;$applier"
} catch (err: Throwable) {
"$time;$groupCode;$applier"
}
LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq")
if (!GlobalEventTransmitter.RequestTransmitter
.transGroupApply(time, applier, applierUid, reason, groupCode, flag, GroupApplyType.GROUP_APPLY_ADD)
) {
LogCenter.log("入群申请推送失败!", Level.WARN)
}
}
528 -> {
val event = body.msgContent!!.decodeProtobuf<GroupInvitedApplyEvent>()
val groupCode = event.applyInfo?.groupCode ?: return
val applierUid = event.applyInfo?.applierUid ?: return
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
if (applier == QQInterfaces.app.longAccountUin) {
return
}
if ((event.applyInfo?.type ?: return) < 3) {
// todo
return
}
val flag = try {
var reqs = requestGroupSystemMsgNew(10, 1)
val riskReqs = requestGroupSystemMsgNew(5, 2)
reqs = reqs + riskReqs
val req = reqs.firstOrNull() {
it.msg_time.get() == time
}
val seq = req?.msg_seq?.get() ?: time
if (applier == 0L) {
applier = req?.req_uin?.get() ?: 0L
}
"$seq;$groupCode;$applier"
} catch (err: Throwable) {
"$time;$groupCode;$applier"
}
LogCenter.log("邀请入群申请($groupCode): $applier")
if (!GlobalEventTransmitter.RequestTransmitter
.transGroupApply(time, applier, applierUid, "", groupCode, flag, GroupApplyType.GROUP_APPLY_ADD)
) {
LogCenter.log("邀请入群申请推送失败!", Level.WARN)
}
}
}
}
private suspend fun onInviteGroup(time: Long, msgHead: ResponseHead, body: MsgBody) {
val event = body.msgContent!!.decodeProtobuf<GroupInviteEvent>()
val groupCode = event.groupCode
val invitorUid = event.inviterUid
val invitor = ContactHelper.getUinByUidAsync(invitorUid).toLong()
val uin = msgHead.receiver
LogCenter.log("邀请入群: $groupCode, 邀请者: \"$invitor\"")
val flag = try {
var reqs = requestGroupSystemMsgNew(10, 1)
val riskReqs = requestGroupSystemMsgNew(10, 2)
reqs = reqs + riskReqs
val req = reqs.firstOrNull {
it.msg_time.get() == time
}
val seq = req?.msg_seq?.get() ?: time
"$seq;$groupCode;$uin"
} catch (err: Throwable) {
"$time;$groupCode;$uin"
}
if (!GlobalEventTransmitter.RequestTransmitter
.transGroupApply(time, invitor, invitorUid, "", groupCode, flag, GroupApplyType.GROUP_APPLY_INVITE)
) {
LogCenter.log("邀请入群推送失败!", Level.WARN)
}
}
}

View File

@ -195,7 +195,7 @@ abstract class SimpleKernelMsgListener: IKernelMsgListener {
} }
override fun onMsgRecall(i2: Int, str: String?, j2: Long) { override fun onMsgRecall(chatType: Int, tips: String?, msgId: Long) {
} }

View File

@ -3,6 +3,7 @@ package qq.service.msg
import com.tencent.qqnt.kernel.api.IKernelService import com.tencent.qqnt.kernel.api.IKernelService
import com.tencent.qqnt.kernel.nativeinterface.Contact import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.kernel.nativeinterface.TempChatInfo import com.tencent.qqnt.kernel.nativeinterface.TempChatInfo
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
@ -32,12 +33,28 @@ internal object MessageHelper: QQInterfaces() {
return Result.success(info) return Result.success(info)
} }
suspend fun generateContact(record: MsgRecord): Contact {
val peerId = when (record.chatType) {
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> record.senderUid
MsgConstant.KCHATTYPEGUILD -> record.channelId
else -> record.peerUin.toString()
}
return Contact(record.chatType, peerId, if (record.chatType == MsgConstant.KCHATTYPEGUILD) {
record.guildId
} else if(record.chatType == MsgConstant.KCHATTYPETEMPC2CFROMGROUP) {
val tempInfo = getTempChatInfo(record.chatType, peerId).getOrThrow()
tempInfo.groupCode
} else {
null
})
}
suspend fun generateContact(chatType: Int, id: String, subId: String = ""): Contact { suspend fun generateContact(chatType: Int, id: String, subId: String = ""): Contact {
val peerId = when (chatType) { val peerId = when (chatType) {
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> { MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
ContactHelper.getUidByUinAsync(id.toLong()) if (id.startsWith("u_")) id
else ContactHelper.getUidByUinAsync(id.toLong())
} }
else -> id else -> id
} }
return if (chatType == MsgConstant.KCHATTYPEGUILD) { return if (chatType == MsgConstant.KCHATTYPEGUILD) {

View File

@ -1,8 +1,10 @@
package qq.service.msg package qq.service.msg
import com.tencent.mobileqq.qroute.QRoute
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 com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.event.AtElement import io.kritor.event.AtElement
import io.kritor.event.Element import io.kritor.event.Element
import io.kritor.event.ElementKt import io.kritor.event.ElementKt
@ -21,10 +23,13 @@ import io.kritor.event.imageElement
import io.kritor.event.jsonElement import io.kritor.event.jsonElement
import io.kritor.event.locationElement import io.kritor.event.locationElement
import io.kritor.event.pokeElement import io.kritor.event.pokeElement
import io.kritor.event.replyElement
import io.kritor.event.rpsElement import io.kritor.event.rpsElement
import io.kritor.event.textElement import io.kritor.event.textElement
import io.kritor.event.videoElement import io.kritor.event.videoElement
import io.kritor.event.voiceElement import io.kritor.event.voiceElement
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.ActionMsgException import moe.fuqiuluo.shamrock.helper.ActionMsgException
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
@ -40,6 +45,7 @@ import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.utils.PlatformUtils.QQ_9_0_8_VER import moe.fuqiuluo.shamrock.utils.PlatformUtils.QQ_9_0_8_VER
import qq.service.bdh.RichProtoSvc import qq.service.bdh.RichProtoSvc
import qq.service.contact.ContactHelper import qq.service.contact.ContactHelper
import kotlin.coroutines.resume
typealias NtMessages = ArrayList<MsgElement> typealias NtMessages = ArrayList<MsgElement>
typealias Convertor = suspend (MsgRecord, MsgElement) -> Result<Element> typealias Convertor = suspend (MsgRecord, MsgElement) -> Result<Element>
@ -308,8 +314,22 @@ private object MsgConvertor {
suspend fun convertReply(record: MsgRecord, element: MsgElement): Result<Element> { suspend fun convertReply(record: MsgRecord, element: MsgElement): Result<Element> {
val reply = element.replyElement val reply = element.replyElement
val elem = Element.newBuilder() val elem = Element.newBuilder()
elem.setReply(io.kritor.event.replyElement { elem.setReply(replyElement {
this.messageId = reply.replayMsgId val msgSeq = reply.replayMsgSeq
val contact = MessageHelper.generateContact(record)
val sourceRecords = withTimeoutOrNull(3000) {
suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records ->
it.resume(records)
}
}
}
if (sourceRecords.isNullOrEmpty()) {
LogCenter.log("无法查询到回复的消息ID: seq = $msgSeq", Level.WARN)
this.messageId = reply.replayMsgId
} else {
this.messageId = sourceRecords.first().msgId
}
}) })
return Result.success(elem.build()) return Result.success(elem.build())
} }