Make all packets internal

This commit is contained in:
Him188 2019-12-06 23:47:48 +08:00
parent 1ea8478584
commit e47143e872
37 changed files with 273 additions and 236 deletions

View File

@ -7,6 +7,7 @@ package net.mamoe.mirai
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
@ -30,7 +31,9 @@ suspend inline fun Bot.getQQ(@PositiveNumbers number: Long): QQ = this.contacts.
suspend inline fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number) suspend inline fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number)
suspend inline fun Bot.getGroup(id: UInt): Group = this.contacts.getGroup(GroupId(id)) suspend inline fun Bot.getGroup(id: UInt): Group = this.contacts.getGroup(GroupId(id))
suspend inline fun Bot.getGroup(@PositiveNumbers id: Long): Group = this.contacts.getGroup(GroupId(id.coerceAtLeastOrFail(0).toUInt())) suspend inline fun Bot.getGroup(@PositiveNumbers id: Long): Group =
this.contacts.getGroup(GroupId(id.coerceAtLeastOrFail(0).toUInt()))
suspend inline fun Bot.getGroup(id: GroupId): Group = this.contacts.getGroup(id) suspend inline fun Bot.getGroup(id: GroupId): Group = this.contacts.getGroup(id)
suspend inline fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.getGroup(internalId) suspend inline fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.getGroup(internalId)
@ -60,7 +63,8 @@ inline fun <R> Bot.withSession(block: BotSession.() -> R): R {
* 发送数据包 * 发送数据包
* @throws IllegalStateException [BotNetworkHandler.socket] 未开启时 * @throws IllegalStateException [BotNetworkHandler.socket] 未开启时
*/ */
suspend inline fun Bot.sendPacket(packet: OutgoingPacket) = this.network.sendPacket(packet) internal suspend inline fun Bot.sendPacket(packet: OutgoingPacket) =
(this.network as TIMBotNetworkHandler).socket.sendPacket(packet)
/** /**
* 使用在默认配置基础上修改的配置进行登录 * 使用在默认配置基础上修改的配置进行登录
@ -109,7 +113,8 @@ suspend inline fun Bot.alsoLogin(message: String): Bot {
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@JvmOverloads @JvmOverloads
suspend inline fun Bot.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult = contacts.addFriend(id, message, remark) suspend inline fun Bot.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult =
contacts.addFriend(id, message, remark)
/** /**
* 取得机器人的 QQ * 取得机器人的 QQ

View File

@ -9,10 +9,8 @@ import net.mamoe.mirai.network.protocol.tim.packet.Packet
/** /**
* 数据包相关事件 * 数据包相关事件
*
* @param P 指代数据包的类型. 这个类型是 **invariant(不变的)**
*/ */
sealed class PacketEvent<P : Packet>(bot: Bot, open val packet: P) : BotEvent(bot) internal sealed class PacketEvent<P : Packet>(bot: Bot, open val packet: P) : BotEvent(bot)
/* Client to Server */ /* Client to Server */
@ -20,21 +18,21 @@ sealed class PacketEvent<P : Packet>(bot: Bot, open val packet: P) : BotEvent(bo
/** /**
* 发送给服务器的数据包的相关事件 * 发送给服务器的数据包的相关事件
*/ */
sealed class OutgoingPacketEvent(bot: Bot, packet: OutgoingPacket) : PacketEvent<OutgoingPacket>(bot, packet) internal sealed class OutgoingPacketEvent(bot: Bot, packet: OutgoingPacket) : PacketEvent<OutgoingPacket>(bot, packet)
/** /**
* 包已发送, 此时包数据已完全发送至服务器, 且包已被关闭. * 包已发送, 此时包数据已完全发送至服务器, 且包已被关闭.
* *
* 不可被取消 * 不可被取消
*/ */
class PacketSentEvent(bot: Bot, packet: OutgoingPacket) : OutgoingPacketEvent(bot, packet) internal class PacketSentEvent(bot: Bot, packet: OutgoingPacket) : OutgoingPacketEvent(bot, packet)
/** /**
* 包发送前, 此时包数据已经编码完成. * 包发送前, 此时包数据已经编码完成.
* *
* 可被取消 * 可被取消
*/ */
class BeforePacketSendEvent(bot: Bot, packet: OutgoingPacket) : OutgoingPacketEvent(bot, packet), Cancellable internal class BeforePacketSendEvent(bot: Bot, packet: OutgoingPacket) : OutgoingPacketEvent(bot, packet), Cancellable
/* Server to Client */ /* Server to Client */
@ -42,9 +40,9 @@ class BeforePacketSendEvent(bot: Bot, packet: OutgoingPacket) : OutgoingPacketEv
/** /**
* 来自服务器的数据包的相关事件 * 来自服务器的数据包的相关事件
*/ */
sealed class ServerPacketEvent<P : Packet>(bot: Bot, packet: P) : PacketEvent<P>(bot, packet) internal sealed class ServerPacketEvent<P : Packet>(bot: Bot, packet: P) : PacketEvent<P>(bot, packet)
/** /**
* 服务器数据包接收事件. 此时包已经解密完成. * 服务器数据包接收事件. 此时包已经解密完成.
*/ */
class ServerPacketReceivedEvent<P : Packet>(bot: Bot, packet: P) : ServerPacketEvent<P>(bot, packet), Cancellable internal class ServerPacketReceivedEvent<P : Packet>(bot: Bot, packet: P) : ServerPacketEvent<P>(bot, packet), Cancellable

View File

@ -47,20 +47,6 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : CoroutineScope {
*/ */
suspend fun login(): LoginResult suspend fun login(): LoginResult
/**
* 添加一个临时包处理器, 并发送相应的包
*
* @see [BotSession.sendAndExpectAsync] 发送并期待一个包
* @see [TemporaryPacketHandler] 临时包处理器
*/
@MiraiInternalAPI
suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*, *>)
/**
* 发送数据包
*/
suspend fun sendPacket(packet: OutgoingPacket)
/** /**
* 等待直到与服务器断开连接. 若未连接则立即返回 * 等待直到与服务器断开连接. 若未连接则立即返回
*/ */

View File

@ -46,7 +46,7 @@ internal inline fun TIMBotNetworkHandler.BotSession(
* @author Him188moe * @author Him188moe
*/ */
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
expect class BotSession( expect class BotSession internal constructor(
bot: Bot, bot: Bot,
sessionKey: SessionKey, sessionKey: SessionKey,
socket: DataPacketSocketAdapter, socket: DataPacketSocketAdapter,
@ -58,9 +58,9 @@ expect class BotSession(
*/ */
@MiraiInternalAPI @MiraiInternalAPI
// cannot be internal because of `public BotSession` // cannot be internal because of `public BotSession`
abstract class BotSessionBase( abstract class BotSessionBase internal constructor(
val bot: Bot, val bot: Bot,
val sessionKey: SessionKey, internal val sessionKey: SessionKey,
val socket: DataPacketSocketAdapter, val socket: DataPacketSocketAdapter,
val NetworkScope: CoroutineScope val NetworkScope: CoroutineScope
) { ) {
@ -110,33 +110,36 @@ abstract class BotSessionBase(
* *
* @see Bot.withSession 转换 receiver, `this` 的指向, [BotSession] * @see Bot.withSession 转换 receiver, `this` 的指向, [BotSession]
*/ */
suspend inline fun <reified P : Packet, R> OutgoingPacket.sendAndExpectAsync( internal suspend inline fun <reified P : Packet, R> OutgoingPacket.sendAndExpectAsync(
checkSequence: Boolean = true, checkSequence: Boolean = true,
noinline handler: suspend (P) -> R noinline handler: suspend (P) -> R
): Deferred<R> { ): Deferred<R> {
val deferred: CompletableDeferred<R> = CompletableDeferred(coroutineContext[Job]) val deferred: CompletableDeferred<R> = CompletableDeferred(coroutineContext[Job])
bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this@BotSessionBase as BotSession, checkSequence, coroutineContext + deferred).also { (bot.network as TIMBotNetworkHandler).addHandler(TemporaryPacketHandler(
P::class, deferred, this@BotSessionBase as BotSession, checkSequence, coroutineContext + deferred
).also {
it.toSend(this) it.toSend(this)
it.onExpect(handler) it.onExpect(handler)
}) })
return deferred return deferred
} }
suspend inline fun <reified P : Packet> OutgoingPacket.sendAndExpectAsync(checkSequence: Boolean = true): Deferred<P> = internal suspend inline fun <reified P : Packet> OutgoingPacket.sendAndExpectAsync(checkSequence: Boolean = true): Deferred<P> =
sendAndExpectAsync<P, P>(checkSequence) { it } sendAndExpectAsync<P, P>(checkSequence) { it }
suspend inline fun <reified P : Packet, R> OutgoingPacket.sendAndExpect( internal suspend inline fun <reified P : Packet, R> OutgoingPacket.sendAndExpect(
checkSequence: Boolean = true, checkSequence: Boolean = true,
timeout: TimeSpan = 5.seconds, timeout: TimeSpan = 5.seconds,
crossinline mapper: (P) -> R crossinline mapper: (P) -> R
): R = withTimeout(timeout.millisecondsLong) { sendAndExpectAsync<P, R>(checkSequence) { mapper(it) }.await() } ): R = withTimeout(timeout.millisecondsLong) { sendAndExpectAsync<P, R>(checkSequence) { mapper(it) }.await() }
suspend inline fun <reified P : Packet> OutgoingPacket.sendAndExpect( internal suspend inline fun <reified P : Packet> OutgoingPacket.sendAndExpect(
checkSequence: Boolean = true, checkSequence: Boolean = true,
timeout: TimeSpan = 5.seconds timeout: TimeSpan = 5.seconds
): P = withTimeout(timeout.millisecondsLong) { sendAndExpectAsync<P, P>(checkSequence) { it }.await() } ): P = withTimeout(timeout.millisecondsLong) { sendAndExpectAsync<P, P>(checkSequence) { it }.await() }
suspend inline fun OutgoingPacket.send() = socket.sendPacket(this) internal suspend inline fun OutgoingPacket.send() =
(socket as TIMBotNetworkHandler.BotSocketAdapter).sendPacket(this)
suspend inline fun Int.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0).toUInt()) suspend inline fun Int.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0).toUInt())
@ -150,8 +153,16 @@ abstract class BotSessionBase(
suspend inline fun GroupInternalId.group(): Group = bot.getGroup(this) suspend inline fun GroupInternalId.group(): Group = bot.getGroup(this)
suspend fun Image.getLink(): ImageLink = when (this.id) { suspend fun Image.getLink(): ImageLink = when (this.id) {
is ImageId0x06 -> FriendImagePacket.RequestImageLink(bot.qqAccount, bot.sessionKey, id).sendAndExpect<FriendImageLink>() is ImageId0x06 -> FriendImagePacket.RequestImageLink(
is ImageId0x03 -> GroupImagePacket.RequestImageLink(bot.qqAccount, bot.sessionKey, id).sendAndExpect<ImageDownloadInfo>().requireSuccess() bot.qqAccount,
bot.sessionKey,
id
).sendAndExpect<FriendImageLink>()
is ImageId0x03 -> GroupImagePacket.RequestImageLink(
bot.qqAccount,
bot.sessionKey,
id
).sendAndExpect<GroupImageLink>().requireSuccess()
else -> assertUnreachable() else -> assertUnreachable()
} }
@ -167,7 +178,7 @@ inline val BotSession.qqAccount: UInt get() = bot.account.id // 为了与群和
* 取得 [BotNetworkHandler] sessionKey. * 取得 [BotNetworkHandler] sessionKey.
* 实际上是一个捷径. * 实际上是一个捷径.
*/ */
inline val BotNetworkHandler<*>.sessionKey: SessionKey get() = this.session.sessionKey internal inline val BotNetworkHandler<*>.sessionKey: SessionKey get() = this.session.sessionKey
/** /**
* 取得 [Bot] [BotSession]. * 取得 [Bot] [BotSession].
@ -179,14 +190,14 @@ inline val Bot.session: BotSession get() = this.network.session
* 取得 [Bot] `sessionKey`. * 取得 [Bot] `sessionKey`.
* 实际上是一个捷径. * 实际上是一个捷径.
*/ */
inline val Bot.sessionKey: SessionKey get() = this.session.sessionKey internal inline val Bot.sessionKey: SessionKey get() = this.session.sessionKey
/** /**
* 发送数据包 * 发送数据包
* @throws IllegalStateException [BotNetworkHandler.socket] 未开启时 * @throws IllegalStateException [BotNetworkHandler.socket] 未开启时
*/ */
suspend inline fun BotSession.sendPacket(packet: OutgoingPacket) = this.bot.sendPacket(packet) internal suspend inline fun BotSession.sendPacket(packet: OutgoingPacket) = this.bot.sendPacket(packet)
suspend inline fun BotSession.getQQ(@PositiveNumbers number: Long): QQ = this.bot.getQQ(number) suspend inline fun BotSession.getQQ(@PositiveNumbers number: Long): QQ = this.bot.getQQ(number)

View File

@ -59,8 +59,7 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
private var heartbeatJob: Job? = null private var heartbeatJob: Job? = null
@UseExperimental(MiraiInternalAPI::class) suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*, *>) {
override suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*, *>) {
handlersLock.withLock { handlersLock.withLock {
temporaryPacketHandlers.add(temporaryPacketHandler) temporaryPacketHandlers.add(temporaryPacketHandler)
} }
@ -116,8 +115,6 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
this.socket.close() this.socket.close()
} }
override suspend fun sendPacket(packet: OutgoingPacket) = socket.sendPacket(packet)
internal inner class BotSocketAdapter(override val serverIp: String) : internal inner class BotSocketAdapter(override val serverIp: String) :
DataPacketSocketAdapter { DataPacketSocketAdapter {
@ -255,7 +252,7 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
loginHandler?.onPacketReceived(packet) loginHandler?.onPacketReceived(packet)
} }
override suspend fun sendPacket(packet: OutgoingPacket): Unit = withContext(coroutineContext + CoroutineName("sendPacket")) { internal suspend fun sendPacket(packet: OutgoingPacket): Unit = withContext(coroutineContext + CoroutineName("sendPacket")) {
check(channel.isOpen) { "channel is not open" } check(channel.isOpen) { "channel is not open" }
if (BeforePacketSendEvent(bot, packet).broadcast().cancelled) { if (BeforePacketSendEvent(bot, packet).broadcast().cancelled) {

View File

@ -8,6 +8,7 @@ import net.mamoe.mirai.event.events.ServerPacketReceivedEvent
import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.PlatformDatagramChannel import net.mamoe.mirai.utils.io.PlatformDatagramChannel
/** /**
@ -36,14 +37,5 @@ interface DataPacketSocketAdapter : Closeable {
*/ */
val isOpen: Boolean val isOpen: Boolean
/**
* 发送一个数据包(非异步).
*
* 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回.
*
* @see [BotSession.sendAndExpectAsync] kotlin DSL
*/
suspend fun sendPacket(packet: OutgoingPacket)
override fun close() override fun close()
} }

View File

@ -7,6 +7,7 @@ import kotlinx.coroutines.withContext
import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.Packet import net.mamoe.mirai.network.protocol.tim.packet.Packet
import net.mamoe.mirai.network.sendPacket
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -23,7 +24,7 @@ import kotlin.reflect.KClass
* *
* @see BotSession.sendAndExpectAsync * @see BotSession.sendAndExpectAsync
*/ */
class TemporaryPacketHandler<P : Packet, R>( internal class TemporaryPacketHandler<P : Packet, R>(
private val expectationClass: KClass<P>, private val expectationClass: KClass<P>,
private val deferred: CompletableDeferred<R>, private val deferred: CompletableDeferred<R>,
private val fromSession: BotSession, private val fromSession: BotSession,
@ -40,24 +41,26 @@ class TemporaryPacketHandler<P : Packet, R>(
lateinit var session: BotSession//无需覆盖 lateinit var session: BotSession//无需覆盖
fun toSend(packet: OutgoingPacket) { @Suppress("NOTHING_TO_INLINE")
inline fun toSend(packet: OutgoingPacket) {
this.toSend = packet this.toSend = packet
} }
fun onExpect(handler: suspend (P) -> R) { @Suppress("NOTHING_TO_INLINE")
inline fun onExpect(noinline handler: suspend (P) -> R) {
this.handler = handler this.handler = handler
} }
internal suspend fun send(session: BotSession) { internal suspend inline fun send(session: BotSession) {
require(::handler.isInitialized) { "handler is not initialized" }
this.session = session this.session = session
session.socket.sendPacket(toSend) session.sendPacket(toSend)
} }
internal fun filter(session: BotSession, packet: Packet, sequenceId: UShort): Boolean = @Suppress("NOTHING_TO_INLINE")
internal inline fun filter(session: BotSession, packet: Packet, sequenceId: UShort): Boolean =
expectationClass.isInstance(packet) && session === this.fromSession && if (checkSequence) sequenceId == toSend.sequenceId else true expectationClass.isInstance(packet) && session === this.fromSession && if (checkSequence) sequenceId == toSend.sequenceId else true
internal suspend fun doReceiveWithoutExceptions(packet: Packet) { internal suspend inline fun doReceiveWithoutExceptions(packet: Packet) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val ret = try { val ret = try {
withContext(callerContext) { withContext(callerContext) {
@ -68,6 +71,5 @@ class TemporaryPacketHandler<P : Packet, R>(
return return
} }
deferred.complete(ret) deferred.complete(ret)
return
} }
} }

View File

@ -9,11 +9,11 @@ package net.mamoe.mirai.network.protocol.tim.packet
@MustBeDocumented @MustBeDocumented
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
annotation class AnnotatedId( // 注解无法在 JS 平台使用, 但现在暂不需要考虑 JS internal annotation class AnnotatedId( // 注解无法在 JS 平台使用, 但现在暂不需要考虑 JS
val id: KnownPacketId val id: KnownPacketId
) )
inline val AnnotatedId.value: UShort get() = id.value internal inline val AnnotatedId.value: UShort get() = id.value
/** /**
* 包的最后一次修改时间, 和分析时使用的 TIM 版本 * 包的最后一次修改时间, 和分析时使用的 TIM 版本

View File

@ -8,14 +8,14 @@ import net.mamoe.mirai.utils.decryptBy
/** /**
* 会话密匙 * 会话密匙
*/ */
inline class SessionKey(override val value: ByteArray) : DecrypterByteArray { internal inline class SessionKey(override val value: ByteArray) : DecrypterByteArray {
companion object Type : DecrypterType<SessionKey> companion object Type : DecrypterType<SessionKey>
} }
/** /**
* [ByteArray] 解密器 * [ByteArray] 解密器
*/ */
interface DecrypterByteArray : Decrypter { internal interface DecrypterByteArray : Decrypter {
val value: ByteArray val value: ByteArray
override fun decrypt(input: ByteReadPacket): ByteReadPacket = input.decryptBy(value) override fun decrypt(input: ByteReadPacket): ByteReadPacket = input.decryptBy(value)
} }
@ -23,7 +23,7 @@ interface DecrypterByteArray : Decrypter {
/** /**
* [IoBuffer] 解密器 * [IoBuffer] 解密器
*/ */
interface DecrypterIoBuffer : Decrypter { internal interface DecrypterIoBuffer : Decrypter {
val value: IoBuffer val value: IoBuffer
override fun decrypt(input: ByteReadPacket): ByteReadPacket = input.decryptBy(value) override fun decrypt(input: ByteReadPacket): ByteReadPacket = input.decryptBy(value)
} }
@ -31,18 +31,18 @@ interface DecrypterIoBuffer : Decrypter {
/** /**
* 连接在一起的解密器 * 连接在一起的解密器
*/ */
inline class LinkedDecrypter(inline val block: (ByteReadPacket) -> ByteReadPacket) : Decrypter { internal inline class LinkedDecrypter(inline val block: (ByteReadPacket) -> ByteReadPacket) : Decrypter {
override fun decrypt(input: ByteReadPacket): ByteReadPacket = block(input) override fun decrypt(input: ByteReadPacket): ByteReadPacket = block(input)
} }
object NoDecrypter : Decrypter, DecrypterType<NoDecrypter> { internal object NoDecrypter : Decrypter, DecrypterType<NoDecrypter> {
override fun decrypt(input: ByteReadPacket): ByteReadPacket = input override fun decrypt(input: ByteReadPacket): ByteReadPacket = input
} }
/** /**
* 解密器 * 解密器
*/ */
interface Decrypter { internal interface Decrypter {
fun decrypt(input: ByteReadPacket): ByteReadPacket fun decrypt(input: ByteReadPacket): ByteReadPacket
/** /**
* 连接后将会先用 this 解密, 再用 [another] 解密 * 连接后将会先用 this 解密, 再用 [another] 解密
@ -50,4 +50,4 @@ interface Decrypter {
operator fun plus(another: Decrypter): Decrypter = LinkedDecrypter { another.decrypt(this.decrypt(it)) } operator fun plus(another: Decrypter): Decrypter = LinkedDecrypter { another.decrypt(this.decrypt(it)) }
} }
interface DecrypterType<D : Decrypter> internal interface DecrypterType<D : Decrypter>

View File

@ -18,7 +18,7 @@ import kotlin.jvm.JvmOverloads
/** /**
* 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket], * 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket],
*/ */
class OutgoingPacket( internal class OutgoingPacket(
name: String?, name: String?,
val packetId: PacketId, val packetId: PacketId,
val sequenceId: UShort, val sequenceId: UShort,
@ -35,7 +35,7 @@ class OutgoingPacket(
* *
* @param TPacket invariant * @param TPacket invariant
*/ */
abstract class SessionPacketFactory<TPacket : Packet> : PacketFactory<TPacket, SessionKey>(SessionKey) { internal abstract class SessionPacketFactory<TPacket : Packet> : PacketFactory<TPacket, SessionKey>(SessionKey) {
/** /**
* [BotNetworkHandler] 下处理这个包. 广播事件等. * [BotNetworkHandler] 下处理这个包. 广播事件等.
*/ */
@ -49,7 +49,7 @@ abstract class SessionPacketFactory<TPacket : Packet> : PacketFactory<TPacket, S
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@JvmOverloads @JvmOverloads
inline fun PacketFactory<*, *>.buildOutgoingPacket( internal inline fun PacketFactory<*, *>.buildOutgoingPacket(
name: String? = null, name: String? = null,
id: PacketId = this.id, id: PacketId = this.id,
sequenceId: UShort = PacketFactory.atomicNextSequenceId(), sequenceId: UShort = PacketFactory.atomicNextSequenceId(),
@ -81,7 +81,7 @@ inline fun PacketFactory<*, *>.buildOutgoingPacket(
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@JvmOverloads @JvmOverloads
inline fun PacketFactory<*, *>.buildSessionPacket( internal inline fun PacketFactory<*, *>.buildSessionPacket(
bot: UInt, bot: UInt,
sessionKey: SessionKey, sessionKey: SessionKey,
name: String? = null, name: String? = null,
@ -110,7 +110,7 @@ inline fun PacketFactory<*, *>.buildSessionPacket(
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@JvmOverloads @JvmOverloads
fun <T> PacketFactory<*, *>.buildSessionProtoPacket( internal fun <T> PacketFactory<*, *>.buildSessionProtoPacket(
bot: UInt, bot: UInt,
sessionKey: SessionKey, sessionKey: SessionKey,
name: String? = null, name: String? = null,
@ -142,7 +142,18 @@ fun <T> PacketFactory<*, *>.buildSessionProtoPacket(
writeFully(head) writeFully(head)
writeFully(proto) writeFully(proto)
} }
is String -> buildSessionProtoPacket(bot, sessionKey, name, id, sequenceId, headerSizeHint, version, head.hexToBytes(), serializer, protoObj) is String -> buildSessionProtoPacket(
bot,
sessionKey,
name,
id,
sequenceId,
headerSizeHint,
version,
head.hexToBytes(),
serializer,
protoObj
)
} }
} }
} }

View File

@ -12,18 +12,18 @@ interface Packet
/** /**
* 被忽略的数据包. * 被忽略的数据包.
*/ */
inline class IgnoredPacket(val id: PacketId) : Packet internal inline class IgnoredPacket(internal val id: PacketId) : Packet
/** /**
* 未知的包. * 未知的包.
*/ */
class UnknownPacket(val id: PacketId, val body: ByteReadPacket) : Packet { internal class UnknownPacket(val id: PacketId, val body: ByteReadPacket) : Packet {
override fun toString(): String = "UnknownPacket(${id.value.toUHexString()})\nbody=${body.readBytes().toUHexString()}" override fun toString(): String = "UnknownPacket(${id.value.toUHexString()})\nbody=${body.readBytes().toUHexString()}"
} }
/** /**
* 仅用于替换类型应为 [Unit] 的情况 * 仅用于替换类型应为 [Unit] 的情况
*/ */
object NoPacket : Packet { internal object NoPacket : Packet {
override fun toString(): String = "NoPacket" override fun toString(): String = "NoPacket"
} }

View File

@ -10,6 +10,7 @@ import kotlinx.io.pool.useInstance
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.ByteArrayPool import net.mamoe.mirai.utils.io.ByteArrayPool
import net.mamoe.mirai.utils.io.debugPrint import net.mamoe.mirai.utils.io.debugPrint
import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.read
@ -23,7 +24,7 @@ import net.mamoe.mirai.utils.readProtoMap
* @param TPacket 服务器回复包解析结果 * @param TPacket 服务器回复包解析结果
* @param TDecrypter 服务器回复包解密器 * @param TDecrypter 服务器回复包解密器
*/ */
abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypter>(val decrypterType: DecrypterType<TDecrypter>) { internal abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypter>(val decrypterType: DecrypterType<TDecrypter>) {
/** /**
* 2 Ubyte. * 2 Ubyte.
@ -46,7 +47,10 @@ abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypter>(val d
abstract suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): TPacket abstract suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): TPacket
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
fun <T> ByteReadPacket.decodeProtoPacket(deserializer: DeserializationStrategy<T>, debuggingTag: String? = null): T { fun <T> ByteReadPacket.decodeProtoPacket(
deserializer: DeserializationStrategy<T>,
debuggingTag: String? = null
): T {
val headLength = readInt() val headLength = readInt()
val protoLength = readInt() val protoLength = readInt()
if (debuggingTag != null) { if (debuggingTag != null) {
@ -72,7 +76,7 @@ abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypter>(val d
} }
} }
object UnknownPacketFactory : SessionPacketFactory<UnknownPacket>() { internal object UnknownPacketFactory : SessionPacketFactory<UnknownPacket>() {
override suspend fun BotNetworkHandler<*>.handlePacket(packet: UnknownPacket) { override suspend fun BotNetworkHandler<*>.handlePacket(packet: UnknownPacket) {
ByteArrayPool.useInstance { ByteArrayPool.useInstance {
packet.body.readAvailable(it) packet.body.readAvailable(it)
@ -81,11 +85,19 @@ object UnknownPacketFactory : SessionPacketFactory<UnknownPacket>() {
packet.body.close() packet.body.close()
} }
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): UnknownPacket { override suspend fun ByteReadPacket.decode(
id: PacketId,
sequenceId: UShort,
handler: BotNetworkHandler<*>
): UnknownPacket {
return UnknownPacket(id, this) return UnknownPacket(id, this)
} }
} }
object IgnoredPacketFactory : SessionPacketFactory<IgnoredPacket>() { internal object IgnoredPacketFactory : SessionPacketFactory<IgnoredPacket>() {
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): IgnoredPacket = IgnoredPacket(id) override suspend fun ByteReadPacket.decode(
id: PacketId,
sequenceId: UShort,
handler: BotNetworkHandler<*>
): IgnoredPacket = IgnoredPacket(id)
} }

View File

@ -12,13 +12,13 @@ import net.mamoe.mirai.utils.io.toUHexString
/** /**
* 通过 [value] 匹配一个 [IgnoredPacketId] [KnownPacketId], 无匹配则返回一个 [UnknownPacketId]. * 通过 [value] 匹配一个 [IgnoredPacketId] [KnownPacketId], 无匹配则返回一个 [UnknownPacketId].
*/ */
fun matchPacketId(value: UShort): PacketId = internal fun matchPacketId(value: UShort): PacketId =
IgnoredPacketIds.firstOrNull { it.value == value } ?: KnownPacketId.values().firstOrNull { it.value == value } ?: UnknownPacketId(value) IgnoredPacketIds.firstOrNull { it.value == value } ?: KnownPacketId.values().firstOrNull { it.value == value } ?: UnknownPacketId(value)
/** /**
* ID. * ID.
*/ */
interface PacketId { internal interface PacketId {
val value: UShort val value: UShort
val factory: PacketFactory<*, *> val factory: PacketFactory<*, *>
} }
@ -27,7 +27,7 @@ interface PacketId {
* 用于代表 `null`. 调用任何属性时都将会得到一个 [error] * 用于代表 `null`. 调用任何属性时都将会得到一个 [error]
*/ */
@Suppress("unused") @Suppress("unused")
object NullPacketId : PacketId { internal object NullPacketId : PacketId {
override val factory: PacketFactory<*, *> get() = error("uninitialized") override val factory: PacketFactory<*, *> get() = error("uninitialized")
override val value: UShort get() = error("uninitialized") override val value: UShort get() = error("uninitialized")
override fun toString(): String = "NullPacketId" override fun toString(): String = "NullPacketId"
@ -36,17 +36,17 @@ object NullPacketId : PacketId {
/** /**
* 未知的 [PacketId] * 未知的 [PacketId]
*/ */
inline class UnknownPacketId(override inline val value: UShort) : PacketId { internal inline class UnknownPacketId(override inline val value: UShort) : PacketId {
override val factory: PacketFactory<*, *> get() = UnknownPacketFactory override val factory: PacketFactory<*, *> get() = UnknownPacketFactory
override fun toString(): String = "UnknownPacketId(${value.toUHexString()})" override fun toString(): String = "UnknownPacketId(${value.toUHexString()})"
} }
object IgnoredPacketIds : List<IgnoredPacketId> by { internal object IgnoredPacketIds : List<IgnoredPacketId> by {
listOf<UShort>( listOf<UShort>(
).map { IgnoredPacketId(it.toUShort()) } ).map { IgnoredPacketId(it.toUShort()) }
}() }()
inline class IgnoredPacketId constructor(override val value: UShort) : PacketId { internal inline class IgnoredPacketId constructor(override val value: UShort) : PacketId {
override val factory: PacketFactory<*, *> get() = IgnoredPacketFactory override val factory: PacketFactory<*, *> get() = IgnoredPacketFactory
override fun toString(): String = "IgnoredPacketId(${value.toUHexString()})" override fun toString(): String = "IgnoredPacketId(${value.toUHexString()})"
} }
@ -55,7 +55,7 @@ inline class IgnoredPacketId constructor(override val value: UShort) : PacketId
* 已知的 [matchPacketId]. 所有在 Mirai 中实现过的包都会使用这些 Id * 已知的 [matchPacketId]. 所有在 Mirai 中实现过的包都会使用这些 Id
*/ */
@Suppress("unused") @Suppress("unused")
enum class KnownPacketId(override inline val value: UShort, override inline val factory: PacketFactory<*, *>) : internal enum class KnownPacketId(override inline val value: UShort, override inline val factory: PacketFactory<*, *>) :
PacketId { PacketId {
inline TOUCH(0x0825u, TouchPacket), inline TOUCH(0x0825u, TouchPacket),
inline SESSION_KEY(0x0828u, RequestSessionPacket), inline SESSION_KEY(0x0828u, RequestSessionPacket),

View File

@ -21,7 +21,7 @@ import net.mamoe.mirai.withSession
*/ */
@AnnotatedId(KnownPacketId.QUERY_PREVIOUS_NAME) @AnnotatedId(KnownPacketId.QUERY_PREVIOUS_NAME)
@PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)")
object QueryPreviousNamePacket : SessionPacketFactory<PreviousNameList>() { internal object QueryPreviousNamePacket : SessionPacketFactory<PreviousNameList>() {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
sessionKey: SessionKey, sessionKey: SessionKey,
@ -79,7 +79,7 @@ class PreviousNameList(
*/ */
@AnnotatedId(KnownPacketId.CAN_ADD_FRIEND) @AnnotatedId(KnownPacketId.CAN_ADD_FRIEND)
@PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)")
object CanAddFriendPacket : SessionPacketFactory<CanAddFriendResponse>() { internal object CanAddFriendPacket : SessionPacketFactory<CanAddFriendResponse>() {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
qq: UInt, qq: UInt,
@ -112,7 +112,7 @@ object CanAddFriendPacket : SessionPacketFactory<CanAddFriendResponse>() {
} }
sealed class CanAddFriendResponse : EventPacket { internal sealed class CanAddFriendResponse : EventPacket {
abstract val qq: QQ abstract val qq: QQ
/** /**
@ -157,7 +157,7 @@ inline class FriendAdditionKey(val value: IoBuffer)
*/ */
@AnnotatedId(KnownPacketId.REQUEST_FRIEND_ADDITION_KEY) @AnnotatedId(KnownPacketId.REQUEST_FRIEND_ADDITION_KEY)
@PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)")
object RequestFriendAdditionKeyPacket : SessionPacketFactory<RequestFriendAdditionKeyPacket.Response>() { internal object RequestFriendAdditionKeyPacket : SessionPacketFactory<RequestFriendAdditionKeyPacket.Response>() {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
qq: UInt, qq: UInt,
@ -183,8 +183,9 @@ object RequestFriendAdditionKeyPacket : SessionPacketFactory<RequestFriendAdditi
* 请求添加好友 * 请求添加好友
*/ */
@AnnotatedId(KnownPacketId.ADD_FRIEND) @AnnotatedId(KnownPacketId.ADD_FRIEND)
object AddFriendPacket : SessionPacketFactory<AddFriendPacket.Response>() { internal object AddFriendPacket : SessionPacketFactory<AddFriendPacket.Response>() {
@PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)")
@Suppress("FunctionName")
fun RequestAdd( fun RequestAdd(
bot: UInt, bot: UInt,
qq: UInt, qq: UInt,
@ -198,7 +199,7 @@ object AddFriendPacket : SessionPacketFactory<AddFriendPacket.Response>() {
*/ */
remark: String?, //// TODO: 2019/11/15 无备注的情况 remark: String?, //// TODO: 2019/11/15 无备注的情况
key: FriendAdditionKey key: FriendAdditionKey
): OutgoingPacket = buildSessionPacket(bot, sessionKey) { ): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "AddFriendPacket.RequestAdd") {
//02 5D 12 93 30 //02 5D 12 93 30
// 00 // 00
@ -264,7 +265,7 @@ object AddFriendPacket : SessionPacketFactory<AddFriendPacket.Response>() {
* 备注. 不设置则需要为 `null` TODO 需要确认是否还需发送一个设置备注包. 因为测试时若有备注则会多发一个包并且包里面有所设置的备注 * 备注. 不设置则需要为 `null` TODO 需要确认是否还需发送一个设置备注包. 因为测试时若有备注则会多发一个包并且包里面有所设置的备注
*/ */
remark: String? remark: String?
): OutgoingPacket = buildSessionPacket(bot, sessionKey, version = TIMProtocol.version0x02) { ): OutgoingPacket = buildSessionPacket(bot, sessionKey, version = TIMProtocol.version0x02, name = "AddFriendPacket.Approve") {
writeByte(0x03) writeByte(0x03)
writeQQ(qq) writeQQ(qq)
writeZero(1) writeZero(1)
@ -282,7 +283,7 @@ object AddFriendPacket : SessionPacketFactory<AddFriendPacket.Response>() {
writeHex("00 05 00 00 00 00 01") writeHex("00 05 00 00 00 00 01")
} }
object Response : Packet { internal object Response : Packet {
override fun toString(): String = "AddFriendPacket.Response" override fun toString(): String = "AddFriendPacket.Response"
} }

View File

@ -29,31 +29,30 @@ import net.mamoe.mirai.withSession
* @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时 * @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时
*/ */
suspend fun QQ.uploadImage(image: ExternalImage): ImageId = bot.withSession { suspend fun QQ.uploadImage(image: ExternalImage): ImageId = bot.withSession {
FriendImagePacket.RequestImageId(qqAccount, sessionKey, id, image) FriendImagePacket.RequestImageId(qqAccount, sessionKey, id, image).sendAndExpect<FriendImageResponse>().let {
.sendAndExpectAsync<FriendImageResponse, ImageId> { when (it) {
return@sendAndExpectAsync when (it) { is FriendImageUKey -> {
is FriendImageUKey -> { Http.postImage(
Http.postImage( htcmd = "0x6ff0070",
htcmd = "0x6ff0070", uin = bot.qqAccount,
uin = bot.qqAccount, groupId = null,
groupId = null, uKeyHex = it.uKey.toUHexString(""),
uKeyHex = it.uKey.toUHexString(""), imageInput = image.input,
imageInput = image.input, inputSize = image.inputSize
inputSize = image.inputSize )
) it.imageId
it.imageId
}
is FriendImageAlreadyExists -> it.imageId
is FriendImageOverFileSizeMax -> throw OverFileSizeMaxException()
else -> error("This shouldn't happen")
} }
}.await() is FriendImageAlreadyExists -> it.imageId
is FriendImageOverFileSizeMax -> throw OverFileSizeMaxException()
else -> error("This shouldn't happen")
}
}
} }
// region FriendImageResponse // region FriendImageResponse
interface FriendImageResponse : EventPacket internal interface FriendImageResponse : EventPacket
/** /**
* 图片数据地址. * 图片数据地址.
@ -66,7 +65,7 @@ data class FriendImageLink(override inline val original: String) : FriendImageRe
/** /**
* 访问 HTTP API 时使用的 uKey * 访问 HTTP API 时使用的 uKey
*/ */
class FriendImageUKey(inline val imageId: ImageId, inline val uKey: ByteArray) : FriendImageResponse { internal class FriendImageUKey(inline val imageId: ImageId, inline val uKey: ByteArray) : FriendImageResponse {
override fun toString(): String = "FriendImageUKey(imageId=${imageId.value}, uKey=${uKey.toUHexString()})" override fun toString(): String = "FriendImageUKey(imageId=${imageId.value}, uKey=${uKey.toUHexString()})"
} }
@ -74,14 +73,14 @@ class FriendImageUKey(inline val imageId: ImageId, inline val uKey: ByteArray) :
* 图片 ID 已存在 * 图片 ID 已存在
* 发送消息时使用的 id * 发送消息时使用的 id
*/ */
inline class FriendImageAlreadyExists(inline val imageId: ImageId) : FriendImageResponse { internal inline class FriendImageAlreadyExists(inline val imageId: ImageId) : FriendImageResponse {
override fun toString(): String = "FriendImageAlreadyExists(imageId=${imageId.value})" override fun toString(): String = "FriendImageAlreadyExists(imageId=${imageId.value})"
} }
/** /**
* 超过文件大小上限 * 超过文件大小上限
*/ */
object FriendImageOverFileSizeMax : FriendImageResponse { internal object FriendImageOverFileSizeMax : FriendImageResponse {
override fun toString(): String = "FriendImageOverFileSizeMax" override fun toString(): String = "FriendImageOverFileSizeMax"
} }
@ -95,14 +94,19 @@ object FriendImageOverFileSizeMax : FriendImageResponse {
*/ */
@AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID) @AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
@PacketVersion(date = "2019.11.16", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.16", timVersion = "2.3.2 (21173)")
object FriendImagePacket : SessionPacketFactory<FriendImageResponse>() { internal object FriendImagePacket : SessionPacketFactory<FriendImageResponse>() {
@Suppress("FunctionName") @Suppress("FunctionName")
fun RequestImageId( fun RequestImageId(
bot: UInt, bot: UInt,
sessionKey: SessionKey, sessionKey: SessionKey,
target: UInt, target: UInt,
image: ExternalImage image: ExternalImage
): OutgoingPacket = buildSessionPacket(bot, sessionKey, version = TIMProtocol.version0x04) { ): OutgoingPacket = buildSessionPacket(
bot,
sessionKey,
version = TIMProtocol.version0x04,
name = "FriendImagePacket.RequestPacketId"
) {
writeHex("00 00 00 07 00 00") writeHex("00 00 00 07 00 00")
@ -186,7 +190,12 @@ object FriendImagePacket : SessionPacketFactory<FriendImageResponse>() {
// TODO: 2019/11/22 should be ProtoBuf // TODO: 2019/11/22 should be ProtoBuf
return buildSessionPacket(bot, sessionKey, version = TIMProtocol.version0x04) { return buildSessionPacket(
bot,
sessionKey,
version = TIMProtocol.version0x04,
name = "FriendImagePacket.RequestImageLink"
) {
writeHex("00 00 00 07 00 00") writeHex("00 00 00 07 00 00")
writeUShort(0x004Bu) writeUShort(0x004Bu)
@ -206,7 +215,11 @@ object FriendImagePacket : SessionPacketFactory<FriendImageResponse>() {
} }
} }
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): FriendImageResponse { override suspend fun ByteReadPacket.decode(
id: PacketId,
sequenceId: UShort,
handler: BotNetworkHandler<*>
): FriendImageResponse {
// 上传图片, 成功获取ID // 上传图片, 成功获取ID
//00 00 00 08 00 00 //00 00 00 08 00 00
@ -304,7 +317,6 @@ object FriendImagePacket : SessionPacketFactory<FriendImageResponse>() {
// 3A 00 80 01 00 // 3A 00 80 01 00
//00 00 00 08 00 00 //00 00 00 08 00 00
// [02 29] // [02 29]
// 12 [06] 98 01 02 A0 01 00 // 12 [06] 98 01 02 A0 01 00

View File

@ -17,7 +17,7 @@ import net.mamoe.mirai.utils.io.writeQQ
* @author Him188moe * @author Him188moe
*/ */
@AnnotatedId(KnownPacketId.ACCOUNT_INFO) @AnnotatedId(KnownPacketId.ACCOUNT_INFO)
object RequestAccountInfoPacket : SessionPacketFactory<RequestAccountInfoPacket.Response>() { internal object RequestAccountInfoPacket : SessionPacketFactory<RequestAccountInfoPacket.Response>() {
operator fun invoke( operator fun invoke(
qq: UInt, qq: UInt,
sessionKey: SessionKey sessionKey: SessionKey

View File

@ -21,7 +21,6 @@ import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.Http import net.mamoe.mirai.utils.Http
import net.mamoe.mirai.utils.assertUnreachable import net.mamoe.mirai.utils.assertUnreachable
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
import kotlin.coroutines.coroutineContext
/** /**
@ -58,13 +57,13 @@ suspend fun Group.uploadImage(image: ExternalImage): ImageId = withSession {
return image.groupImageId return image.groupImageId
} }
interface GroupImageResponse : EventPacket internal interface GroupImageResponse : EventPacket
// endregion // endregion
@Suppress("unused") @Suppress("unused")
@Serializable @Serializable
class ImageDownloadInfo( class GroupImageLink(
@SerialId(3) val errorCode: Int = 0, // 0 for success @SerialId(3) val errorCode: Int = 0, // 0 for success
@SerialId(4) val errorMessage: String? = null, // 感动中国 @SerialId(4) val errorMessage: String? = null, // 感动中国
@ -84,13 +83,14 @@ class ImageDownloadInfo(
override fun toString(): String = "ImageDownloadInfo(${_original?.let { original } ?: errorMessage ?: "unknown"})" override fun toString(): String = "ImageDownloadInfo(${_original?.let { original } ?: errorMessage ?: "unknown"})"
} }
fun ImageDownloadInfo.requireSuccess(): ImageDownloadInfo { @Suppress("NOTHING_TO_INLINE")
internal inline fun GroupImageLink.requireSuccess(): GroupImageLink {
require(this.errorCode == 0) { this.errorMessage ?: "null" } require(this.errorCode == 0) { this.errorMessage ?: "null" }
return this return this
} }
@Serializable @Serializable
class ImageUploadInfo( internal class ImageUploadInfo(
@SerialId(8) val uKey: ByteArray? = null @SerialId(8) val uKey: ByteArray? = null
) : GroupImageResponse { ) : GroupImageResponse {
override fun toString(): String = "ImageUploadInfo(uKey=${uKey?.toUHexString()})" override fun toString(): String = "ImageUploadInfo(uKey=${uKey?.toUHexString()})"
@ -101,7 +101,7 @@ class ImageUploadInfo(
*/ */
@AnnotatedId(KnownPacketId.GROUP_IMAGE_ID) @AnnotatedId(KnownPacketId.GROUP_IMAGE_ID)
@PacketVersion(date = "2019.11.22", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.22", timVersion = "2.3.2 (21173)")
object GroupImagePacket : SessionPacketFactory<GroupImageResponse>() { internal object GroupImagePacket : SessionPacketFactory<GroupImageResponse>() {
private val constValue3 = byteArrayOf( private val constValue3 = byteArrayOf(
0x28, 0x00, 0x5A, 0x00, 0x53, 0x00, 0x41, 0x00, 0x58, 0x00, 0x40, 0x00, 0x57, 0x28, 0x00, 0x5A, 0x00, 0x53, 0x00, 0x41, 0x00, 0x58, 0x00, 0x40, 0x00, 0x57,
@ -115,7 +115,7 @@ object GroupImagePacket : SessionPacketFactory<GroupImageResponse>() {
@SerialId(3) var body: Body @SerialId(3) var body: Body
) { ) {
@Serializable @Serializable
class Body( internal class Body(
@SerialId(1) val group: Int, @SerialId(1) val group: Int,
@SerialId(2) val bot: Int, @SerialId(2) val bot: Int,
@SerialId(3) val const1: Byte = 0, @SerialId(3) val const1: Byte = 0,
@ -142,7 +142,7 @@ object GroupImagePacket : SessionPacketFactory<GroupImageResponse>() {
@SerialId(4) var body: Body @SerialId(4) var body: Body
) { ) {
@Serializable @Serializable
class Body( internal class Body(
@SerialId(1) val group: Int, @SerialId(1) val group: Int,
@SerialId(2) val bot: Int, @SerialId(2) val bot: Int,
@SerialId(3) val uniqueId: Int, @SerialId(3) val uniqueId: Int,
@ -226,13 +226,13 @@ object GroupImagePacket : SessionPacketFactory<GroupImageResponse>() {
@Serializable @Serializable
data class GroupImageResponseProto( data class GroupImageResponseProto(
@SerialId(3) val imageUploadInfoPacket: ImageUploadInfo? = null, @SerialId(3) val imageUploadInfoPacket: ImageUploadInfo? = null,
@SerialId(4) val imageDownloadInfo: ImageDownloadInfo? = null @SerialId(4) val groupImageLink: GroupImageLink? = null
) )
val proto = decodeProtoPacket(GroupImageResponseProto.serializer()) val proto = decodeProtoPacket(GroupImageResponseProto.serializer())
return when { return when {
proto.imageUploadInfoPacket != null -> proto.imageUploadInfoPacket proto.imageUploadInfoPacket != null -> proto.imageUploadInfoPacket
proto.imageDownloadInfo != null -> proto.imageDownloadInfo proto.groupImageLink != null -> proto.groupImageLink
else -> assertUnreachable() else -> assertUnreachable()
} }
} }

View File

@ -4,7 +4,7 @@ package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.internal.MemberImpl import net.mamoe.mirai.contact.internal.Member
import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.message.internal.toPacket import net.mamoe.mirai.message.internal.toPacket
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
@ -32,10 +32,11 @@ class GroupInfo(
val announcement: String get() = _announcement val announcement: String get() = _announcement
val members: ContactList<Member> get() = _members val members: ContactList<Member> get() = _members
override fun toString(): String = "GroupInfo(id=${group.id}, owner=$owner, name=$name, announcement=$announcement, members=${members.idContentString}" override fun toString(): String =
"GroupInfo(id=${group.id}, owner=$owner, name=$name, announcement=$announcement, members=${members.idContentString}"
} }
data class RawGroupInfo( internal data class RawGroupInfo(
val group: UInt, val group: UInt,
val owner: UInt, val owner: UInt,
val name: String, val name: String,
@ -49,10 +50,12 @@ data class RawGroupInfo(
suspend inline fun parseBy(group: Group): GroupInfo = group.bot.withSession { suspend inline fun parseBy(group: Group): GroupInfo = group.bot.withSession {
return GroupInfo( return GroupInfo(
group, group,
MemberImpl(this@RawGroupInfo.owner.qq(), group, MemberPermission.OWNER), this@RawGroupInfo.owner.qq().let { Member(it, group, MemberPermission.OWNER, it.coroutineContext) },
this@RawGroupInfo.name, this@RawGroupInfo.name,
this@RawGroupInfo.announcement, this@RawGroupInfo.announcement,
ContactList(this@RawGroupInfo.members.mapValuesTo(MutableContactList()) { MemberImpl(it.key.qq(), group, it.value) }) ContactList(this@RawGroupInfo.members.mapValuesTo(MutableContactList()) { entry: Map.Entry<UInt, MemberPermission> ->
entry.key.qq().let { Member(it,group, entry.value, it.coroutineContext) }
})
) )
} }
} }
@ -69,14 +72,14 @@ inline class QuitGroupResponse(private val _group: GroupInternalId?) : Packet, G
@Suppress("FunctionName") @Suppress("FunctionName")
@AnnotatedId(KnownPacketId.GROUP_PACKET) @AnnotatedId(KnownPacketId.GROUP_PACKET)
object GroupPacket : SessionPacketFactory<GroupPacket.GroupPacketResponse>() { internal object GroupPacket : SessionPacketFactory<GroupPacket.GroupPacketResponse>() {
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)")
fun Message( fun Message(
bot: UInt, bot: UInt,
groupInternalId: GroupInternalId, groupInternalId: GroupInternalId,
sessionKey: SessionKey, sessionKey: SessionKey,
message: MessageChain message: MessageChain
): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "GroupMessage") { ): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "GroupPacket.GroupMessage") {
writeUByte(0x2Au) writeUByte(0x2Au)
writeGroup(groupInternalId) writeGroup(groupInternalId)
@ -102,7 +105,7 @@ object GroupPacket : SessionPacketFactory<GroupPacket.GroupPacketResponse>() {
bot: UInt, bot: UInt,
sessionKey: SessionKey, sessionKey: SessionKey,
group: GroupInternalId group: GroupInternalId
): OutgoingPacket = buildSessionPacket(bot, sessionKey) { ): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "GroupPacket.QuitGroup") {
writeUByte(0x09u) writeUByte(0x09u)
writeGroup(group) writeGroup(group)
} }
@ -115,7 +118,7 @@ object GroupPacket : SessionPacketFactory<GroupPacket.GroupPacketResponse>() {
bot: UInt, bot: UInt,
groupInternalId: GroupInternalId, groupInternalId: GroupInternalId,
sessionKey: SessionKey sessionKey: SessionKey
): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "QueryGroupInfo", headerSizeHint = 9) { ): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "GroupPacket.QueryGroupInfo", headerSizeHint = 9) {
writeUByte(0x72u) writeUByte(0x72u)
writeGroup(groupInternalId) writeGroup(groupInternalId)
writeZero(4) writeZero(4)
@ -134,7 +137,7 @@ object GroupPacket : SessionPacketFactory<GroupPacket.GroupPacketResponse>() {
* 0 为取消 * 0 为取消
*/ */
timeSeconds: UInt timeSeconds: UInt
): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "MuteMember") { ): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "GroupPacket.Mute") {
writeUByte(0x7Eu) writeUByte(0x7Eu)
writeGroup(groupInternalId) writeGroup(groupInternalId)
writeByte(0x20) writeByte(0x20)
@ -144,21 +147,25 @@ object GroupPacket : SessionPacketFactory<GroupPacket.GroupPacketResponse>() {
writeUInt(timeSeconds) writeUInt(timeSeconds)
} }
interface GroupPacketResponse : Packet internal interface GroupPacketResponse : Packet
@NoLog @NoLog
object MessageResponse : Packet, GroupPacketResponse { internal object MessageResponse : Packet, GroupPacketResponse {
override fun toString(): String = "GroupPacket.MessageResponse" override fun toString(): String = "GroupPacket.MessageResponse"
} }
@NoLog @NoLog
object MuteResponse : Packet, GroupPacketResponse { internal object MuteResponse : Packet, GroupPacketResponse {
override fun toString(): String = "GroupPacket.MuteResponse" override fun toString(): String = "GroupPacket.MuteResponse"
} }
@PacketVersion(date = "2019.11.27", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.27", timVersion = "2.3.2 (21173)")
@UseExperimental(ExperimentalStdlibApi::class) @UseExperimental(ExperimentalStdlibApi::class)
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): GroupPacketResponse { override suspend fun ByteReadPacket.decode(
id: PacketId,
sequenceId: UShort,
handler: BotNetworkHandler<*>
): GroupPacketResponse {
return when (readUByte().toUInt()) { return when (readUByte().toUInt()) {
0x2Au -> MessageResponse 0x2Au -> MessageResponse
0x7Eu -> MuteResponse // 成功: 7E 00 22 96 29 7B; 0x7Eu -> MuteResponse // 成功: 7E 00 22 96 29 7B;

View File

@ -15,7 +15,7 @@ import net.mamoe.mirai.utils.io.*
* 请求获取头像 * 请求获取头像
*/ */
@AnnotatedId(KnownPacketId.REQUEST_PROFILE_AVATAR) @AnnotatedId(KnownPacketId.REQUEST_PROFILE_AVATAR)
object RequestProfileAvatarPacket : SessionPacketFactory<NoPacket>() { internal object RequestProfileAvatarPacket : SessionPacketFactory<NoPacket>() {
//00 01 00 17 D4 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5 //00 01 00 17 D4 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
@ -39,7 +39,7 @@ object RequestProfileAvatarPacket : SessionPacketFactory<NoPacket>() {
* @see Profile * @see Profile
*/ */
@AnnotatedId(KnownPacketId.REQUEST_PROFILE_DETAILS) @AnnotatedId(KnownPacketId.REQUEST_PROFILE_DETAILS)
object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfileDetailsResponse>() { internal object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfileDetailsResponse>() {
//00 01 3E F8 FB E3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5 //00 01 3E F8 FB E3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5
//00 01 B1 89 BE 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5 //00 01 B1 89 BE 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5
//00 01 87 73 86 9D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5 //00 01 87 73 86 9D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5
@ -91,7 +91,7 @@ object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfileDetailsR
} }
@AnnotatedId(KnownPacketId.REQUEST_PROFILE_DETAILS) @AnnotatedId(KnownPacketId.REQUEST_PROFILE_DETAILS)
data class RequestProfileDetailsResponse( internal data class RequestProfileDetailsResponse(
val qq: UInt, val qq: UInt,
val profile: Profile val profile: Profile
) : Packet { ) : Packet {

View File

@ -16,7 +16,7 @@ import net.mamoe.mirai.utils.io.writeZero
inline class FriendNameRemark(val value: String) : Packet inline class FriendNameRemark(val value: String) : Packet
@AnnotatedId(KnownPacketId.QUERY_FRIEND_REMARK) @AnnotatedId(KnownPacketId.QUERY_FRIEND_REMARK)
object QueryFriendRemarkPacket : SessionPacketFactory<FriendNameRemark>() { internal object QueryFriendRemarkPacket : SessionPacketFactory<FriendNameRemark>() {
/** /**
* 查询好友的备注 * 查询好友的备注
*/ */

View File

@ -11,7 +11,7 @@ import net.mamoe.mirai.utils.io.writeZero
class FriendList : Packet class FriendList : Packet
@PacketVersion(date = "2019.11.24", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.24", timVersion = "2.3.2 (21173)")
object RequestFriendListPacket : SessionPacketFactory<FriendList>() { internal object RequestFriendListPacket : SessionPacketFactory<FriendList>() {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
sessionKey: SessionKey sessionKey: SessionKey

View File

@ -13,7 +13,7 @@ import net.mamoe.mirai.utils.md5
@AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE) @AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)")
object SendFriendMessagePacket : SessionPacketFactory<SendFriendMessagePacket.Response>() { internal object SendFriendMessagePacket : SessionPacketFactory<SendFriendMessagePacket.Response>() {
operator fun invoke( operator fun invoke(
botQQ: UInt, botQQ: UInt,
targetQQ: UInt, targetQQ: UInt,
@ -65,7 +65,7 @@ object SendFriendMessagePacket : SessionPacketFactory<SendFriendMessagePacket.Re
} }
@NoLog @NoLog
object Response : Packet { internal object Response : Packet {
override fun toString(): String = "SendFriendMessagePacket.Response" override fun toString(): String = "SendFriendMessagePacket.Response"
} }

View File

@ -5,6 +5,7 @@ package net.mamoe.mirai.network.protocol.tim.packet.event
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.sessionKey import net.mamoe.mirai.network.sessionKey
import net.mamoe.mirai.qqAccount import net.mamoe.mirai.qqAccount
@ -13,7 +14,7 @@ import net.mamoe.mirai.utils.io.readIoBuffer
/** /**
* 事件的识别 ID. ACK 时使用 * 事件的识别 ID. ACK 时使用
*/ */
class EventPacketIdentity( internal class EventPacketIdentity(
val from: UInt,//对于好友消息, 这个是发送人 val from: UInt,//对于好友消息, 这个是发送人
val to: UInt,//对于好友消息, 这个是bot val to: UInt,//对于好友消息, 这个是bot
internal val uniqueId: IoBuffer//8 internal val uniqueId: IoBuffer//8
@ -21,7 +22,7 @@ class EventPacketIdentity(
override fun toString(): String = "($from->$to)" override fun toString(): String = "($from->$to)"
} }
fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) = with(identity) { internal fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) = with(identity) {
writeUInt(from) writeUInt(from)
writeUInt(to) writeUInt(to)
writeFully(uniqueId) writeFully(uniqueId)
@ -29,7 +30,7 @@ fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) =
@Suppress("FunctionName") @Suppress("FunctionName")
fun matchEventPacketFactory(value: UShort): EventParserAndHandler<*> = internal fun matchEventPacketFactory(value: UShort): EventParserAndHandler<*> =
KnownEventParserAndHandler.firstOrNull { it.id == value } ?: IgnoredEventIds.firstOrNull { it.id == value } ?: UnknownEventParserAndHandler(value) KnownEventParserAndHandler.firstOrNull { it.id == value } ?: IgnoredEventIds.firstOrNull { it.id == value } ?: UnknownEventParserAndHandler(value)
/** /**
@ -37,14 +38,14 @@ fun matchEventPacketFactory(value: UShort): EventParserAndHandler<*> =
*/ */
@NoLog @NoLog
@Suppress("FunctionName") @Suppress("FunctionName")
object EventPacketFactory : PacketFactory<Packet, SessionKey>(SessionKey) { internal object EventPacketFactory : PacketFactory<Packet, SessionKey>(SessionKey) {
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): Packet { override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): Packet {
val eventIdentity = EventPacketIdentity( val eventIdentity = EventPacketIdentity(
from = readUInt(), from = readUInt(),
to = readUInt(), to = readUInt(),
uniqueId = readIoBuffer(8) uniqueId = readIoBuffer(8)
) )
handler.sendPacket(EventPacketFactory(id, sequenceId, handler.bot.qqAccount, handler.sessionKey, eventIdentity)) (handler as TIMBotNetworkHandler).socket.sendPacket(EventPacketFactory(id, sequenceId, handler.bot.qqAccount, handler.sessionKey, eventIdentity))
discardExact(2) // 1F 40 discardExact(2) // 1F 40
return with(matchEventPacketFactory(readUShort())) { parse(handler.bot, eventIdentity) }.also { return with(matchEventPacketFactory(readUShort())) { parse(handler.bot, eventIdentity) }.also {
@ -75,7 +76,7 @@ object EventPacketFactory : PacketFactory<Packet, SessionKey>(SessionKey) {
} }
} }
interface EventParserAndHandler<TPacket : Packet> { internal interface EventParserAndHandler<TPacket : Packet> {
val id: UShort val id: UShort
suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): TPacket suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): TPacket
@ -86,7 +87,7 @@ interface EventParserAndHandler<TPacket : Packet> {
suspend fun BotNetworkHandler<*>.handlePacket(packet: TPacket) {} suspend fun BotNetworkHandler<*>.handlePacket(packet: TPacket) {}
} }
abstract class KnownEventParserAndHandler<TPacket : Packet>(override val id: UShort) : EventParserAndHandler<TPacket> { internal abstract class KnownEventParserAndHandler<TPacket : Packet>(override val id: UShort) : EventParserAndHandler<TPacket> {
companion object FactoryList : MutableList<KnownEventParserAndHandler<*>> by mutableListOf( companion object FactoryList : MutableList<KnownEventParserAndHandler<*>> by mutableListOf(
AndroidDeviceOnlineStatusChangedEventFactory, AndroidDeviceOnlineStatusChangedEventFactory,
FriendConversationInitializedEventParserAndHandler, FriendConversationInitializedEventParserAndHandler,

View File

@ -24,7 +24,7 @@ data class FriendStatusChanged(
* 好友在线状态改变 * 好友在线状态改变
*/ */
@AnnotatedId(KnownPacketId.FRIEND_ONLINE_STATUS_CHANGE) @AnnotatedId(KnownPacketId.FRIEND_ONLINE_STATUS_CHANGE)
object FriendOnlineStatusChangedPacket : SessionPacketFactory<FriendStatusChanged>() { internal object FriendOnlineStatusChangedPacket : SessionPacketFactory<FriendStatusChanged>() {
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): FriendStatusChanged { override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): FriendStatusChanged {
val qq = readUInt() val qq = readUInt()

View File

@ -6,17 +6,17 @@ import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
inline class IgnoredEventPacket(val id: UShort) : EventPacket { internal inline class IgnoredEventPacket(val id: UShort) : EventPacket {
override fun toString(): String = "IgnoredEventPacket(id=0x${id.toUHexString("")})" override fun toString(): String = "IgnoredEventPacket(id=0x${id.toUHexString("")})"
} }
object IgnoredEventIds : List<IgnoredEventParserAndHandler> by { internal object IgnoredEventIds : List<IgnoredEventParserAndHandler> by {
listOf( listOf(
//0x0021u, // 与群成员加入有关 //0x0021u, // 与群成员加入有关
0x0210u // 新朋友等字符串通知 0x0210u // 新朋友等字符串通知
).map { IgnoredEventParserAndHandler(it.toUShort()) } ).map { IgnoredEventParserAndHandler(it.toUShort()) }
}() }()
inline class IgnoredEventParserAndHandler(override val id: UShort) : EventParserAndHandler<IgnoredEventPacket> { internal inline class IgnoredEventParserAndHandler(override val id: UShort) : EventParserAndHandler<IgnoredEventPacket> {
override suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): IgnoredEventPacket = IgnoredEventPacket(id) override suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): IgnoredEventPacket = IgnoredEventPacket(id)
} }

View File

@ -8,6 +8,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.internal.Member
import net.mamoe.mirai.contact.internal.MemberImpl import net.mamoe.mirai.contact.internal.MemberImpl
import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.broadcast
@ -74,7 +75,8 @@ internal object MemberJoinPacketHandler : KnownEventParserAndHandler<MemberJoinE
val group = bot.getGroup(readUInt()) val group = bot.getGroup(readUInt())
discardExact(1) // 01 discardExact(1) // 01
val member = MemberImpl(bot.getQQ(readUInt()), group, MemberPermission.MEMBER) val qq = bot.getQQ(readUInt())
val member = Member(qq, group, MemberPermission.MEMBER, qq.coroutineContext)
return if (readByte().toInt() == 0x03) { return if (readByte().toInt() == 0x03) {
MemberJoinEventPacket(member, null) MemberJoinEventPacket(member, null)

View File

@ -18,7 +18,7 @@ import net.mamoe.mirai.qqAccount
/** /**
* 某群成员被禁言事件 * 某群成员被禁言事件
*/ */
@Suppress("unused") @Suppress("unused", "MemberVisibilityCanBePrivate")
class MemberMuteEvent( class MemberMuteEvent(
val member: Member, val member: Member,
override val duration: TimeSpan, override val duration: TimeSpan,

View File

@ -10,7 +10,7 @@ import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.io.ByteArrayPool import net.mamoe.mirai.utils.io.ByteArrayPool
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
data class UnknownEventPacket( internal data class UnknownEventPacket(
val id: UShort, val id: UShort,
val identity: EventPacketIdentity, val identity: EventPacketIdentity,
val body: ByteReadPacket val body: ByteReadPacket
@ -25,7 +25,7 @@ Mirai 21:54:15 : Packet received: UnknownEventPacket(id=00 57, identity=(9205034
*/ */
//TODO This class should be declared with `inline`, but a CompilationException will be thrown //TODO This class should be declared with `inline`, but a CompilationException will be thrown
class UnknownEventParserAndHandler(override val id: UShort) : EventParserAndHandler<UnknownEventPacket> { internal class UnknownEventParserAndHandler(override val id: UShort) : EventParserAndHandler<UnknownEventPacket> {
override suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): UnknownEventPacket { override suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): UnknownEventPacket {
// MiraiLogger.debug("UnknownEventPacket(${id.toUHexString()}) = ${readBytes().toUHexString()}") // MiraiLogger.debug("UnknownEventPacket(${id.toUHexString()}) = ${readBytes().toUHexString()}")

View File

@ -8,12 +8,12 @@ import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
object CaptchaKey : DecrypterByteArray, DecrypterType<CaptchaKey> { internal object CaptchaKey : DecrypterByteArray, DecrypterType<CaptchaKey> {
override val value: ByteArray = TIMProtocol.key00BA override val value: ByteArray = TIMProtocol.key00BA
} }
@AnnotatedId(KnownPacketId.CAPTCHA) @AnnotatedId(KnownPacketId.CAPTCHA)
object CaptchaPacket : PacketFactory<CaptchaPacket.CaptchaResponse, CaptchaKey>(CaptchaKey) { internal object CaptchaPacket : PacketFactory<CaptchaPacket.CaptchaResponse, CaptchaKey>(CaptchaKey) {
/** /**
* 请求验证码传输 * 请求验证码传输
*/ */
@ -97,7 +97,7 @@ object CaptchaPacket : PacketFactory<CaptchaPacket.CaptchaResponse, CaptchaKey>(
} }
} }
sealed class CaptchaResponse : Packet { internal sealed class CaptchaResponse : Packet {
lateinit var token00BA: ByteArray//56 bytes lateinit var token00BA: ByteArray//56 bytes
class Correct : CaptchaResponse() { class Correct : CaptchaResponse() {

View File

@ -17,7 +17,7 @@ import net.mamoe.mirai.utils.io.writeQQ
* 改变在线状态: "我在线上", "隐身" * 改变在线状态: "我在线上", "隐身"
*/ */
@AnnotatedId(KnownPacketId.CHANGE_ONLINE_STATUS) @AnnotatedId(KnownPacketId.CHANGE_ONLINE_STATUS)
object ChangeOnlineStatusPacket : PacketFactory<ChangeOnlineStatusPacket.ChangeOnlineStatusResponse, NoDecrypter>(NoDecrypter) { internal object ChangeOnlineStatusPacket : PacketFactory<ChangeOnlineStatusPacket.ChangeOnlineStatusResponse, NoDecrypter>(NoDecrypter) {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
sessionKey: SessionKey, sessionKey: SessionKey,
@ -32,7 +32,7 @@ object ChangeOnlineStatusPacket : PacketFactory<ChangeOnlineStatusPacket.ChangeO
} }
} }
object ChangeOnlineStatusResponse : Packet { internal object ChangeOnlineStatusResponse : Packet {
override fun toString(): String = this::class.simpleName!! override fun toString(): String = this::class.simpleName!!
} }

View File

@ -14,7 +14,7 @@ import net.mamoe.mirai.utils.io.writeQQ
@NoLog @NoLog
@AnnotatedId(KnownPacketId.HEARTBEAT) @AnnotatedId(KnownPacketId.HEARTBEAT)
object HeartbeatPacket : SessionPacketFactory<HeartbeatPacketResponse>() { internal object HeartbeatPacket : SessionPacketFactory<HeartbeatPacketResponse>() {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
sessionKey: SessionKey sessionKey: SessionKey
@ -32,4 +32,4 @@ object HeartbeatPacket : SessionPacketFactory<HeartbeatPacketResponse>() {
@NoLog @NoLog
@AnnotatedId(KnownPacketId.HEARTBEAT) @AnnotatedId(KnownPacketId.HEARTBEAT)
object HeartbeatPacketResponse : Packet, Subscribable internal object HeartbeatPacketResponse : Packet, Subscribable

View File

@ -13,15 +13,15 @@ import net.mamoe.mirai.utils.encryptBy
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.writeCRC32 import net.mamoe.mirai.utils.writeCRC32
object ShareKey : DecrypterByteArray, DecrypterType<ShareKey> { internal object ShareKey : DecrypterByteArray, DecrypterType<ShareKey> {
override val value: ByteArray = TIMProtocol.shareKey override val value: ByteArray = TIMProtocol.shareKey
} }
inline class PrivateKey(override val value: ByteArray) : DecrypterByteArray { internal inline class PrivateKey(override val value: ByteArray) : DecrypterByteArray {
companion object Type : DecrypterType<PrivateKey> companion object Type : DecrypterType<PrivateKey>
} }
inline class SubmitPasswordResponseDecrypter(private val privateKey: PrivateKey) : Decrypter { internal inline class SubmitPasswordResponseDecrypter(private val privateKey: PrivateKey) : Decrypter {
override fun decrypt(input: ByteReadPacket): ByteReadPacket { override fun decrypt(input: ByteReadPacket): ByteReadPacket {
var decrypted = ShareKey.decrypt(input) var decrypted = ShareKey.decrypt(input)
(decrypted.remaining).let { (decrypted.remaining).let {
@ -45,7 +45,7 @@ inline class SubmitPasswordResponseDecrypter(private val privateKey: PrivateKey)
* 提交密码 * 提交密码
*/ */
@AnnotatedId(KnownPacketId.LOGIN) @AnnotatedId(KnownPacketId.LOGIN)
object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse, SubmitPasswordResponseDecrypter>(SubmitPasswordResponseDecrypter) { internal object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse, SubmitPasswordResponseDecrypter>(SubmitPasswordResponseDecrypter) {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
password: String, password: String,
@ -76,7 +76,7 @@ object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse,
} }
} }
sealed class LoginResponse : Packet { internal sealed class LoginResponse : Packet {
class KeyExchange( class KeyExchange(
val tlv0006: IoBuffer,//120bytes val tlv0006: IoBuffer,//120bytes
val tokenUnknown: ByteArray?, val tokenUnknown: ByteArray?,
@ -259,7 +259,7 @@ object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse,
} }
} }
inline class SessionResponseDecryptionKey(private val delegate: IoBuffer) : Decrypter { internal inline class SessionResponseDecryptionKey(private val delegate: IoBuffer) : Decrypter {
override fun decrypt(input: ByteReadPacket): ByteReadPacket = input.decryptBy(delegate) override fun decrypt(input: ByteReadPacket): ByteReadPacket = input.decryptBy(delegate)
override fun toString(): String = "SessionResponseDecryptionKey" override fun toString(): String = "SessionResponseDecryptionKey"

View File

@ -15,7 +15,7 @@ import net.mamoe.mirai.withSession
internal fun BotSession.RequestSKeyPacket(): OutgoingPacket = RequestSKeyPacket(qqAccount, sessionKey) internal fun BotSession.RequestSKeyPacket(): OutgoingPacket = RequestSKeyPacket(qqAccount, sessionKey)
inline class SKey( internal inline class SKey(
val value: String val value: String
) : Packet ) : Packet

View File

@ -10,7 +10,7 @@ import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.localIpAddress import net.mamoe.mirai.utils.localIpAddress
@AnnotatedId(KnownPacketId.SESSION_KEY) @AnnotatedId(KnownPacketId.SESSION_KEY)
object RequestSessionPacket : PacketFactory<RequestSessionPacket.SessionKeyResponse, SessionResponseDecryptionKey>(SessionResponseDecryptionKey) { internal object RequestSessionPacket : PacketFactory<RequestSessionPacket.SessionKeyResponse, SessionResponseDecryptionKey>(SessionResponseDecryptionKey) {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
serverIp: String, serverIp: String,
@ -59,7 +59,7 @@ object RequestSessionPacket : PacketFactory<RequestSessionPacket.SessionKeyRespo
} }
} }
class SessionKeyResponse( internal class SessionKeyResponse(
val sessionKey: SessionKey, val sessionKey: SessionKey,
val tlv0105: ByteReadPacket? = null val tlv0105: ByteReadPacket? = null
) : Packet { ) : Packet {

View File

@ -11,7 +11,7 @@ import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
object TouchKey : DecrypterByteArray, DecrypterType<TouchKey> { internal object TouchKey : DecrypterByteArray, DecrypterType<TouchKey> {
override val value: ByteArray = TIMProtocol.touchKey override val value: ByteArray = TIMProtocol.touchKey
} }
@ -21,7 +21,7 @@ object TouchKey : DecrypterByteArray, DecrypterType<TouchKey> {
* @author Him188moe * @author Him188moe
*/ */
@AnnotatedId(KnownPacketId.TOUCH) @AnnotatedId(KnownPacketId.TOUCH)
object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey>(TouchKey) { internal object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey>(TouchKey) {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
serverIp: String, serverIp: String,
@ -45,7 +45,7 @@ object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey>(TouchKey
} }
} }
sealed class TouchResponse : Packet { internal sealed class TouchResponse : Packet {
class OK( class OK(
var loginTime: Int, var loginTime: Int,
val loginIP: String, val loginIP: String,

View File

@ -17,7 +17,7 @@ import kotlin.random.Random
/** /**
* 解密错误 * 解密错误
*/ */
class DecryptionFailedException : Exception() internal class DecryptionFailedException : Exception()
// region encrypt // region encrypt
@ -28,9 +28,9 @@ class DecryptionFailedException : Exception()
* @param key 长度至少为 16 * @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时 * @throws DecryptionFailedException 解密错误时
*/ */
fun ByteArray.encryptBy(key: ByteArray, length: Int = this.size): ByteArray = TEA.encrypt(this, key, sourceLength = length) internal fun ByteArray.encryptBy(key: ByteArray, length: Int = this.size): ByteArray = TEA.encrypt(this, key, sourceLength = length)
fun ByteArray.encryptBy(key: DecrypterByteArray, length: Int = this.size): ByteArray = TEA.encrypt(this, key.value, sourceLength = length) internal fun ByteArray.encryptBy(key: DecrypterByteArray, length: Int = this.size): ByteArray = TEA.encrypt(this, key.value, sourceLength = length)
/** /**
* [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密. * [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密.
@ -39,7 +39,7 @@ fun ByteArray.encryptBy(key: DecrypterByteArray, length: Int = this.size): ByteA
* @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程 * @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程
* @throws DecryptionFailedException 解密错误时 * @throws DecryptionFailedException 解密错误时
*/ */
inline fun ByteReadPacket.encryptBy(key: ByteArray, offset: Int = 0, length: Int = remaining.toInt() - offset, consumer: (ByteArray) -> Unit) { internal inline fun ByteReadPacket.encryptBy(key: ByteArray, offset: Int = 0, length: Int = remaining.toInt() - offset, consumer: (ByteArray) -> Unit) {
ByteArrayPool.useInstance { ByteArrayPool.useInstance {
this.readFully(it, offset, length) this.readFully(it, offset, length)
consumer(it.encryptBy(key, length = length)) consumer(it.encryptBy(key, length = length))
@ -57,7 +57,7 @@ inline fun ByteReadPacket.encryptBy(key: ByteArray, offset: Int = 0, length: Int
* @param key 固定长度 16 * @param key 固定长度 16
* @throws DecryptionFailedException 解密错误时 * @throws DecryptionFailedException 解密错误时
*/ */
fun ByteArray.decryptBy(key: ByteArray, length: Int = this.size): ByteArray = internal fun ByteArray.decryptBy(key: ByteArray, length: Int = this.size): ByteArray =
TEA.decrypt(checkDataLengthAndReturnSelf(length), key, sourceLength = length) TEA.decrypt(checkDataLengthAndReturnSelf(length), key, sourceLength = length)
/** /**
@ -68,7 +68,7 @@ fun ByteArray.decryptBy(key: ByteArray, length: Int = this.size): ByteArray =
* @param key 长度至少为 16 * @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时 * @throws DecryptionFailedException 解密错误时
*/ */
fun ByteArray.decryptBy(key: IoBuffer, length: Int = this.size): ByteArray { internal fun ByteArray.decryptBy(key: IoBuffer, length: Int = this.size): ByteArray {
checkDataLengthAndReturnSelf(length) checkDataLengthAndReturnSelf(length)
return ByteArrayPool.useInstance { keyBuffer -> return ByteArrayPool.useInstance { keyBuffer ->
key.readFully(keyBuffer, 0, key.readRemaining) key.readFully(keyBuffer, 0, key.readRemaining)
@ -82,7 +82,7 @@ fun ByteArray.decryptBy(key: IoBuffer, length: Int = this.size): ByteArray {
* @param key 长度至少为 16 * @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时 * @throws DecryptionFailedException 解密错误时
*/ */
fun IoBuffer.decryptBy(key: ByteArray, offset: Int = 0, length: Int = readRemaining - offset): ByteArray { internal fun IoBuffer.decryptBy(key: ByteArray, offset: Int = 0, length: Int = readRemaining - offset): ByteArray {
return ByteArrayPool.useInstance { return ByteArrayPool.useInstance {
this.readFully(it, offset, length) this.readFully(it, offset, length)
it.checkDataLengthAndReturnSelf(length) it.checkDataLengthAndReturnSelf(length)
@ -94,20 +94,20 @@ fun IoBuffer.decryptBy(key: ByteArray, offset: Int = 0, length: Int = readRemain
// region ByteReadPacket extension // region ByteReadPacket extension
fun ByteReadPacket.decryptBy(key: ByteArray): ByteReadPacket = decryptAsByteArray(key) { data -> ByteReadPacket(data, 0) } internal fun ByteReadPacket.decryptBy(key: ByteArray): ByteReadPacket = decryptAsByteArray(key) { data -> ByteReadPacket(data, 0) }
fun ByteReadPacket.decryptBy(key: IoBuffer): ByteReadPacket = decryptAsByteArray(key) { data -> ByteReadPacket(data, 0) } internal fun ByteReadPacket.decryptBy(key: IoBuffer): ByteReadPacket = decryptAsByteArray(key) { data -> ByteReadPacket(data, 0) }
fun ByteReadPacket.decryptBy(key: Decrypter): ByteReadPacket = key.decrypt(this) internal fun ByteReadPacket.decryptBy(key: Decrypter): ByteReadPacket = key.decrypt(this)
inline fun <R> ByteReadPacket.decryptAsByteArray(key: ByteArray, consumer: (ByteArray) -> R): R = internal inline fun <R> ByteReadPacket.decryptAsByteArray(key: ByteArray, consumer: (ByteArray) -> R): R =
ByteArrayPool.useInstance { ByteArrayPool.useInstance {
val length = remaining.toInt() val length = remaining.toInt()
readFully(it, 0, length) readFully(it, 0, length)
consumer(it.decryptBy(key, length)) consumer(it.decryptBy(key, length))
}.also { close() } }.also { close() }
inline fun <R> ByteReadPacket.decryptAsByteArray(key: IoBuffer, consumer: (ByteArray) -> R): R = internal inline fun <R> ByteReadPacket.decryptAsByteArray(key: IoBuffer, consumer: (ByteArray) -> R): R =
ByteArrayPool.useInstance { ByteArrayPool.useInstance {
val length = remaining.toInt() val length = remaining.toInt()
readFully(it, 0, length) readFully(it, 0, length)

View File

@ -16,46 +16,46 @@ import net.mamoe.mirai.utils.internal.coerceAtMostOrFail
import kotlin.random.Random import kotlin.random.Random
import kotlin.random.nextInt import kotlin.random.nextInt
fun BytePacketBuilder.writeZero(count: Int) { internal fun BytePacketBuilder.writeZero(count: Int) {
require(count != 0) { "Trying to write zero with count 0, you made a mistake?" } require(count != 0) { "Trying to write zero with count 0, you made a mistake?" }
require(count > 0) { "writeZero: count must > 0" } require(count > 0) { "writeZero: count must > 0" }
repeat(count) { this.writeByte(0) } repeat(count) { this.writeByte(0) }
} }
fun BytePacketBuilder.writeRandom(length: Int) = repeat(length) { this.writeByte(Random.Default.nextInt(255).toByte()) } internal fun BytePacketBuilder.writeRandom(length: Int) = repeat(length) { this.writeByte(Random.Default.nextInt(255).toByte()) }
fun BytePacketBuilder.writeQQ(qq: Long) = this.writeUInt(qq.toUInt()) internal fun BytePacketBuilder.writeQQ(qq: Long) = this.writeUInt(qq.toUInt())
fun BytePacketBuilder.writeQQ(qq: UInt) = this.writeUInt(qq) internal fun BytePacketBuilder.writeQQ(qq: UInt) = this.writeUInt(qq)
fun BytePacketBuilder.writeGroup(groupId: GroupId) = this.writeUInt(groupId.value) internal fun BytePacketBuilder.writeGroup(groupId: GroupId) = this.writeUInt(groupId.value)
fun BytePacketBuilder.writeGroup(groupInternalId: GroupInternalId) = this.writeUInt(groupInternalId.value) internal fun BytePacketBuilder.writeGroup(groupInternalId: GroupInternalId) = this.writeUInt(groupInternalId.value)
fun BytePacketBuilder.writeFully(value: DecrypterByteArray) = this.writeFully(value.value) internal fun BytePacketBuilder.writeFully(value: DecrypterByteArray) = this.writeFully(value.value)
fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray) { internal fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray) {
this.writeShort(byteArray.size.toShort()) this.writeShort(byteArray.size.toShort())
this.writeFully(byteArray) this.writeFully(byteArray)
} }
fun BytePacketBuilder.writeShortLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long)? = null, builder: BytePacketBuilder.() -> Unit) = internal fun BytePacketBuilder.writeShortLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long)? = null, builder: BytePacketBuilder.() -> Unit) =
BytePacketBuilder().apply(builder).build().use { BytePacketBuilder().apply(builder).build().use {
if (tag != null) writeUByte(tag) if (tag != null) writeUByte(tag)
writeUShort((lengthOffset?.invoke(it.remaining) ?: it.remaining).coerceAtMostOrFail(0xFFFFL).toUShort()) writeUShort((lengthOffset?.invoke(it.remaining) ?: it.remaining).coerceAtMostOrFail(0xFFFFL).toUShort())
writePacket(it) writePacket(it)
} }
fun BytePacketBuilder.writeUVarIntLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long)? = null, builder: BytePacketBuilder.() -> Unit) = internal fun BytePacketBuilder.writeUVarIntLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long)? = null, builder: BytePacketBuilder.() -> Unit) =
BytePacketBuilder().apply(builder).build().use { BytePacketBuilder().apply(builder).build().use {
if (tag != null) writeUByte(tag) if (tag != null) writeUByte(tag)
writeUVarInt((lengthOffset?.invoke(it.remaining) ?: it.remaining).coerceAtMostOrFail(0xFFFFL)) writeUVarInt((lengthOffset?.invoke(it.remaining) ?: it.remaining).coerceAtMostOrFail(0xFFFFL))
writePacket(it) writePacket(it)
} }
fun BytePacketBuilder.writeShortLVString(str: String) = writeShortLVByteArray(str.toByteArray()) internal fun BytePacketBuilder.writeShortLVString(str: String) = writeShortLVByteArray(str.toByteArray())
fun BytePacketBuilder.writeIP(ip: String) = writeFully(ip.trim().split(".").map { it.toUByte() }.toUByteArray()) internal fun BytePacketBuilder.writeIP(ip: String) = writeFully(ip.trim().split(".").map { it.toUByte() }.toUByteArray())
fun BytePacketBuilder.writeTime() = this.writeInt(currentTime.toInt()) internal fun BytePacketBuilder.writeTime() = this.writeInt(currentTime.toInt())
fun BytePacketBuilder.writeHex(uHex: String) { internal fun BytePacketBuilder.writeHex(uHex: String) {
uHex.split(" ").forEach { uHex.split(" ").forEach {
if (it.isNotBlank()) { if (it.isNotBlank()) {
writeUByte(it.toUByte(16)) writeUByte(it.toUByte(16))
@ -63,49 +63,49 @@ fun BytePacketBuilder.writeHex(uHex: String) {
} }
} }
fun <T> BytePacketBuilder.writeProto(serializer: SerializationStrategy<T>, obj: T) = writeFully(ProtoBuf.dump(serializer, obj)) internal fun <T> BytePacketBuilder.writeProto(serializer: SerializationStrategy<T>, obj: T) = writeFully(ProtoBuf.dump(serializer, obj))
fun BytePacketBuilder.writeTLV(tag: UByte, values: UByteArray) { internal fun BytePacketBuilder.writeTLV(tag: UByte, values: UByteArray) {
writeUByte(tag) writeUByte(tag)
writeUVarInt(values.size.toUInt()) writeUVarInt(values.size.toUInt())
writeFully(values) writeFully(values)
} }
fun BytePacketBuilder.writeTLV(tag: UByte, values: ByteArray) { internal fun BytePacketBuilder.writeTLV(tag: UByte, values: ByteArray) {
writeUByte(tag) writeUByte(tag)
writeUVarInt(values.size.toUInt()) writeUVarInt(values.size.toUInt())
writeFully(values) writeFully(values)
} }
fun BytePacketBuilder.writeTHex(tag: UByte, uHex: String) { internal fun BytePacketBuilder.writeTHex(tag: UByte, uHex: String) {
this.writeUByte(tag) this.writeUByte(tag)
this.writeFully(uHex.hexToUBytes()) this.writeFully(uHex.hexToUBytes())
} }
fun BytePacketBuilder.writeTV(tagValue: UShort) = writeUShort(tagValue) internal fun BytePacketBuilder.writeTV(tagValue: UShort) = writeUShort(tagValue)
fun BytePacketBuilder.writeTV(tag: UByte, value: UByte) { internal fun BytePacketBuilder.writeTV(tag: UByte, value: UByte) {
writeUByte(tag) writeUByte(tag)
writeUByte(value) writeUByte(value)
} }
fun BytePacketBuilder.writeTUbyte(tag: UByte, value: UByte) { internal fun BytePacketBuilder.writeTUbyte(tag: UByte, value: UByte) {
this.writeUByte(tag) this.writeUByte(tag)
this.writeUByte(value) this.writeUByte(value)
} }
fun BytePacketBuilder.writeTUVarint(tag: UByte, value: UInt) { internal fun BytePacketBuilder.writeTUVarint(tag: UByte, value: UInt) {
this.writeUByte(tag) this.writeUByte(tag)
this.writeUVarInt(value) this.writeUVarInt(value)
} }
fun BytePacketBuilder.writeTByteArray(tag: UByte, value: ByteArray) { internal fun BytePacketBuilder.writeTByteArray(tag: UByte, value: ByteArray) {
this.writeUByte(tag) this.writeUByte(tag)
this.writeFully(value) this.writeFully(value)
} }
fun BytePacketBuilder.writeTByteArray(tag: UByte, value: UByteArray) { internal fun BytePacketBuilder.writeTByteArray(tag: UByte, value: UByteArray) {
this.writeUByte(tag) this.writeUByte(tag)
this.writeFully(value) this.writeFully(value)
} }
@ -113,19 +113,19 @@ fun BytePacketBuilder.writeTByteArray(tag: UByte, value: UByteArray) {
/** /**
* 会使用 [ByteArrayPool] 缓存 * 会使用 [ByteArrayPool] 缓存
*/ */
inline fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) = internal inline fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) =
BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) } BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) }
inline fun BytePacketBuilder.encryptAndWrite(key: IoBuffer, encoder: BytePacketBuilder.() -> Unit) = ByteArrayPool.useInstance { internal inline fun BytePacketBuilder.encryptAndWrite(key: IoBuffer, encoder: BytePacketBuilder.() -> Unit) = ByteArrayPool.useInstance {
key.readFully(it, 0, key.readRemaining) key.readFully(it, 0, key.readRemaining)
encryptAndWrite(it, encoder) encryptAndWrite(it, encoder)
} }
inline fun BytePacketBuilder.encryptAndWrite(key: DecrypterByteArray, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(key.value, encoder) internal inline fun BytePacketBuilder.encryptAndWrite(key: DecrypterByteArray, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(key.value, encoder)
inline fun BytePacketBuilder.encryptAndWrite(keyHex: String, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(keyHex.hexToBytes(), encoder) internal inline fun BytePacketBuilder.encryptAndWrite(keyHex: String, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(keyHex.hexToBytes(), encoder)
fun BytePacketBuilder.writeTLV0006(qq: UInt, password: String, loginTime: Int, loginIP: String, privateKey: PrivateKey) { internal fun BytePacketBuilder.writeTLV0006(qq: UInt, password: String, loginTime: Int, loginIP: String, privateKey: PrivateKey) {
val firstMD5 = md5(password) val firstMD5 = md5(password)
val secondMD5 = md5(firstMD5 + byteArrayOf(0, 0, 0, 0) + qq.toUInt().toByteArray()) val secondMD5 = md5(firstMD5 + byteArrayOf(0, 0, 0, 0) + qq.toUInt().toByteArray())
@ -149,7 +149,7 @@ fun BytePacketBuilder.writeTLV0006(qq: UInt, password: String, loginTime: Int, l
} }
@Tested @Tested
fun BytePacketBuilder.writeDeviceName(random: Boolean) { internal fun BytePacketBuilder.writeDeviceName(random: Boolean) {
val deviceName: String = if (random) { val deviceName: String = if (random) {
"DESKTOP-" + String(ByteArray(7) { "DESKTOP-" + String(ByteArray(7) {
(if (Random.nextBoolean()) Random.nextInt('A'.toInt()..'Z'.toInt()) (if (Random.nextBoolean()) Random.nextInt('A'.toInt()..'Z'.toInt())