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

View File

@ -3,11 +3,15 @@ package qq.service.friend
import com.tencent.mobileqq.data.Friends
import com.tencent.mobileqq.friend.api.IFriendDataService
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.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import moe.fuqiuluo.shamrock.tools.slice
import qq.service.QQInterfaces
import tencent.mobileim.structmsg.structmsg
import kotlin.coroutines.resume
internal object FriendHelper: QQInterfaces() {
@ -21,6 +25,69 @@ internal object FriendHelper: QQInterfaces() {
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 {
val service = app.getRuntimeService(IFriendHandlerService::class.java, "all")
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.oidb_sso
import tencent.im.troop.honor.troop_honor
import tencent.mobileim.structmsg.structmsg
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.nio.ByteBuffer
@ -217,6 +218,106 @@ internal object GroupHelper: QQInterfaces() {
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) {
val localMemberInfo = getTroopMemberInfoByUin(groupId, userId, true).getOrThrow()
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.LogCenter
import moe.fuqiuluo.shamrock.internals.GlobalEventTransmitter
import qq.service.bdh.RichProtoSvc
import qq.service.kernel.SimpleKernelMsgListener
import qq.service.msg.MessageHelper
@ -29,6 +30,8 @@ object AioListener: SimpleKernelMsgListener() {
private suspend fun onMsg(record: MsgRecord) {
when (record.chatType) {
MsgConstant.KCHATTYPEGROUP -> {
if (record.senderUin == 0L) return
LogCenter.log("群消息(group = ${record.peerName}(${record.peerUin}), uin = ${record.senderUin}, id = ${record.msgId})")
if (!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(record, record.elements)) {
@ -75,4 +78,63 @@ object AioListener: SimpleKernelMsgListener() {
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,9 +52,13 @@ internal object MSFHandler {
fun onPush(fromServiceMsg: FromServiceMsg) {
val cmd = fromServiceMsg.serviceCmd
if (cmd == "trpc.msg.olpush.OlPushService.MsgPush") {
PrimitiveListener.onPush(fromServiceMsg)
} else {
val push = mPushHandlers[cmd]
push?.invoke(fromServiceMsg)
}
}
fun onResp(toServiceMsg: ToServiceMsg, fromServiceMsg: FromServiceMsg) {
runCatching {

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.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.kernel.nativeinterface.TempChatInfo
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
@ -32,12 +33,28 @@ internal object MessageHelper: QQInterfaces() {
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 {
val peerId = when (chatType) {
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
ContactHelper.getUidByUinAsync(id.toLong())
if (id.startsWith("u_")) id
else ContactHelper.getUidByUinAsync(id.toLong())
}
else -> id
}
return if (chatType == MsgConstant.KCHATTYPEGUILD) {

View File

@ -1,8 +1,10 @@
package qq.service.msg
import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.event.AtElement
import io.kritor.event.Element
import io.kritor.event.ElementKt
@ -21,10 +23,13 @@ import io.kritor.event.imageElement
import io.kritor.event.jsonElement
import io.kritor.event.locationElement
import io.kritor.event.pokeElement
import io.kritor.event.replyElement
import io.kritor.event.rpsElement
import io.kritor.event.textElement
import io.kritor.event.videoElement
import io.kritor.event.voiceElement
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.ActionMsgException
import moe.fuqiuluo.shamrock.helper.Level
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 qq.service.bdh.RichProtoSvc
import qq.service.contact.ContactHelper
import kotlin.coroutines.resume
typealias NtMessages = ArrayList<MsgElement>
typealias Convertor = suspend (MsgRecord, MsgElement) -> Result<Element>
@ -308,8 +314,22 @@ private object MsgConvertor {
suspend fun convertReply(record: MsgRecord, element: MsgElement): Result<Element> {
val reply = element.replyElement
val elem = Element.newBuilder()
elem.setReply(io.kritor.event.replyElement {
elem.setReply(replyElement {
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())
}