diff --git a/kritor b/kritor index f007631..e4aac65 160000 --- a/kritor +++ b/kritor @@ -1 +1 @@ -Subproject commit f007631c7e17fe6220055a404fdaaa6e7a24a7ef +Subproject commit e4aac653e14249cbb6f27567ffd463165f6deebe diff --git a/xposed/src/main/java/kritor/server/KritorServer.kt b/xposed/src/main/java/kritor/server/KritorServer.kt index f7fa4c1..9496286 100644 --- a/xposed/src/main/java/kritor/server/KritorServer.kt +++ b/xposed/src/main/java/kritor/server/KritorServer.kt @@ -24,6 +24,8 @@ class KritorServer( .addService(FriendService) .addService(GroupService) .addService(GroupFileService) + .addService(MessageService) + .addService(EventService) .build()!! fun start(block: Boolean = false) { diff --git a/xposed/src/main/java/kritor/service/EventService.kt b/xposed/src/main/java/kritor/service/EventService.kt new file mode 100644 index 0000000..58c1235 --- /dev/null +++ b/xposed/src/main/java/kritor/service/EventService.kt @@ -0,0 +1,29 @@ +package kritor.service + +import io.kritor.event.EventRequest +import io.kritor.event.EventServiceGrpcKt +import io.kritor.event.EventStructure +import io.kritor.event.EventType +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 { + return channelFlow { + when(request.type!!) { + EventType.CORE_EVENT -> TODO() + EventType.MESSAGE -> GlobalEventTransmitter.onMessageEvent { + send(eventStructure { + this.type = EventType.MESSAGE + this.message = it.second + }) + } + EventType.NOTICE -> TODO() + EventType.REQUEST -> TODO() + EventType.UNRECOGNIZED -> TODO() + } + } + } +} diff --git a/xposed/src/main/java/kritor/service/MessageService.kt b/xposed/src/main/java/kritor/service/MessageService.kt new file mode 100644 index 0000000..3776912 --- /dev/null +++ b/xposed/src/main/java/kritor/service/MessageService.kt @@ -0,0 +1,7 @@ +package kritor.service + +import io.kritor.message.MessageServiceGrpcKt + +internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImplBase() { + +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/GlobalEventTransmitter.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/GlobalEventTransmitter.kt new file mode 100644 index 0000000..cc72ed2 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/GlobalEventTransmitter.kt @@ -0,0 +1,473 @@ +package moe.fuqiuluo.shamrock.internals + +import com.tencent.qqnt.kernel.nativeinterface.MsgElement +import com.tencent.qqnt.kernel.nativeinterface.MsgRecord +import io.kritor.Scene +import io.kritor.contact +import io.kritor.event.MessageEvent +import io.kritor.event.messageEvent +import io.kritor.sender +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 + +internal object GlobalEventTransmitter: QQInterfaces() { + private val messageEventFlow by lazy { + MutableSharedFlow>() + } + //private val noticeEventFlow by lazy { + // MutableSharedFlow() + //} + //private val requestEventFlow by lazy { + // MutableSharedFlow() + //} + + //private suspend fun pushNotice(noticeEvent: NoticeEvent) = noticeEventFlow.emit(noticeEvent) + + //private suspend fun pushRequest(requestEvent: RequestEvent) = requestEventFlow.emit(requestEvent) + + private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message) + + object MessageTransmitter { + suspend fun transGroupMessage( + record: MsgRecord, + elements: ArrayList, + ): Boolean { + transMessageEvent(record, messageEvent { + this.time = record.msgTime.toInt() + this.scene = Scene.GROUP + this.messageId = record.msgId + this.messageSeq = record.msgSeq + this.contact = contact { + this.scene = scene + this.peer = record.peerUin.toString() + this.subPeer = record.peerUid + } + this.sender = sender { + this.uin = record.senderUin + this.uid = record.senderUid + this.nick = record.sendNickName + } + }) + return true + } + + suspend fun transPrivateMessage( + record: MsgRecord, + elements: ArrayList, + ): Boolean { + val botUin = app.longAccountUin + var nickName = record.sendNickName + + return true + } + + suspend fun transTempMessage( + record: MsgRecord, + elements: ArrayList, + groupCode: Long, + fromNick: String, + ): Boolean { + val botUin = app.longAccountUin + var nickName = record.sendNickName + + return true + } + + suspend fun transGuildMessage( + record: MsgRecord, + elements: ArrayList, + ): Boolean { + val botUin = app.longAccountUin + var nickName = record.sendNickName + + return true + } + } + + /* + /** + * 文件通知 通知器 + */ + object FileNoticeTransmitter { + /** + * 推送私聊文件事件 + */ + suspend fun transPrivateFileEvent( + msgTime: Long, + userId: Long, + fileId: String, + fileSubId: String, + fileName: String, + fileSize: Long, + 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 + ) + )) + return true + } + + /** + * 推送私聊文件事件 + */ + suspend fun transGroupFileEvent( + msgTime: Long, + userId: Long, + groupId: Long, + uuid: String, + fileName: String, + fileSize: Long, + 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 + ) + )) + return true + } + } + + /** + * 群聊通知 通知器 + */ + object GroupNoticeTransmitter { + suspend fun transGroupSign(time: Long, target: Long, action: String?, rankImg: String?, groupCode: Long): Boolean { + pushNotice(NoticeEvent( + time = time, + selfId = app.longAccountUin, + postType = PostType.Notice, + type = NoticeType.Notify, + subType = NoticeSubType.Sign, + userId = target, + groupId = groupCode, + target = target, + signDetail = SignDetail( + rankImg = rankImg, + action = action + ) + )) + return true + } + + suspend fun transGroupPoke(time: Long, operation: Long, target: Long, action: String?, suffix: String?, actionImg: String?, groupCode: Long): Boolean { + 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 + ) + )) + return true + } + + suspend fun transGroupMemberNumChanged( + time: Long, + target: Long, + targetUid: String, + groupCode: Long, + operator: Long, + operatorUid: String, + noticeType: NoticeType, + noticeSubType: NoticeSubType + ): 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 + )) + return true + } + + suspend fun transGroupAdminChanged( + msgTime: Long, + target: Long, + targetUid: String, + 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 + )) + return true + } + + suspend fun transGroupBan( + msgTime: Long, + subType: NoticeSubType, + operator: Long, + operatorUid: String, + target: Long, + targetUid: String, + 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 + )) + return true + } + + suspend fun transGroupMsgRecall( + time: Long, + operator: Long, + target: Long, + groupCode: Long, + msgHash: Int, + 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 + )) + return true + } + + suspend fun transCardChange( + time: Long, + targetId: Long, + oldCard: String, + 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 + } + + suspend fun transTitleChange( + time: Long, + targetId: Long, + 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 + )) + return true + } + + suspend fun transEssenceChange( + time: Long, + senderUin: Long, + operatorUin: Long, + msgId: Int, + groupId: Long, + subType: NoticeSubType + ): Boolean { + pushNotice(NoticeEvent( + time = time, + selfId = app.longAccountUin, + postType = PostType.Notice, + type = NoticeType.Essence, + senderId = senderUin, + groupId = groupId, + operatorId = operatorUin, + msgId = msgId, + subType = subType + )) + return true + } + } + + /** + * 私聊通知 通知器 + */ + 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 + ) + )) + 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 + )) + return true + } + + } + + /** + * 请求 通知器 + */ + 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 + )) + return true + } + + suspend fun transGroupApply( + time: Long, + applier: Long, + applierUid: String, + reason: String, + groupCode: Long, + flag: String, + subType: RequestSubType + ): 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 + )) + return true + } + }*/ + + suspend inline fun onMessageEvent(collector: FlowCollector>) { + messageEventFlow.collect { + GlobalScope.launch { + collector.emit(it) + } + } + } + + /* + suspend inline fun onNoticeEvent(collector: FlowCollector) { + noticeEventFlow.collect { + GlobalScope.launch { + collector.emit(it) + } + } + } + + suspend inline fun onRequestEvent(collector: FlowCollector) { + requestEventFlow.collect { + GlobalScope.launch { + collector.emit(it) + } + } + }*/ +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/FetchService.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/FetchService.kt index c8cf092..d84ee96 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/FetchService.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/FetchService.kt @@ -11,7 +11,7 @@ import moe.fuqiuluo.shamrock.tools.hookMethod import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter -import moe.fuqiuluo.shamrock.internals.NTServiceFetcher +import qq.service.internals.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.loader.NativeLoader import moe.fuqiuluo.symbols.XposedHook diff --git a/xposed/src/main/java/qq/service/contact/ContactHelper.kt b/xposed/src/main/java/qq/service/contact/ContactHelper.kt index 30240eb..a516495 100644 --- a/xposed/src/main/java/qq/service/contact/ContactHelper.kt +++ b/xposed/src/main/java/qq/service/contact/ContactHelper.kt @@ -10,7 +10,7 @@ import com.tencent.mobileqq.profilecard.observer.ProfileCardObserver import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import moe.fuqiuluo.shamrock.internals.NTServiceFetcher +import qq.service.internals.NTServiceFetcher import qq.service.QQInterfaces import kotlin.coroutines.resume diff --git a/xposed/src/main/java/qq/service/group/GroupHelper.kt b/xposed/src/main/java/qq/service/group/GroupHelper.kt index ca57424..e6dc318 100644 --- a/xposed/src/main/java/qq/service/group/GroupHelper.kt +++ b/xposed/src/main/java/qq/service/group/GroupHelper.kt @@ -19,7 +19,7 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withTimeoutOrNull import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter -import moe.fuqiuluo.shamrock.internals.NTServiceFetcher +import qq.service.internals.NTServiceFetcher import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty import moe.fuqiuluo.shamrock.tools.putBuf32Long import moe.fuqiuluo.shamrock.tools.slice diff --git a/xposed/src/main/java/qq/service/internals/AioListener.kt b/xposed/src/main/java/qq/service/internals/AioListener.kt index c14fe19..86e7a27 100644 --- a/xposed/src/main/java/qq/service/internals/AioListener.kt +++ b/xposed/src/main/java/qq/service/internals/AioListener.kt @@ -24,6 +24,7 @@ import com.tencent.qqnt.kernel.nativeinterface.ImportOldDbMsgNotifyInfo import com.tencent.qqnt.kernel.nativeinterface.InputStatusInfo import com.tencent.qqnt.kernel.nativeinterface.KickedInfo import com.tencent.qqnt.kernel.nativeinterface.MsgAbstract +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.kernel.nativeinterface.MsgSetting @@ -33,12 +34,74 @@ import com.tencent.qqnt.kernel.nativeinterface.SearchGroupFileResult import com.tencent.qqnt.kernel.nativeinterface.TabStatusInfo import com.tencent.qqnt.kernel.nativeinterface.TempChatInfo import com.tencent.qqnt.kernel.nativeinterface.UnreadCntInfo +import kotlinx.coroutines.GlobalScope +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.msg.MessageHelper object AioListener: IKernelMsgListener { - override fun onRecvMsg(arrayList: ArrayList?) { + override fun onRecvMsg(msgs: ArrayList) { + msgs.forEach { + GlobalScope.launch { + try { + onMsg(it) + } catch (e: Exception) { + LogCenter.log("OnMessage: " + e.stackTraceToString(), Level.WARN) + } + } + } + } + private suspend fun onMsg(record: MsgRecord) { + when (record.chatType) { + MsgConstant.KCHATTYPEGROUP -> { + LogCenter.log("群消息(group = ${record.peerName}(${record.peerUin}), uin = ${record.senderUin}, id = ${record.msgId})") + + if (!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(record, record.elements)) { + LogCenter.log("群消息推送失败 -> 推送目标可能不存在", Level.WARN) + } + } + + MsgConstant.KCHATTYPEC2C -> { + LogCenter.log("私聊消息(private = ${record.senderUin}, id = [${record.msgId} | ${record.msgSeq}])") + + if (!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage( + record, record.elements + ) + ) { + LogCenter.log("私聊消息推送失败 -> MessageTransmitter", Level.WARN) + } + } + + MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> { + var groupCode = 0L + var fromNick = "" + MessageHelper.getTempChatInfo(record.chatType, record.senderUid).onSuccess { + groupCode = it.groupCode.toLong() + fromNick = it.fromNick + } + + LogCenter.log("私聊临时消息(private = ${record.senderUin}, groupId=$groupCode)") + + if (!GlobalEventTransmitter.MessageTransmitter.transTempMessage(record, record.elements, groupCode, fromNick) + ) { + LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN) + } + } + + MsgConstant.KCHATTYPEGUILD -> { + LogCenter.log("频道消息(guildId = ${record.guildId}, sender = ${record.senderUid}, id = [${record.msgId}])") + if (!GlobalEventTransmitter.MessageTransmitter + .transGuildMessage(record, record.elements) + ) { + LogCenter.log("频道消息推送失败 -> MessageTransmitter", Level.WARN) + } + } + + else -> LogCenter.log("不支持PUSH事件: ${record.chatType}") + } } override fun onMsgRecall(chatType: Int, peerId: String, msgId: Long) { diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/NTServiceFetcher.kt b/xposed/src/main/java/qq/service/internals/NTServiceFetcher.kt similarity index 96% rename from xposed/src/main/java/moe/fuqiuluo/shamrock/internals/NTServiceFetcher.kt rename to xposed/src/main/java/qq/service/internals/NTServiceFetcher.kt index 8f03741..5c6f8c8 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/NTServiceFetcher.kt +++ b/xposed/src/main/java/qq/service/internals/NTServiceFetcher.kt @@ -1,4 +1,4 @@ -package moe.fuqiuluo.shamrock.internals +package qq.service.internals import com.tencent.qqnt.kernel.api.IKernelService import com.tencent.qqnt.kernel.api.impl.MsgService @@ -10,8 +10,6 @@ import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.tools.hookMethod import moe.fuqiuluo.shamrock.utils.PlatformUtils -import qq.service.internals.AioListener -import qq.service.internals.msgService internal object NTServiceFetcher { private lateinit var iKernelService: IKernelService diff --git a/xposed/src/main/java/qq/service/msg/MessageHelper.kt b/xposed/src/main/java/qq/service/msg/MessageHelper.kt new file mode 100644 index 0000000..29c5c28 --- /dev/null +++ b/xposed/src/main/java/qq/service/msg/MessageHelper.kt @@ -0,0 +1,31 @@ +package qq.service.msg + +import com.tencent.qqnt.kernel.api.IKernelService +import com.tencent.qqnt.kernel.nativeinterface.TempChatInfo +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withTimeoutOrNull +import moe.fuqiuluo.shamrock.helper.Level +import moe.fuqiuluo.shamrock.helper.LogCenter +import qq.service.QQInterfaces +import qq.service.internals.msgService +import kotlin.coroutines.resume + +internal object MessageHelper: QQInterfaces() { + suspend fun getTempChatInfo(chatType: Int, uid: String): Result { + val msgService = app.getRuntimeService(IKernelService::class.java, "all").msgService + ?: return Result.failure(Exception("获取消息服务失败")) + val info: TempChatInfo = withTimeoutOrNull(5000) { + suspendCancellableCoroutine { + msgService.getTempChatInfo(chatType, uid) { code, msg, tempChatInfo -> + if (code == 0) { + it.resume(tempChatInfo) + } else { + LogCenter.log("获取临时会话信息失败: $code:$msg", Level.ERROR) + it.resume(null) + } + } + } + } ?: return Result.failure(Exception("获取临时会话信息失败")) + return Result.success(info) + } +} \ No newline at end of file