Commit 8178a8c8 authored by Him188's avatar Him188

Improve performance

parent efb32133
......@@ -4,6 +4,7 @@ package net.mamoe.mirai.event
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.newCoroutineContext
import kotlinx.coroutines.withContext
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.event.internal.broadcastInternal
......@@ -75,7 +76,7 @@ suspend fun <E : Event> E.broadcast(context: CoroutineContext = EmptyCoroutineCo
EventLogger.debug(this::class.simpleName + " pre broadcast")
}
try {
return withContext(EventScope.coroutineContext + context) { this@broadcast.broadcastInternal() }
return withContext(EventScope.newCoroutineContext(context)) { this@broadcast.broadcastInternal() }
} finally {
if (EventDebuggingFlag) {
EventLogger.debug(this::class.simpleName + " after broadcast")
......
......@@ -14,17 +14,17 @@ sealed class PacketEvent<out P : Packet>(bot: Bot, open val packet: P) : BotEven
/* 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 */
......
package net.mamoe.mirai.event.internal
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
......@@ -70,7 +71,8 @@ internal class Handler<E : Event>(@JvmField val handler: suspend (E) -> Listenin
* 所有的 [BotEvent.bot] `!==` `bot` 的事件都不会被处理
*/
@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) {
if (event !is BotEvent || event.bot !== this) {
return ListeningStatus.LISTENING
......@@ -144,19 +146,28 @@ internal suspend fun <E : Event> E.broadcastInternal(): E {
//自己持有, 则是在一个事件中
if (listeners.mainMutex.holdsLock(listeners)) {
callAndRemoveIfRequired()
} else listeners.mainMutex.withLock(listeners) {
callAndRemoveIfRequired()
} else {
while (!listeners.mainMutex.tryLock(this)) {
delay(10)
}
try {
callAndRemoveIfRequired()
} finally {
listeners.mainMutex.unlock(this)
}
}
}
callListeners(this::class.listeners() as EventListeners<in E>)
//FIXME 这可能不支持所有的平台. 可能需要修改.
loopAllListeners(this::class) { callListeners(it as EventListeners<in E>) }
applyAllListeners(this::class) { callListeners(it as EventListeners<in E>) }
return this
}
internal expect suspend inline fun <E : Event> loopAllListeners(
private suspend inline fun <E : Event> applyAllListeners(
clazz: KClass<E>,
consumer: (EventListeners<in E>) -> Unit
)
\ No newline at end of file
block: (EventListeners<in E>) -> Unit
) = clazz.supertypes.map { it.classifier }.filterIsInstance<KClass<out Event>>().forEach {
@Suppress("UNCHECKED_CAST")
block(it.listeners() as EventListeners<in E>)
}
......@@ -103,7 +103,25 @@ inline val BotSession.isOpen: Boolean get() = socket.isOpen
inline val BotSession.qqAccount: UInt get() = bot.account.id
/**
* 取得 [T] 的 [BotSession].
* 取得 [BotNetworkHandler] 的 [BotSession].
* 实际上是一个捷径.
*/
val <T : BotNetworkHandler<*>> T.session get() = this[ActionPacketHandler].session
\ No newline at end of file
val BotNetworkHandler<*>.session get() = this[ActionPacketHandler].session
/**
* 取得 [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
*
* @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 =
Dispatchers.Default + CoroutineExceptionHandler { _, e ->
bot.logger.error(e)
bot.logger.error("An exception was thrown in a coroutine under TIMBotNetworkHandler", e)
} + SupervisorJob()
......@@ -58,22 +59,23 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
temporaryPacketHandler.send(this[ActionPacketHandler].session)
}
override suspend fun login(configuration: BotNetworkConfiguration): LoginResult = withContext(this.coroutineContext) {
TIMProtocol.SERVER_IP.forEach { ip ->
bot.logger.info("Connecting server $ip")
socket = BotSocketAdapter(ip, configuration)
override suspend fun login(configuration: BotNetworkConfiguration): LoginResult =
withContext(this.coroutineContext) {
TIMProtocol.SERVER_IP.forEach { ip ->
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()
bot.logger.warning("Timeout. Retrying next server")
println()
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()
......@@ -85,7 +87,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
add(EventPacketHandler(session).asNode(EventPacketHandler))
add(ActionPacketHandler(session).asNode(ActionPacketHandler))
bot.logger.warning("Successfully logged in")
bot.logger.info("Successfully logged in")
}
private lateinit var sessionKey: ByteArray
......@@ -210,7 +212,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
// Remove first to release the lock
handlersLock.withLock {
temporaryPacketHandlers.filter { it.filter(session, packet) }.also { temporaryPacketHandlers.removeAll(it) }
temporaryPacketHandlers.filter { it.filter(session, packet) }
.also { temporaryPacketHandlers.removeAll(it) }
}.forEach {
it.doReceiveWithoutExceptions(packet)
}
......@@ -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) {
check(channel.isOpen) { "channel is not open" }
......@@ -275,10 +242,10 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
return@withContext
}
packet.buildAndUse { build ->
packet.delegate.use { built ->
val buffer = IoBuffer.Pool.borrow()
try {
build.readAvailable(buffer)
built.readAvailable(buffer)
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)
} catch (e: SendPacketInternalException) {
......@@ -476,7 +443,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
is SessionKeyResponsePacket -> {
sessionKey = packet.sessionKey
bot.logger.warning("sessionKey = ${sessionKey.toUHexString()}")
bot.logger.info("sessionKey = ${sessionKey.toUHexString()}")
heartbeatJob = launch {
while (socket.isOpen) {
......
......@@ -14,7 +14,7 @@ import kotlin.properties.Delegates
*
* TODO 真的是在线状态改变么
*/
@PacketId(0x00_81u)
@AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
class ServerFriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket(input) {
var qq: UInt by Delegates.notNull()
lateinit var status: OnlineStatus
......@@ -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 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) {
fun decrypt(sessionKey: ByteArray): ServerFriendOnlineStatusChangedPacket = ServerFriendOnlineStatusChangedPacket(this.decryptBy(sessionKey)).applySequence(sequenceId)
}
......
......@@ -2,7 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.writeUByte
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
......@@ -15,22 +14,22 @@ import net.mamoe.mirai.utils.io.writeQQ
*
* @author Him188moe
*/
@PacketId(0x00_5Cu)
class RequestAccountInfoPacket(
private val qq: UInt,
private val sessionKey: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(qq)
this.writeHex(TIMProtocol.fixVer2)
this.encryptAndWrite(sessionKey) {
@AnnotatedId(KnownPacketId.ACCOUNT_INFO)
object RequestAccountInfoPacket : OutgoingPacketBuilder {
operator fun invoke(
qq: UInt,
sessionKey: ByteArray
) = buildOutgoingPacket {
writeQQ(qq)
writeHex(TIMProtocol.fixVer2)
encryptAndWrite(sessionKey) {
writeUByte(0x88u)
writeQQ(qq)
writeByte(0x00)
}
}
@PacketId(0x00_5Cu)
@AnnotatedId(KnownPacketId.ACCOUNT_INFO)
class Response(input: ByteReadPacket) : ResponsePacket(input) {
//等级
//升级剩余活跃天数
......
......@@ -2,19 +2,18 @@
package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeHex
import net.mamoe.mirai.utils.io.writeQQ
@PacketId(0x00_58u)
class HeartbeatPacket(
private val bot: UInt,
private val sessionKey: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
@AnnotatedId(KnownPacketId.HEARTBEAT)
object HeartbeatPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
sessionKey: ByteArray
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer)
encryptAndWrite(sessionKey) {
......@@ -22,6 +21,6 @@ class HeartbeatPacket(
}
}
@PacketId(0x00_58u)
@AnnotatedId(KnownPacketId.HEARTBEAT)
class Response(input: ByteReadPacket) : ResponsePacket(input)
}
\ No newline at end of file
......@@ -3,61 +3,77 @@
package net.mamoe.mirai.network.protocol.tim.packet
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.utils.io.writeHex
import kotlin.jvm.JvmStatic
import kotlin.jvm.JvmOverloads
/**
* 发给服务器的数据包. 必须有 [PacketId] 注解或 `override` [id]. 否则将会抛出 [IllegalStateException]
* 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket],
*/
abstract class OutgoingPacket : Packet(), Closeable {
/**
* Encode this packet.
*
* Before sending the packet, a [tail][TIMProtocol.tail] is added.
*/
protected abstract fun encode(builder: BytePacketBuilder)
override val sequenceId: UShort by lazy {
atomicNextSequenceId()
class OutgoingPacket(
name: String?,
override val packetId: PacketId,
override val sequenceId: UShort,
val delegate: ByteReadPacket
) : Packet {
private val name: String by lazy {
name ?: packetId.toString()
}
companion object {
@JvmStatic
private val sequenceIdInternal = atomic(1)
internal fun atomicNextSequenceId() = sequenceIdInternal.getAndIncrement().toUShort()
}
constructor(id: PacketId, sequenceId: UShort, delegate: ByteReadPacket) : this(null, id, sequenceId, delegate)
inline fun buildAndUse(block: (ByteReadPacket) -> Unit) {
buildPacket().use(block)
}
constructor(annotation: AnnotatedId, sequenceId: UShort, delegate: ByteReadPacket) :
this(annotation.toString(), annotation.id, sequenceId, delegate)
@PublishedApi
internal fun buildPacket(): ByteReadPacket = buildPacket {
writeHex(TIMProtocol.head)
writeHex(TIMProtocol.ver)
writePacketId()
encode(this)
writeHex(TIMProtocol.tail)
}
override fun toString(): String = packetToString(name)
}
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() {
writeUShort(this@OutgoingPacket.id)
writeUShort(sequenceId)
@PublishedApi
internal fun atomicNextSequenceId() = sequenceIdInternal.getAndIncrement().toUShort()
}
}
@Suppress("unused")
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class PacketVersion(val date: String, val timVersion: String)
private val Uninitialized = ByteReadPacket(IoBuffer.Empty, IoBuffer.EmptyPool)
/**
* 构造一个待发送给服务器的数据包.
* 若不提供参数 [id], 则会通过注解 [AnnotatedId] 获取 id.
*/
@JvmOverloads
fun OutgoingPacketBuilder.buildOutgoingPacket(
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 @@
package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.writeFully
import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeQQ
class OutgoingRawPacket(
override val id: UShort,
private val bot: UInt,
private val version: ByteArray,
private val sessionKey: ByteArray,
private val data: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
object OutgoingRawPacket : OutgoingPacketBuilder {
operator fun invoke(
id: PacketId,
bot: UInt,
version: ByteArray,
sessionKey: ByteArray,
data: ByteArray
): OutgoingPacket = buildOutgoingPacket(id = id) {
writeQQ(bot)
writeFully(version)
......
......@@ -2,23 +2,193 @@
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 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
* 它们都使用 sessionKey 解密.
* 它们都必须有一个公开的仅有一个 [ByteReadPacket] 参数的构造器.
*
* 注意: 需要指定 ID, 通过 [PacketId].
* 注意: 需要指定 ID, 通过 [AnnotatedId].
*/
abstract class ResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
......
......@@ -20,8 +20,11 @@ import kotlin.properties.Delegates
*
* @see parseServerPacket 解析包种类
*/
abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable {
override val id: UShort by lazy { super.id }
abstract class ServerPacket(val input: ByteReadPacket) : Packet, Closeable {
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()
......@@ -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: 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(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 =
ByteArrayPool.useInstance {
......@@ -63,7 +69,9 @@ inline fun <R> ServerPacket.decryptAsByteArray(key: ByteArray, consumer: (ByteAr
consumer(it.decryptBy(key, length))
}.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 =
ByteArrayPool.useInstance {
val length = input.remaining.toInt() - 1
......
......@@ -4,15 +4,13 @@ package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes
import net.mamoe.mirai.utils.LoggerTextFormat
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.io.toUHexString
class UnknownServerPacket(
input: ByteReadPacket,
override var id: UShort,
override var sequenceId: UShort
input: ByteReadPacket,
override val packetId: PacketId,
override var sequenceId: UShort
) : ServerPacket(input) {
override fun decode() {
val raw = this.input.readBytes()
......@@ -20,16 +18,12 @@ class UnknownServerPacket(
}
class Encrypted(
input: ByteReadPacket,
override var id: UShort,
override var sequenceId: UShort
input: ByteReadPacket,
override val packetId: PacketId,
override var sequenceId: UShort
) : ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): UnknownServerPacket = UnknownServerPacket(this.decryptBy(sessionKey), this.id, this.sequenceId)
}
override fun toString(): String {
@Suppress("RemoveRedundantQualifierName")
return LoggerTextFormat.LIGHT_RED.toString() + super.toString()
fun decrypt(sessionKey: ByteArray): UnknownServerPacket =
UnknownServerPacket(this.decryptBy(sessionKey), this.packetId, this.sequenceId)
}
}
......
......@@ -2,7 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUShort
......@@ -18,13 +17,13 @@ import net.mamoe.mirai.utils.io.writeQQ
* @author Him188moe
*/
@Response(CanAddFriendPacket.Response::class)
@PacketId(0x00_A7u)
class CanAddFriendPacket(
val bot: UInt,
val qq: UInt,
val sessionKey: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
@AnnotatedId(KnownPacketId.CAN_ADD_FRIEND)
object CanAddFriendPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
qq: UInt,
sessionKey: ByteArray
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer2)
encryptAndWrite(sessionKey) {
......@@ -32,7 +31,7 @@ class CanAddFriendPacket(
}
}
@PacketId(0x00_A7u)
@AnnotatedId(KnownPacketId.CAN_ADD_FRIEND)
class Response(input: ByteReadPacket) : ResponsePacket(input) {
lateinit var state: State
......@@ -81,16 +80,16 @@ class CanAddFriendPacket(
/**
* 请求添加好友
*/
@PacketId(0x00_AEu)
class AddFriendPacket(
val bot: UInt,
val qq: UInt,
val sessionKey: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex(TIMProtocol.fixVer2)
this.encryptAndWrite(sessionKey) {
@AnnotatedId(KnownPacketId.CAN_ADD_FRIEND)
object AddFriendPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
qq: UInt,
sessionKey: ByteArray
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer2)
encryptAndWrite(sessionKey) {
writeHex("01 00 01")
writeQQ(qq)
}
......
......@@ -2,17 +2,15 @@
package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.BytePacketBuilder
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.*
// 用户资料的头像
/**
* 请求获取头像
*/
@PacketId(0x00_31u)
class RequestProfilePicturePacket : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) {
TODO("not implemented")
@AnnotatedId(KnownPacketId.REQUEST_PROFILE)
object RequestProfilePicturePacket : OutgoingPacketBuilder {
operator fun invoke(): OutgoingPacket = buildOutgoingPacket {
}
}
\ No newline at end of file
......@@ -6,76 +6,23 @@ import kotlinx.io.core.*
import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.message.internal.toPacket
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.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.*
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.md5
@PacketId(0x00_CDu)
@AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173")
class SendFriendMessagePacket(
private val botQQ: UInt,
private val targetQQ: UInt,
private val sessionKey: ByteArray,
private val message: MessageChain
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
object SendFriendMessagePacket : OutgoingPacketBuilder {
operator fun invoke(
botQQ: UInt,
targetQQ: UInt,
sessionKey: ByteArray,
message: MessageChain
): OutgoingPacket = buildOutgoingPacket {
writeQQ(botQQ)
writeHex(TIMProtocol.version0x02)
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(targetQQ)
writeHex("00 00 00 08 00 01 00 04 00 00 00 00")
......@@ -86,8 +33,10 @@ class SendFriendMessagePacket(
writeHex("00 0B")
writeRandom(2)
writeTime()
writeHex("01 1D" +
" 00 00 00 00")
writeHex(
"01 1D" +
" 00 00 00 00"
)
//消息过多要分包发送
//如果只有一个
......@@ -119,6 +68,6 @@ class SendFriendMessagePacket(
}
}
@PacketId(0x00_CDu)
@AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
class Response(input: ByteReadPacket) : ResponsePacket(input)
}
\ No newline at end of file
......@@ -2,26 +2,23 @@
package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.contact.GroupInternalId
import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.message.internal.toPacket
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.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.io.*
@PacketId(0x00_02u)
class SendGroupMessagePacket(
private val botQQ: UInt,
private val groupInternalId: GroupInternalId,
private val sessionKey: ByteArray,
private val message: MessageChain
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
@AnnotatedId(KnownPacketId.SEND_GROUP_MESSAGE)
object SendGroupMessagePacket : OutgoingPacketBuilder {
operator fun invoke(
botQQ: UInt,
groupInternalId: GroupInternalId,
sessionKey: ByteArray,
message: MessageChain
): OutgoingPacket = buildOutgoingPacket {
writeQQ(botQQ)
writeHex(TIMProtocol.fixVer2)
......@@ -49,6 +46,6 @@ class SendGroupMessagePacket(
}
}
@PacketId(0x00_02u)
@AnnotatedId(KnownPacketId.SEND_GROUP_MESSAGE)
class Response(input: ByteReadPacket) : ResponsePacket(input)
}
\ No newline at end of file
......@@ -12,10 +12,7 @@ import kotlinx.io.core.*
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.ImageId
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.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.*
import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket.Response.State.*
import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.qqAccount
......@@ -132,15 +129,15 @@ internal suspend inline fun HttpClient.postImage(
* 似乎没有必要. 服务器的返回永远都是 01 00 00 00 02 00 00
*/
@Deprecated("Useless packet")
@PacketId(0X01_BDu)
@AnnotatedId(KnownPacketId.SUBMIT_IMAGE_FILE_NAME)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class SubmitImageFilenamePacket(
private val bot: UInt,
private val target: UInt,
private val filename: String,
private val sessionKey: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
object SubmitImageFilenamePacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
target: UInt,
filename: String,
sessionKey: ByteArray
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer2)//?
//writeHex("04 00 00 00 01 2E 01 00 00 69 35")
......@@ -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
}
@PacketId(0x01_BDu)
@AnnotatedId(KnownPacketId.SUBMIT_IMAGE_FILE_NAME)
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173")
class Response(input: ByteReadPacket) : ResponsePacket(input) {
override fun decode() = with(input) {
......@@ -188,16 +185,15 @@ class SubmitImageFilenamePacket(
* - 服务器已经存有这个图片
* - 服务器未存有, 返回一个 key 用于客户端上传
*/
@PacketId(0x03_52u)
@AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class FriendImageIdRequestPacket(
private val bot: UInt,
private val sessionKey: ByteArray,
private val target: UInt,
private val image: ExternalImage
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
object FriendImageIdRequestPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
sessionKey: ByteArray,
target: UInt,
image: ExternalImage
) = buildOutgoingPacket {
writeQQ(bot)
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(
}
}
@PacketId(0x0352u)
@AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class Response(input: ByteReadPacket) : ResponsePacket(input) {
/**
......@@ -317,16 +313,15 @@ class FriendImageIdRequestPacket(
/**
* 获取 Image Id 和上传用的一个 uKey
*/
@PacketId(0x0388u)
@AnnotatedId(KnownPacketId.GROUP_IMAGE_ID)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class GroupImageIdRequestPacket(
private val bot: UInt,
private val groupInternalId: GroupInternalId,
private val image: ExternalImage,
private val sessionKey: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
object GroupImageIdRequestPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
groupInternalId: GroupInternalId,
image: ExternalImage,
sessionKey: ByteArray
) = buildOutgoingPacket {
writeQQ(bot)
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(
}
}
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")
class Response(input: ByteReadPacket) : ResponsePacket(input) {
lateinit var state: State
......
......@@ -4,20 +4,17 @@ package net.mamoe.mirai.network.protocol.tim.packet.event
import kotlinx.io.core.*
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.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.applySequence
import net.mamoe.mirai.network.protocol.tim.packet.decryptBy
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.io.*
/**
* 事件的识别 ID. 在 [事件确认包][ServerEventPacket.ResponsePacket] 中被使用.
* 事件的识别 ID. 在 [事件确认包][ServerEventPacket.EventResponse] 中被使用.
*/
data class EventPacketIdentity(
val from: UInt,//对于好友消息, 这个是发送人
val to: UInt,//对于好友消息, 这个是bot
internal val uniqueId: IoBuffer//8
val from: UInt,//对于好友消息, 这个是发送人
val to: UInt,//对于好友消息, 这个是bot
internal val uniqueId: IoBuffer//8
) {
override fun toString(): String = "($from->$to)"
}
......@@ -28,8 +25,8 @@ fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) =
writeFully(uniqueId)
}
fun <S : ServerEventPacket> S.applyId(id: UShort): S {
this.id = id
fun <S : ServerEventPacket> S.applyId(id: PacketId): S {
this.packetId = id
return this
}
......@@ -39,14 +36,14 @@ fun <S : ServerEventPacket> S.applyId(id: UShort): S {
* @author Him188moe
*/
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) {
val eventIdentity = EventPacketIdentity(
from = readUInt(),
to = readUInt(),
uniqueId = readIoBuffer(8)
from = readUInt(),
to = readUInt(),
uniqueId = readIoBuffer(8)
)
discardExact(2)
val type = readUShort()
......@@ -81,10 +78,10 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity)*/
}*/
0x0210u -> IgnoredServerEventPacket(
eventId = type.toByteArray(),
showData = true,
input = input,
eventIdentity = eventIdentity
eventId = type.toByteArray(),
showData = true,
input = input,
eventIdentity = eventIdentity
)
//"02 10", "00 12" -> ServerUnknownEventPacket(input, eventIdentity)
......@@ -93,29 +90,37 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
MiraiLogger.debug("UnknownEvent type = ${type.toByteArray().toUHexString()}")
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) {
fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey), id).applySequence(sequenceId)
class Encrypted(input: ByteReadPacket, override var packetId: PacketId, override var sequenceId: UShort) :
ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): Raw =
Raw(this.decryptBy(sessionKey), packetId).applySequence(sequenceId)
}
}
inner class ResponsePacket(
val bot: UInt,
val sessionKey: ByteArray
) : OutgoingPacket() {
override val id: UShort get() = this@ServerEventPacket.id
override val sequenceId: UShort get() = this@ServerEventPacket.sequenceId
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex(TIMProtocol.fixVer2)
this.encryptAndWrite(sessionKey) {
writeEventPacketIdentity(eventIdentity)
@Suppress("FunctionName")
fun ResponsePacket(
bot: UInt,
sessionKey: ByteArray
): OutgoingPacket = EventResponse(this.packetId, this.sequenceId, bot, sessionKey, this.eventIdentity)
@Suppress("FunctionName")
object EventResponse : OutgoingPacketBuilder {
operator fun invoke(
id: PacketId,
sequenceId: UShort,
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
* 忽略的事件
*/
@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() {
if (showData) {
MiraiLogger.debug("IgnoredServerEventPacket data: " + this.input.readBytes().toUHexString())
......@@ -136,7 +146,8 @@ class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boo
/**
* Unknown event
*/
class UnknownServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) {
class UnknownServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) :
ServerEventPacket(input, eventIdentity) {
override fun decode() {
MiraiLogger.debug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString())
}
......
......@@ -5,25 +5,23 @@ package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.*
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.Tested
import net.mamoe.mirai.utils.io.*
/**
* 客户端请求验证码图片数据的第几部分
*/
@PacketId(0x00_BAu)
class RequestCaptchaTransmissionPacket(
private val bot: UInt,
private val token0825: ByteArray,
private val captchaSequence: Int,
private val token00BA: ByteArray
) : OutgoingPacket() {
@Tested
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex(TIMProtocol.fixVer)
this.writeHex(TIMProtocol.key00BA)
this.encryptAndWrite(TIMProtocol.key00BA) {
@AnnotatedId(KnownPacketId.CAPTCHA)
object RequestCaptchaTransmissionPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
token0825: ByteArray,
captchaSequence: Int,
token00BA: ByteArray
) = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer)
writeHex(TIMProtocol.key00BA)
encryptAndWrite(TIMProtocol.key00BA) {
writeHex("00 02 00 00 08 04 01 E0")
writeHex(TIMProtocol.constantData2)
writeHex("00 00 38")
......@@ -43,22 +41,19 @@ class RequestCaptchaTransmissionPacket(
/**
* 提交验证码
*/
@PacketId(0x00_BAu)
class SubmitCaptchaPacket(
private val bot: UInt,
private val token0825: ByteArray,
private val captcha: String,
private val captchaToken: IoBuffer
) : OutgoingPacket() {
init {
@AnnotatedId(KnownPacketId.CAPTCHA)
object SubmitCaptchaPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
token0825: ByteArray,
captcha: String,
captchaToken: IoBuffer
) = buildOutgoingPacket {
require(captcha.length == 4) { "captcha.length must == 4" }
}
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex(TIMProtocol.fixVer)
this.writeHex(TIMProtocol.key00BA)
this.encryptAndWrite(TIMProtocol.key00BA) {
writeQQ(bot)
writeHex(TIMProtocol.fixVer)
writeHex(TIMProtocol.key00BA)
encryptAndWrite(TIMProtocol.key00BA) {
writeHex("00 02 00 00 08 04 01 E0")
writeHex(TIMProtocol.constantData2)
writeHex("01 00 38")
......@@ -82,16 +77,16 @@ class SubmitCaptchaPacket(
/**
* 刷新验证码
*/
@PacketId(0x00_BAu)
class OutgoingCaptchaRefreshPacket(
private val qq: UInt,
private val token0825: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(qq)
this.writeHex(TIMProtocol.fixVer)
this.writeHex(TIMProtocol.key00BA)
this.encryptAndWrite(TIMProtocol.key00BA) {
@AnnotatedId(KnownPacketId.CAPTCHA)
object OutgoingCaptchaRefreshPacket : OutgoingPacketBuilder {
operator fun invoke(
qq: UInt,
token0825: ByteArray
) = buildOutgoingPacket {
writeQQ(qq)
writeHex(TIMProtocol.fixVer)
writeHex(TIMProtocol.key00BA)
encryptAndWrite(TIMProtocol.key00BA) {
writeHex("00 02 00 00 08 04 01 E0")
writeHex(TIMProtocol.constantData2)
writeHex("00 00 38")
......@@ -109,15 +104,13 @@ class OutgoingCaptchaRefreshPacket(
*
* @author Him188moe
*/
@PacketId(0x00_BAu)
@AnnotatedId(KnownPacketId.CAPTCHA)
open class CaptchaTransmissionResponsePacket(input: ByteReadPacket) : ServerCaptchaPacket(input) {
lateinit var captchaSectionN: IoBuffer
lateinit var captchaToken: IoBuffer//56bytes
var transmissionCompleted: Boolean = false//验证码是否已经传输完成
lateinit var token00BA: ByteArray//40 bytes
override fun decode() = with(input) {
input.discardExact(10)//13 00 05 01 00 00 01 23 00 38
captchaToken = readIoBuffer(56)
......@@ -148,7 +141,7 @@ fun main() {
*
* @author Him188moe
*/
@PacketId(0x00_BAu)
@AnnotatedId(KnownPacketId.CAPTCHA)
class CaptchaCorrectPacket(input: ByteReadPacket) : ServerCaptchaPacket(input) {
lateinit var token00BA: ByteArray//56 bytes
......@@ -158,10 +151,10 @@ class CaptchaCorrectPacket(input: ByteReadPacket) : ServerCaptchaPacket(input) {
}
}
@PacketId(0x00_BAu)
@AnnotatedId(KnownPacketId.CAPTCHA)
abstract class ServerCaptchaPacket(input: ByteReadPacket) : ServerPacket(input) {
@PacketId(0x00_BAu)
@AnnotatedId(KnownPacketId.CAPTCHA)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(): ServerCaptchaPacket {
return this.decryptAsByteArray(TIMProtocol.key00BA) { data ->
......
......@@ -2,11 +2,9 @@
package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.writeUByte
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.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.OnlineStatus
import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeHex
......@@ -15,21 +13,19 @@ import net.mamoe.mirai.utils.io.writeQQ
/**
* 改变在线状态: "我在线上", "隐身" 等
*/
@PacketId(0x00_ECu)
class ChangeOnlineStatusPacket(
private val bot: UInt,
private val sessionKey: ByteArray,
private val loginStatus: OnlineStatus
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex(TIMProtocol.fixVer2)
this.encryptAndWrite(sessionKey) {
@AnnotatedId(KnownPacketId.CHANGE_ONLINE_STATUS)
object ChangeOnlineStatusPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
sessionKey: ByteArray,
loginStatus: OnlineStatus
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer2)
encryptAndWrite(sessionKey) {
writeHex("01 00")
writeUByte(loginStatus.id)
writeHex("00 01 00 01 00 04 00 00 00 00")
}
}
}
}
\ No newline at end of file
......@@ -43,7 +43,7 @@ enum class LoginResult {
UNKNOWN,
/**
* 未知. 更换服务器或等几分钟再登录可能解决.
* 包数据错误
*/
INTERNAL_ERROR,
......@@ -57,7 +57,7 @@ enum class LoginResult {
* 如果 [this] 不为 [LoginResult.SUCCESS] 就抛出消息为 [lazyMessage] 的 [IllegalStateException]
*/
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() {
* @return 成功时 [Unit], 失败时 `null`
*/
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? =
* @return 成功时 [Unit], 失败时 `null`
*/
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
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.writeFully
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.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.encryptBy
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.writeCRC32
/**
* 提交密码
*/
@PacketId(0x08_36u)
class SubmitPasswordPacket constructor(
private val bot: UInt,
private val password: String,
private val loginTime: Int,
private val loginIP: String,
private val privateKey: ByteArray,
private val token0825: ByteArray,
private val token00BA: ByteArray? = null,
private val randomDeviceName: Boolean = false,
private val tlv0006: IoBuffer? = null
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
@AnnotatedId(KnownPacketId.LOGIN)
object SubmitPasswordPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
password: String,
loginTime: Int,
loginIP: String,
privateKey: ByteArray,
token0825: ByteArray,
token00BA: ByteArray? = null,
randomDeviceName: Boolean = false,
tlv0006: IoBuffer? = null
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.passwordSubmissionTLV1)
......@@ -57,18 +55,16 @@ class SubmitPasswordPacket constructor(
}
}
private fun BytePacketBuilder.writePart1(
qq: UInt,
password: String,
loginTime: Int,
loginIP: String,
privateKey: ByteArray,
token0825: ByteArray,
randomDeviceName: Boolean,
tlv0006: IoBuffer? = null
qq: UInt,
password: String,
loginTime: Int,
loginIP: String,
privateKey: ByteArray,
token0825: ByteArray,
randomDeviceName: Boolean,
tlv0006: IoBuffer? = null
) {
//this.writeInt(System.currentTimeMillis().toInt())
this.writeHex("01 12")//tag
this.writeHex("00 38")//length
......@@ -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
}
private fun BytePacketBuilder.writePart2() {
this.writeHex("03 12")//tag
......
......@@ -2,29 +2,26 @@
package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import net.mamoe.mirai.network.BotSession
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.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.utils.io.*
fun BotSession.RequestSKeyPacket() = RequestSKeyPacket(qqAccount, sessionKey)
fun BotSession.RequestSKeyPacket(): OutgoingPacket = RequestSKeyPacket(qqAccount, sessionKey)
/**
* 请求 `SKey`
* SKey 用于 http api
*/
@PacketId(0x00_1Du)
class RequestSKeyPacket(
private val bot: UInt,
private val sessionKey: ByteArray
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
@AnnotatedId(KnownPacketId.S_KEY)
object RequestSKeyPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
sessionKey: ByteArray
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer2)
encryptAndWrite(sessionKey) {
......@@ -32,7 +29,7 @@ class RequestSKeyPacket(
}
}
@PacketId(0x00_1Du)
@AnnotatedId(KnownPacketId.S_KEY)
class Response(input: ByteReadPacket) : ResponsePacket(input) {
lateinit var sKey: String
......
......@@ -4,26 +4,23 @@ package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.*
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.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.applySequence
import net.mamoe.mirai.network.protocol.tim.packet.decryptBy
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.Tested
import net.mamoe.mirai.utils.io.readBoolean
import net.mamoe.mirai.utils.io.readIoBuffer
import net.mamoe.mirai.utils.io.readString
import kotlin.properties.Delegates
@PacketId(0x08_36u)
@AnnotatedId(KnownPacketId.LOGIN)
sealed class ServerLoginResponsePacket(input: ByteReadPacket) : ServerPacket(input)
@PacketId(0x08_36u)
@AnnotatedId(KnownPacketId.LOGIN)
class LoginResponseFailedPacket(val loginResult: LoginResult, input: ByteReadPacket) : ServerLoginResponsePacket(input)
/**
* 服务器进行加密后返回 privateKey
*/
@PacketId(0x08_36u)
@AnnotatedId(KnownPacketId.LOGIN)
class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
lateinit var tlv0006: IoBuffer//120bytes
var tokenUnknown: ByteArray? = null
......@@ -37,7 +34,6 @@ class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLogi
this.input.discardExact(4)//00 06 00 78
tlv0006 = this.input.readIoBuffer(0x78)
//todo 这边原本会判断是否 `08 36 31 03`, 是才会进行下列2行读取.
try {
this.input.discardExact(8)//01 10 00 3C 00 01 00 38
tokenUnknown = this.input.readBytes(56)
......@@ -46,7 +42,7 @@ class LoginResponseKeyExchangeResponsePacket(input: ByteReadPacket) : ServerLogi
}
}
@PacketId(0x08_36u)
@AnnotatedId(KnownPacketId.LOGIN)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
@Tested
fun decrypt(privateKey: ByteArray): LoginResponseKeyExchangeResponsePacket =
......@@ -59,10 +55,7 @@ enum class Gender(val id: Boolean) {
FEMALE(true);
}
/**
* @author NaturalHG
*/
@PacketId(0x08_36u)
@AnnotatedId(KnownPacketId.LOGIN)
class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
lateinit var sessionResponseDecryptionKey: IoBuffer//16 bytes|
......@@ -122,7 +115,7 @@ class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePac
gender = if (readBoolean()) Gender.FEMALE else Gender.MALE
}
@PacketId(0x08_36u)
@AnnotatedId(KnownPacketId.LOGIN)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(privateKey: ByteArray): LoginResponseSuccessPacket = LoginResponseSuccessPacket(this.decryptBy(TIMProtocol.shareKey, privateKey)).applySequence(sequenceId)
}
......@@ -134,7 +127,7 @@ class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePac
*
* @author Him188moe
*/
@PacketId(0x08_36u)
@AnnotatedId(KnownPacketId.LOGIN)
class LoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
lateinit var captchaPart1: IoBuffer
......@@ -158,7 +151,7 @@ class LoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginRespons
}
@PacketId(0x08_36u)
@AnnotatedId(KnownPacketId.LOGIN)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(): LoginResponseCaptchaInitPacket = LoginResponseCaptchaInitPacket(decryptBy(TIMProtocol.shareKey)).applySequence(sequenceId)
}
......
......@@ -3,7 +3,8 @@
package net.mamoe.mirai.network.protocol.tim.packet.login
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
/**
......@@ -11,5 +12,5 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
*
* @author Him188moe
*/
@PacketId(0x00_ECu)
@AnnotatedId(KnownPacketId.CHANGE_ONLINE_STATUS)
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
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.localIpAddress
@PacketId(0x08_28u)
class RequestSessionPacket(
private val bot: UInt,
private val serverIp: String,
private val token38: IoBuffer,
private val token88: IoBuffer,
private val encryptionKey: IoBuffer
) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A")
this.writeHex("00 38")
this.writeFully(token38)
this.encryptAndWrite(encryptionKey) {
@AnnotatedId(KnownPacketId.SESSION_KEY)
object RequestSessionPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
serverIp: String,
token38: IoBuffer,
token88: IoBuffer,
encryptionKey: IoBuffer
) = buildOutgoingPacket {
writeQQ(bot)
writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A")
writeHex("00 38")
writeFully(token38)
encryptAndWrite(encryptionKey) {
writeHex("00 07 00 88")
writeFully(token88)
writeHex("00 0C 00 16 00 02 00 00 00 00 00 00 00 00 00 00")
......@@ -55,13 +55,13 @@ class RequestSessionPacket(
writeHex("68")
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) {
lateinit var sessionKey: ByteArray
lateinit var tlv0105: ByteReadPacket
......@@ -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) {
fun decrypt(sessionResponseDecryptionKey: IoBuffer): SessionKeyResponsePacket =
SessionKeyResponsePacket(decryptBy(sessionResponseDecryptionKey)).applySequence(sequenceId)
......
......@@ -2,7 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes
......@@ -19,7 +18,7 @@ import net.mamoe.mirai.utils.io.*
* @author Him188moe
*/
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
@PacketId(0x08_25u)
@AnnotatedId(KnownPacketId.TOUCH)
class TouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
var serverIP: String? = null
......@@ -48,9 +47,10 @@ class TouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
}
}
@PacketId(0x08_25u)
@AnnotatedId(KnownPacketId.TOUCH)
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) {
*
* @author Him188moe
*/
@PacketId(0x08_25u)
class TouchPacket(private val bot: UInt, private val serverIp: String) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex(TIMProtocol.fixVer)
this.writeHex(TIMProtocol.touchKey)
@AnnotatedId(KnownPacketId.TOUCH)
object TouchPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
serverIp: String
) = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer)
writeHex(TIMProtocol.touchKey)
this.encryptAndWrite(TIMProtocol.touchKey) {
encryptAndWrite(TIMProtocol.touchKey) {
writeHex(TIMProtocol.constantData1)
writeHex(TIMProtocol.constantData2)
writeQQ(bot)
......@@ -83,21 +86,24 @@ class TouchPacket(private val bot: UInt, private val serverIp: String) : Outgoin
*
* @author Him188moe
*/
@PacketId(0x08_25u)
class RedirectionPacket(private val bot: UInt, private val serverIP: String) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex(TIMProtocol.fixVer)
this.writeHex(TIMProtocol.touchKey)//redirection key
@AnnotatedId(KnownPacketId.TOUCH)
object RedirectionPacket : OutgoingPacketBuilder {
operator fun invoke(
bot: UInt,
serverIP: String
) = buildOutgoingPacket {
writeQQ(bot)
writeHex(TIMProtocol.fixVer)
writeHex(TIMProtocol.touchKey)//redirection key
this.encryptAndWrite(TIMProtocol.touchKey) {
this.writeHex(TIMProtocol.constantData1)
this.writeHex(TIMProtocol.constantData2)
this.writeQQ(bot)
this.writeHex("00 01 00 00 03 09 00 0C 00 01")
this.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")
this.writeHex(TIMProtocol.publicKey)
encryptAndWrite(TIMProtocol.touchKey) {
writeHex(TIMProtocol.constantData1)
writeHex(TIMProtocol.constantData2)
writeQQ(bot)
writeHex("00 01 00 00 03 09 00 0C 00 01")
writeIP(serverIP)
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(TIMProtocol.publicKey)
}
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ package net.mamoe.mirai.utils.io
import kotlinx.io.core.*
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
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.event.ServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.*
......@@ -13,11 +14,11 @@ import net.mamoe.mirai.utils.decryptBy
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) }
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) }
fun ByteReadPacket.readIoBuffer(n: Number) = this.readIoBuffer(n.toInt())
......@@ -30,63 +31,55 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
val sequenceId = readUShort()
discardExact(7)//4 for qq number, 3 for 0x00 0x00 0x00. 但更可能是应该 discard 8
return when (id.toUInt()) {
0x08_25u -> TouchResponsePacket.Encrypted(this)
0x08_36u -> {
return when (PacketId(id)) {
TOUCH -> TouchResponsePacket.Encrypted(this)
LOGIN ->
//todo 不要用size分析
when (size) {
271, 207 -> return LoginResponseKeyExchangeResponsePacket.Encrypted(this).applySequence(sequenceId)
871 -> return LoginResponseCaptchaInitPacket.Encrypted(this).applySequence(sequenceId)
when {
size == 271 || size == 207 -> LoginResponseKeyExchangeResponsePacket.Encrypted(this)
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)
}
if (size > 700) return LoginResponseSuccessPacket.Encrypted(this).applySequence(sequenceId)
println("登录包size=$size")
return LoginResponseFailedPacket(
when (size) {
135 -> {//包数据错误. 目前怀疑是 tlv0006
this.readRemainingBytes().cutTail(1).decryptBy(TIMProtocol.shareKey).read {
discardExact(51)
MiraiLogger.error("Internal error: " + readLVString())//抱歉,请重新输入密码。
}
LoginResult.INTERNAL_ERROR
} //可能是包数据错了. 账号没有被ban, 用TIM官方可以登录
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
/*
//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)
SESSION_KEY -> SessionKeyResponsePacket.Encrypted(this)
CHANGE_ONLINE_STATUS -> ServerLoginSuccessPacket(this)
CAPTCHA -> ServerCaptchaPacket.Encrypted(this)
SERVER_EVENT_1, SERVER_EVENT_2 -> ServerEventPacket.Raw.Encrypted(this, PacketId(id), sequenceId)
FRIEND_ONLINE_STATUS_CHANGE -> ServerFriendOnlineStatusChangedPacket.Encrypted(this)
S_KEY -> ResponsePacket.Encrypted<RequestSKeyPacket.Response>(this)
ACCOUNT_INFO -> ResponsePacket.Encrypted<RequestAccountInfoPacket.Response>(this)
SEND_GROUP_MESSAGE -> ResponsePacket.Encrypted<SendGroupMessagePacket.Response>(this)
SEND_FRIEND_MESSAGE -> ResponsePacket.Encrypted<SendFriendMessagePacket.Response>(this)
CAN_ADD_FRIEND -> ResponsePacket.Encrypted<CanAddFriendPacket.Response>(this)
HEARTBEAT -> ResponsePacket.Encrypted<HeartbeatPacket.Response>(this)
GROUP_IMAGE_ID -> ResponsePacket.Encrypted<GroupImageIdRequestPacket.Response>(this)
FRIEND_IMAGE_ID
-> ResponsePacket.Encrypted<FriendImageIdRequestPacket.Response>(this)
// 0x01_BDu -> EventResponse.Encrypted<SubmitImageFilenamePacket.Response>(this)
else -> UnknownServerPacket.Encrypted(this, PacketId(id), sequenceId)
}.applySequence(sequenceId)
}
......@@ -130,7 +123,8 @@ fun Input.readTLVMap(expectingEOF: Boolean = false): Map<Int, ByteArray> {
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()))
......
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")
package mirai.test
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.runBlocking
......
@file:Suppress("ObjectPropertyName", "unused", "NonAsciiCharacters", "MayBeConstant")
package mirai.test
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
......@@ -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.event.ServerEventPacket
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.decryptBy
import net.mamoe.mirai.utils.io.*
......
......@@ -12,6 +12,7 @@ import net.mamoe.mirai.event.events.FriendMessageEvent
import net.mamoe.mirai.login
import net.mamoe.mirai.message.*
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.login.ifFail
import net.mamoe.mirai.network.session
......@@ -19,7 +20,7 @@ import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.toByteArray
import net.mamoe.mirai.utils.io.toUHexString
import net.mamoe.mirai.utils.toExternalImage
import net.mamoe.mirai.utils.suspendToExternalImage
import java.io.File
import kotlin.system.exitProcess
......@@ -227,7 +228,7 @@ suspend fun directlySubscribe(bot: Bot) {
.hexToBytes()
it.bot.network.socket.sendPacket(
OutgoingRawPacket(
0x01_BDu,
PacketId(0x01_BDu),
it.bot.qqAccount,
"00 00 00 01 2E 01 00 00 69 35".hexToBytes(),
it.bot.network.session.sessionKey,
......@@ -240,7 +241,7 @@ suspend fun directlySubscribe(bot: Bot) {
val filename = it.message.toString().substringAfter("上传群图片")
val image = File(
"C:\\Users\\Him18\\Desktop\\$filename"
).toExternalImage()
).suspendToExternalImage()
920503456u.group().uploadImage(image)
it.reply(image.groupImageId.value)
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