Commit 8178a8c8 authored by Him188's avatar Him188

Improve performance

parent efb32133
...@@ -4,6 +4,7 @@ package net.mamoe.mirai.event ...@@ -4,6 +4,7 @@ package net.mamoe.mirai.event
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.newCoroutineContext
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.event.internal.broadcastInternal import net.mamoe.mirai.event.internal.broadcastInternal
...@@ -75,7 +76,7 @@ suspend fun <E : Event> E.broadcast(context: CoroutineContext = EmptyCoroutineCo ...@@ -75,7 +76,7 @@ suspend fun <E : Event> E.broadcast(context: CoroutineContext = EmptyCoroutineCo
EventLogger.debug(this::class.simpleName + " pre broadcast") EventLogger.debug(this::class.simpleName + " pre broadcast")
} }
try { try {
return withContext(EventScope.coroutineContext + context) { this@broadcast.broadcastInternal() } return withContext(EventScope.newCoroutineContext(context)) { this@broadcast.broadcastInternal() }
} finally { } finally {
if (EventDebuggingFlag) { if (EventDebuggingFlag) {
EventLogger.debug(this::class.simpleName + " after broadcast") EventLogger.debug(this::class.simpleName + " after broadcast")
......
...@@ -14,17 +14,17 @@ sealed class PacketEvent<out P : Packet>(bot: Bot, open val packet: P) : BotEven ...@@ -14,17 +14,17 @@ sealed class PacketEvent<out P : Packet>(bot: Bot, open val packet: P) : BotEven
/* Client to Server */ /* Client to Server */
sealed class ClientPacketEvent<out P : OutgoingPacket>(bot: Bot, packet: P) : PacketEvent<P>(bot, packet) sealed class ClientPacketEvent(bot: Bot, packet: OutgoingPacket) : PacketEvent<OutgoingPacket>(bot, packet)
/** /**
* 包已发送. 不可被取消 * 包已发送. 不可被取消
*/ */
class PacketSentEvent<P : OutgoingPacket>(bot: Bot, packet: P) : ClientPacketEvent<P>(bot, packet) class PacketSentEvent(bot: Bot, packet: OutgoingPacket) : ClientPacketEvent(bot, packet)
/** /**
* 包发送前. 可被取消 * 包发送前. 可被取消
*/ */
class BeforePacketSendEvent<P : OutgoingPacket>(bot: Bot, packet: P) : ClientPacketEvent<P>(bot, packet), Cancellable class BeforePacketSendEvent(bot: Bot, packet: OutgoingPacket) : ClientPacketEvent(bot, packet), Cancellable
/* Server to Client */ /* Server to Client */
......
package net.mamoe.mirai.event.internal package net.mamoe.mirai.event.internal
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
...@@ -70,7 +71,8 @@ internal class Handler<E : Event>(@JvmField val handler: suspend (E) -> Listenin ...@@ -70,7 +71,8 @@ internal class Handler<E : Event>(@JvmField val handler: suspend (E) -> Listenin
* 所有的 [BotEvent.bot] `!==` `bot` 的事件都不会被处理 * 所有的 [BotEvent.bot] `!==` `bot` 的事件都不会被处理
*/ */
@PublishedApi @PublishedApi
internal class HandlerWithBot<E : Event>(val bot: Bot, @JvmField val handler: suspend Bot.(E) -> ListeningStatus) : Listener<E>() { internal class HandlerWithBot<E : Event>(val bot: Bot, @JvmField val handler: suspend Bot.(E) -> ListeningStatus) :
Listener<E>() {
override suspend fun onEvent(event: E): ListeningStatus = with(bot) { override suspend fun onEvent(event: E): ListeningStatus = with(bot) {
if (event !is BotEvent || event.bot !== this) { if (event !is BotEvent || event.bot !== this) {
return ListeningStatus.LISTENING return ListeningStatus.LISTENING
...@@ -144,19 +146,28 @@ internal suspend fun <E : Event> E.broadcastInternal(): E { ...@@ -144,19 +146,28 @@ internal suspend fun <E : Event> E.broadcastInternal(): E {
//自己持有, 则是在一个事件中 //自己持有, 则是在一个事件中
if (listeners.mainMutex.holdsLock(listeners)) { if (listeners.mainMutex.holdsLock(listeners)) {
callAndRemoveIfRequired() callAndRemoveIfRequired()
} else listeners.mainMutex.withLock(listeners) { } else {
callAndRemoveIfRequired() while (!listeners.mainMutex.tryLock(this)) {
delay(10)
}
try {
callAndRemoveIfRequired()
} finally {
listeners.mainMutex.unlock(this)
}
} }
} }
callListeners(this::class.listeners() as EventListeners<in E>) callListeners(this::class.listeners() as EventListeners<in E>)
//FIXME 这可能不支持所有的平台. 可能需要修改. applyAllListeners(this::class) { callListeners(it as EventListeners<in E>) }
loopAllListeners(this::class) { callListeners(it as EventListeners<in E>) }
return this return this
} }
internal expect suspend inline fun <E : Event> loopAllListeners( private suspend inline fun <E : Event> applyAllListeners(
clazz: KClass<E>, clazz: KClass<E>,
consumer: (EventListeners<in E>) -> Unit block: (EventListeners<in E>) -> Unit
) ) = clazz.supertypes.map { it.classifier }.filterIsInstance<KClass<out Event>>().forEach {
\ No newline at end of file @Suppress("UNCHECKED_CAST")
block(it.listeners() as EventListeners<in E>)
}
...@@ -103,7 +103,25 @@ inline val BotSession.isOpen: Boolean get() = socket.isOpen ...@@ -103,7 +103,25 @@ inline val BotSession.isOpen: Boolean get() = socket.isOpen
inline val BotSession.qqAccount: UInt get() = bot.account.id inline val BotSession.qqAccount: UInt get() = bot.account.id
/** /**
* 取得 [T] 的 [BotSession]. * 取得 [BotNetworkHandler] 的 [BotSession].
* 实际上是一个捷径. * 实际上是一个捷径.
*/ */
val <T : BotNetworkHandler<*>> T.session get() = this[ActionPacketHandler].session val BotNetworkHandler<*>.session get() = this[ActionPacketHandler].session
\ No newline at end of file
/**
* 取得 [BotNetworkHandler] 的 sessionKey.
* 实际上是一个捷径.
*/
inline val BotNetworkHandler<*>.sessionKey get() = this.session.sessionKey
/**
* 取得 [Bot] 的 [BotSession].
* 实际上是一个捷径.
*/
inline val Bot.session get() = this.network.session
/**
* 取得 [Bot] 的 `sessionKey`.
* 实际上是一个捷径.
*/
inline val Bot.sessionKey get() = this.session.sessionKey
\ No newline at end of file
...@@ -34,11 +34,12 @@ import kotlin.coroutines.CoroutineContext ...@@ -34,11 +34,12 @@ import kotlin.coroutines.CoroutineContext
* *
* @see BotNetworkHandler * @see BotNetworkHandler
*/ */
internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() { internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() {
override val coroutineContext: CoroutineContext = override val coroutineContext: CoroutineContext =
Dispatchers.Default + CoroutineExceptionHandler { _, e -> Dispatchers.Default + CoroutineExceptionHandler { _, e ->
bot.logger.error(e) bot.logger.error("An exception was thrown in a coroutine under TIMBotNetworkHandler", e)
} + SupervisorJob() } + SupervisorJob()
...@@ -58,22 +59,23 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -58,22 +59,23 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
temporaryPacketHandler.send(this[ActionPacketHandler].session) temporaryPacketHandler.send(this[ActionPacketHandler].session)
} }
override suspend fun login(configuration: BotNetworkConfiguration): LoginResult = withContext(this.coroutineContext) { override suspend fun login(configuration: BotNetworkConfiguration): LoginResult =
TIMProtocol.SERVER_IP.forEach { ip -> withContext(this.coroutineContext) {
bot.logger.info("Connecting server $ip") TIMProtocol.SERVER_IP.forEach { ip ->
socket = BotSocketAdapter(ip, configuration) bot.logger.info("Connecting server $ip")
socket = BotSocketAdapter(ip, configuration)
loginResult = CompletableDeferred() loginResult = CompletableDeferred()
socket.resendTouch().takeIf { it != LoginResult.TIMEOUT }?.let { return@withContext it } socket.resendTouch().takeIf { it != LoginResult.TIMEOUT }?.let { return@withContext it }
println() println()
bot.logger.warning("Timeout. Retrying next server") bot.logger.warning("Timeout. Retrying next server")
socket.close() socket.close()
}
return@withContext LoginResult.TIMEOUT
} }
return@withContext LoginResult.TIMEOUT
}
internal var loginResult: CompletableDeferred<LoginResult> = CompletableDeferred() internal var loginResult: CompletableDeferred<LoginResult> = CompletableDeferred()
...@@ -85,7 +87,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -85,7 +87,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
add(EventPacketHandler(session).asNode(EventPacketHandler)) add(EventPacketHandler(session).asNode(EventPacketHandler))
add(ActionPacketHandler(session).asNode(ActionPacketHandler)) add(ActionPacketHandler(session).asNode(ActionPacketHandler))
bot.logger.warning("Successfully logged in") bot.logger.info("Successfully logged in")
} }
private lateinit var sessionKey: ByteArray private lateinit var sessionKey: ByteArray
...@@ -210,7 +212,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -210,7 +212,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
// Remove first to release the lock // Remove first to release the lock
handlersLock.withLock { handlersLock.withLock {
temporaryPacketHandlers.filter { it.filter(session, packet) }.also { temporaryPacketHandlers.removeAll(it) } temporaryPacketHandlers.filter { it.filter(session, packet) }
.also { temporaryPacketHandlers.removeAll(it) }
}.forEach { }.forEach {
it.doReceiveWithoutExceptions(packet) it.doReceiveWithoutExceptions(packet)
} }
...@@ -232,42 +235,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -232,42 +235,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
} }
} }
/* todo 修改为这个模式是否更好?
interface Pk
object TestPacket : Pk {
operator fun invoke(bot: UInt): TestPacket.(BytePacketBuilder) -> Unit {
}
}
override inline fun <reified P : Pk> send(p: P.(BytePacketBuilder) -> Unit): UShort {
val encoded = with(P::class.objectInstance!!){
buildPacket {
this@with.p(this)
}
}
}*/
/* todo 修改为这个模式是否更好?
interface Pk
object TestPacket : Pk {
operator fun invoke(bot: UInt):ByteReadPacket = buildPacket {
}
}
override inline fun send(p: ByteReadPacket): UShort {
sendPacket{
// write packet head
// write version
// write bot qq number
writePacket(p)
}
}*/
override suspend fun sendPacket(packet: OutgoingPacket): Unit = withContext(coroutineContext) { override suspend fun sendPacket(packet: OutgoingPacket): Unit = withContext(coroutineContext) {
check(channel.isOpen) { "channel is not open" } check(channel.isOpen) { "channel is not open" }
...@@ -275,10 +242,10 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -275,10 +242,10 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
return@withContext return@withContext
} }
packet.buildAndUse { build -> packet.delegate.use { built ->
val buffer = IoBuffer.Pool.borrow() val buffer = IoBuffer.Pool.borrow()
try { try {
build.readAvailable(buffer) built.readAvailable(buffer)
val shouldBeSent = buffer.readRemaining val shouldBeSent = buffer.readRemaining
check(channel.send(buffer) == shouldBeSent) { "Buffer is not entirely sent. Required sent length=$shouldBeSent, but after channel.send, buffer remains ${buffer.readBytes().toUHexString()}" }//JVM: withContext(IO) check(channel.send(buffer) == shouldBeSent) { "Buffer is not entirely sent. Required sent length=$shouldBeSent, but after channel.send, buffer remains ${buffer.readBytes().toUHexString()}" }//JVM: withContext(IO)
} catch (e: SendPacketInternalException) { } catch (e: SendPacketInternalException) {
...@@ -476,7 +443,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -476,7 +443,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
is SessionKeyResponsePacket -> { is SessionKeyResponsePacket -> {
sessionKey = packet.sessionKey sessionKey = packet.sessionKey
bot.logger.warning("sessionKey = ${sessionKey.toUHexString()}") bot.logger.info("sessionKey = ${sessionKey.toUHexString()}")
heartbeatJob = launch { heartbeatJob = launch {
while (socket.isOpen) { while (socket.isOpen) {
......
...@@ -14,7 +14,7 @@ import kotlin.properties.Delegates ...@@ -14,7 +14,7 @@ import kotlin.properties.Delegates
* *
* TODO 真的是在线状态改变么 * TODO 真的是在线状态改变么
*/ */
@PacketId(0x00_81u) @AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
class ServerFriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket(input) { class ServerFriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket(input) {
var qq: UInt by Delegates.notNull() var qq: UInt by Delegates.notNull()
lateinit var status: OnlineStatus lateinit var status: OnlineStatus
...@@ -29,7 +29,7 @@ class ServerFriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacke ...@@ -29,7 +29,7 @@ class ServerFriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacke
//在线 XX XX XX XX 01 00 00 00 00 00 00 00 0A 15 E3 10 00 01 2E 01 00 00 00 00 00 00 00 00 00 00 00 13 08 02 C2 76 E4 B8 DD 00 00 00 00 00 00 00 00 00 00 00 //在线 XX XX XX XX 01 00 00 00 00 00 00 00 0A 15 E3 10 00 01 2E 01 00 00 00 00 00 00 00 00 00 00 00 13 08 02 C2 76 E4 B8 DD 00 00 00 00 00 00 00 00 00 00 00
//忙碌 XX XX XX XX 01 00 00 00 00 00 00 00 32 15 E3 10 00 01 2E 01 00 00 00 00 00 00 00 00 00 00 00 13 08 02 C2 76 E4 B8 DD 00 00 00 00 00 00 00 00 00 00 00 //忙碌 XX XX XX XX 01 00 00 00 00 00 00 00 32 15 E3 10 00 01 2E 01 00 00 00 00 00 00 00 00 00 00 00 13 08 02 C2 76 E4 B8 DD 00 00 00 00 00 00 00 00 00 00 00
@PacketId(0x00_81u) @AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): ServerFriendOnlineStatusChangedPacket = ServerFriendOnlineStatusChangedPacket(this.decryptBy(sessionKey)).applySequence(sequenceId) fun decrypt(sessionKey: ByteArray): ServerFriendOnlineStatusChangedPacket = ServerFriendOnlineStatusChangedPacket(this.decryptBy(sessionKey)).applySequence(sequenceId)
} }
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.writeUByte import kotlinx.io.core.writeUByte
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
...@@ -15,22 +14,22 @@ import net.mamoe.mirai.utils.io.writeQQ ...@@ -15,22 +14,22 @@ import net.mamoe.mirai.utils.io.writeQQ
* *
* @author Him188moe * @author Him188moe
*/ */
@PacketId(0x00_5Cu) @AnnotatedId(KnownPacketId.ACCOUNT_INFO)
class RequestAccountInfoPacket( object RequestAccountInfoPacket : OutgoingPacketBuilder {
private val qq: UInt, operator fun invoke(
private val sessionKey: ByteArray qq: UInt,
) : OutgoingPacket() { sessionKey: ByteArray
override fun encode(builder: BytePacketBuilder) = with(builder) { ) = buildOutgoingPacket {
this.writeQQ(qq) writeQQ(qq)
this.writeHex(TIMProtocol.fixVer2) writeHex(TIMProtocol.fixVer2)
this.encryptAndWrite(sessionKey) { encryptAndWrite(sessionKey) {
writeUByte(0x88u) writeUByte(0x88u)
writeQQ(qq) writeQQ(qq)
writeByte(0x00) writeByte(0x00)
} }
} }
@PacketId(0x00_5Cu) @AnnotatedId(KnownPacketId.ACCOUNT_INFO)
class Response(input: ByteReadPacket) : ResponsePacket(input) { class Response(input: ByteReadPacket) : ResponsePacket(input) {
//等级 //等级
//升级剩余活跃天数 //升级剩余活跃天数
......
...@@ -2,19 +2,18 @@ ...@@ -2,19 +2,18 @@
package net.mamoe.mirai.network.protocol.tim.packet package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.utils.io.encryptAndWrite import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeHex import net.mamoe.mirai.utils.io.writeHex
import net.mamoe.mirai.utils.io.writeQQ import net.mamoe.mirai.utils.io.writeQQ
@PacketId(0x00_58u) @AnnotatedId(KnownPacketId.HEARTBEAT)
class HeartbeatPacket( object HeartbeatPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val sessionKey: ByteArray bot: UInt,
) : OutgoingPacket() { sessionKey: ByteArray
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot) writeQQ(bot)
writeHex(TIMProtocol.fixVer) writeHex(TIMProtocol.fixVer)
encryptAndWrite(sessionKey) { encryptAndWrite(sessionKey) {
...@@ -22,6 +21,6 @@ class HeartbeatPacket( ...@@ -22,6 +21,6 @@ class HeartbeatPacket(
} }
} }
@PacketId(0x00_58u) @AnnotatedId(KnownPacketId.HEARTBEAT)
class Response(input: ByteReadPacket) : ResponsePacket(input) class Response(input: ByteReadPacket) : ResponsePacket(input)
} }
\ No newline at end of file
...@@ -3,61 +3,77 @@ ...@@ -3,61 +3,77 @@
package net.mamoe.mirai.network.protocol.tim.packet package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.io.core.* import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.use
import kotlinx.io.core.writeUShort
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.utils.io.writeHex import net.mamoe.mirai.utils.io.writeHex
import kotlin.jvm.JvmStatic import kotlin.jvm.JvmOverloads
/** /**
* 发给服务器的数据包. 必须有 [PacketId] 注解或 `override` [id]. 否则将会抛出 [IllegalStateException] * 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket],
*/ */
abstract class OutgoingPacket : Packet(), Closeable { class OutgoingPacket(
/** name: String?,
* Encode this packet. override val packetId: PacketId,
* override val sequenceId: UShort,
* Before sending the packet, a [tail][TIMProtocol.tail] is added. val delegate: ByteReadPacket
*/ ) : Packet {
protected abstract fun encode(builder: BytePacketBuilder) private val name: String by lazy {
name ?: packetId.toString()
override val sequenceId: UShort by lazy {
atomicNextSequenceId()
} }
companion object { constructor(id: PacketId, sequenceId: UShort, delegate: ByteReadPacket) : this(null, id, sequenceId, delegate)
@JvmStatic
private val sequenceIdInternal = atomic(1)
internal fun atomicNextSequenceId() = sequenceIdInternal.getAndIncrement().toUShort()
}
inline fun buildAndUse(block: (ByteReadPacket) -> Unit) { constructor(annotation: AnnotatedId, sequenceId: UShort, delegate: ByteReadPacket) :
buildPacket().use(block) this(annotation.toString(), annotation.id, sequenceId, delegate)
}
@PublishedApi override fun toString(): String = packetToString(name)
internal fun buildPacket(): ByteReadPacket = buildPacket { }
writeHex(TIMProtocol.head)
writeHex(TIMProtocol.ver)
writePacketId()
encode(this)
writeHex(TIMProtocol.tail)
}
override fun toString(): String = packetToString() /**
* 发给服务器的数据包的构建器.
* 应由一个 `object` 实现, 且实现 `operator fun invoke`
*/
interface OutgoingPacketBuilder {
/**
* 2 Ubyte.
* 默认为读取注解 [AnnotatedId]
*/
val annotatedId: AnnotatedId
get() = (this::class.annotations.firstOrNull { it is AnnotatedId } as? AnnotatedId)
?: error("Annotation AnnotatedId not found")
override fun close() { companion object {
} private val sequenceIdInternal = atomic(1)
private fun BytePacketBuilder.writePacketId() { @PublishedApi
writeUShort(this@OutgoingPacket.id) internal fun atomicNextSequenceId() = sequenceIdInternal.getAndIncrement().toUShort()
writeUShort(sequenceId)
} }
} }
@Suppress("unused") /**
@MustBeDocumented * 构造一个待发送给服务器的数据包.
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) * 若不提供参数 [id], 则会通过注解 [AnnotatedId] 获取 id.
@Retention(AnnotationRetention.SOURCE) */
annotation class PacketVersion(val date: String, val timVersion: String) @JvmOverloads
fun OutgoingPacketBuilder.buildOutgoingPacket(
private val Uninitialized = ByteReadPacket(IoBuffer.Empty, IoBuffer.EmptyPool) name: String? = null,
id: PacketId = this.annotatedId.id,
sequenceId: UShort = OutgoingPacketBuilder.atomicNextSequenceId(),
headerSizeHint: Int = 0,
block: BytePacketBuilder.() -> Unit
): OutgoingPacket {
BytePacketBuilder(headerSizeHint).use {
with(it) {
writeHex(TIMProtocol.head)
writeHex(TIMProtocol.ver)
writeUShort(id.value)
writeUShort(sequenceId)
block(this)
writeHex(TIMProtocol.tail)
}
return OutgoingPacket(name, id, sequenceId, it.build())
}
}
\ No newline at end of file
...@@ -2,19 +2,18 @@ ...@@ -2,19 +2,18 @@
package net.mamoe.mirai.network.protocol.tim.packet package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import net.mamoe.mirai.utils.io.encryptAndWrite import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeQQ import net.mamoe.mirai.utils.io.writeQQ
class OutgoingRawPacket( object OutgoingRawPacket : OutgoingPacketBuilder {
override val id: UShort, operator fun invoke(
private val bot: UInt, id: PacketId,
private val version: ByteArray, bot: UInt,
private val sessionKey: ByteArray, version: ByteArray,
private val data: ByteArray sessionKey: ByteArray,
) : OutgoingPacket() { data: ByteArray
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket(id = id) {
writeQQ(bot) writeQQ(bot)
writeFully(version) writeFully(version)
......
...@@ -2,23 +2,193 @@ ...@@ -2,23 +2,193 @@
package net.mamoe.mirai.network.protocol.tim.packet package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.IoBuffer
import net.mamoe.mirai.network.protocol.tim.packet.NullPacketId.value
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.KProperty1
import kotlin.reflect.KVisibility
/** /**
* 数据包. * 数据包.
*/ */
abstract class Packet { interface Packet {
/** /**
* 2 Ubyte * 包序列 ID. 唯一. 所有包共用一个原子自增序列 ID 生成
*/ */
open val id: UShort by lazy { (this::class.annotations.firstOrNull { it is PacketId } as? PacketId)?.value ?: error("Annotation PacketId not found") } val sequenceId: UShort
/** /**
* 包序列 id. 唯一 * 包识别 ID
*/ */
abstract val sequenceId: UShort val packetId: PacketId
}
/**
* ID Hex. 格式为 `00 00 00 00`
*/
val Packet.idHexString: String get() = (packetId.value.toInt().shl(16) or sequenceId.toInt()).toUHexString()
// region Packet id
val idHexString: String get() = (id.toInt().shl(16) or sequenceId.toInt()).toUHexString() /**
* 包 ID
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class AnnotatedId(
val id: KnownPacketId
)
inline val AnnotatedId.value: UShort get() = id.value
/**
* 通过 [value] 匹配一个 [KnownPacketId], 无匹配则返回一个 [UnknownPacketId].
*/
@Suppress("FunctionName")
fun PacketId(value: UShort): PacketId =
KnownPacketId.values().firstOrNull { it.value == value } ?: UnknownPacketId(value)
/**
* 包 ID.
*/
interface PacketId {
val value: UShort
}
/**
* 用于代表 `null`. 调用属性 [value] 时将会得到一个 [error]
*/
object NullPacketId : PacketId {
override val value: UShort get() = error("Packet id is not initialized")
} }
internal expect fun Packet.packetToString(): String /**
\ No newline at end of file * 未知的 [PacketId]
*/
inline class UnknownPacketId(override val value: UShort) : PacketId
/**
* 已知的 [PacketId]. 所有在 Mirai 中实现过的包都会使用这些 Id
*/
enum class KnownPacketId(override val value: UShort) : PacketId {
inline TOUCH(0x0825u),
inline SESSION_KEY(0x0828u),
inline LOGIN(0X0836u),
inline CAPTCHA(0X00BAU),
inline SERVER_EVENT_1(0X00CEU),
inline SERVER_EVENT_2(0X0017U),
inline FRIEND_ONLINE_STATUS_CHANGE(0X0081U),
inline CHANGE_ONLINE_STATUS(0x00_ECu),
inline HEARTBEAT(0x0058u),
inline S_KEY(0X001DU),
inline ACCOUNT_INFO(0X005CU),
inline SEND_GROUP_MESSAGE(0X0002U),
inline SEND_FRIEND_MESSAGE(0X00CDU),
inline CAN_ADD_FRIEND(0X00A7U),
inline GROUP_IMAGE_ID(0X0388U),
inline FRIEND_IMAGE_ID(0X0352U),
inline REQUEST_PROFILE(0x00_31u),
inline SUBMIT_IMAGE_FILE_NAME(0X01_BDu),
;
}
// endregion
// region Internal utils
/**
* 版本信息
*/
@Suppress("unused")
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
internal annotation class PacketVersion(val date: String, val timVersion: String)
private object PacketNameFormatter {
private var longestNameLength: Int = 43
fun adjustName(name: String): String =
if (name.length > longestNameLength) {
longestNameLength = name.length
name
} else " ".repeat(longestNameLength - name.length) + name
}
private object IgnoreIdListEquals : List<String> by listOf(
"idHex",
"id",
"packetId",
"sequenceIdInternal",
"sequenceId",
"fixedId",
"idByteArray",
"encoded",
"packet",
"EMPTY_ID_HEX",
"input",
"sequenceId",
"output",
"bot",
"UninitializedByteReadPacket",
"sessionKey"
)
private object IgnoreIdListInclude : List<String> by listOf(
"Companion",
"EMPTY_ID_HEX",
"input",
"output",
"this\$",
"\$\$delegatedProperties",
"UninitializedByteReadPacket",
"\$FU",
"RefVolatile"
)
/**
* 这个方法会翻倍内存占用, 考虑修改.
*/
@Suppress("UNCHECKED_CAST")
internal fun Packet.packetToString(name: String = this::class.simpleName.toString()): String =
PacketNameFormatter.adjustName(name + "(${this.idHexString})") +
this::class.members
.filterIsInstance<KProperty<*>>()
.filterNot { it.isConst || it.isSuspend || it.visibility == KVisibility.PRIVATE }
.filterNot { prop -> prop.name in IgnoreIdListEquals || IgnoreIdListInclude.any { it in prop.name } }
.joinToString(", ", "{", "}") { it.briefDescription(this@packetToString) }
@Suppress("UNCHECKED_CAST")
private fun KProperty<*>.briefDescription(thisRef: Packet): String =
try {
when (this) {
is KProperty0<*> -> get()
is KProperty1<*, *> -> (this as KProperty1<in Packet, Any>).get(thisRef)
else -> null
}
} catch (e: Exception) {
null
}.let { value: Any? ->
@Suppress("UNCHECKED_CAST")
name.replace("\$delegate", "") + "=" + when (value) {
null -> "_"
is ByteArray -> value.toUHexString()
is UByteArray -> value.toUHexString()
is ByteReadPacket -> "[ByteReadPacket(${value.remaining})]"
is IoBuffer -> "[IoBuffer(${value.readRemaining})]"
is Lazy<*> -> "[Lazy]"
is ReadWriteProperty<*, *> -> (value as? ReadWriteProperty<Packet, *>)?.getValue(
thisRef,
this
) ?: "[UnknownProperty]"
else -> value.toString()
}
}
// endregion
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.packet
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class PacketId(
val value: UShort
)
...@@ -8,7 +8,7 @@ import kotlinx.io.core.ByteReadPacket ...@@ -8,7 +8,7 @@ import kotlinx.io.core.ByteReadPacket
* 它们都使用 sessionKey 解密. * 它们都使用 sessionKey 解密.
* 它们都必须有一个公开的仅有一个 [ByteReadPacket] 参数的构造器. * 它们都必须有一个公开的仅有一个 [ByteReadPacket] 参数的构造器.
* *
* 注意: 需要指定 ID, 通过 [PacketId]. * 注意: 需要指定 ID, 通过 [AnnotatedId].
*/ */
abstract class ResponsePacket(input: ByteReadPacket) : ServerPacket(input) { abstract class ResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
......
...@@ -20,8 +20,11 @@ import kotlin.properties.Delegates ...@@ -20,8 +20,11 @@ import kotlin.properties.Delegates
* *
* @see parseServerPacket 解析包种类 * @see parseServerPacket 解析包种类
*/ */
abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable { abstract class ServerPacket(val input: ByteReadPacket) : Packet, Closeable {
override val id: UShort by lazy { super.id } override val packetId: PacketId by lazy {
(this::class.annotations.firstOrNull { it is AnnotatedId } as? AnnotatedId)?.id
?: error("Annotation AnnotatedId not found")
}
override var sequenceId: UShort by Delegates.notNull() override var sequenceId: UShort by Delegates.notNull()
...@@ -52,9 +55,12 @@ fun ServerPacket.decryptBy(key1: ByteArray, key2: ByteArray): ByteReadPacket = ...@@ -52,9 +55,12 @@ fun ServerPacket.decryptBy(key1: ByteArray, key2: ByteArray): ByteReadPacket =
fun ServerPacket.decryptBy(key1: String, key2: ByteArray): ByteReadPacket = this.decryptBy(key1.hexToBytes(), key2) fun ServerPacket.decryptBy(key1: String, key2: ByteArray): ByteReadPacket = this.decryptBy(key1.hexToBytes(), key2)
fun ServerPacket.decryptBy(key1: String, key2: IoBuffer): ByteReadPacket = this.decryptBy(key1.hexToBytes(), key2.readBytes()) fun ServerPacket.decryptBy(key1: String, key2: IoBuffer): ByteReadPacket =
this.decryptBy(key1.hexToBytes(), key2.readBytes())
fun ServerPacket.decryptBy(key1: ByteArray, key2: String): ByteReadPacket = this.decryptBy(key1, key2.hexToBytes()) fun ServerPacket.decryptBy(key1: ByteArray, key2: String): ByteReadPacket = this.decryptBy(key1, key2.hexToBytes())
fun ServerPacket.decryptBy(keyHex1: String, keyHex2: String): ByteReadPacket = this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes()) fun ServerPacket.decryptBy(keyHex1: String, keyHex2: String): ByteReadPacket =
this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes())
inline fun <R> ServerPacket.decryptAsByteArray(key: ByteArray, consumer: (ByteArray) -> R): R = inline fun <R> ServerPacket.decryptAsByteArray(key: ByteArray, consumer: (ByteArray) -> R): R =
ByteArrayPool.useInstance { ByteArrayPool.useInstance {
...@@ -63,7 +69,9 @@ inline fun <R> ServerPacket.decryptAsByteArray(key: ByteArray, consumer: (ByteAr ...@@ -63,7 +69,9 @@ inline fun <R> ServerPacket.decryptAsByteArray(key: ByteArray, consumer: (ByteAr
consumer(it.decryptBy(key, length)) consumer(it.decryptBy(key, length))
}.also { input.close() } }.also { input.close() }
inline fun <R> ServerPacket.decryptAsByteArray(keyHex: String, consumer: (ByteArray) -> R): R = this.decryptAsByteArray(keyHex.hexToBytes(), consumer) inline fun <R> ServerPacket.decryptAsByteArray(keyHex: String, consumer: (ByteArray) -> R): R =
this.decryptAsByteArray(keyHex.hexToBytes(), consumer)
inline fun <R> ServerPacket.decryptAsByteArray(key: IoBuffer, consumer: (ByteArray) -> R): R = inline fun <R> ServerPacket.decryptAsByteArray(key: IoBuffer, consumer: (ByteArray) -> R): R =
ByteArrayPool.useInstance { ByteArrayPool.useInstance {
val length = input.remaining.toInt() - 1 val length = input.remaining.toInt() - 1
......
...@@ -4,15 +4,13 @@ package net.mamoe.mirai.network.protocol.tim.packet ...@@ -4,15 +4,13 @@ package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
import net.mamoe.mirai.utils.LoggerTextFormat
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
class UnknownServerPacket( class UnknownServerPacket(
input: ByteReadPacket, input: ByteReadPacket,
override var id: UShort, override val packetId: PacketId,
override var sequenceId: UShort override var sequenceId: UShort
) : ServerPacket(input) { ) : ServerPacket(input) {
override fun decode() { override fun decode() {
val raw = this.input.readBytes() val raw = this.input.readBytes()
...@@ -20,16 +18,12 @@ class UnknownServerPacket( ...@@ -20,16 +18,12 @@ class UnknownServerPacket(
} }
class Encrypted( class Encrypted(
input: ByteReadPacket, input: ByteReadPacket,
override var id: UShort, override val packetId: PacketId,
override var sequenceId: UShort override var sequenceId: UShort
) : ServerPacket(input) { ) : ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): UnknownServerPacket = UnknownServerPacket(this.decryptBy(sessionKey), this.id, this.sequenceId) fun decrypt(sessionKey: ByteArray): UnknownServerPacket =
} UnknownServerPacket(this.decryptBy(sessionKey), this.packetId, this.sequenceId)
override fun toString(): String {
@Suppress("RemoveRedundantQualifierName")
return LoggerTextFormat.LIGHT_RED.toString() + super.toString()
} }
} }
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet.action package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import kotlinx.io.core.readUShort import kotlinx.io.core.readUShort
...@@ -18,13 +17,13 @@ import net.mamoe.mirai.utils.io.writeQQ ...@@ -18,13 +17,13 @@ import net.mamoe.mirai.utils.io.writeQQ
* @author Him188moe * @author Him188moe
*/ */
@Response(CanAddFriendPacket.Response::class) @Response(CanAddFriendPacket.Response::class)
@PacketId(0x00_A7u) @AnnotatedId(KnownPacketId.CAN_ADD_FRIEND)
class CanAddFriendPacket( object CanAddFriendPacket : OutgoingPacketBuilder {
val bot: UInt, operator fun invoke(
val qq: UInt, bot: UInt,
val sessionKey: ByteArray qq: UInt,
) : OutgoingPacket() { sessionKey: ByteArray
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot) writeQQ(bot)
writeHex(TIMProtocol.fixVer2) writeHex(TIMProtocol.fixVer2)
encryptAndWrite(sessionKey) { encryptAndWrite(sessionKey) {
...@@ -32,7 +31,7 @@ class CanAddFriendPacket( ...@@ -32,7 +31,7 @@ class CanAddFriendPacket(
} }
} }
@PacketId(0x00_A7u) @AnnotatedId(KnownPacketId.CAN_ADD_FRIEND)
class Response(input: ByteReadPacket) : ResponsePacket(input) { class Response(input: ByteReadPacket) : ResponsePacket(input) {
lateinit var state: State lateinit var state: State
...@@ -81,16 +80,16 @@ class CanAddFriendPacket( ...@@ -81,16 +80,16 @@ class CanAddFriendPacket(
/** /**
* 请求添加好友 * 请求添加好友
*/ */
@PacketId(0x00_AEu) @AnnotatedId(KnownPacketId.CAN_ADD_FRIEND)
class AddFriendPacket( object AddFriendPacket : OutgoingPacketBuilder {
val bot: UInt, operator fun invoke(
val qq: UInt, bot: UInt,
val sessionKey: ByteArray qq: UInt,
) : OutgoingPacket() { sessionKey: ByteArray
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket {
this.writeQQ(bot) writeQQ(bot)
this.writeHex(TIMProtocol.fixVer2) writeHex(TIMProtocol.fixVer2)
this.encryptAndWrite(sessionKey) { encryptAndWrite(sessionKey) {
writeHex("01 00 01") writeHex("01 00 01")
writeQQ(qq) writeQQ(qq)
} }
......
...@@ -2,17 +2,15 @@ ...@@ -2,17 +2,15 @@
package net.mamoe.mirai.network.protocol.tim.packet.action package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.BytePacketBuilder import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
// 用户资料的头像 // 用户资料的头像
/** /**
* 请求获取头像 * 请求获取头像
*/ */
@PacketId(0x00_31u) @AnnotatedId(KnownPacketId.REQUEST_PROFILE)
class RequestProfilePicturePacket : OutgoingPacket() { object RequestProfilePicturePacket : OutgoingPacketBuilder {
override fun encode(builder: BytePacketBuilder) { operator fun invoke(): OutgoingPacket = buildOutgoingPacket {
TODO("not implemented")
} }
} }
\ No newline at end of file
...@@ -6,76 +6,23 @@ import kotlinx.io.core.* ...@@ -6,76 +6,23 @@ import kotlinx.io.core.*
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.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.md5
@PacketId(0x00_CDu) @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")
class SendFriendMessagePacket( object SendFriendMessagePacket : OutgoingPacketBuilder {
private val botQQ: UInt, operator fun invoke(
private val targetQQ: UInt, botQQ: UInt,
private val sessionKey: ByteArray, targetQQ: UInt,
private val message: MessageChain sessionKey: ByteArray,
) : OutgoingPacket() { message: MessageChain
): OutgoingPacket = buildOutgoingPacket {
override fun encode(builder: BytePacketBuilder) = with(builder) {
writeQQ(botQQ) writeQQ(botQQ)
writeHex(TIMProtocol.version0x02) writeHex(TIMProtocol.version0x02)
encryptAndWrite(sessionKey) { encryptAndWrite(sessionKey) {
// TIM最新, 消息内容 "牛逼"
// 3E 03 3F A2
// 76 E4 B8 DD
// 00 00 00 [08] 00 01 00 04 00 00 00 00
// 38 03
// 3E 03 3F A2
// 76 E4 B8 DD
// C6 FB 06 30 0C 69 0C AD C6 AD 14 BF 0B C6 38 EA
// 00 0B
// 3D 7F
// 5D AA A8 E2
// 01 1D
// 00 00 00 00
// 01
// 00
// 00
// 00 01 4D 53 47 00 00 00 00 00
// 5D AA A8 E2
// E2 AE 94 2D
// 00 00 00 00 0C 00 86
// 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91
// 00 00
//
// 01 00 09 01 00 06 E7 89 9B E9 80 BC
// TIM最新, 消息内容 "发图片群"
// 3E 03 3F A2
// 76 E4 B8 DD
// 00 00 00 [0D] 00 01 00 04 00 00 00 00 00 03 00 01 01
// 38 03
// 3E 03 3F A2
// 76 E4 B8 DD
// C6 FB 06 30 0C 69 0C AD C6 AD 14 BF 0B C6 38 EA
// 00 0B
// 3D 88
// 5D AA AE 33
// 01 1D
// 00 00 00 00
// 01 00 00
// 00 01 4D 53 47 00 00 00 00 00
// 5D AA AE 33
// 7E 51 1D AA
// 00 00 00 00 0C 00 86
// 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91
// 00 00
//
// 01 00 0F 01 00 0C E5 8F 91 E5 9B BE E7 89 87 E7 BE A4
writeQQ(botQQ) writeQQ(botQQ)
writeQQ(targetQQ) writeQQ(targetQQ)
writeHex("00 00 00 08 00 01 00 04 00 00 00 00") writeHex("00 00 00 08 00 01 00 04 00 00 00 00")
...@@ -86,8 +33,10 @@ class SendFriendMessagePacket( ...@@ -86,8 +33,10 @@ class SendFriendMessagePacket(
writeHex("00 0B") writeHex("00 0B")
writeRandom(2) writeRandom(2)
writeTime() writeTime()
writeHex("01 1D" + writeHex(
" 00 00 00 00") "01 1D" +
" 00 00 00 00"
)
//消息过多要分包发送 //消息过多要分包发送
//如果只有一个 //如果只有一个
...@@ -119,6 +68,6 @@ class SendFriendMessagePacket( ...@@ -119,6 +68,6 @@ class SendFriendMessagePacket(
} }
} }
@PacketId(0x00_CDu) @AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
class Response(input: ByteReadPacket) : ResponsePacket(input) class Response(input: ByteReadPacket) : ResponsePacket(input)
} }
\ No newline at end of file
...@@ -2,26 +2,23 @@ ...@@ -2,26 +2,23 @@
package net.mamoe.mirai.network.protocol.tim.packet.action package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.contact.GroupInternalId import net.mamoe.mirai.contact.GroupInternalId
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.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
@PacketId(0x00_02u) @AnnotatedId(KnownPacketId.SEND_GROUP_MESSAGE)
class SendGroupMessagePacket( object SendGroupMessagePacket : OutgoingPacketBuilder {
private val botQQ: UInt, operator fun invoke(
private val groupInternalId: GroupInternalId, botQQ: UInt,
private val sessionKey: ByteArray, groupInternalId: GroupInternalId,
private val message: MessageChain sessionKey: ByteArray,
) : OutgoingPacket() { message: MessageChain
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket {
writeQQ(botQQ) writeQQ(botQQ)
writeHex(TIMProtocol.fixVer2) writeHex(TIMProtocol.fixVer2)
...@@ -49,6 +46,6 @@ class SendGroupMessagePacket( ...@@ -49,6 +46,6 @@ class SendGroupMessagePacket(
} }
} }
@PacketId(0x00_02u) @AnnotatedId(KnownPacketId.SEND_GROUP_MESSAGE)
class Response(input: ByteReadPacket) : ResponsePacket(input) class Response(input: ByteReadPacket) : ResponsePacket(input)
} }
\ No newline at end of file
...@@ -12,10 +12,7 @@ import kotlinx.io.core.* ...@@ -12,10 +12,7 @@ import kotlinx.io.core.*
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.ImageId import net.mamoe.mirai.message.ImageId
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket.Response.State.* import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket.Response.State.*
import net.mamoe.mirai.network.qqAccount import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.qqAccount import net.mamoe.mirai.qqAccount
...@@ -132,15 +129,15 @@ internal suspend inline fun HttpClient.postImage( ...@@ -132,15 +129,15 @@ internal suspend inline fun HttpClient.postImage(
* 似乎没有必要. 服务器的返回永远都是 01 00 00 00 02 00 00 * 似乎没有必要. 服务器的返回永远都是 01 00 00 00 02 00 00
*/ */
@Deprecated("Useless packet") @Deprecated("Useless packet")
@PacketId(0X01_BDu) @AnnotatedId(KnownPacketId.SUBMIT_IMAGE_FILE_NAME)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class SubmitImageFilenamePacket( object SubmitImageFilenamePacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val target: UInt, bot: UInt,
private val filename: String, target: UInt,
private val sessionKey: ByteArray filename: String,
) : OutgoingPacket() { sessionKey: ByteArray
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot) writeQQ(bot)
writeHex(TIMProtocol.fixVer2)//? writeHex(TIMProtocol.fixVer2)//?
//writeHex("04 00 00 00 01 2E 01 00 00 69 35") //writeHex("04 00 00 00 01 2E 01 00 00 69 35")
...@@ -168,7 +165,7 @@ class SubmitImageFilenamePacket( ...@@ -168,7 +165,7 @@ class SubmitImageFilenamePacket(
//解密body=01 3E 03 3F A2 7C BC D3 C1 00 00 27 1C 00 0A 00 01 00 01 00 30 55 73 65 72 44 61 74 61 43 75 73 74 6F 6D 46 61 63 65 3A 31 5C 29 37 42 53 4B 48 32 44 35 54 51 28 5A 35 7D 35 24 56 5D 32 35 49 4E 2E 6A 70 67 00 00 03 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2F 02 //解密body=01 3E 03 3F A2 7C BC D3 C1 00 00 27 1C 00 0A 00 01 00 01 00 30 55 73 65 72 44 61 74 61 43 75 73 74 6F 6D 46 61 63 65 3A 31 5C 29 37 42 53 4B 48 32 44 35 54 51 28 5A 35 7D 35 24 56 5D 32 35 49 4E 2E 6A 70 67 00 00 03 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2F 02
} }
@PacketId(0x01_BDu) @AnnotatedId(KnownPacketId.SUBMIT_IMAGE_FILE_NAME)
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173")
class Response(input: ByteReadPacket) : ResponsePacket(input) { class Response(input: ByteReadPacket) : ResponsePacket(input) {
override fun decode() = with(input) { override fun decode() = with(input) {
...@@ -188,16 +185,15 @@ class SubmitImageFilenamePacket( ...@@ -188,16 +185,15 @@ class SubmitImageFilenamePacket(
* - 服务器已经存有这个图片 * - 服务器已经存有这个图片
* - 服务器未存有, 返回一个 key 用于客户端上传 * - 服务器未存有, 返回一个 key 用于客户端上传
*/ */
@PacketId(0x03_52u) @AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class FriendImageIdRequestPacket( object FriendImageIdRequestPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val sessionKey: ByteArray, bot: UInt,
private val target: UInt, sessionKey: ByteArray,
private val image: ExternalImage target: UInt,
) : OutgoingPacket() { image: ExternalImage
) = buildOutgoingPacket {
override fun encode(builder: BytePacketBuilder) = with(builder) {
writeQQ(bot) writeQQ(bot)
writeHex("04 00 00 00 01 2E 01 00 00 69 35 00 00 00 00 00 00 00 00") writeHex("04 00 00 00 01 2E 01 00 00 69 35 00 00 00 00 00 00 00 00")
...@@ -258,7 +254,7 @@ class FriendImageIdRequestPacket( ...@@ -258,7 +254,7 @@ class FriendImageIdRequestPacket(
} }
} }
@PacketId(0x0352u) @AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class Response(input: ByteReadPacket) : ResponsePacket(input) { class Response(input: ByteReadPacket) : ResponsePacket(input) {
/** /**
...@@ -317,16 +313,15 @@ class FriendImageIdRequestPacket( ...@@ -317,16 +313,15 @@ class FriendImageIdRequestPacket(
/** /**
* 获取 Image Id 和上传用的一个 uKey * 获取 Image Id 和上传用的一个 uKey
*/ */
@PacketId(0x0388u) @AnnotatedId(KnownPacketId.GROUP_IMAGE_ID)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class GroupImageIdRequestPacket( object GroupImageIdRequestPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val groupInternalId: GroupInternalId, bot: UInt,
private val image: ExternalImage, groupInternalId: GroupInternalId,
private val sessionKey: ByteArray image: ExternalImage,
) : OutgoingPacket() { sessionKey: ByteArray
) = buildOutgoingPacket {
override fun encode(builder: BytePacketBuilder) = with(builder) {
writeQQ(bot) writeQQ(bot)
writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00") writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
...@@ -378,11 +373,9 @@ class GroupImageIdRequestPacket( ...@@ -378,11 +373,9 @@ class GroupImageIdRequestPacket(
} }
} }
companion object { private val value0x6A: UByteArray = ubyteArrayOf(0x05u, 0x32u, 0x36u, 0x36u, 0x35u, 0x36u)
private val value0x6A: UByteArray = ubyteArrayOf(0x05u, 0x32u, 0x36u, 0x36u, 0x35u, 0x36u)
}
@PacketId(0x0388u) @AnnotatedId(KnownPacketId.GROUP_IMAGE_ID)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class Response(input: ByteReadPacket) : ResponsePacket(input) { class Response(input: ByteReadPacket) : ResponsePacket(input) {
lateinit var state: State lateinit var state: State
......
...@@ -4,20 +4,17 @@ package net.mamoe.mirai.network.protocol.tim.packet.event ...@@ -4,20 +4,17 @@ package net.mamoe.mirai.network.protocol.tim.packet.event
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.applySequence
import net.mamoe.mirai.network.protocol.tim.packet.decryptBy
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
/** /**
* 事件的识别 ID. 在 [事件确认包][ServerEventPacket.ResponsePacket] 中被使用. * 事件的识别 ID. 在 [事件确认包][ServerEventPacket.EventResponse] 中被使用.
*/ */
data class EventPacketIdentity( data 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
) { ) {
override fun toString(): String = "($from->$to)" override fun toString(): String = "($from->$to)"
} }
...@@ -28,8 +25,8 @@ fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) = ...@@ -28,8 +25,8 @@ fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) =
writeFully(uniqueId) writeFully(uniqueId)
} }
fun <S : ServerEventPacket> S.applyId(id: UShort): S { fun <S : ServerEventPacket> S.applyId(id: PacketId): S {
this.id = id this.packetId = id
return this return this
} }
...@@ -39,14 +36,14 @@ fun <S : ServerEventPacket> S.applyId(id: UShort): S { ...@@ -39,14 +36,14 @@ fun <S : ServerEventPacket> S.applyId(id: UShort): S {
* @author Him188moe * @author Him188moe
*/ */
abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: EventPacketIdentity) : ServerPacket(input) { abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: EventPacketIdentity) : ServerPacket(input) {
override var id: UShort = 0u override var packetId: PacketId = NullPacketId
class Raw(input: ByteReadPacket, override val id: UShort) : ServerPacket(input) { class Raw(input: ByteReadPacket, override val packetId: PacketId) : ServerPacket(input) {
fun distribute(): ServerEventPacket = with(input) { fun distribute(): ServerEventPacket = with(input) {
val eventIdentity = EventPacketIdentity( val eventIdentity = EventPacketIdentity(
from = readUInt(), from = readUInt(),
to = readUInt(), to = readUInt(),
uniqueId = readIoBuffer(8) uniqueId = readIoBuffer(8)
) )
discardExact(2) discardExact(2)
val type = readUShort() val type = readUShort()
...@@ -81,10 +78,10 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event ...@@ -81,10 +78,10 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity)*/ else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity)*/
}*/ }*/
0x0210u -> IgnoredServerEventPacket( 0x0210u -> IgnoredServerEventPacket(
eventId = type.toByteArray(), eventId = type.toByteArray(),
showData = true, showData = true,
input = input, input = input,
eventIdentity = eventIdentity eventIdentity = eventIdentity
) )
//"02 10", "00 12" -> ServerUnknownEventPacket(input, eventIdentity) //"02 10", "00 12" -> ServerUnknownEventPacket(input, eventIdentity)
...@@ -93,29 +90,37 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event ...@@ -93,29 +90,37 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
MiraiLogger.debug("UnknownEvent type = ${type.toByteArray().toUHexString()}") MiraiLogger.debug("UnknownEvent type = ${type.toByteArray().toUHexString()}")
UnknownServerEventPacket(input, eventIdentity) UnknownServerEventPacket(input, eventIdentity)
} }
}.applyId(id).applySequence(sequenceId) }.applyId(packetId).applySequence(sequenceId)
} }
class Encrypted(input: ByteReadPacket, override var id: UShort, override var sequenceId: UShort) : ServerPacket(input) { class Encrypted(input: ByteReadPacket, override var packetId: PacketId, override var sequenceId: UShort) :
fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey), id).applySequence(sequenceId) ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): Raw =
Raw(this.decryptBy(sessionKey), packetId).applySequence(sequenceId)
} }
} }
inner class ResponsePacket( @Suppress("FunctionName")
val bot: UInt, fun ResponsePacket(
val sessionKey: ByteArray bot: UInt,
) : OutgoingPacket() { sessionKey: ByteArray
override val id: UShort get() = this@ServerEventPacket.id ): OutgoingPacket = EventResponse(this.packetId, this.sequenceId, bot, sessionKey, this.eventIdentity)
override val sequenceId: UShort get() = this@ServerEventPacket.sequenceId
@Suppress("FunctionName")
override fun encode(builder: BytePacketBuilder) = with(builder) { object EventResponse : OutgoingPacketBuilder {
this.writeQQ(bot) operator fun invoke(
this.writeHex(TIMProtocol.fixVer2) id: PacketId,
this.encryptAndWrite(sessionKey) { sequenceId: UShort,
writeEventPacketIdentity(eventIdentity) bot: UInt,
sessionKey: ByteArray,
identity: EventPacketIdentity
): OutgoingPacket = buildOutgoingPacket(name = "EventResponse", id = id, sequenceId = sequenceId) {
writeQQ(bot)
writeHex(TIMProtocol.fixVer2)
encryptAndWrite(sessionKey) {
writeEventPacketIdentity(identity)
} }
} }
} }
} }
...@@ -123,7 +128,12 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event ...@@ -123,7 +128,12 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
* 忽略的事件 * 忽略的事件
*/ */
@Suppress("unused") @Suppress("unused")
class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boolean = false, input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) { class IgnoredServerEventPacket(
val eventId: ByteArray,
private val showData: Boolean = false,
input: ByteReadPacket,
eventIdentity: EventPacketIdentity
) : ServerEventPacket(input, eventIdentity) {
override fun decode() { override fun decode() {
if (showData) { if (showData) {
MiraiLogger.debug("IgnoredServerEventPacket data: " + this.input.readBytes().toUHexString()) MiraiLogger.debug("IgnoredServerEventPacket data: " + this.input.readBytes().toUHexString())
...@@ -136,7 +146,8 @@ class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boo ...@@ -136,7 +146,8 @@ class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boo
/** /**
* Unknown event * Unknown event
*/ */
class UnknownServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) { class UnknownServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) :
ServerEventPacket(input, eventIdentity) {
override fun decode() { override fun decode() {
MiraiLogger.debug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString()) MiraiLogger.debug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString())
} }
......
...@@ -5,25 +5,23 @@ package net.mamoe.mirai.network.protocol.tim.packet.login ...@@ -5,25 +5,23 @@ package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.network.protocol.tim.TIMProtocol 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.Tested
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
/** /**
* 客户端请求验证码图片数据的第几部分 * 客户端请求验证码图片数据的第几部分
*/ */
@PacketId(0x00_BAu) @AnnotatedId(KnownPacketId.CAPTCHA)
class RequestCaptchaTransmissionPacket( object RequestCaptchaTransmissionPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val token0825: ByteArray, bot: UInt,
private val captchaSequence: Int, token0825: ByteArray,
private val token00BA: ByteArray captchaSequence: Int,
) : OutgoingPacket() { token00BA: ByteArray
@Tested ) = buildOutgoingPacket {
override fun encode(builder: BytePacketBuilder) = with(builder) { writeQQ(bot)
this.writeQQ(bot) writeHex(TIMProtocol.fixVer)
this.writeHex(TIMProtocol.fixVer) writeHex(TIMProtocol.key00BA)
this.writeHex(TIMProtocol.key00BA) encryptAndWrite(TIMProtocol.key00BA) {
this.encryptAndWrite(TIMProtocol.key00BA) {
writeHex("00 02 00 00 08 04 01 E0") writeHex("00 02 00 00 08 04 01 E0")
writeHex(TIMProtocol.constantData2) writeHex(TIMProtocol.constantData2)
writeHex("00 00 38") writeHex("00 00 38")
...@@ -43,22 +41,19 @@ class RequestCaptchaTransmissionPacket( ...@@ -43,22 +41,19 @@ class RequestCaptchaTransmissionPacket(
/** /**
* 提交验证码 * 提交验证码
*/ */
@PacketId(0x00_BAu) @AnnotatedId(KnownPacketId.CAPTCHA)
class SubmitCaptchaPacket( object SubmitCaptchaPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val token0825: ByteArray, bot: UInt,
private val captcha: String, token0825: ByteArray,
private val captchaToken: IoBuffer captcha: String,
) : OutgoingPacket() { captchaToken: IoBuffer
init { ) = buildOutgoingPacket {
require(captcha.length == 4) { "captcha.length must == 4" } require(captcha.length == 4) { "captcha.length must == 4" }
} writeQQ(bot)
writeHex(TIMProtocol.fixVer)
override fun encode(builder: BytePacketBuilder) = with(builder) { writeHex(TIMProtocol.key00BA)
this.writeQQ(bot) encryptAndWrite(TIMProtocol.key00BA) {
this.writeHex(TIMProtocol.fixVer)
this.writeHex(TIMProtocol.key00BA)
this.encryptAndWrite(TIMProtocol.key00BA) {
writeHex("00 02 00 00 08 04 01 E0") writeHex("00 02 00 00 08 04 01 E0")
writeHex(TIMProtocol.constantData2) writeHex(TIMProtocol.constantData2)
writeHex("01 00 38") writeHex("01 00 38")
...@@ -82,16 +77,16 @@ class SubmitCaptchaPacket( ...@@ -82,16 +77,16 @@ class SubmitCaptchaPacket(
/** /**
* 刷新验证码 * 刷新验证码
*/ */
@PacketId(0x00_BAu) @AnnotatedId(KnownPacketId.CAPTCHA)
class OutgoingCaptchaRefreshPacket( object OutgoingCaptchaRefreshPacket : OutgoingPacketBuilder {
private val qq: UInt, operator fun invoke(
private val token0825: ByteArray qq: UInt,
) : OutgoingPacket() { token0825: ByteArray
override fun encode(builder: BytePacketBuilder) = with(builder) { ) = buildOutgoingPacket {
this.writeQQ(qq) writeQQ(qq)
this.writeHex(TIMProtocol.fixVer) writeHex(TIMProtocol.fixVer)
this.writeHex(TIMProtocol.key00BA) writeHex(TIMProtocol.key00BA)
this.encryptAndWrite(TIMProtocol.key00BA) { encryptAndWrite(TIMProtocol.key00BA) {
writeHex("00 02 00 00 08 04 01 E0") writeHex("00 02 00 00 08 04 01 E0")
writeHex(TIMProtocol.constantData2) writeHex(TIMProtocol.constantData2)
writeHex("00 00 38") writeHex("00 00 38")
...@@ -109,15 +104,13 @@ class OutgoingCaptchaRefreshPacket( ...@@ -109,15 +104,13 @@ class OutgoingCaptchaRefreshPacket(
* *
* @author Him188moe * @author Him188moe
*/ */
@PacketId(0x00_BAu) @AnnotatedId(KnownPacketId.CAPTCHA)
open class CaptchaTransmissionResponsePacket(input: ByteReadPacket) : ServerCaptchaPacket(input) { open class CaptchaTransmissionResponsePacket(input: ByteReadPacket) : ServerCaptchaPacket(input) {
lateinit var captchaSectionN: IoBuffer lateinit var captchaSectionN: IoBuffer
lateinit var captchaToken: IoBuffer//56bytes lateinit var captchaToken: IoBuffer//56bytes
var transmissionCompleted: Boolean = false//验证码是否已经传输完成 var transmissionCompleted: Boolean = false//验证码是否已经传输完成
lateinit var token00BA: ByteArray//40 bytes lateinit var token00BA: ByteArray//40 bytes
override fun decode() = with(input) { override fun decode() = with(input) {
input.discardExact(10)//13 00 05 01 00 00 01 23 00 38 input.discardExact(10)//13 00 05 01 00 00 01 23 00 38
captchaToken = readIoBuffer(56) captchaToken = readIoBuffer(56)
...@@ -148,7 +141,7 @@ fun main() { ...@@ -148,7 +141,7 @@ fun main() {
* *
* @author Him188moe * @author Him188moe
*/ */
@PacketId(0x00_BAu) @AnnotatedId(KnownPacketId.CAPTCHA)
class CaptchaCorrectPacket(input: ByteReadPacket) : ServerCaptchaPacket(input) { class CaptchaCorrectPacket(input: ByteReadPacket) : ServerCaptchaPacket(input) {
lateinit var token00BA: ByteArray//56 bytes lateinit var token00BA: ByteArray//56 bytes
...@@ -158,10 +151,10 @@ class CaptchaCorrectPacket(input: ByteReadPacket) : ServerCaptchaPacket(input) { ...@@ -158,10 +151,10 @@ class CaptchaCorrectPacket(input: ByteReadPacket) : ServerCaptchaPacket(input) {
} }
} }
@PacketId(0x00_BAu) @AnnotatedId(KnownPacketId.CAPTCHA)
abstract class ServerCaptchaPacket(input: ByteReadPacket) : ServerPacket(input) { abstract class ServerCaptchaPacket(input: ByteReadPacket) : ServerPacket(input) {
@PacketId(0x00_BAu) @AnnotatedId(KnownPacketId.CAPTCHA)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(): ServerCaptchaPacket { fun decrypt(): ServerCaptchaPacket {
return this.decryptAsByteArray(TIMProtocol.key00BA) { data -> return this.decryptAsByteArray(TIMProtocol.key00BA) { data ->
......
...@@ -2,11 +2,9 @@ ...@@ -2,11 +2,9 @@
package net.mamoe.mirai.network.protocol.tim.packet.login package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.writeUByte import kotlinx.io.core.writeUByte
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.utils.OnlineStatus import net.mamoe.mirai.utils.OnlineStatus
import net.mamoe.mirai.utils.io.encryptAndWrite import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeHex import net.mamoe.mirai.utils.io.writeHex
...@@ -15,21 +13,19 @@ import net.mamoe.mirai.utils.io.writeQQ ...@@ -15,21 +13,19 @@ import net.mamoe.mirai.utils.io.writeQQ
/** /**
* 改变在线状态: "我在线上", "隐身" 等 * 改变在线状态: "我在线上", "隐身" 等
*/ */
@PacketId(0x00_ECu) @AnnotatedId(KnownPacketId.CHANGE_ONLINE_STATUS)
class ChangeOnlineStatusPacket( object ChangeOnlineStatusPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val sessionKey: ByteArray, bot: UInt,
private val loginStatus: OnlineStatus sessionKey: ByteArray,
) : OutgoingPacket() { loginStatus: OnlineStatus
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket {
this.writeQQ(bot) writeQQ(bot)
this.writeHex(TIMProtocol.fixVer2) writeHex(TIMProtocol.fixVer2)
this.encryptAndWrite(sessionKey) { encryptAndWrite(sessionKey) {
writeHex("01 00") writeHex("01 00")
writeUByte(loginStatus.id) writeUByte(loginStatus.id)
writeHex("00 01 00 01 00 04 00 00 00 00") writeHex("00 01 00 01 00 04 00 00 00 00")
} }
} }
} }
\ No newline at end of file
...@@ -43,7 +43,7 @@ enum class LoginResult { ...@@ -43,7 +43,7 @@ enum class LoginResult {
UNKNOWN, UNKNOWN,
/** /**
* 未知. 更换服务器或等几分钟再登录可能解决. * 包数据错误
*/ */
INTERNAL_ERROR, INTERNAL_ERROR,
...@@ -57,7 +57,7 @@ enum class LoginResult { ...@@ -57,7 +57,7 @@ enum class LoginResult {
* 如果 [this] 不为 [LoginResult.SUCCESS] 就抛出消息为 [lazyMessage] 的 [IllegalStateException] * 如果 [this] 不为 [LoginResult.SUCCESS] 就抛出消息为 [lazyMessage] 的 [IllegalStateException]
*/ */
fun LoginResult.requireSuccess(lazyMessage: (LoginResult) -> String) { fun LoginResult.requireSuccess(lazyMessage: (LoginResult) -> String) {
if (this != LoginResult.SUCCESS) error(lazyMessage(this)) if (this != SUCCESS) error(lazyMessage(this))
} }
...@@ -77,7 +77,7 @@ fun LoginResult.requireSuccess() { ...@@ -77,7 +77,7 @@ fun LoginResult.requireSuccess() {
* @return 成功时 [Unit], 失败时 `null` * @return 成功时 [Unit], 失败时 `null`
*/ */
fun LoginResult.requireSuccessOrNull(): Unit? = fun LoginResult.requireSuccessOrNull(): Unit? =
if (this != LoginResult.SUCCESS) Unit else null if (this == SUCCESS) Unit else null
/** /**
...@@ -87,4 +87,4 @@ fun LoginResult.requireSuccessOrNull(): Unit? = ...@@ -87,4 +87,4 @@ fun LoginResult.requireSuccessOrNull(): Unit? =
* @return 成功时 [Unit], 失败时 `null` * @return 成功时 [Unit], 失败时 `null`
*/ */
inline fun <R> LoginResult.ifFail(block: (LoginResult) -> R): R? = inline fun <R> LoginResult.ifFail(block: (LoginResult) -> R): R? =
if (this != LoginResult.SUCCESS) block(this) else null if (this != SUCCESS) block(this) else null
...@@ -6,29 +6,27 @@ import kotlinx.io.core.BytePacketBuilder ...@@ -6,29 +6,27 @@ import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.IoBuffer import kotlinx.io.core.IoBuffer
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.utils.encryptBy 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
/** /**
* 提交密码 * 提交密码
*/ */
@PacketId(0x08_36u) @AnnotatedId(KnownPacketId.LOGIN)
class SubmitPasswordPacket constructor( object SubmitPasswordPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val password: String, bot: UInt,
private val loginTime: Int, password: String,
private val loginIP: String, loginTime: Int,
private val privateKey: ByteArray, loginIP: String,
private val token0825: ByteArray, privateKey: ByteArray,
private val token00BA: ByteArray? = null, token0825: ByteArray,
private val randomDeviceName: Boolean = false, token00BA: ByteArray? = null,
private val tlv0006: IoBuffer? = null randomDeviceName: Boolean = false,
) : OutgoingPacket() { tlv0006: IoBuffer? = null
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot) writeQQ(bot)
writeHex(TIMProtocol.passwordSubmissionTLV1) writeHex(TIMProtocol.passwordSubmissionTLV1)
...@@ -57,18 +55,16 @@ class SubmitPasswordPacket constructor( ...@@ -57,18 +55,16 @@ class SubmitPasswordPacket constructor(
} }
} }
private fun BytePacketBuilder.writePart1( private fun BytePacketBuilder.writePart1(
qq: UInt, qq: UInt,
password: String, password: String,
loginTime: Int, loginTime: Int,
loginIP: String, loginIP: String,
privateKey: ByteArray, privateKey: ByteArray,
token0825: ByteArray, token0825: ByteArray,
randomDeviceName: Boolean, randomDeviceName: Boolean,
tlv0006: IoBuffer? = null tlv0006: IoBuffer? = null
) { ) {
//this.writeInt(System.currentTimeMillis().toInt()) //this.writeInt(System.currentTimeMillis().toInt())
this.writeHex("01 12")//tag this.writeHex("01 12")//tag
this.writeHex("00 38")//length this.writeHex("00 38")//length
...@@ -103,7 +99,6 @@ private fun BytePacketBuilder.writePart1( ...@@ -103,7 +99,6 @@ private fun BytePacketBuilder.writePart1(
this.writeHex("60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6")//key this.writeHex("60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6")//key
} }
private fun BytePacketBuilder.writePart2() { private fun BytePacketBuilder.writePart2() {
this.writeHex("03 12")//tag this.writeHex("03 12")//tag
......
...@@ -2,29 +2,26 @@ ...@@ -2,29 +2,26 @@
package net.mamoe.mirai.network.protocol.tim.packet.login package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
import net.mamoe.mirai.network.qqAccount import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
fun BotSession.RequestSKeyPacket() = RequestSKeyPacket(qqAccount, sessionKey) fun BotSession.RequestSKeyPacket(): OutgoingPacket = RequestSKeyPacket(qqAccount, sessionKey)
/** /**
* 请求 `SKey` * 请求 `SKey`
* SKey 用于 http api * SKey 用于 http api
*/ */
@PacketId(0x00_1Du) @AnnotatedId(KnownPacketId.S_KEY)
class RequestSKeyPacket( object RequestSKeyPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val sessionKey: ByteArray bot: UInt,
) : OutgoingPacket() { sessionKey: ByteArray
override fun encode(builder: BytePacketBuilder) = with(builder) { ): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot) writeQQ(bot)
writeHex(TIMProtocol.fixVer2) writeHex(TIMProtocol.fixVer2)
encryptAndWrite(sessionKey) { encryptAndWrite(sessionKey) {
...@@ -32,7 +29,7 @@ class RequestSKeyPacket( ...@@ -32,7 +29,7 @@ class RequestSKeyPacket(
} }
} }
@PacketId(0x00_1Du) @AnnotatedId(KnownPacketId.S_KEY)
class Response(input: ByteReadPacket) : ResponsePacket(input) { class Response(input: ByteReadPacket) : ResponsePacket(input) {
lateinit var sKey: String lateinit var sKey: String
......
...@@ -4,26 +4,23 @@ package net.mamoe.mirai.network.protocol.tim.packet.login ...@@ -4,26 +4,23 @@ package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.PacketId import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.applySequence
import net.mamoe.mirai.network.protocol.tim.packet.decryptBy
import net.mamoe.mirai.utils.Tested import net.mamoe.mirai.utils.Tested
import net.mamoe.mirai.utils.io.readBoolean import net.mamoe.mirai.utils.io.readBoolean
import net.mamoe.mirai.utils.io.readIoBuffer import net.mamoe.mirai.utils.io.readIoBuffer
import net.mamoe.mirai.utils.io.readString import net.mamoe.mirai.utils.io.readString
import kotlin.properties.Delegates import kotlin.properties.Delegates
@PacketId(0x08_36u) @AnnotatedId(KnownPacketId.LOGIN)
sealed class ServerLoginResponsePacket(input: ByteReadPacket) : ServerPacket(input) sealed class ServerLoginResponsePacket(input: ByteReadPacket) : ServerPacket(input)
@PacketId(0x08_36u) @AnnotatedId(KnownPacketId.LOGIN)
class LoginResponseFailedPacket(val loginResult: LoginResult, input: ByteReadPacket) : ServerLoginResponsePacket(input) class LoginResponseFailedPacket(val loginResult: LoginResult, input: ByteReadPacket) : ServerLoginResponsePacket(input)
/** /**
* 服务器进行加密后返回 privateKey * 服务器进行加密后返回 privateKey
*/ */
@PacketId(0x08_36u) @AnnotatedId(KnownPacketId.LOGIN)
class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
lateinit var tlv0006: IoBuffer//120bytes lateinit var tlv0006: IoBuffer//120bytes
var tokenUnknown: ByteArray? = null var tokenUnknown: ByteArray? = null
...@@ -37,7 +34,6 @@ class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLogi ...@@ -37,7 +34,6 @@ class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLogi
this.input.discardExact(4)//00 06 00 78 this.input.discardExact(4)//00 06 00 78
tlv0006 = this.input.readIoBuffer(0x78) tlv0006 = this.input.readIoBuffer(0x78)
//todo 这边原本会判断是否 `08 36 31 03`, 是才会进行下列2行读取.
try { try {
this.input.discardExact(8)//01 10 00 3C 00 01 00 38 this.input.discardExact(8)//01 10 00 3C 00 01 00 38
tokenUnknown = this.input.readBytes(56) tokenUnknown = this.input.readBytes(56)
...@@ -46,7 +42,7 @@ class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLogi ...@@ -46,7 +42,7 @@ class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLogi
} }
} }
@PacketId(0x08_36u) @AnnotatedId(KnownPacketId.LOGIN)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
@Tested @Tested
fun decrypt(privateKey: ByteArray): LoginResponseKeyExchangeResponsePacket = fun decrypt(privateKey: ByteArray): LoginResponseKeyExchangeResponsePacket =
...@@ -59,10 +55,7 @@ enum class Gender(val id: Boolean) { ...@@ -59,10 +55,7 @@ enum class Gender(val id: Boolean) {
FEMALE(true); FEMALE(true);
} }
/** @AnnotatedId(KnownPacketId.LOGIN)
* @author NaturalHG
*/
@PacketId(0x08_36u)
class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
lateinit var sessionResponseDecryptionKey: IoBuffer//16 bytes| lateinit var sessionResponseDecryptionKey: IoBuffer//16 bytes|
...@@ -122,7 +115,7 @@ class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePac ...@@ -122,7 +115,7 @@ class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePac
gender = if (readBoolean()) Gender.FEMALE else Gender.MALE gender = if (readBoolean()) Gender.FEMALE else Gender.MALE
} }
@PacketId(0x08_36u) @AnnotatedId(KnownPacketId.LOGIN)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(privateKey: ByteArray): LoginResponseSuccessPacket = LoginResponseSuccessPacket(this.decryptBy(TIMProtocol.shareKey, privateKey)).applySequence(sequenceId) fun decrypt(privateKey: ByteArray): LoginResponseSuccessPacket = LoginResponseSuccessPacket(this.decryptBy(TIMProtocol.shareKey, privateKey)).applySequence(sequenceId)
} }
...@@ -134,7 +127,7 @@ class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePac ...@@ -134,7 +127,7 @@ class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePac
* *
* @author Him188moe * @author Him188moe
*/ */
@PacketId(0x08_36u) @AnnotatedId(KnownPacketId.LOGIN)
class LoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { class LoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
lateinit var captchaPart1: IoBuffer lateinit var captchaPart1: IoBuffer
...@@ -158,7 +151,7 @@ class LoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginRespons ...@@ -158,7 +151,7 @@ class LoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginRespons
} }
@PacketId(0x08_36u) @AnnotatedId(KnownPacketId.LOGIN)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(): LoginResponseCaptchaInitPacket = LoginResponseCaptchaInitPacket(decryptBy(TIMProtocol.shareKey)).applySequence(sequenceId) fun decrypt(): LoginResponseCaptchaInitPacket = LoginResponseCaptchaInitPacket(decryptBy(TIMProtocol.shareKey)).applySequence(sequenceId)
} }
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
package net.mamoe.mirai.network.protocol.tim.packet.login package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.network.protocol.tim.packet.PacketId import net.mamoe.mirai.network.protocol.tim.packet.AnnotatedId
import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
/** /**
...@@ -11,5 +12,5 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket ...@@ -11,5 +12,5 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
* *
* @author Him188moe * @author Him188moe
*/ */
@PacketId(0x00_ECu) @AnnotatedId(KnownPacketId.CHANGE_ONLINE_STATUS)
class ServerLoginSuccessPacket(input: ByteReadPacket) : ServerPacket(input)//TODO 可能只是 login status change 的返回包 class ServerLoginSuccessPacket(input: ByteReadPacket) : ServerPacket(input)//TODO 可能只是 login status change 的返回包
\ No newline at end of file
...@@ -9,20 +9,20 @@ import net.mamoe.mirai.utils.Tested ...@@ -9,20 +9,20 @@ import net.mamoe.mirai.utils.Tested
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.localIpAddress import net.mamoe.mirai.utils.localIpAddress
@PacketId(0x08_28u) @AnnotatedId(KnownPacketId.SESSION_KEY)
class RequestSessionPacket( object RequestSessionPacket : OutgoingPacketBuilder {
private val bot: UInt, operator fun invoke(
private val serverIp: String, bot: UInt,
private val token38: IoBuffer, serverIp: String,
private val token88: IoBuffer, token38: IoBuffer,
private val encryptionKey: IoBuffer token88: IoBuffer,
) : OutgoingPacket() { encryptionKey: IoBuffer
override fun encode(builder: BytePacketBuilder) = with(builder) { ) = buildOutgoingPacket {
this.writeQQ(bot) writeQQ(bot)
this.writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A") writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A")
this.writeHex("00 38") writeHex("00 38")
this.writeFully(token38) writeFully(token38)
this.encryptAndWrite(encryptionKey) { encryptAndWrite(encryptionKey) {
writeHex("00 07 00 88") writeHex("00 07 00 88")
writeFully(token88) writeFully(token88)
writeHex("00 0C 00 16 00 02 00 00 00 00 00 00 00 00 00 00") writeHex("00 0C 00 16 00 02 00 00 00 00 00 00 00 00 00 00")
...@@ -55,13 +55,13 @@ class RequestSessionPacket( ...@@ -55,13 +55,13 @@ class RequestSessionPacket(
writeHex("68") writeHex("68")
writeHex("00 00 00 00 00 2D 00 06 00 01") writeHex("00 00 00 00 00 2D 00 06 00 01")
writeIP(localIpAddress())//todo random to avoid being banned? writeIP(localIpAddress())//todo random to avoid being banned? or that may cause errors?
} }
} }
} }
@PacketId(0x08_28u) @AnnotatedId(KnownPacketId.SESSION_KEY)
class SessionKeyResponsePacket(input: ByteReadPacket) : ServerPacket(input) { class SessionKeyResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
lateinit var sessionKey: ByteArray lateinit var sessionKey: ByteArray
lateinit var tlv0105: ByteReadPacket lateinit var tlv0105: ByteReadPacket
...@@ -110,7 +110,7 @@ Discarded(11) =41 01 00 02 03 3C 01 03 00 00 86 ...@@ -110,7 +110,7 @@ Discarded(11) =41 01 00 02 03 3C 01 03 00 00 86
} }
@PacketId(0x08_28u) @AnnotatedId(KnownPacketId.SESSION_KEY)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(sessionResponseDecryptionKey: IoBuffer): SessionKeyResponsePacket = fun decrypt(sessionResponseDecryptionKey: IoBuffer): SessionKeyResponsePacket =
SessionKeyResponsePacket(decryptBy(sessionResponseDecryptionKey)).applySequence(sequenceId) SessionKeyResponsePacket(decryptBy(sessionResponseDecryptionKey)).applySequence(sequenceId)
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet.login package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
...@@ -19,7 +18,7 @@ import net.mamoe.mirai.utils.io.* ...@@ -19,7 +18,7 @@ import net.mamoe.mirai.utils.io.*
* @author Him188moe * @author Him188moe
*/ */
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") @Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
@PacketId(0x08_25u) @AnnotatedId(KnownPacketId.TOUCH)
class TouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) { class TouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
var serverIP: String? = null var serverIP: String? = null
...@@ -48,9 +47,10 @@ class TouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) { ...@@ -48,9 +47,10 @@ class TouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
} }
} }
@PacketId(0x08_25u) @AnnotatedId(KnownPacketId.TOUCH)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(): TouchResponsePacket = TouchResponsePacket(decryptBy(TIMProtocol.touchKey.hexToBytes())).applySequence(sequenceId) fun decrypt(): TouchResponsePacket =
TouchResponsePacket(decryptBy(TIMProtocol.touchKey.hexToBytes())).applySequence(sequenceId)
} }
} }
...@@ -59,14 +59,17 @@ class TouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) { ...@@ -59,14 +59,17 @@ class TouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
* *
* @author Him188moe * @author Him188moe
*/ */
@PacketId(0x08_25u) @AnnotatedId(KnownPacketId.TOUCH)
class TouchPacket(private val bot: UInt, private val serverIp: String) : OutgoingPacket() { object TouchPacket : OutgoingPacketBuilder {
override fun encode(builder: BytePacketBuilder) = with(builder) { operator fun invoke(
this.writeQQ(bot) bot: UInt,
this.writeHex(TIMProtocol.fixVer) serverIp: String
this.writeHex(TIMProtocol.touchKey) ) = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer)
writeHex(TIMProtocol.touchKey)
this.encryptAndWrite(TIMProtocol.touchKey) { encryptAndWrite(TIMProtocol.touchKey) {
writeHex(TIMProtocol.constantData1) writeHex(TIMProtocol.constantData1)
writeHex(TIMProtocol.constantData2) writeHex(TIMProtocol.constantData2)
writeQQ(bot) writeQQ(bot)
...@@ -83,21 +86,24 @@ class TouchPacket(private val bot: UInt, private val serverIp: String) : Outgoin ...@@ -83,21 +86,24 @@ class TouchPacket(private val bot: UInt, private val serverIp: String) : Outgoin
* *
* @author Him188moe * @author Him188moe
*/ */
@PacketId(0x08_25u) @AnnotatedId(KnownPacketId.TOUCH)
class RedirectionPacket(private val bot: UInt, private val serverIP: String) : OutgoingPacket() { object RedirectionPacket : OutgoingPacketBuilder {
override fun encode(builder: BytePacketBuilder) = with(builder) { operator fun invoke(
this.writeQQ(bot) bot: UInt,
this.writeHex(TIMProtocol.fixVer) serverIP: String
this.writeHex(TIMProtocol.touchKey)//redirection key ) = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer)
writeHex(TIMProtocol.touchKey)//redirection key
this.encryptAndWrite(TIMProtocol.touchKey) { encryptAndWrite(TIMProtocol.touchKey) {
this.writeHex(TIMProtocol.constantData1) writeHex(TIMProtocol.constantData1)
this.writeHex(TIMProtocol.constantData2) writeHex(TIMProtocol.constantData2)
this.writeQQ(bot) writeQQ(bot)
this.writeHex("00 01 00 00 03 09 00 0C 00 01") writeHex("00 01 00 00 03 09 00 0C 00 01")
this.writeIP(serverIP) writeIP(serverIP)
this.writeHex("01 6F A1 58 22 01 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 03 00 19") writeHex("01 6F A1 58 22 01 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 03 00 19")
this.writeHex(TIMProtocol.publicKey) writeHex(TIMProtocol.publicKey)
} }
} }
} }
\ No newline at end of file
...@@ -5,6 +5,7 @@ package net.mamoe.mirai.utils.io ...@@ -5,6 +5,7 @@ package net.mamoe.mirai.utils.io
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.network.protocol.tim.TIMProtocol 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.network.protocol.tim.packet.KnownPacketId.*
import net.mamoe.mirai.network.protocol.tim.packet.action.* import net.mamoe.mirai.network.protocol.tim.packet.action.*
import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.* import net.mamoe.mirai.network.protocol.tim.packet.login.*
...@@ -13,11 +14,11 @@ import net.mamoe.mirai.utils.decryptBy ...@@ -13,11 +14,11 @@ import net.mamoe.mirai.utils.decryptBy
fun ByteReadPacket.readRemainingBytes( fun ByteReadPacket.readRemainingBytes(
n: Int = remaining.toInt()//not that safe but adequate n: Int = remaining.toInt()//not that safe but adequate
): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) } ): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) }
fun ByteReadPacket.readIoBuffer( fun ByteReadPacket.readIoBuffer(
n: Int = remaining.toInt()//not that safe but adequate n: Int = remaining.toInt()//not that safe but adequate
): IoBuffer = IoBuffer.Pool.borrow().also { this.readFully(it, n) } ): IoBuffer = IoBuffer.Pool.borrow().also { this.readFully(it, n) }
fun ByteReadPacket.readIoBuffer(n: Number) = this.readIoBuffer(n.toInt()) fun ByteReadPacket.readIoBuffer(n: Number) = this.readIoBuffer(n.toInt())
...@@ -30,63 +31,55 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket { ...@@ -30,63 +31,55 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
val sequenceId = readUShort() val sequenceId = readUShort()
discardExact(7)//4 for qq number, 3 for 0x00 0x00 0x00. 但更可能是应该 discard 8 discardExact(7)//4 for qq number, 3 for 0x00 0x00 0x00. 但更可能是应该 discard 8
return when (id.toUInt()) { return when (PacketId(id)) {
0x08_25u -> TouchResponsePacket.Encrypted(this) TOUCH -> TouchResponsePacket.Encrypted(this)
0x08_36u -> { LOGIN ->
//todo 不要用size分析 //todo 不要用size分析
when (size) { when {
271, 207 -> return LoginResponseKeyExchangeResponsePacket.Encrypted(this).applySequence(sequenceId) size == 271 || size == 207 -> LoginResponseKeyExchangeResponsePacket.Encrypted(this)
871 -> return LoginResponseCaptchaInitPacket.Encrypted(this).applySequence(sequenceId) size == 871 -> LoginResponseCaptchaInitPacket.Encrypted(this)
size > 700 -> LoginResponseSuccessPacket.Encrypted(this)
else -> LoginResponseFailedPacket(
when (size) {
135 -> {//包数据错误. 目前怀疑是 tlv0006
this.readRemainingBytes().cutTail(1).decryptBy(TIMProtocol.shareKey).read {
discardExact(51)
MiraiLogger.error("Internal error: " + readLVString())//抱歉,请重新输入密码。
}
LoginResult.INTERNAL_ERROR
}
319, 351 -> LoginResult.WRONG_PASSWORD
//135 -> LoginState.RETYPE_PASSWORD
63 -> LoginResult.BLOCKED
263 -> LoginResult.UNKNOWN_QQ_NUMBER
279, 495, 551, 487 -> LoginResult.DEVICE_LOCK
343, 359 -> LoginResult.TAKEN_BACK
else -> LoginResult.UNKNOWN
}, this)
} }
SESSION_KEY -> SessionKeyResponsePacket.Encrypted(this)
if (size > 700) return LoginResponseSuccessPacket.Encrypted(this).applySequence(sequenceId)
CHANGE_ONLINE_STATUS -> ServerLoginSuccessPacket(this)
println("登录包size=$size") CAPTCHA -> ServerCaptchaPacket.Encrypted(this)
return LoginResponseFailedPacket( SERVER_EVENT_1, SERVER_EVENT_2 -> ServerEventPacket.Raw.Encrypted(this, PacketId(id), sequenceId)
when (size) { FRIEND_ONLINE_STATUS_CHANGE -> ServerFriendOnlineStatusChangedPacket.Encrypted(this)
135 -> {//包数据错误. 目前怀疑是 tlv0006
this.readRemainingBytes().cutTail(1).decryptBy(TIMProtocol.shareKey).read { S_KEY -> ResponsePacket.Encrypted<RequestSKeyPacket.Response>(this)
discardExact(51) ACCOUNT_INFO -> ResponsePacket.Encrypted<RequestAccountInfoPacket.Response>(this)
MiraiLogger.error("Internal error: " + readLVString())//抱歉,请重新输入密码。 SEND_GROUP_MESSAGE -> ResponsePacket.Encrypted<SendGroupMessagePacket.Response>(this)
} SEND_FRIEND_MESSAGE -> ResponsePacket.Encrypted<SendFriendMessagePacket.Response>(this)
CAN_ADD_FRIEND -> ResponsePacket.Encrypted<CanAddFriendPacket.Response>(this)
LoginResult.INTERNAL_ERROR HEARTBEAT -> ResponsePacket.Encrypted<HeartbeatPacket.Response>(this)
} //可能是包数据错了. 账号没有被ban, 用TIM官方可以登录 GROUP_IMAGE_ID -> ResponsePacket.Encrypted<GroupImageIdRequestPacket.Response>(this)
FRIEND_IMAGE_ID
319, 351 -> LoginResult.WRONG_PASSWORD -> ResponsePacket.Encrypted<FriendImageIdRequestPacket.Response>(this)
//135 -> LoginState.RETYPE_PASSWORD // 0x01_BDu -> EventResponse.Encrypted<SubmitImageFilenamePacket.Response>(this)
63 -> LoginResult.BLOCKED
263 -> LoginResult.UNKNOWN_QQ_NUMBER else -> UnknownServerPacket.Encrypted(this, PacketId(id), sequenceId)
279, 495, 551, 487 -> LoginResult.DEVICE_LOCK
343, 359 -> LoginResult.TAKEN_BACK
else -> LoginResult.UNKNOWN
/*
//unknown
63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
else -> throw IllegalArgumentException(bytes.size.toString())*/
}, this).applySequence(sequenceId)
}
0x08_28u -> SessionKeyResponsePacket.Encrypted(this)
0x00_ECu -> ServerLoginSuccessPacket(this)
0x00_BAu -> ServerCaptchaPacket.Encrypted(this)
0x00_CEu, 0x00_17u -> ServerEventPacket.Raw.Encrypted(this, id, sequenceId)
0x00_81u -> ServerFriendOnlineStatusChangedPacket.Encrypted(this)
0x00_1Du -> ResponsePacket.Encrypted<RequestSKeyPacket.Response>(this)
0X00_5Cu -> ResponsePacket.Encrypted<RequestAccountInfoPacket.Response>(this)
0x00_02u -> ResponsePacket.Encrypted<SendGroupMessagePacket.Response>(this)
0x00_CDu -> ResponsePacket.Encrypted<SendFriendMessagePacket.Response>(this)
0x00_A7u -> ResponsePacket.Encrypted<CanAddFriendPacket.Response>(this)
0x00_58u -> ResponsePacket.Encrypted<HeartbeatPacket.Response>(this)
0x03_88u -> ResponsePacket.Encrypted<GroupImageIdRequestPacket.Response>(this)
0x03_52u -> ResponsePacket.Encrypted<FriendImageIdRequestPacket.Response>(this)
// 0x01_BDu -> ResponsePacket.Encrypted<SubmitImageFilenamePacket.Response>(this)
else -> UnknownServerPacket.Encrypted(this, id, sequenceId)
}.applySequence(sequenceId) }.applySequence(sequenceId)
} }
...@@ -130,7 +123,8 @@ fun Input.readTLVMap(expectingEOF: Boolean = false): Map<Int, ByteArray> { ...@@ -130,7 +123,8 @@ fun Input.readTLVMap(expectingEOF: Boolean = false): Map<Int, ByteArray> {
return map return map
} }
fun Map<Int, ByteArray>.printTLVMap(name: String) = debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }) fun Map<Int, ByteArray>.printTLVMap(name: String) =
debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() })
fun Input.readString(length: Number): String = String(this.readBytes(length.toInt())) fun Input.readString(length: Number): String = String(this.readBytes(length.toInt()))
......
package net.mamoe.mirai.event.internal
import net.mamoe.mirai.event.Event
import kotlin.reflect.KClass
import kotlin.reflect.full.allSuperclasses
import kotlin.reflect.full.isSuperclassOf
@Suppress("UNCHECKED_CAST")
internal actual suspend inline fun <E : Event> loopAllListeners(
clazz: KClass<E>,
consumer: (EventListeners<in E>) -> Unit
) {
clazz.allSuperclasses.forEach {
if (Event::class.isSuperclassOf(it)) {
consumer((it as KClass<out Event>).listeners() as EventListeners<in E>)
}
}
}
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE") @file:Suppress("EXPERIMENTAL_API_USAGE")
package mirai.test
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
......
@file:Suppress("ObjectPropertyName", "unused", "NonAsciiCharacters", "MayBeConstant") @file:Suppress("ObjectPropertyName", "unused", "NonAsciiCharacters", "MayBeConstant")
package mirai.test
import net.mamoe.mirai.utils.io.printCompareHex import net.mamoe.mirai.utils.io.printCompareHex
......
package mirai.test
import kotlin.reflect.KProperty
class A {
val valProp: Any = Any()
}
fun main() {
A::class.members.filterIsInstance<KProperty<*>>().forEach {
println(it.getter.call(A()))
}
}
\ No newline at end of file
package mirai.test
import java.io.File import java.io.File
fun main() { fun main() {
......
package mirai.test
fun main() { fun main() {
repeat(100) { repeat(100) {
println("\u001b[1;${it}m" + it) println("\u001b[1;${it}m" + it)
......
...@@ -13,6 +13,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket ...@@ -13,6 +13,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.UnknownServerPacket import net.mamoe.mirai.network.protocol.tim.packet.UnknownServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.event.UnknownServerEventPacket import net.mamoe.mirai.network.protocol.tim.packet.event.UnknownServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.idHexString
import net.mamoe.mirai.utils.DecryptionFailedException import net.mamoe.mirai.utils.DecryptionFailedException
import net.mamoe.mirai.utils.decryptBy import net.mamoe.mirai.utils.decryptBy
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
......
...@@ -12,6 +12,7 @@ import net.mamoe.mirai.event.events.FriendMessageEvent ...@@ -12,6 +12,7 @@ import net.mamoe.mirai.event.events.FriendMessageEvent
import net.mamoe.mirai.login import net.mamoe.mirai.login
import net.mamoe.mirai.message.* import net.mamoe.mirai.message.*
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingRawPacket import net.mamoe.mirai.network.protocol.tim.packet.OutgoingRawPacket
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
import net.mamoe.mirai.network.protocol.tim.packet.login.ifFail import net.mamoe.mirai.network.protocol.tim.packet.login.ifFail
import net.mamoe.mirai.network.session import net.mamoe.mirai.network.session
...@@ -19,7 +20,7 @@ import net.mamoe.mirai.qqAccount ...@@ -19,7 +20,7 @@ import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.toByteArray import net.mamoe.mirai.utils.io.toByteArray
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
import net.mamoe.mirai.utils.toExternalImage import net.mamoe.mirai.utils.suspendToExternalImage
import java.io.File import java.io.File
import kotlin.system.exitProcess import kotlin.system.exitProcess
...@@ -227,7 +228,7 @@ suspend fun directlySubscribe(bot: Bot) { ...@@ -227,7 +228,7 @@ suspend fun directlySubscribe(bot: Bot) {
.hexToBytes() .hexToBytes()
it.bot.network.socket.sendPacket( it.bot.network.socket.sendPacket(
OutgoingRawPacket( OutgoingRawPacket(
0x01_BDu, PacketId(0x01_BDu),
it.bot.qqAccount, it.bot.qqAccount,
"00 00 00 01 2E 01 00 00 69 35".hexToBytes(), "00 00 00 01 2E 01 00 00 69 35".hexToBytes(),
it.bot.network.session.sessionKey, it.bot.network.session.sessionKey,
...@@ -240,7 +241,7 @@ suspend fun directlySubscribe(bot: Bot) { ...@@ -240,7 +241,7 @@ suspend fun directlySubscribe(bot: Bot) {
val filename = it.message.toString().substringAfter("上传群图片") val filename = it.message.toString().substringAfter("上传群图片")
val image = File( val image = File(
"C:\\Users\\Him18\\Desktop\\$filename" "C:\\Users\\Him18\\Desktop\\$filename"
).toExternalImage() ).suspendToExternalImage()
920503456u.group().uploadImage(image) 920503456u.group().uploadImage(image)
it.reply(image.groupImageId.value) it.reply(image.groupImageId.value)
delay(100) delay(100)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment