Redesign notice handling and introduce NoticeProcessorPipeline part 2

Do not broadcast StrangerAddedEvent if added twice, fix stranger scope not closed

Do not add new instance if there is already one

Close and remove corresponding stranger instance if there is new friend.
This commit is contained in:
Him188 2021-06-27 17:26:09 +08:00
parent e1ffaa5410
commit 56cbe2d8a2
33 changed files with 918 additions and 742 deletions

View File

@ -28,34 +28,34 @@ import java.util.concurrent.atomic.AtomicBoolean
/**
* 好友昵称改变事件. 目前仅支持解析 (来自 PC 端的修改).
*/
public data class FriendRemarkChangeEvent internal constructor(
public data class FriendRemarkChangeEvent @MiraiInternalApi public constructor(
public override val friend: Friend,
public val oldRemark: String,
public val newRemark: String
public val newRemark: String,
) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
/**
* 成功添加了一个新好友的事件
*/
public data class FriendAddEvent @MiraiInternalApi constructor(
public data class FriendAddEvent @MiraiInternalApi public constructor(
/**
* 新好友. 已经添加到 [Bot.friends]
*/
public override val friend: Friend
public override val friend: Friend,
) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
/**
* 好友已被删除或主动删除的事件.
*/
public data class FriendDeleteEvent internal constructor(
public override val friend: Friend
public data class FriendDeleteEvent @MiraiInternalApi public constructor(
public override val friend: Friend,
) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
/**
* 一个账号请求添加机器人为好友的事件
*/
@Suppress("DEPRECATION")
public data class NewFriendRequestEvent internal constructor(
public data class NewFriendRequestEvent @MiraiInternalApi public constructor(
public override val bot: Bot,
/**
* 事件唯一识别号
@ -76,7 +76,7 @@ public data class NewFriendRequestEvent internal constructor(
/**
* 群名片或好友昵称
*/
public val fromNick: String
public val fromNick: String,
) : BotEvent, Packet, AbstractEvent(), FriendInfoChangeEvent {
@JvmField
internal val responded: AtomicBoolean = AtomicBoolean(false)
@ -97,25 +97,25 @@ public data class NewFriendRequestEvent internal constructor(
/**
* [Friend] 头像被修改. 在此事件广播前就已经修改完毕.
*/
public data class FriendAvatarChangedEvent internal constructor(
public override val friend: Friend
public data class FriendAvatarChangedEvent @MiraiInternalApi public constructor(
public override val friend: Friend,
) : FriendEvent, Packet, AbstractEvent()
/**
* [Friend] 昵称改变事件, 在此事件广播时好友已经完成改名
* @see BotNickChangedEvent
*/
public data class FriendNickChangedEvent internal constructor(
public data class FriendNickChangedEvent @MiraiInternalApi public constructor(
public override val friend: Friend,
public val from: String,
public val to: String
public val to: String,
) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
/**
* 好友输入状态改变的事件当开始输入文字退出聊天窗口或清空输入框时会触发此事件
*/
public data class FriendInputStatusChangedEvent internal constructor(
public data class FriendInputStatusChangedEvent @MiraiInternalApi public constructor(
public override val friend: Friend,
public val inputting: Boolean
public val inputting: Boolean,
) : FriendEvent, Packet, AbstractEvent()
) : FriendEvent, Packet, AbstractEvent()

View File

@ -24,7 +24,7 @@ public data class StrangerAddEvent @MiraiInternalApi public constructor(
/**
* 新的陌生人. 已经添加到 [Bot.strangers]
*/
public override val stranger: Stranger
public override val stranger: Stranger,
) : StrangerEvent, Packet, AbstractEvent()
@ -33,19 +33,16 @@ public data class StrangerAddEvent @MiraiInternalApi public constructor(
*
*/
public sealed class StrangerRelationChangeEvent(
public override val stranger: Stranger
public override val stranger: Stranger,
) : StrangerEvent, Packet, AbstractEvent() {
/**
* 主动删除陌生人或陌生人被删除的事件
*
* 除主动删除外此事件为惰性广播无法确保实时性
* 目前被动删除仅会在陌生人二次添加时才会进行广播
* 主动删除陌生人或陌生人被删除的事件, 不一定能接收到被动删除的事件
*/
public class Deleted(
/**
* 被删除的陌生人
*/
stranger: Stranger
stranger: Stranger,
) : StrangerRelationChangeEvent(stranger)
/**
@ -63,7 +60,7 @@ public sealed class StrangerRelationChangeEvent(
*
* 已经添加到Bot的好友列表中
*/
public val friend: Friend
public val friend: Friend,
) : StrangerRelationChangeEvent(stranger)
}

View File

@ -95,10 +95,13 @@ internal abstract class AbstractBot constructor(
final override val groups: ContactList<GroupImpl> = ContactList()
final override val strangers: ContactList<StrangerImpl> = ContactList()
final override val asFriend: FriendImpl by lazy { Mirai.newFriend(this, FriendInfoImpl(uin, nick, "")).cast() }
final override val asFriend: FriendImpl by lazy {
Mirai.newFriend(this, FriendInfoImpl(uin, "", "")).cast()
} // nick is initialized later on login
final override val asStranger: StrangerImpl by lazy {
Mirai.newStranger(this, StrangerInfoImpl(bot.id, bot.nick)).cast()
}
final override var nick: String by asFriend.info::nick
override fun close(cause: Throwable?) {
if (!this.isActive) return

View File

@ -14,6 +14,8 @@ import io.ktor.client.engine.okhttp.*
import io.ktor.client.features.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.http.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.io.core.discardExact
@ -30,7 +32,9 @@ import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl.Companion.impl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl.Companion.impl
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
import net.mamoe.mirai.internal.network.components.EventDispatcher

View File

@ -230,12 +230,6 @@ internal open class QQAndroidBot constructor(
get() = client.wLoginSigInfo.sKey.data
.fold(5381) { acc: Int, b: Byte -> acc + acc.shl(5) + b.toInt() }
.and(Int.MAX_VALUE)
///////////////////////////////////////////////////////////////////////////
// contacts
///////////////////////////////////////////////////////////////////////////
override lateinit var nick: String
}
internal fun QQAndroidBot.getGroupByUinOrFail(uin: Long) =

View File

@ -10,6 +10,9 @@
package net.mamoe.mirai.internal.contact
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.Stranger
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.data.UserInfo
import net.mamoe.mirai.event.broadcast
@ -28,17 +31,32 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x352
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
import net.mamoe.mirai.internal.utils.AtomicIntSeq
import net.mamoe.mirai.internal.utils.C2CPkgMsgParsingCache
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.isContentEmpty
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.*
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
internal val User.info: UserInfo? get() = this.castOrNull<AbstractUser>()?.info
@Suppress("NOTHING_TO_INLINE")
internal inline fun User.impl(): AbstractUser {
contract { returns() implies (this@impl is AbstractUser) }
check(this is AbstractUser)
return this
}
internal val User.correspondingMessageSourceKind
get() = when (this) {
is Friend -> MessageSourceKind.FRIEND
is Member -> MessageSourceKind.TEMP
is Stranger -> MessageSourceKind.STRANGER
else -> error("Unknown user: ${this::class.qualifiedName}")
}
internal abstract class AbstractUser(
bot: QQAndroidBot,
parentCoroutineContext: CoroutineContext,
@ -49,6 +67,9 @@ internal abstract class AbstractUser(
final override var nick: String = userInfo.nick
final override val remark: String = userInfo.remark
val messageSeq = AtomicIntSeq.forMessageSeq()
val fragmentedMessageMerger = C2CPkgMsgParsingCache()
open val info: UserInfo = userInfo
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@ -58,7 +79,8 @@ internal abstract class AbstractUser(
}
val resp = bot.network.run {
LongConn.OffPicUp(
bot.client, Cmd0x352.TryUpImgReq(
bot.client,
Cmd0x352.TryUpImgReq(
buType = 1,
srcUin = bot.id,
dstUin = this@AbstractUser.id,
@ -66,8 +88,8 @@ internal abstract class AbstractUser(
fileSize = resource.size,
fileName = resource.md5.toUHexString("") + "." + resource.formatName,
imgOriginal = true,
buildVer = bot.client.buildVer
)
buildVer = bot.client.buildVer,
),
).sendAndExpect<LongConn.OffPicUp.Response>()
}

View File

@ -14,12 +14,9 @@
package net.mamoe.mirai.internal.contact
import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.data.FriendInfo
import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
import net.mamoe.mirai.internal.QQAndroidBot
@ -31,7 +28,6 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.audioCodec
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.utils.C2CPkgMsgParsingCache
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.MessageReceipt
@ -53,9 +49,9 @@ internal fun net.mamoe.mirai.internal.network.protocol.data.jce.FriendInfo.toMir
)
@OptIn(ExperimentalContracts::class)
internal inline fun Friend.checkIsFriendImpl(): FriendImpl {
internal inline fun Friend.impl(): FriendImpl {
contract {
returns() implies (this@checkIsFriendImpl is FriendImpl)
returns() implies (this@impl is FriendImpl)
}
check(this is FriendImpl) { "A Friend instance is not instance of FriendImpl. Your instance: ${this::class.qualifiedName}" }
return this
@ -64,11 +60,8 @@ internal inline fun Friend.checkIsFriendImpl(): FriendImpl {
internal class FriendImpl(
bot: QQAndroidBot,
parentCoroutineContext: CoroutineContext,
internal val friendInfo: FriendInfo,
) : Friend, AbstractUser(bot, parentCoroutineContext, friendInfo) {
@Suppress("unused") // bug
val lastMessageSequence: AtomicInt = atomic(-1)
val friendPkgMsgParsingCache = C2CPkgMsgParsingCache()
override val info: FriendInfoImpl,
) : Friend, AbstractUser(bot, parentCoroutineContext, info) {
override suspend fun delete() {
check(bot.friends[this.id] != null) {
"Friend ${this.id} had already been deleted"

View File

@ -303,7 +303,8 @@ internal fun Group.newMember(memberInfo: MemberInfo): Member {
)
}
internal fun Group.addNewNormalMember(memberInfo: MemberInfo): NormalMemberImpl {
internal fun Group.addNewNormalMember(memberInfo: MemberInfo): NormalMemberImpl? {
if (members.contains(memberInfo.uin)) return null
return newNormalMember(memberInfo).also {
members.delegate.add(it)
}

View File

@ -11,8 +11,6 @@
package net.mamoe.mirai.internal.contact
import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
@ -36,12 +34,9 @@ import kotlin.coroutines.CoroutineContext
@Suppress("MemberVisibilityCanBePrivate")
internal class NormalMemberImpl constructor(
group: GroupImpl,
coroutineContext: CoroutineContext,
parentCoroutineContext: CoroutineContext,
memberInfo: MemberInfo,
) : NormalMember, AbstractMember(group, coroutineContext, memberInfo) {
@Suppress("unused") // false positive
val lastMessageSequence: AtomicInt = atomic(-1)
) : NormalMember, AbstractMember(group, parentCoroutineContext, memberInfo) {
override val joinTimestamp: Int get() = info.joinTimestamp
override val lastSpeakTimestamp: Int get() = info.lastSpeakTimestamp
@ -57,7 +52,7 @@ internal class NormalMemberImpl constructor(
?: handler.sendMessageImpl<NormalMember>(
message = message,
preSendEventConstructor = ::GroupTempMessagePreSendEvent,
postSendEventConstructor = ::GroupTempMessagePostSendEvent.cast()
postSendEventConstructor = ::GroupTempMessagePostSendEvent.cast(),
)
}
@ -102,7 +97,7 @@ internal class NormalMemberImpl constructor(
TroopManagement.EditGroupNametag(
bot.client,
this@NormalMemberImpl,
newValue
newValue,
).sendWithoutExpect()
}
MemberCardChangeEvent(oldValue, newValue, this@NormalMemberImpl).broadcast()
@ -122,7 +117,7 @@ internal class NormalMemberImpl constructor(
TroopManagement.EditSpecialTitle(
bot.client,
this@NormalMemberImpl,
newValue
newValue,
).sendWithoutExpect()
}
MemberSpecialTitleChangeEvent(oldValue, newValue, this@NormalMemberImpl, null).broadcast()
@ -143,7 +138,7 @@ internal class NormalMemberImpl constructor(
client = bot.client,
groupCode = group.id,
memberUin = this@NormalMemberImpl.id,
timeInSecond = durationSeconds
timeInSecond = durationSeconds,
).sendAndExpect<TroopManagement.Mute.Response>()
}
@ -159,7 +154,7 @@ internal class NormalMemberImpl constructor(
client = bot.client,
groupCode = group.id,
memberUin = this@NormalMemberImpl.id,
timeInSecond = 0
timeInSecond = 0,
).sendAndExpect<TroopManagement.Mute.Response>()
}
@ -210,7 +205,7 @@ internal class NormalMemberImpl constructor(
val resp: TroopManagement.ModifyAdmin.Response = TroopManagement.ModifyAdmin(
client = bot.client,
member = this@NormalMemberImpl,
operation = operation
operation = operation,
).sendAndExpect()
check(resp.success) {
@ -227,7 +222,7 @@ internal class NormalMemberImpl constructor(
internal fun Member.checkBotPermissionHighest(operationName: String) {
check(group.botPermission == MemberPermission.OWNER) {
throw PermissionDeniedException(
"`$operationName` operation requires the OWNER permission, while bot has ${group.botPermission}"
"`$operationName` operation requires the OWNER permission, while bot has ${group.botPermission}",
)
}
}
@ -236,7 +231,7 @@ internal fun Member.checkBotPermissionHigherThanThis(operationName: String) {
check(group.botPermission > this.permission) {
throw PermissionDeniedException(
"`$operationName` operation requires a higher permission, while " +
"${group.botPermission} < ${this.permission}"
"${group.botPermission} < ${this.permission}",
)
}
}

View File

@ -17,8 +17,6 @@
package net.mamoe.mirai.internal.contact
import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.contact.Stranger
import net.mamoe.mirai.contact.User
@ -39,10 +37,8 @@ import kotlin.coroutines.CoroutineContext
@OptIn(ExperimentalContracts::class)
internal inline fun Stranger.checkIsImpl(): StrangerImpl {
contract {
returns() implies (this@checkIsImpl is StrangerImpl)
}
internal inline fun Stranger.impl(): StrangerImpl {
contract { returns() implies (this@impl is StrangerImpl) }
check(this is StrangerImpl) { "A Stranger instance is not instance of StrangerImpl. Your instance: ${this::class.qualifiedName}" }
return this
}
@ -50,10 +46,8 @@ internal inline fun Stranger.checkIsImpl(): StrangerImpl {
internal class StrangerImpl(
bot: QQAndroidBot,
parentCoroutineContext: CoroutineContext,
internal val strangerInfo: StrangerInfo,
) : Stranger, AbstractUser(bot, parentCoroutineContext, strangerInfo) {
@Suppress("unused") // bug
val lastMessageSequence: AtomicInt = atomic(-1)
override val info: StrangerInfo,
) : Stranger, AbstractUser(bot, parentCoroutineContext, info) {
override suspend fun delete() {
check(bot.strangers[this.id] != null) {
"Stranger ${this.id} had already been deleted"
@ -66,7 +60,7 @@ internal class StrangerImpl(
}
}
private val handler by lazy { StrangerSendMessageHandler(this) }
private val handler: StrangerSendMessageHandler by lazy { StrangerSendMessageHandler(this) }
@Suppress("DuplicatedCode")
override suspend fun sendMessage(message: Message): MessageReceipt<Stranger> {

View File

@ -18,4 +18,8 @@ internal data class FriendInfoImpl(
override val uin: Long,
override var nick: String,
override var remark: String,
) : FriendInfo
) : FriendInfo {
companion object {
fun FriendInfo.impl() = if (this is FriendInfoImpl) this else FriendInfoImpl(uin, nick, remark)
}
}

View File

@ -20,4 +20,8 @@ internal class StrangerInfoImpl(
override val nick: String,
override val fromGroup: Long = 0,
override val remark: String = "",
) : StrangerInfo
) : StrangerInfo {
companion object {
fun StrangerInfo.impl() = if (this is StrangerInfoImpl) this else StrangerInfoImpl(uin, nick, fromGroup, remark)
}
}

View File

@ -24,6 +24,7 @@ import net.mamoe.mirai.internal.network.components.SsoSession
import net.mamoe.mirai.internal.network.protocol.SyncingCacheList
import net.mamoe.mirai.internal.network.protocol.data.jce.FileStoragePushFSSvcList
import net.mamoe.mirai.internal.network.protocol.packet.Tlv
import net.mamoe.mirai.internal.utils.AtomicIntSeq
import net.mamoe.mirai.internal.utils.MiraiProtocolInternal
import net.mamoe.mirai.internal.utils.NetworkType
import net.mamoe.mirai.utils.*
@ -124,13 +125,10 @@ internal open class QQAndroidClient(
internal var strangerSeq: Int = 0
// TODO: 2021/4/14 investigate whether they can be minimized
private val friendSeq: AtomicInt = atomic(getRandomUnsignedInt())
internal fun getFriendSeq(): Int = friendSeq.value
internal fun nextFriendSeq(): Int = friendSeq.incrementAndGet()
internal fun setFriendSeq(compare: Int, id: Int): Boolean = friendSeq.compareAndSet(compare, id % 65535)
/**
* for send
*/
val sendFriendMessageSeq = AtomicIntSeq.forPrivateSync()
internal val groupConfig: GroupConfig = GroupConfig()

View File

@ -19,10 +19,8 @@ import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.sync.withPermit
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.data.FriendInfo
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.FriendImpl
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.StrangerImpl
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
@ -34,6 +32,7 @@ import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.isValid
import net.mamoe.mirai.internal.network.notice.NewContactSupport
import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
import net.mamoe.mirai.internal.network.protocol.data.jce.SvcRespRegister
import net.mamoe.mirai.internal.network.protocol.data.jce.isValid
@ -76,7 +75,7 @@ internal class ContactUpdaterImpl(
val bot: QQAndroidBot, // not good
val components: ComponentStorage,
private val logger: MiraiLogger,
) : ContactUpdater {
) : ContactUpdater, NewContactSupport {
override val otherClientsLock: Mutex = Mutex()
override val groupListModifyLock: Mutex = Mutex()
private val cacheService get() = components[ContactCacheService]
@ -176,16 +175,13 @@ internal class ContactUpdaterImpl(
}
for (friendInfoImpl in list) {
addFriendToBot(friendInfoImpl)
bot.addNewFriendAndRemoveStranger(friendInfoImpl)
}
initFriendOk = true
}
private fun addFriendToBot(it: FriendInfo) =
bot.friends.delegate.add(FriendImpl(bot, bot.coroutineContext, it))
private suspend fun addGroupToBot(stTroopNum: StTroopNum) = stTroopNum.run {
suspend fun refreshGroupMemberList(): Sequence<MemberInfo> {
return Mirai.getRawGroupMemberList(
@ -214,11 +210,11 @@ internal class ContactUpdaterImpl(
bot.groups.delegate.add(
GroupImpl(
bot = bot,
coroutineContext = bot.coroutineContext,
parentCoroutineContext = bot.coroutineContext,
id = groupCode,
groupInfo = GroupInfoImpl(stTroopNum),
members = members
)
members = members,
),
)
}

View File

@ -17,6 +17,7 @@ import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.notice.decoders.MsgType0x2DC
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgInfo
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush
import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo
@ -32,6 +33,19 @@ import kotlin.concurrent.read
import kotlin.concurrent.write
import kotlin.reflect.KClass
/**
* Centralized processor pipeline for [MessageSvcPbGetMsg] and [OnlinePushPbPushTransMsg]
*/
internal interface NoticeProcessorPipeline {
fun registerProcessor(processor: NoticeProcessor)
suspend fun process(bot: QQAndroidBot, data: Any?, attributes: TypeSafeMap = TypeSafeMap()): Collection<Packet>
companion object : ComponentKey<NoticeProcessorPipeline> {
val ComponentStorage.noticeProcessorPipeline get() = get(NoticeProcessorPipeline)
}
}
internal interface PipelineContext {
val bot: QQAndroidBot
val attributes: TypeSafeMap
@ -40,7 +54,7 @@ internal interface PipelineContext {
val isConsumed: Boolean
/**
* Mark the input as consumed so that there will not be warnings like 'Unknown type xxx'
* Mark the input as consumed so that there will not be warnings like 'Unknown type xxx'. This will not stop the pipeline.
*
* If this is executed, make sure you provided all information important for debugging.
*
@ -79,19 +93,6 @@ internal interface PipelineContext {
internal inline val PipelineContext.context get() = this
/**
* Centralized processor pipeline for [MessageSvcPbGetMsg] and [OnlinePushPbPushTransMsg]
*/
internal interface NoticeProcessorPipeline {
fun registerProcessor(processor: NoticeProcessor)
suspend fun process(bot: QQAndroidBot, data: Any?, attributes: TypeSafeMap = TypeSafeMap()): Collection<Packet>
companion object : ComponentKey<NoticeProcessorPipeline> {
val ComponentStorage.noticeProcessorPipeline get() = get(NoticeProcessorPipeline)
}
}
internal class NoticeProcessorPipelineImpl(
private val logger: MiraiLogger,
) : NoticeProcessorPipeline {
@ -142,6 +143,10 @@ internal class NoticeProcessorPipelineImpl(
}
///////////////////////////////////////////////////////////////////////////
// NoticeProcessor
///////////////////////////////////////////////////////////////////////////
/**
* A processor handling some specific type of message.
*/
@ -157,11 +162,11 @@ internal abstract class SimpleNoticeProcessor<T : Any>(
final override suspend fun process(context: PipelineContext, data: Any?) {
if (type.isInstance(data)) {
context.process0(data.uncheckedCast())
context.processImpl(data.uncheckedCast())
}
}
protected abstract suspend fun PipelineContext.process0(data: T)
protected abstract suspend fun PipelineContext.processImpl(data: T)
companion object {
@JvmStatic
@ -170,11 +175,11 @@ internal abstract class SimpleNoticeProcessor<T : Any>(
}
internal abstract class MsgCommonMsgProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type()) {
abstract override suspend fun PipelineContext.process0(data: MsgComm.Msg)
abstract override suspend fun PipelineContext.processImpl(data: MsgComm.Msg)
}
internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
final override suspend fun PipelineContext.process0(data: Any) {
final override suspend fun PipelineContext.processImpl(data: Any) {
when (data) {
is MsgInfo -> processImpl(data)
is PbMsgInfo -> processImpl(data)
@ -183,14 +188,16 @@ internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
is MsgType0x210 -> processImpl(data)
is MsgType0x2DC -> processImpl(data)
is Structmsg.StructMsg -> processImpl(data)
is RequestPushStatus -> processImpl(data)
}
}
protected open suspend fun PipelineContext.processImpl(data: MsgInfo) {}
protected open suspend fun PipelineContext.processImpl(data: MsgType0x210) {}
protected open suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {}
protected open suspend fun PipelineContext.processImpl(data: MsgType0x210) {} // 528
protected open suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {} // 732
protected open suspend fun PipelineContext.processImpl(data: PbMsgInfo) {}
protected open suspend fun PipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {}
protected open suspend fun PipelineContext.processImpl(data: MsgComm.Msg) {}
protected open suspend fun PipelineContext.processImpl(data: Structmsg.StructMsg) {}
protected open suspend fun PipelineContext.processImpl(data: RequestPushStatus) {}
}

View File

@ -9,8 +9,6 @@
package net.mamoe.mirai.internal.network.notice
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUByte
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.components.PipelineContext
import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
@ -18,27 +16,12 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMs
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.utils.read
internal class BinaryMessageProcessor : SimpleNoticeProcessor<PbMsgInfo>(type()), GroupEventProcessorContext {
override suspend fun PipelineContext.process0(data: PbMsgInfo) {
internal class BinaryMessageProcessor : SimpleNoticeProcessor<PbMsgInfo>(type()), NewContactSupport {
override suspend fun PipelineContext.processImpl(data: PbMsgInfo) {
data.msgData.read<Unit> {
when (data.msgType) {
44 -> {
// 3D C4 33 DD 01 FF CD 76 F4 03 C3 7E 2E 34
// 群转让
// start with 3D C4 33 DD 01 FF
// 3D C4 33 DD 01 FF C3 7E 2E 34 CD 76 F4 03
// 权限变更
// 3D C4 33 DD 01 00/01 .....
// 3D C4 33 DD 01 01 C3 7E 2E 34 01
this.discardExact(5)
when (val mode = readUByte().toInt()) {
0xFF -> {
TODO("removed")
}
else -> {
TODO("removed")
}
}
TODO("removed")
}
34 -> {
TODO("removed")
@ -46,50 +29,7 @@ internal class BinaryMessageProcessor : SimpleNoticeProcessor<PbMsgInfo>(type())
else -> {
when {
data.msgType == 529 && data.msgSubtype == 9 -> {
/*
PbMsgInfo#1773430973 {
fromUin=0x0000000026BA1173(649728371)
generalFlag=0x00000001(1)
msgData=0A 07 70 72 69 6E 74 65 72 10 02 1A CD 02 0A 1F 53 61 6D 73 75 6E 67 20 4D 4C 2D 31 38 36 30 20 53 65 72 69 65 73 20 28 55 53 42 30 30 31 29 0A 16 4F 6E 65 4E 6F 74 65 20 66 6F 72 20 57 69 6E 64 6F 77 73 20 31 30 0A 19 50 68 61 6E 74 6F 6D 20 50 72 69 6E 74 20 74 6F 20 45 76 65 72 6E 6F 74 65 0A 11 4F 6E 65 4E 6F 74 65 20 28 44 65 73 6B 74 6F 70 29 0A 1D 4D 69 63 72 6F 73 6F 66 74 20 58 50 53 20 44 6F 63 75 6D 65 6E 74 20 57 72 69 74 65 72 0A 16 4D 69 63 72 6F 73 6F 66 74 20 50 72 69 6E 74 20 74 6F 20 50 44 46 0A 15 46 6F 78 69 74 20 50 68 61 6E 74 6F 6D 20 50 72 69 6E 74 65 72 0A 03 46 61 78 32 09 0A 03 6A 70 67 10 01 18 00 32 0A 0A 04 6A 70 65 67 10 01 18 00 32 09 0A 03 70 6E 67 10 01 18 00 32 09 0A 03 67 69 66 10 01 18 00 32 09 0A 03 62 6D 70 10 01 18 00 32 09 0A 03 64 6F 63 10 01 18 01 32 0A 0A 04 64 6F 63 78 10 01 18 01 32 09 0A 03 74 78 74 10 00 18 00 32 09 0A 03 70 64 66 10 01 18 01 32 09 0A 03 70 70 74 10 01 18 01 32 0A 0A 04 70 70 74 78 10 01 18 01 32 09 0A 03 78 6C 73 10 01 18 01 32 0A 0A 04 78 6C 73 78 10 01 18 01
msgSeq=0x00001AFF(6911)
msgSubtype=0x00000009(9)
msgTime=0x5FDF21A3(1608458659)
msgType=0x00000211(529)
msgUid=0x010000005FDEE04C(72057595646369868)
realMsgTime=0x5FDF21A3(1608458659)
svrIp=0x3E689409(1047041033)
toUin=0x0000000026BA1173(649728371)
}
*/
/*
*
printer
Samsung ML-1860 Series (USB001)
OneNote for Windows 10
Phantom Print to Evernote
OneNote (Desktop)
Microsoft XPS Document Writer
Microsoft Print to PDF
Foxit Phantom Printer
Fax2
jpg2
jpeg2
png2
gif2
bmp2
doc2
docx2
txt2
pdf2
ppt2
pptx2
xls2
xlsx*/
return
TODO("removed")
}
}
throw contextualBugReportException(

View File

@ -0,0 +1,206 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUByte
import kotlinx.io.core.readUShort
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.impl
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.toMiraiFriendInfo
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
import net.mamoe.mirai.internal.network.components.PipelineContext
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
import net.mamoe.mirai.internal.network.protocol.data.proto.FrdSysMsg
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x115.SubMsgType0x115
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27.SubMsgType0x27.*
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x44.Submsgtype0x44
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0xb3.SubMsgType0xb3
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList.GetFriendGroupList
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.utils.*
/**
* All [FriendEvent] except [FriendMessageEvent]
*
* @see FriendInputStatusChangedEvent
* @see FriendAddEvent
* @see StrangerRelationChangeEvent.Friended
*/
internal class FriendNoticeProcessor(
private val logger: MiraiLogger,
) : MixedNoticeProcessor(), NewContactSupport {
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
if (msgHead.msgType != 191) return
var fromGroup = 0L
var pbNick = ""
msgBody.msgContent.read {
readUByte() // version
discardExact(readUByte().toInt()) //skip
readUShort() //source id
readUShort() //SourceSubID
discardExact(readUShort().toLong()) //skip size
if (readUShort().toInt() != 0) { //hasExtraInfo
discardExact(readUShort().toInt()) //mail address info, skip
}
discardExact(4 + readUShort().toInt()) //skip
for (i in 1..readUByte().toInt()) { //pb size
val type = readUShort().toInt()
val pbArray = ByteArray(readUShort().toInt() and 0xFF)
readAvailable(pbArray)
when (type) {
1000 -> pbArray.loadAs(FrdSysMsg.GroupInfo.serializer()).let { fromGroup = it.groupUin }
1002 -> pbArray.loadAs(FrdSysMsg.FriendMiscInfo.serializer())
.let { pbNick = it.fromuinNick }
else -> {
} //ignore
}
}
}
msgHead.context {
if (fromUin == authUin) {
logger.error { "Could not determine uin since `fromUin` = `authUin` = $fromUin" }
return
}
val id = fromUin or authUin // 对方 qq
if (bot.getStranger(id) != null) return
val nick = fromNick.ifEmpty { authNick }.ifEmpty { pbNick }
collect(StrangerAddEvent(bot.addNewStranger(StrangerInfoImpl(id, nick, fromGroup)) ?: return))
}
}
override suspend fun PipelineContext.processImpl(data: MsgType0x210) = data.context {
when (data.uSubMsgType) {
0xB3L -> {
// 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07
val body: SubMsgType0xb3.MsgBody = vProtobuf.loadAs(SubMsgType0xb3.MsgBody.serializer())
handleFriendAddedB(data, body)
}
0x44L -> {
val body = vProtobuf.loadAs(Submsgtype0x44.MsgBody.serializer())
handleFriendAddedA(body)
}
0x27L -> {
val body = vProtobuf.loadAs(SubMsgType0x27MsgBody.serializer())
for (msgModInfo in body.msgModInfos) {
when {
msgModInfo.msgModFriendRemark != null -> handleRemarkChanged(msgModInfo.msgModFriendRemark)
msgModInfo.msgDelFriend != null -> handleFriendDeleted(msgModInfo.msgDelFriend)
msgModInfo.msgModCustomFace != null -> handleAvatarChanged(msgModInfo.msgModCustomFace)
msgModInfo.msgModProfile != null -> handleProfileChanged(msgModInfo.msgModProfile)
}
}
}
0x115L -> {
val body = vProtobuf.loadAs(SubMsgType0x115.MsgBody.serializer())
handleInputStatusChanged(body)
}
else -> return
}
markAsConsumed()
}
private fun PipelineContext.handleInputStatusChanged(body: SubMsgType0x115.MsgBody) {
val friend = bot.getFriend(body.fromUin) ?: return
val item = body.msgNotifyItem ?: return
collect(FriendInputStatusChangedEvent(friend, item.eventType == 1))
}
private fun PipelineContext.handleProfileChanged(body: ModProfile) {
var containsUnknown = false
for (profileInfo in body.msgProfileInfos) {
when (profileInfo.field) {
20002 -> { // 昵称修改
val to = profileInfo.value
if (body.uin == bot.id) {
val from = bot.nick
if (from == to) continue
collect(BotNickChangedEvent(bot, from, to))
bot.nick = to
} else {
val friend = bot.getFriend(body.uin)?.impl() ?: continue
val from = bot.nick
if (from == to) continue
collect(FriendNickChangedEvent(friend, from, to))
friend.info.nick = to
}
}
else -> containsUnknown = true
}
}
if (body.msgProfileInfos.isEmpty() || containsUnknown) {
logger.debug { "Transformers528 0x27L: ProfileChanged new data: ${body._miraiContentToString()}" }
}
}
private fun PipelineContext.handleRemarkChanged(body: ModFriendRemark) {
for (new in body.msgFrdRmk) {
val friend = bot.getFriend(new.fuin)?.impl() ?: continue
// TODO: 2020/4/10 ADD REMARK QUERY
collect(FriendRemarkChangeEvent(friend, friend.remark, new.rmkName))
friend.info.remark = new.rmkName
}
}
private fun PipelineContext.handleAvatarChanged(body: ModCustomFace) {
if (body.uin == bot.id) {
collect(BotAvatarChangedEvent(bot))
}
collect(FriendAvatarChangedEvent(bot.getFriend(body.uin) ?: return))
}
private fun PipelineContext.handleFriendDeleted(body: DelFriend) {
for (id in body.uint64Uins) {
collect(FriendDeleteEvent(bot.removeFriend(id) ?: continue))
}
}
private suspend fun PipelineContext.handleFriendAddedA(
body: Submsgtype0x44.MsgBody,
) = body.msgFriendMsgSync.context {
if (this == null) return
when (processtype) {
3, 9, 10 -> {
if (bot.getFriend(fuin) != null) return
val response = GetFriendGroupList.forSingleFriend(bot.client, fuin).sendAndExpect(bot)
val info = response.friendList.firstOrNull() ?: return
collect(
FriendAddEvent(bot.addNewFriendAndRemoveStranger(info.toMiraiFriendInfo()) ?: return),
)
}
}
}
private fun PipelineContext.handleFriendAddedB(data: MsgType0x210, body: SubMsgType0xb3.MsgBody) = data.context {
val info = FriendInfoImpl(
uin = body.msgAddFrdNotify.fuin,
nick = body.msgAddFrdNotify.fuinNick,
remark = "",
)
val removed = bot.removeStranger(info.uin)
val added = bot.addNewFriendAndRemoveStranger(info) ?: return
collect(FriendAddEvent(added))
if (removed != null) collect(StrangerRelationChangeEvent.Friended(removed, added))
}
}

View File

@ -1,66 +0,0 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.info.GroupInfoImpl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.getGroupByUin
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
internal interface GroupEventProcessorContext {
fun MsgComm.Msg.getNewMemberInfo(): MemberInfoImpl {
return MemberInfoImpl(
nameCard = msgHead.authNick.ifEmpty { msgHead.fromNick },
permission = MemberPermission.MEMBER,
specialTitle = "",
muteTimestamp = 0,
uin = msgHead.authUin,
nick = msgHead.authNick.ifEmpty { msgHead.fromNick },
remark = "",
anonymousId = null
)
}
suspend fun QQAndroidBot.createGroupForBot(groupUin: Long): GroupImpl? {
val group = getGroupByUin(groupUin)
if (group != null) {
return null
}
return getNewGroup(Mirai.calculateGroupCodeByGroupUin(groupUin))?.apply { groups.delegate.add(this) }
}
suspend fun QQAndroidBot.getNewGroup(groupCode: Long): GroupImpl? {
val troopNum = FriendList.GetTroopListSimplify(client)
.sendAndExpect(network, timeoutMillis = 10_000, retry = 5)
.groups.firstOrNull { it.groupCode == groupCode } ?: return null
return GroupImpl(
bot = this,
coroutineContext = coroutineContext,
id = groupCode,
groupInfo = GroupInfoImpl(troopNum),
members = Mirai.getRawGroupMemberList(
this,
troopNum.groupUin,
troopNum.groupCode,
troopNum.dwGroupOwnerUin
)
)
}
}

View File

@ -39,6 +39,9 @@ import net.mamoe.mirai.utils.read
/**
* Member/Bot invited/active join // force/active leave
* Member/Bot permission change
*
* @see BotJoinGroupEvent
* @see MemberJoinEvent
*
@ -47,10 +50,13 @@ import net.mamoe.mirai.utils.read
*
* @see MemberPermissionChangeEvent
* @see BotGroupPermissionChangeEvent
*
* @see BotInvitedJoinGroupRequestEvent
* @see MemberJoinRequestEvent
*/
internal class GroupListNoticeProcessor(
private val logger: MiraiLogger
) : MixedNoticeProcessor(), GroupEventProcessorContext {
private val logger: MiraiLogger,
) : MixedNoticeProcessor(), NewContactSupport {
override suspend fun PipelineContext.processImpl(data: MsgType0x210) {
if (data.uSubMsgType != 0x44L) return
@ -60,7 +66,7 @@ internal class GroupListNoticeProcessor(
when (msg.msgGroupMsgSync.msgType) {
1, 2 -> {
bot.components[ContactUpdater].groupListModifyLock.withLock {
bot.createGroupForBot(Mirai.calculateGroupUinByGroupCode(msg.msgGroupMsgSync.grpCode))?.let {
bot.addNewGroupByCode(msg.msgGroupMsgSync.grpCode)?.let {
collect(BotJoinGroupEvent.Active(it))
}
}
@ -85,10 +91,10 @@ internal class GroupListNoticeProcessor(
1 -> {
val dataList = message.parseToMessageDataList()
val invitor = dataList.first().let { messageData ->
group.getOrFail(messageData.data.toLong())
group[messageData.data.toLong()] ?: return
}
val member = dataList.last().let { messageData ->
group.addNewNormalMember(messageData.toMemberInfo())
group.addNewNormalMember(messageData.toMemberInfo()) ?: return
}
collect(MemberJoinEvent.Invite(member, invitor))
}
@ -116,50 +122,73 @@ internal class GroupListNoticeProcessor(
* @see BotJoinGroupEvent.Active
*/
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
if (data.msgHead.msgType != 33) return
bot.components[ContactUpdater].groupListModifyLock.withLock {
msgBody.msgContent.read {
val groupUin = Mirai.calculateGroupUinByGroupCode(readUInt().toLong())
val group =
bot.getGroupByUin(groupUin) ?: bot.createGroupForBot(groupUin) ?: return markAsConsumed()
discardExact(1)
val joinedMemberUin = readUInt().toLong()
val joinType = readByte().toInt()
val invitorUin = readUInt().toLong()
when (joinType) {
// 邀请加入
-125, 3 -> {
val invitor = group[invitorUin] ?: return markAsConsumed()
collected += if (joinedMemberUin == bot.id) {
BotJoinGroupEvent.Invite(invitor)
} else {
MemberJoinEvent.Invite(group.addNewNormalMember(getNewMemberInfo()), invitor)
}
}
// 通过群员分享的二维码/直接加入
-126, 2 -> {
collected += if (joinedMemberUin == bot.id) {
BotJoinGroupEvent.Active(group)
} else {
MemberJoinEvent.Active(group.addNewNormalMember(getNewMemberInfo()))
}
}
// 忽略
else -> {
when (data.msgHead.msgType) {
33 -> processGroupJoin33(data)
34 -> Unit // 34 与 33 重复, 忽略 34
38 -> processGroupJoin38(data)
85 -> processGroupJoin85(data)
else -> return
}
markAsConsumed()
}
}
// 33
private suspend fun PipelineContext.processGroupJoin33(data: MsgComm.Msg) = data.context {
msgBody.msgContent.read {
val groupUin = Mirai.calculateGroupUinByGroupCode(readUInt().toLong())
val group = bot.getGroupByUin(groupUin) ?: bot.addNewGroupByUin(groupUin) ?: return
discardExact(1)
val joinedMemberUin = readUInt().toLong()
val joinType = readByte().toInt()
val invitorUin = readUInt().toLong()
when (joinType) {
// 邀请加入
-125, 3 -> {
val invitor = group[invitorUin] ?: return
collected += if (joinedMemberUin == bot.id) {
BotJoinGroupEvent.Invite(invitor)
} else {
MemberJoinEvent.Invite(group.addNewNormalMember(getNewMemberInfo()) ?: return, invitor)
}
}
// 通过群员分享的二维码/直接加入
-126, 2 -> {
collected += if (joinedMemberUin == bot.id) {
BotJoinGroupEvent.Active(group)
} else {
MemberJoinEvent.Active(group.addNewNormalMember(getNewMemberInfo()) ?: return)
}
}
// 忽略
else -> {
}
}
// 邀请入群
// package: 27 0B 60 E7 01 CA CC 69 8B 83 44 71 47 90 06 B9 DC C0 ED D4 B1 00 30 33 44 30 42 38 46 30 39 37 32 38 35 43 34 31 38 30 33 36 41 34 36 31 36 31 35 32 37 38 46 46 43 30 41 38 30 36 30 36 45 38 31 43 39 41 34 38 37
// package: groupUin + 01 CA CC 69 8B 83 + invitorUin + length(06) + string + magicKey
// 主动入群, 直接加入: msgContent=27 0B 60 E7 01 76 E4 B8 DD 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 42 39 41 30 33 45 38 34 30 39 34 42 46 30 45 32 45 38 42 31 43 43 41 34 32 42 38 42 44 42 35 34 44 42 31 44 32 32 30 46 30 38 39 46 46 35 41 38
// 主动直接加入 27 0B 60 E7 01 76 E4 B8 DD 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 33 30 45 38 42 31 33 46 41 41 31 33 46 38 31 35 34 41 38 33 32 37 31 43 34 34 38 35 33 35 46 45 31 38 32 43 39 42 43 46 46 32 44 39 39 46 41 37
// 有人被邀请(经过同意后)加入 27 0B 60 E7 01 76 E4 B8 DD 83 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 34 30 34 38 32 33 38 35 37 41 37 38 46 33 45 37 35 38 42 39 38 46 43 45 44 43 32 41 30 31 36 36 30 34 31 36 39 35 39 30 38 39 30 39 45 31 34 34
// 搜索到群, 直接加入 27 0B 60 E7 01 07 6E 47 BA 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 32 30 39 39 42 39 41 46 32 39 41 35 42 33 46 34 32 30 44 36 44 36 39 35 44 38 45 34 35 30 46 30 45 30 38 45 31 41 39 42 46 46 45 32 30 32 34 35
}
// 邀请入群
// package: 27 0B 60 E7 01 CA CC 69 8B 83 44 71 47 90 06 B9 DC C0 ED D4 B1 00 30 33 44 30 42 38 46 30 39 37 32 38 35 43 34 31 38 30 33 36 41 34 36 31 36 31 35 32 37 38 46 46 43 30 41 38 30 36 30 36 45 38 31 43 39 41 34 38 37
// package: groupUin + 01 CA CC 69 8B 83 + invitorUin + length(06) + string + magicKey
// 主动入群, 直接加入: msgContent=27 0B 60 E7 01 76 E4 B8 DD 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 42 39 41 30 33 45 38 34 30 39 34 42 46 30 45 32 45 38 42 31 43 43 41 34 32 42 38 42 44 42 35 34 44 42 31 44 32 32 30 46 30 38 39 46 46 35 41 38
// 主动直接加入 27 0B 60 E7 01 76 E4 B8 DD 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 33 30 45 38 42 31 33 46 41 41 31 33 46 38 31 35 34 41 38 33 32 37 31 43 34 34 38 35 33 35 46 45 31 38 32 43 39 42 43 46 46 32 44 39 39 46 41 37
// 有人被邀请(经过同意后)加入 27 0B 60 E7 01 76 E4 B8 DD 83 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 34 30 34 38 32 33 38 35 37 41 37 38 46 33 45 37 35 38 42 39 38 46 43 45 44 43 32 41 30 31 36 36 30 34 31 36 39 35 39 30 38 39 30 39 45 31 34 34
// 搜索到群, 直接加入 27 0B 60 E7 01 07 6E 47 BA 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 32 30 39 39 42 39 41 46 32 39 41 35 42 33 46 34 32 30 44 36 44 36 39 35 44 38 45 34 35 30 46 30 45 30 38 45 31 41 39 42 46 46 45 32 30 32 34 35
}
// 38
private suspend fun PipelineContext.processGroupJoin38(data: MsgComm.Msg) = data.context {
if (bot.getGroupByUin(msgHead.fromUin) != null) return
bot.addNewGroupByUin(msgHead.fromUin)?.let { collect(BotJoinGroupEvent.Active(it)) }
}
// 85
private suspend fun PipelineContext.processGroupJoin85(data: MsgComm.Msg) = data.context {
// msgHead.authUin: 处理人
if (msgHead.toUin != bot.id) return
processGroupJoin38(data)
}
///////////////////////////////////////////////////////////////////////////
@ -201,10 +230,10 @@ internal class GroupListNoticeProcessor(
}
2 -> { // 被邀请入群, 自动同意, 不需处理
// val group = bot.getNewGroup(groupCode) ?: return null
// val invitor = group[actionUin]
//
// BotJoinGroupEvent.Invite(invitor)
// val group = bot.getNewGroup(groupCode) ?: return null
// val invitor = group[actionUin]
//
// BotJoinGroupEvent.Invite(invitor)
}
3 -> { // 已被请他管理员处理
}
@ -250,6 +279,13 @@ internal class GroupListNoticeProcessor(
markAsConsumed()
when (data.msgType) {
44 -> data.msgData.read {
// 3D C4 33 DD 01 FF CD 76 F4 03 C3 7E 2E 34
// 群转让
// start with 3D C4 33 DD 01 FF
// 3D C4 33 DD 01 FF C3 7E 2E 34 CD 76 F4 03
// 权限变更
// 3D C4 33 DD 01 00/01 .....
// 3D C4 33 DD 01 01 C3 7E 2E 34 01
discardExact(5)
val kind = readUByte().toInt()
if (kind == 0xFF) {
@ -327,7 +363,7 @@ toUin=0x0000000026BA1173(649728371)
target: Long,
kind: Int,
operator: Long,
groupUin: Long
groupUin: Long,
) {
when (kind) {
2, 0x82 -> bot.getGroupByUin(groupUin)?.let { group ->
@ -367,7 +403,7 @@ toUin=0x0000000026BA1173(649728371)
private fun PipelineContext.handlePermissionChange(
data: OnlinePushTrans.PbMsgInfo,
target: Long,
newPermissionByte: Int
newPermissionByte: Int,
) {
val group = bot.getGroupByUin(data.fromUin) ?: return
@ -410,7 +446,7 @@ toUin=0x0000000026BA1173(649728371)
// member Retrieve or permission changed to OWNER
var newOwner = group[to]
if (newOwner == null) {
newOwner = group.addNewNormalMember(MemberInfoImpl(uin = to, nick = "", permission = OWNER))
newOwner = group.addNewNormalMember(MemberInfoImpl(uin = to, nick = "", permission = OWNER)) ?: return
collect(MemberJoinEvent.Retrieve(newOwner))
} else if (newOwner.permission != OWNER) {
collect(MemberPermissionChangeEvent(newOwner, newOwner.permission, OWNER))
@ -421,7 +457,7 @@ toUin=0x0000000026BA1173(649728371)
// bot Retrieve or permission changed to OWNER
if (group == null) {
collect(BotJoinGroupEvent.Retrieve(bot.createGroupForBot(data.fromUin)!!))
collect(BotJoinGroupEvent.Retrieve(bot.addNewGroupByUin(data.fromUin) ?: return))
return
}

View File

@ -35,6 +35,9 @@ import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.message.data.MessageSourceKind
import net.mamoe.mirai.utils.*
/**
* Handles [GroupMessageEvent]. For private message events, see [PrivateMessageNoticeProcessor]
*/
internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPushMsg>(type()) {
internal data class SendGroupMessageReceipt(
val messageRandom: Int,
@ -61,7 +64,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
}
override suspend fun PipelineContext.process0(data: MsgOnlinePush.PbPushMsg) {
override suspend fun PipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {
val msgHead = data.msg.msgHead
val isFromSelfAccount = msgHead.fromUin == bot.id
@ -73,13 +76,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
// 3116=group music share
// 2021=group file
// message sent by bot
collect(
SendGroupMessageReceipt(
messageRandom,
msgHead.msgSeq,
msgHead.fromAppid
)
)
collect(SendGroupMessageReceipt(messageRandom, msgHead.msgSeq, msgHead.fromAppid))
return
}
// else: sync form other device
@ -132,7 +129,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
group = group,
sender = sender,
senderName = nameCard.nick,
)
),
)
return
} else {
@ -145,8 +142,8 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
sender = sender,
message = msgs.map { it.msg }.toMessageChainOnline(bot, group.id, MessageSourceKind.GROUP),
permission = sender.permission,
time = msgHead.msgTime
)
time = msgHead.msgTime,
),
)
return
}
@ -154,7 +151,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
private suspend inline fun broadcastNameCardChangedEventIfNecessary(
sender: Member,
new: MemberNick
new: MemberNick,
) {
if (sender is NormalMemberImpl) {
val currentNameCard = sender.nameCard
@ -177,7 +174,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
private fun findSenderName(
extraInfo: ImMsgBody.ExtraInfo?,
groupInfo: MsgComm.GroupInfo
groupInfo: MsgComm.GroupInfo,
): MemberNick? =
extraInfo?.groupCard?.takeIf { it.isNotEmpty() }?.decodeCommCardNameBuf()?.let {
MemberNick(it, true)

View File

@ -0,0 +1,113 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
import kotlinx.coroutines.cancel
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.FriendImpl
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.StrangerImpl
import net.mamoe.mirai.internal.contact.impl
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.GroupInfoImpl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.getGroupByUin
import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
internal interface NewContactSupport {
fun MsgComm.Msg.getNewMemberInfo(): MemberInfoImpl {
return MemberInfoImpl(
nameCard = msgHead.authNick.ifEmpty { msgHead.fromNick },
permission = MemberPermission.MEMBER,
specialTitle = "",
muteTimestamp = 0,
uin = msgHead.authUin,
nick = msgHead.authNick.ifEmpty { msgHead.fromNick },
remark = "",
anonymousId = null,
)
}
suspend fun QQAndroidBot.addNewGroupByCode(code: Long): GroupImpl? {
if (getGroup(code) != null) return null
return getNewGroup(code)?.apply { groups.delegate.add(this) }
}
suspend fun QQAndroidBot.addNewGroupByUin(groupUin: Long): GroupImpl? {
if (getGroupByUin(groupUin) != null) return null
return getNewGroup(Mirai.calculateGroupCodeByGroupUin(groupUin))?.apply { groups.delegate.add(this) }
}
suspend fun QQAndroidBot.addNewGroup(stTroopNum: StTroopNum): GroupImpl? {
if (getGroup(stTroopNum.groupCode) != null) return null
return getNewGroup(stTroopNum)?.apply { groups.delegate.add(this) }
}
fun QQAndroidBot.removeStranger(id: Long): StrangerImpl? {
val instance = strangers[id] ?: return null
strangers.remove(instance.id)
instance.cancel()
return instance
}
fun QQAndroidBot.removeFriend(id: Long): FriendImpl? {
val instance = friends[id] ?: return null
friends.remove(instance.id)
instance.cancel()
return instance
}
fun QQAndroidBot.addNewFriendAndRemoveStranger(info: FriendInfoImpl): FriendImpl? {
if (friends.contains(info.uin)) return null
strangers[info.uin]?.let { removeStranger(it.id) }
val friend = Mirai.newFriend(bot, info).impl()
friends.delegate.add(friend)
return friend
}
fun QQAndroidBot.addNewStranger(info: StrangerInfoImpl): StrangerImpl? {
if (friends.contains(info.uin)) return null // cannot have both stranger and friend
if (strangers.contains(info.uin)) return null
val stranger = Mirai.newStranger(bot, info).impl()
strangers.delegate.add(stranger)
return stranger
}
private suspend fun QQAndroidBot.getNewGroup(groupCode: Long): GroupImpl? {
val troopNum = FriendList.GetTroopListSimplify(client)
.sendAndExpect(network, timeoutMillis = 10_000, retry = 5)
.groups.firstOrNull { it.groupCode == groupCode } ?: return null
return getNewGroup(troopNum)
}
private suspend fun QQAndroidBot.getNewGroup(troopNum: StTroopNum): GroupImpl? {
return GroupImpl(
bot = this,
parentCoroutineContext = coroutineContext,
id = troopNum.groupCode,
groupInfo = GroupInfoImpl(troopNum),
members = Mirai.getRawGroupMemberList(
this,
troopNum.groupUin,
troopNum.groupCode,
troopNum.dwGroupOwnerUin,
),
)
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.ClientKind
import net.mamoe.mirai.contact.OtherClientInfo
import net.mamoe.mirai.contact.Platform
import net.mamoe.mirai.event.events.OtherClientMessageEvent
import net.mamoe.mirai.event.events.OtherClientOfflineEvent
import net.mamoe.mirai.event.events.OtherClientOnlineEvent
import net.mamoe.mirai.internal.contact.appId
import net.mamoe.mirai.internal.contact.createOtherClient
import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.components.ContactUpdater
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
import net.mamoe.mirai.internal.network.components.PipelineContext
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.SubMsgType0x7
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.buildMessageChain
import net.mamoe.mirai.utils.context
/**
* @see OtherClientOnlineEvent
* @see OtherClientOfflineEvent
*
* @see OtherClientMessageEvent
*/
internal class OtherClientNoticeProcessor : MixedNoticeProcessor() {
/**
* @see OtherClientOnlineEvent
* @see OtherClientOfflineEvent
*/
override suspend fun PipelineContext.processImpl(data: RequestPushStatus) {
markAsConsumed()
bot.components[ContactUpdater].otherClientsLock.withLock {
val instanceInfo = data.vecInstanceList?.firstOrNull()
val appId = instanceInfo?.iAppId ?: 1
when (data.status.toInt()) {
1 -> { // online
if (bot.otherClients.any { appId == it.appId }) return
suspend fun tryFindInQuery(): OtherClientInfo? {
return Mirai.getOnlineOtherClientsList(bot).find { it.appId == appId }
?: kotlin.run {
delay(2000) // sometimes server sync slow
Mirai.getOnlineOtherClientsList(bot).find { it.appId == appId }
}
}
val info =
tryFindInQuery() ?: kotlin.run {
bot.network.logger.warning(
contextualBugReportException(
"SvcRequestPushStatus (OtherClient online)",
"packet: \n" + data._miraiContentToString() +
"\n\nquery: \n" +
Mirai.getOnlineOtherClientsList(bot)._miraiContentToString(),
additional = "Failed to find corresponding instanceInfo.",
),
)
OtherClientInfo(appId, Platform.WINDOWS, "", "电脑")
}
val client = bot.createOtherClient(info)
bot.otherClients.delegate.add(client)
collected += OtherClientOnlineEvent(
client,
ClientKind[data.nClientType?.toInt() ?: 0],
)
}
2 -> { // off
val client = bot.otherClients.find { it.appId == appId } ?: return
client.cancel(CancellationException("Offline"))
bot.otherClients.delegate.remove(client)
collected += OtherClientOfflineEvent(client)
}
else -> throw contextualBugReportException(
"decode SvcRequestPushStatus (PC Client status change)",
data._miraiContentToString(),
additional = "unknown status=${data.status}",
)
}
}
}
/**
* @see OtherClientMessageEvent
*/
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
if (msgHead.msgType != 529) return
markAsConsumed() // todo check
if (msgHead.c2cCmd != 7) return
val body = msgBody.msgContent.loadAs(SubMsgType0x7.MsgBody.serializer())
val textMsg =
body.msgSubcmd0x4Generic?.buf?.loadAs(SubMsgType0x7.MsgBody.QQDataTextMsg.serializer())
?: return
with(body.msgHeader ?: return) {
if (dstUin != bot.id) return
val client = bot.otherClients.find { it.appId == srcInstId }
?: return // don't compare with dstAppId. diff.
val chain = buildMessageChain {
+OnlineMessageSourceFromFriendImpl(bot, listOf(data))
for (msgItem in textMsg.msgItems) {
when (msgItem.type) {
1 -> +PlainText(msgItem.text)
else -> {
}
}
}
}
collect(OtherClientMessageEvent(client, chain, msgHead.msgTime))
}
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.getGroupByUin
import net.mamoe.mirai.internal.message.toMessageChainOnline
import net.mamoe.mirai.internal.network.components.PipelineContext
import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.notice.SystemMessageProcessor.Companion.fromSync
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.utils.context
/**
* Handles [UserMessageEvent] and their sync events. For [GroupMessageEvent], see [GroupMessageProcessor]
*
* @see StrangerMessageEvent
* @see StrangerMessageSyncEvent
*
* @see FriendMessageEvent
* @see FriendMessageSyncEvent
*
* @see GroupTempMessageEvent
* @see GroupTempMessageSyncEvent
*/
internal class PrivateMessageNoticeProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type()) {
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
if (msgHead.fromUin == bot.id && fromSync) {
// Bot send message to himself? or from other client? I am not the implementer.
bot.client.sendFriendMessageSeq.updateIfSmallerThan(msgHead.msgSeq)
return
}
if (!bot.components[SsoProcessor].firstLoginSucceed) return
val senderUin = if (fromSync) msgHead.toUin else msgHead.fromUin
when (msgHead.msgType) {
166, 167, // 单向好友
208, // friend ptt, maybe also support stranger
-> {
handlePrivateMessage(data, bot.getFriend(senderUin) ?: bot.getStranger(senderUin) ?: return)
markAsConsumed()
}
141, // group temp
-> {
val tmpHead = msgHead.c2cTmpMsgHead ?: return
val group = bot.getGroupByUin(tmpHead.groupUin) ?: return
handlePrivateMessage(data, group[senderUin] ?: return)
markAsConsumed()
}
}
}
private suspend fun PipelineContext.handlePrivateMessage(
data: MsgComm.Msg,
user: User,
) = data.context {
user.impl()
if (!user.messageSeq.updateIfDifferentWith(msgHead.msgSeq)) return
if (contentHead?.autoReply == 1) return
val msgs = user.fragmentedMessageMerger.tryMerge(this)
if (msgs.isEmpty()) return
val chain = msgs.toMessageChainOnline(bot, 0, user.correspondingMessageSourceKind)
val time = msgHead.msgTime
collected += if (fromSync) {
when (user) {
is FriendImpl -> FriendMessageSyncEvent(user, chain, time)
is StrangerImpl -> StrangerMessageSyncEvent(user, chain, time)
is NormalMemberImpl -> GroupTempMessageSyncEvent(user, chain, time)
else -> null
}
} else {
when (user) {
is FriendImpl -> FriendMessageEvent(user, chain, time)
is StrangerImpl -> StrangerMessageEvent(user, chain, time)
is NormalMemberImpl -> GroupTempMessageEvent(user, chain, time)
else -> null
}
} ?: error("unreachable")
}
}

View File

@ -10,39 +10,24 @@
package net.mamoe.mirai.internal.network.notice
import kotlinx.coroutines.sync.withLock
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUByte
import kotlinx.io.core.readUShort
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.getGroupByUin
import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl
import net.mamoe.mirai.internal.message.toMessageChainOnline
import net.mamoe.mirai.internal.network.components.ContactUpdater
import net.mamoe.mirai.internal.network.components.MsgCommonMsgProcessor
import net.mamoe.mirai.internal.network.components.PipelineContext
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.protocol.data.proto.FrdSysMsg
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.SubMsgType0x7
import net.mamoe.mirai.internal.network.protocol.packet.chat.NewContact
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.message.data.MessageSourceKind
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.buildMessageChain
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.TypeKey
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.toUHexString
internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProcessorContext {
internal class SystemMessageProcessor : MsgCommonMsgProcessor(), NewContactSupport {
companion object {
val KEY_FROM_SYNC = TypeKey<Boolean>("fromSync")
val PipelineContext.fromSync get() = attributes[KEY_FROM_SYNC]
}
override suspend fun PipelineContext.process0(data: MsgComm.Msg): Unit = data.run {
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg): Unit = data.run {
// TODO: 2021/6/26 extract logic into multiple processors
when (msgHead.msgType) {
33 -> bot.components[ContactUpdater].groupListModifyLock.withLock {
@ -53,19 +38,11 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
}
38 -> bot.components[ContactUpdater].groupListModifyLock.withLock { // 建群
bot.createGroupForBot(msgHead.fromUin)
?.let { collect(BotJoinGroupEvent.Active(it)) }
return
TODO("removed")
}
85 -> bot.components[ContactUpdater].groupListModifyLock.withLock { // 其他客户端入群
// msgHead.authUin: 处理人
if (msgHead.toUin == bot.id) {
bot.createGroupForBot(msgHead.fromUin)
?.let { collect(BotJoinGroupEvent.Active(it)) }
}
return
TODO("removed")
}
/*
@ -116,92 +93,11 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
//167 单向好友
166, 167 -> {
//我也不知道为什么要这样写,但它就是能跑
if (msgHead.fromUin == bot.id && fromSync) {
loop@ while (true) {
val instance = bot.client.getFriendSeq()
if (instance < msgHead.msgSeq) {
if (bot.client.setFriendSeq(instance, msgHead.msgSeq)) {
break@loop
}
} else break@loop
}
return
}
if (!bot.components[SsoProcessor].firstLoginSucceed) {
return
}
val fromUin = if (fromSync) {
msgHead.toUin
} else {
msgHead.fromUin
}
bot.getFriend(fromUin)?.let { friend ->
friend.checkIsFriendImpl()
friend.lastMessageSequence.loop {
//我也不知道为什么要这样写,但它就是能跑
if (friend.lastMessageSequence.value != msgHead.msgSeq
&& friend.lastMessageSequence.compareAndSet(it, msgHead.msgSeq)
&& contentHead?.autoReply != 1
) {
val msgs = friend.friendPkgMsgParsingCache.tryMerge(this)
if (msgs.isNotEmpty()) {
collect(
if (fromSync) {
FriendMessageSyncEvent(
friend,
msgs.toMessageChainOnline(bot, 0, MessageSourceKind.FRIEND),
msgHead.msgTime
)
} else {
FriendMessageEvent(
friend,
msgs.toMessageChainOnline(bot, 0, MessageSourceKind.FRIEND),
msgHead.msgTime
)
}
)
} else return
}
return
}
} ?: bot.getStranger(fromUin)?.let { stranger ->
stranger.checkIsImpl()
stranger.lastMessageSequence.loop {
//我也不知道为什么要这样写,但它就是能跑
if (stranger.lastMessageSequence.value != msgHead.msgSeq && stranger.lastMessageSequence.compareAndSet(
it,
msgHead.msgSeq
) && contentHead?.autoReply != 1
) {
collect(
if (fromSync) {
StrangerMessageSyncEvent(
stranger,
listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.STRANGER),
msgHead.msgTime
)
} else {
StrangerMessageEvent(
stranger,
listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.STRANGER),
msgHead.msgTime
)
}
)
}
return
}
}
return
TODO("removed")
}
208 -> {
// friend ptt
val target = bot.getFriend(msgHead.fromUin) ?: return
val lsc = listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.FRIEND)
collect(FriendMessageEvent(target, lsc, msgHead.msgTime))
return
TODO("removed")
}
529 -> {
@ -210,30 +106,7 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
when (msgHead.c2cCmd) {
// other client sync
7 -> {
val body = msgBody.msgContent.loadAs(SubMsgType0x7.MsgBody.serializer())
val textMsg =
body.msgSubcmd0x4Generic?.buf?.loadAs(SubMsgType0x7.MsgBody.QQDataTextMsg.serializer())
?: return
with(body.msgHeader ?: return) {
if (dstUin != bot.id) return
val client = bot.otherClients.find { it.appId == srcInstId }
?: return// don't compare with dstAppId. diff.
val chain = buildMessageChain {
+OnlineMessageSourceFromFriendImpl(bot, listOf(data))
for (msgItem in textMsg.msgItems) {
when (msgItem.type) {
1 -> +PlainText(msgItem.text)
else -> {
}
}
}
}
collect(OtherClientMessageEvent(client, chain, msgHead.msgTime))
}
TODO("removed")
}
}
@ -245,39 +118,7 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
if (!bot.components[SsoProcessor].firstLoginSucceed || msgHead.fromUin == bot.id && !fromSync) {
return
}
val tmpHead = msgHead.c2cTmpMsgHead ?: return
val member = bot.getGroupByUin(tmpHead.groupUin)?.get(
if (fromSync) {
msgHead.toUin
} else {
msgHead.fromUin
}
)
?: return
member.checkIsMemberImpl()
member.lastMessageSequence.loop { instant ->
if (member.lastMessageSequence.value != msgHead.msgSeq && contentHead?.autoReply != 1) {
if (member.lastMessageSequence.compareAndSet(instant, msgHead.msgSeq)) {
collect(
if (fromSync) {
GroupTempMessageSyncEvent(
member,
listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.TEMP),
msgHead.msgTime
)
} else {
GroupTempMessageEvent(
member,
listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.TEMP),
msgHead.msgTime
)
}
)
}
} else return
}
TODO("removed")
}
84, 87 -> { // 请求入群验证 和 被要求入群
bot.network.run {
@ -298,45 +139,7 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
}
//陌生人添加信息
191 -> {
var fromGroup = 0L
var pbNick = ""
msgBody.msgContent.read {
readUByte()// version
discardExact(readUByte().toInt())//skip
readUShort()//source id
readUShort()//SourceSubID
discardExact(readUShort().toLong())//skip size
if (readUShort().toInt() != 0) {//hasExtraInfo
discardExact(readUShort().toInt())//mail address info, skip
}
discardExact(4 + readUShort().toInt())//skip
for (i in 1..readUByte().toInt()) {//pb size
val type = readUShort().toInt()
val pbArray = ByteArray(readUShort().toInt() and 0xFF)
readAvailable(pbArray)
when (type) {
1000 -> pbArray.loadAs(FrdSysMsg.GroupInfo.serializer()).let { fromGroup = it.groupUin }
1002 -> pbArray.loadAs(FrdSysMsg.FriendMiscInfo.serializer())
.let { pbNick = it.fromuinNick }
else -> {
}//ignore
}
}
}
val nick =
sequenceOf(msgHead.fromNick, msgHead.authNick, pbNick).filter { it.isNotEmpty() }.firstOrNull()
?: return
val id =
sequenceOf(msgHead.fromUin, msgHead.authUin).filter { it != 0L }.firstOrNull() ?: return//对方QQ
Mirai.newStranger(bot, StrangerInfoImpl(id, nick, fromGroup)).checkIsImpl().let {
bot.getStranger(id)?.let { previous ->
bot.strangers.remove(id)
StrangerRelationChangeEvent.Deleted(previous).broadcast()
}
bot.strangers.delegate.add(it)
collect(StrangerAddEvent(it))
}
TODO("removed")
}
// 732: 27 0B 60 E7 0C 01 3E 03 3F A2 5E 90 60 E2 00 01 44 71 47 90 00 00 02 58
// 732: 27 0B 60 E7 11 00 40 08 07 20 E7 C1 AD B8 02 5A 36 08 B4 E7 E0 F0 09 1A 1A 08 9C D4 16 10 F7 D2 D8 F5 05 18 D0 E2 85 F4 06 20 00 28 00 30 B4 E7 E0 F0 09 2A 0E 08 00 12 0A 08 9C D4 16 10 00 18 01 20 00 30 00 38 00

View File

@ -30,7 +30,7 @@ import net.mamoe.mirai.utils.toUHexString
internal class MsgInfoDecoder(
private val logger: MiraiLogger,
) : SimpleNoticeProcessor<MsgInfo>(type()) {
override suspend fun PipelineContext.process0(data: MsgInfo) {
override suspend fun PipelineContext.processImpl(data: MsgInfo) {
when (data.shMsgType.toUShort().toInt()) {
// 528
0x210 -> fire(data.vMsg.loadAs(MsgType0x210.serializer()))

View File

@ -14,7 +14,7 @@ import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
internal class MsgType0x210Decoder : SimpleNoticeProcessor<MsgType0x210>(type()) {
override suspend fun PipelineContext.process0(data: MsgType0x210) {
override suspend fun PipelineContext.processImpl(data: MsgType0x210) {
when (data.uSubMsgType) {
0x8AL -> {
}

View File

@ -10,6 +10,7 @@
package net.mamoe.mirai.internal.network.protocol.data.jce
import kotlinx.serialization.Serializable
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.utils.io.JceStruct
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
@ -25,6 +26,5 @@ internal class RequestPushStatus(
@JvmField @TarsId(6) val nClientType: Long? = null,
@JvmField @TarsId(7) val nInstanceId: Long? = null,
@JvmField @TarsId(8) val vecInstanceList: List<InstanceInfo>? = null,
) : JceStruct
) : JceStruct, Packet

View File

@ -952,7 +952,7 @@ internal class Submsgtype0x27 {
@Serializable
internal class ProfileInfo(
@ProtoNumber(1) @JvmField val field: Int = 0,
@ProtoNumber(2) @JvmField val value: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoNumber(2) @JvmField val value: String = "",
) : ProtoBuf
@Serializable

View File

@ -13,6 +13,7 @@ import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline.Companion.noticeProcessorPipeline
import net.mamoe.mirai.internal.network.components.PacketCodec
import net.mamoe.mirai.internal.network.protocol.packet.chat.*
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
@ -27,7 +28,9 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard
import net.mamoe.mirai.internal.network.toPacket
import net.mamoe.mirai.utils.MiraiLoggerWithSwitch
import net.mamoe.mirai.utils.TypeSafeMap
internal sealed class PacketFactory<TPacket : Packet?> {
/**
@ -36,6 +39,17 @@ internal sealed class PacketFactory<TPacket : Packet?> {
abstract val receivingCommandName: String
open val canBeCached: Boolean get() = true
protected companion object {
@JvmStatic
suspend fun QQAndroidBot.processPacketThroughPipeline(
data: Any?,
attributes: TypeSafeMap = TypeSafeMap(),
): Packet {
return components.noticeProcessorPipeline.process(this, data, attributes).toPacket()
}
}
}
/**

View File

@ -264,7 +264,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
sequenceIds = sequenceIds,
randIds = randIds,
sequenceIdsInitializer = { size ->
IntArray(size) { client.nextFriendSeq() }
IntArray(size) { client.sendFriendMessageSeq.next() }
},
postInit = {
sourceCallback(
@ -278,7 +278,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
)
)
},
doFragmented = fragmented
doFragmented = fragmented,
)
}
/*= buildOutgoingUniPacket(client) {

View File

@ -9,84 +9,18 @@
package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.sync.withLock
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.ClientKind
import net.mamoe.mirai.contact.OtherClientInfo
import net.mamoe.mirai.contact.Platform
import net.mamoe.mirai.event.events.OtherClientOfflineEvent
import net.mamoe.mirai.event.events.OtherClientOnlineEvent
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.appId
import net.mamoe.mirai.internal.contact.createOtherClient
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.components.ContactUpdater
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.io.serialization.readUniPacket
internal object MessageSvcRequestPushStatus : IncomingPacketFactory<Packet?>(
"MessageSvc.RequestPushStatus", ""
internal object MessageSvcRequestPushStatus : IncomingPacketFactory<Packet>(
"MessageSvc.RequestPushStatus", "",
) {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet {
val packet = readUniPacket(RequestPushStatus.serializer())
bot.components[ContactUpdater].otherClientsLock.withLock {
val instanceInfo = packet.vecInstanceList?.firstOrNull()
val appId = instanceInfo?.iAppId ?: 1
return when (packet.status.toInt()) {
1 -> { // online
if (bot.otherClients.any { appId == it.appId }) return null
suspend fun tryFindInQuery(): OtherClientInfo? {
return Mirai.getOnlineOtherClientsList(bot).find { it.appId == appId }
?: kotlin.run {
delay(2000) // sometimes server sync slow
Mirai.getOnlineOtherClientsList(bot).find { it.appId == appId }
}
}
val info =
tryFindInQuery() ?: kotlin.run {
bot.network.logger.warning(
contextualBugReportException(
"SvcRequestPushStatus (OtherClient online)",
"packet: \n" + packet._miraiContentToString() +
"\n\nquery: \n" +
Mirai.getOnlineOtherClientsList(bot)._miraiContentToString(),
additional = "Failed to find corresponding instanceInfo."
)
)
OtherClientInfo(appId, Platform.WINDOWS, "", "电脑")
}
val client = bot.createOtherClient(info)
bot.otherClients.delegate.add(client)
OtherClientOnlineEvent(
client,
ClientKind[packet.nClientType?.toInt() ?: 0]
)
}
2 -> { // off
val client = bot.otherClients.find { it.appId == appId } ?: return null
client.cancel(CancellationException("Offline"))
bot.otherClients.delegate.remove(client)
OtherClientOfflineEvent(client)
}
else -> throw contextualBugReportException(
"decode SvcRequestPushStatus (PC Client status change)",
packet._miraiContentToString(),
additional = "unknown status=${packet.status}"
)
}
}
return bot.processPacketThroughPipeline(packet)
}
}

View File

@ -17,15 +17,15 @@ package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
import kotlinx.io.core.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.NormalMember
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.data.GroupHonorType
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
import net.mamoe.mirai.internal.contact.checkIsMemberImpl
import net.mamoe.mirai.internal.network.MultiPacketImpl
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient
@ -34,17 +34,12 @@ import net.mamoe.mirai.internal.network.protocol.data.jce.MsgInfo
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
import net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x115
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x122
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27.SubMsgType0x27.*
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x44.Submsgtype0x44
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0xb3
import net.mamoe.mirai.internal.network.protocol.data.proto.TroopTips0x857
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.network.protocol.packet.buildResponseUniPacket
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.io.ProtoBuf
import net.mamoe.mirai.internal.utils.io.serialization.*
@ -538,20 +533,7 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
0x111L to ignoredLambda528,
// 新好友
0xB3L to lambda528 { bot ->
// 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07
val body = vProtobuf.loadAs(Submsgtype0xb3.SubMsgType0xb3.MsgBody.serializer())
val new = Mirai.newFriend(
bot, FriendInfoImpl(
uin = body.msgAddFrdNotify.fuin,
nick = body.msgAddFrdNotify.fuinNick,
remark = "",
)
).checkIsFriendImpl()
bot.friends.delegate.add(new)
return@lambda528 bot.getStranger(new.id)?.let {
bot.strangers.remove(new.id)
sequenceOf(StrangerRelationChangeEvent.Friended(it, new), FriendAddEvent(new))
} ?: sequenceOf(FriendAddEvent(new))
TODO("removed")
},
0xE2L to lambda528 { _ ->
// TODO: unknown. maybe messages.
@ -561,35 +543,7 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
return@lambda528 emptySequence()
},
0x44L to lambda528 { bot ->
val msg = vProtobuf.loadAs(Submsgtype0x44.MsgBody.serializer())
val packetList = mutableListOf<Packet>()
if (msg.msgFriendMsgSync != null) {
when (msg.msgFriendMsgSync.processtype) {
3, 9, 10 -> {
if (bot.getFriend(msg.msgFriendMsgSync.fuin) == null) {
val response: FriendList.GetFriendGroupList.Response =
FriendList.GetFriendGroupList.forSingleFriend(
bot.client,
msg.msgFriendMsgSync.fuin
).sendAndExpect(bot)
response.friendList.firstOrNull()?.let {
val friend = Mirai.newFriend(bot, it.toMiraiFriendInfo()).checkIsFriendImpl()
bot.friends.delegate.add(friend)
packetList.add(FriendAddEvent(friend))
}
}
}
}
}
if (msg.msgGroupMsgSync != null) {
when (msg.msgGroupMsgSync.msgType) {
1, 2 -> {
TODO("removed")
}
}
}
return@lambda528 packetList.asSequence()
TODO("removed")
},
// bot 在其他客户端被踢或主动退出而同步情况
0xD4L to lambda528 { _ ->
@ -666,39 +620,10 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
},
//好友输入状态
0x115L to lambda528 { bot ->
val body = vProtobuf.loadAs(Submsgtype0x115.SubMsgType0x115.MsgBody.serializer())
val friend = bot.getFriend(body.fromUin)
val item = body.msgNotifyItem
return@lambda528 if (friend != null && item != null) {
sequenceOf(FriendInputStatusChangedEvent(friend, item.eventType == 1))
} else {
emptySequence()
}
TODO("removed")
},
// 群相关, ModFriendRemark, DelFriend, ModGroupProfile
0x27L to lambda528 { bot ->
fun ModFriendRemark.transform(bot: QQAndroidBot): Sequence<Packet> {
return this.msgFrdRmk.asSequence().mapNotNull {
val friend = bot.getFriend(it.fuin) ?: return@mapNotNull null
val old: String
friend.checkIsFriendImpl().friendInfo
.also { info -> old = info.remark }
.remark = it.rmkName
// TODO: 2020/4/10 ADD REMARK QUERY
FriendRemarkChangeEvent(friend, old, it.rmkName)
}
}
fun DelFriend.transform(bot: QQAndroidBot): Sequence<Packet> {
return this.uint64Uins.asSequence().mapNotNull {
val friend = bot.getFriend(it) ?: return@mapNotNull null
if (bot.friends.delegate.remove(friend)) {
FriendDeleteEvent(friend)
} else null
}
}
fun ModGroupProfile.transform(bot: QQAndroidBot): Sequence<Packet> {
return this.msgGroupProfileInfos.asSequence().mapNotNull { info ->
when (info.field) {
@ -729,23 +654,23 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
var85.troopface = var3;
var85.hasSetNewTroopHead = true;
*/
// bot.logger.debug(
// contextualBugReportException(
// "解析 Transformers528 0x27L ModGroupProfile 群头像修改",
// forDebug = "this=${this._miraiContentToString()}"
// )
// )
// bot.logger.debug(
// contextualBugReportException(
// "解析 Transformers528 0x27L ModGroupProfile 群头像修改",
// forDebug = "this=${this._miraiContentToString()}"
// )
// )
null
}
3 -> { // troop.credit.data
// top_package/akkz.java:3475
// top_package/akkz.java:3498
// bot.logger.debug(
// contextualBugReportException(
// "解析 Transformers528 0x27L ModGroupProfile 群 troop.credit.data",
// forDebug = "this=${this._miraiContentToString()}"
// )
// )
// bot.logger.debug(
// contextualBugReportException(
// "解析 Transformers528 0x27L ModGroupProfile 群 troop.credit.data",
// forDebug = "this=${this._miraiContentToString()}"
// )
// )
null
}
@ -789,64 +714,15 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
}
}
fun ModCustomFace.transform(bot: QQAndroidBot): Sequence<Packet> {
if (uin == bot.id) {
return sequenceOf(BotAvatarChangedEvent(bot))
}
val friend = bot.getFriend(uin) ?: return emptySequence()
return sequenceOf(FriendAvatarChangedEvent(friend))
}
fun ModProfile.transform(bot: QQAndroidBot): Sequence<Packet> = buildList<Packet> {
var containsUnknown = false
msgProfileInfos.forEach { modified ->
when (modified.field) {
20002 -> { // 昵称修改
val value = modified.value
val to = value.encodeToString()
if (uin == bot.id) {
val from = bot.nick
if (from != to) {
bot.nick = to
bot.asFriend.checkIsFriendImpl().nick = to
add(BotNickChangedEvent(bot, from, to))
}
} else {
val friend = (bot.getFriend(uin) ?: return@forEach) as FriendImpl
val info = friend.friendInfo
val from = info.nick
when (info) {
is FriendInfoImpl -> info.nick = to
else -> {
bot.network.logger.debug {
"Unknown how to update nick for $info"
}
}
}
add(FriendNickChangedEvent(friend, from, to))
}
}
else -> {
containsUnknown = true
}
}
}
if (msgProfileInfos.isEmpty() || containsUnknown) {
bot.network.logger.debug {
"Transformers528 0x27L: new data: ${_miraiContentToString()}"
}
}
}.asSequence()
return@lambda528 vProtobuf.loadAs(SubMsgType0x27MsgBody.serializer()).msgModInfos.asSequence()
.flatMap {
when {
it.msgModFriendRemark != null -> it.msgModFriendRemark.transform(bot)
it.msgDelFriend != null -> it.msgDelFriend.transform(bot)
it.msgModFriendRemark != null -> TODO("removed")
it.msgDelFriend != null -> TODO("removed")
it.msgModGroupProfile != null -> it.msgModGroupProfile.transform(bot)
it.msgModGroupMemberProfile != null -> it.msgModGroupMemberProfile.transform(bot)
it.msgModCustomFace != null -> it.msgModCustomFace.transform(bot)
it.msgModProfile != null -> it.msgModProfile.transform(bot)
it.msgModCustomFace != null -> TODO("removed")
it.msgModProfile != null -> TODO("removed")
else -> {
bot.network.logger.debug {
"Transformers528 0x27L: new data: ${it._miraiContentToString()}"
@ -856,5 +732,5 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
}
}
// 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B
}
},
)

View File

@ -0,0 +1,78 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.utils
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.update
import net.mamoe.mirai.utils.getRandomUnsignedInt
import net.mamoe.mirai.utils.toLongUnsigned
// We probably can reduce duplicates by using value classes, but atomicFU compiler might not be able to compile it.
// TODO: 2021/6/27 tests
internal class AtomicIntSeq private constructor(
initial: Int,
private val maxExclusive: Int,
) {
private val value = atomic(initial)
/**
* Increment [value] within the range from 0 (inclusive) to [maxExclusive] (exclusive).
*/
fun next(): Int = value.incrementAndGet().mod(maxExclusive) // positive
/**
* Atomically update [value] if it is smaller than [new].
*/
fun updateIfSmallerThan(new: Int): Boolean {
value.update { instant ->
if (instant < new) new else return false
}
return true
}
fun updateIfDifferentWith(new: Int): Boolean {
value.update { instant ->
if (instant == new) return false
new
}
return true
}
companion object {
@JvmStatic
fun forMessageSeq() = AtomicIntSeq(0, Int.MAX_VALUE)
@JvmStatic
fun forPrivateSync() = AtomicIntSeq(getRandomUnsignedInt(), 65535)
}
}
// TODO: 2021/6/27 tests
internal class AtomicLongSeq(
initial: Long = getRandomUnsignedInt().toLongUnsigned(),
private val maxExclusive: Long = 65535,
) {
private val value = atomic(initial)
/**
* Increment [value] within the range from 0 (inclusive) to [maxExclusive] (exclusive).
*/
fun next(): Long = value.incrementAndGet().mod(maxExclusive) // positive
/**
* Atomically update [value] if it is smaller than [new].
*/
fun updateIfSmallerThan(new: Long) {
value.update { instant ->
if (instant < new) new else return
}
}
}