Commit ae9d2bca authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
parents 4ae589bb 63a4d5f3
......@@ -104,7 +104,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
override suspend fun init() {
delay(5000)
// delay(5000)
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
......@@ -126,10 +126,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
20,
0,
0
).sendAndExpect<FriendList.GetFriendGroupList.Response>(
timeoutMillis = 8000,
retry = 5
)
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 1000)
totalFriendCount = data.totalFriendCount
data.friendList.forEach {
......@@ -153,10 +150,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
bot.logger.info("开始加载组列表")
val troopData = FriendList.GetTroopListSimplify(
bot.client
).sendAndExpect<FriendList.GetTroopListSimplify.Response>(10000)
println(troopData.groups.contentToString())
bot.logger.info("加载组列表成功")
).sendAndExpect<FriendList.GetTroopListSimplify.Response>(timeoutMillis = 1000)
println(troopData.contentToString())
} catch (e: Exception) {
bot.logger.info("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
}
......@@ -240,7 +235,10 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
bot.logger.info("Received packet: $packet")
packetFactory?.run {
bot.handle(packet)
when (this) {
is OutgoingPacketFactory<P> -> bot.handle(packet)
is IncomingPacketFactory<P> -> bot.handle(packet, sequenceId)?.sendWithoutExpect()
}
}
}
......
# QQAndroid Protocol
## Overview
Note: `head` and `body` functions do nothing. They just work as
notations
PseudoCode:
```
OutgoingPacket {
int head.size + body.size + 4
head {
int 0x0A
byte 0x02
int extra data size + 4
byte[] extra data // initially={}
byte 0
int uinAccount.length + 4
byte[] uinAccount // =qqNumber.toString()
}
body { // encrypted by `ByteArray(16)` when login, after which by sessionKey
SSOPacket {
int head.size + 4
head {
int sequenceId
int subAppId
int subAppId
hex "01 00 00 00 00 00 00 00 00 00 01 00" // unknown values
int extraData.size + 4
byte[] extraData // empty when login
int commandName.length + 4
byte[] commandName // e.g. wtlogin.login
int 4 + 4
int 0x02B05B8B
int imei.length + 4
byte[] imei
int 0 + 4
short ksid.length + 2
byte[] ksid
int 0 + 4
}
int body.size + 4
body {
OicqRequestPacket {
head {
byte 2 // head flag
short 27 + 2 + remaining.length
ushort client.protocolVersion // const 8001
ushort commandId // e.g. 0x0810
ushort 0x0001
uint client.uin
byte 3 // const
ubyte encryptMethod.value // [EncryptMethod]
byte 0 // const
int 2 // const
int client.appClientVersion
int 0 // const
}
body {
// only write one of the following two structures!!
// if encryption method is ECDH
EncryptionMethodECDH {
head {
byte 1
byte 1
byte[] privateKey // random key
short 258
short [ECDH.publicKey].size // always 49
byte[] [ECDH.publicKey]
}
body {
// real body
}
}
// if encryption method is SessionKey
EncryptionMethodSessionKey {
head {
byte 1
byte if (currentLoginState == 2) 3 else 2
fully key
short 258 // const
short 0
}
body {
// real body
}
}
}
tail {
byte 3 // tail flag
}
}
}
}
}
}
```
## Packet bodies
### LoginPacket - SubCommand 9
**TO BE UPDATED**
PseudoCode:
```
short 9 // subCommand
tlvList {
}
```
......@@ -2,18 +2,18 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.writeFully
import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352Packet
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.GetImgUrlReq
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket
internal object ImageDownPacket : PacketFactory<ImageDownPacket.ImageDownPacketResponse>("LongConn.OffPicDown") {
internal object ImageDownPacket : OutgoingPacketFactory<ImageDownPacket.ImageDownPacketResponse>("LongConn.OffPicDown") {
operator fun invoke(client: QQAndroidClient, req: GetImgUrlReq): OutgoingPacket {
// TODO: 2020/1/24 测试: bodyType, subAppId
......
......@@ -2,18 +2,18 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.writeFully
import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352Packet
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.UploadImgReq
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket
internal object ImageUpPacket : PacketFactory<ImageUpPacket.ImageUpPacketResponse>("LongConn.OffPicUp") {
internal object ImageUpPacket : OutgoingPacketFactory<ImageUpPacket.ImageUpPacketResponse>("LongConn.OffPicUp") {
operator fun invoke(client: QQAndroidClient, req: UploadImgReq): OutgoingPacket {
// TODO: 2020/1/24 测试: bodyType, subAppId
......
......@@ -19,9 +19,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.utils.toMessageChain
import net.mamoe.mirai.qqandroid.utils.toRichTextElems
import net.mamoe.mirai.utils.MiraiInternalAPI
......@@ -36,19 +34,18 @@ internal class MessageSvc {
/**
* 告知要刷新好友消息
*/
internal object PushNotify : PacketFactory<RequestPushNotify>("MessageSvc.PushNotify") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): RequestPushNotify {
internal object PushNotify : IncomingPacketFactory<RequestPushNotify>("MessageSvc.PushNotify") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): RequestPushNotify {
discardExact(8)
return decodeUniPacket(RequestPushNotify.serializer())
}
override suspend fun QQAndroidBot.handle(packet: RequestPushNotify) {
override suspend fun QQAndroidBot.handle(packet: RequestPushNotify, sequenceId: Int): OutgoingPacket? {
network.run {
PbGetMsg(client, MsgSvc.SyncFlag.START, packet.stMsgInfo?.uMsgTime ?: 0).sendAndExpect<MultiPacket<FriendMessage>>()
return PbGetMsg(client, MsgSvc.SyncFlag.START, packet.stMsgInfo?.uMsgTime ?: 0)
}
}
}
......@@ -56,7 +53,7 @@ internal class MessageSvc {
* 获取好友消息和消息记录
*/
@UseExperimental(MiraiInternalAPI::class)
internal object PbGetMsg : PacketFactory<PbGetMsg.Response>("MessageSvc.PbGetMsg") {
internal object PbGetMsg : OutgoingPacketFactory<PbGetMsg.Response>("MessageSvc.PbGetMsg") {
val EXTRA_DATA =
"08 00 12 33 6D 6F 64 65 6C 3A 78 69 67 6F 6D 69 20 36 3B 6F 73 3A 32 32 3B 76 65 72 73 69 6F 6E 3A 76 32 6D 61 6E 3A 78 69 61 6F 6D 69 73 79 73 3A 4C 4D 59 34 38 5A 18 E4 E1 A4 FF FE 2D 20 E9 E1 A4 FF FE 2D 28 A8 E1 A4 FF FE 2D 30 99 E1 A4 FF FE 2D".hexToBytes()
......@@ -81,7 +78,7 @@ internal class MessageSvc {
syncFlag = syncFlag,
// serverBuf = from.serverBuf ?: EMPTY_BYTE_ARRAY,
syncCookie = client.c2cMessageSync.syncCookie
?: SyncCookie(time = Random.nextLong()).toByteArray(SyncCookie.serializer())//.also { client.c2cMessageSync.syncCookie = it },
?: SyncCookie(time = msgTime + client.timeDifference).toByteArray(SyncCookie.serializer())//.also { client.c2cMessageSync.syncCookie = it },
// syncFlag = client.c2cMessageSync.syncFlag,
//msgCtrlBuf = client.c2cMessageSync.msgCtrlBuf,
//pubaccountCookie = client.c2cMessageSync.pubAccountCookie
......@@ -160,7 +157,7 @@ internal class MessageSvc {
/**
* 被挤下线
*/
internal object PushForceOffline : PacketFactory<ForceOfflineEvent>("MessageSvc.PushForceOffline") {
internal object PushForceOffline : OutgoingPacketFactory<ForceOfflineEvent>("MessageSvc.PushForceOffline") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): ForceOfflineEvent {
discardExact(4)
val struct = this.decodeUniPacket(RequestPushForceOffline.serializer())
......@@ -168,7 +165,7 @@ internal class MessageSvc {
}
}
internal object PbSendMsg : PacketFactory<PbSendMsg.Response>("MessageSvc.PbSendMsg") {
internal object PbSendMsg : OutgoingPacketFactory<PbSendMsg.Response>("MessageSvc.PbSendMsg") {
sealed class Response : Packet {
object SUCCESS : Response() {
override fun toString(): String = "MessageSvc.PbSendMsg.Response.SUCCESS"
......@@ -230,9 +227,9 @@ internal class MessageSvc {
elems = message.toRichTextElems()
)
),
msgSeq = client.atomicNextMessageSequenceId()
// msgRand = 123
//syncCookie = client.c2cMessageSync.syncCookie?.takeIf { it.isNotEmpty() } ?:
msgSeq = client.atomicNextMessageSequenceId(),
//msgRand = Random.nextInt() and 0x7FFF,
syncCookie = client.c2cMessageSync.syncCookie?.takeIf { it.isNotEmpty() } ?: EMPTY_BYTE_ARRAY
//SyncCookie(currentTimeSeconds, Random.nextLong().absoluteValue, Random.nextLong().absoluteValue).toByteArray(SyncCookie.serializer())
// msgVia = 1
)
......
......@@ -11,7 +11,7 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgOnlinePush
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import net.mamoe.mirai.qqandroid.utils.toMessageChain
......@@ -19,9 +19,9 @@ internal class OnlinePush {
/**
* 接受群消息
*/
internal object PbPushGroupMsg : PacketFactory<GroupMessage>("OnlinePush.PbPushGroupMsg") {
internal object PbPushGroupMsg : IncomingPacketFactory<GroupMessage>("OnlinePush.PbPushGroupMsg") {
@UseExperimental(ExperimentalStdlibApi::class)
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): GroupMessage {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): GroupMessage {
// 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00
discardExact(4)
val pbPushMsg = ProtoBufWithNullableSupport.load(MsgOnlinePush.PbPushMsg.serializer(), readBytes())
......@@ -48,9 +48,5 @@ internal class OnlinePush {
}
)
}
override suspend fun QQAndroidBot.handle(packet: GroupMessage) {
}
}
}
\ No newline at end of file
......@@ -14,7 +14,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.jce.*
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Vec0xd50
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.utils.io.discardExact
......@@ -22,7 +22,7 @@ import net.mamoe.mirai.utils.io.discardExact
internal class FriendList {
internal object GetTroopMemberList :
PacketFactory<GetTroopMemberList.Response>("friendlist.GetTroopMemberListReq") {
OutgoingPacketFactory<GetTroopMemberList.Response>("friendlist.GetTroopMemberListReq") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): GetTroopMemberList.Response {
TODO()
}
......@@ -65,7 +65,7 @@ internal class FriendList {
}
internal object GetTroopListSimplify :
PacketFactory<GetTroopListSimplify.Response>("friendlist.GetTroopListReqV2") {
OutgoingPacketFactory<GetTroopListSimplify.Response>("friendlist.GetTroopListReqV2") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): GetTroopListSimplify.Response {
val res = this.decodeUniPacket(GetTroopListRespV2.serializer())
return Response(res.vecTroopList.orEmpty())
......@@ -109,7 +109,8 @@ internal class FriendList {
}
}
}
internal object GetFriendGroupList : PacketFactory<GetFriendGroupList.Response>("friendlist.getFriendGroupList") {
internal object GetFriendGroupList : OutgoingPacketFactory<GetFriendGroupList.Response>("friendlist.getFriendGroupList") {
class Response(
val totalFriendCount: Short,
......
......@@ -9,27 +9,27 @@ import net.mamoe.mirai.qqandroid.io.serialization.jceRequestSBuffer
import net.mamoe.mirai.qqandroid.io.serialization.writeJceStruct
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.PushResp
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.io.debugPrintThis
import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.PushReq as PushReqJceStruct
internal class ConfigPushSvc {
object PushReq : PacketFactory<PushReqJceStruct>("ConfigPushSvc.PushReq") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): PushReqJceStruct {
object PushReq : IncomingPacketFactory<PushReqJceStruct>(
receivingCommandName = "ConfigPushSvc.PushReq",
responseCommandName = "ConfigPushSvc.PushResp"
) {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): PushReqJceStruct {
discardExact(4)
val pushReq = decodeUniPacket(PushReqJceStruct.serializer())
println(pushReq.contentToString())
return pushReq
return decodeUniPacket(PushReqJceStruct.serializer())
}
override suspend fun QQAndroidBot.handle(packet: PushReqJceStruct) {
network.run {
buildOutgoingUniPacket(
override suspend fun QQAndroidBot.handle(packet: PushReqJceStruct, sequenceId: Int): OutgoingPacket? {
return network.run {
buildResponseUniPacket(
client,
sequenceId = client.configPushSvcPushReqSequenceId.also { println("configPushSvcPushReqSequenceId=${client.configPushSvcPushReqSequenceId}") },
sequenceId = client.configPushSvcPushReqSequenceId,
commandName = "ConfigPushSvc.PushResp",
name = "ConfigPushSvc.PushResp"
) {
......@@ -52,8 +52,8 @@ internal class ConfigPushSvc {
),
charset = JceCharset.UTF8
)
writePacket(this.build().debugPrintThis())
}.sendWithoutExpect()
// writePacket(this.build().debugPrintThis())
}
}
}
}
......
......@@ -11,16 +11,19 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.utils.GuidSource
import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag
import net.mamoe.mirai.qqandroid.utils.guidFlag
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.MiraiDebugAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.cryptor.decryptBy
import net.mamoe.mirai.utils.currentTimeSeconds
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.io.discardExact
import net.mamoe.mirai.utils.md5
/**
* OicqRequest
*/
@UseExperimental(ExperimentalUnsignedTypes::class)
internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wtlogin.login") {
internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketResponse>("wtlogin.login") {
/**
* 提交验证码
......@@ -686,7 +689,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
*/
private fun QQAndroidClient.analysisTlv130(t130: ByteArray) = t130.read {
discardExact(2)
timeDifference = readUInt().toLong() - currentTimeMillis
timeDifference = readUInt().toLong() - currentTimeSeconds
ipFromT149 = readBytes(4)
}
......
......@@ -10,7 +10,7 @@ import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.SvcReqRegister
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.oidb.oidb0x769.Oidb0x769
import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket
......@@ -33,7 +33,7 @@ internal enum class RegPushReason {
}
internal class StatSvc {
internal object Register : PacketFactory<Register.Response>("StatSvc.register") {
internal object Register : OutgoingPacketFactory<Register.Response>("StatSvc.register") {
internal object Response : Packet {
override fun toString(): String = "Response(StatSvc.register)"
......
......@@ -7,7 +7,7 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
internal object TransEmpPacket : PacketFactory<TransEmpPacket.Response>("wtlogin.trans_emp") {
internal object TransEmpPacket : OutgoingPacketFactory<TransEmpPacket.Response>("wtlogin.trans_emp") {
private const val appId = 16L
private const val subAppId = 537062845L
......
......@@ -4,10 +4,7 @@ package androidPacketTests
import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.qqandroid.network.protocol.packet.DECRYPTER_16_ZERO
import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
import net.mamoe.mirai.qqandroid.network.protocol.packet.withUse
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.utils.cryptor.ECDH
import net.mamoe.mirai.utils.cryptor.adjustToPublicKey
import net.mamoe.mirai.utils.cryptor.decryptBy
......@@ -99,7 +96,7 @@ private fun processFullPacketWithoutLength(packet: ByteReadPacket) {
try {
bytes.toReadPacket().parseOicqResponse {
debugIfFail {
if (it.packetFactory.commandName == "wtlogin.login") {
if ((it.packetFactory as? OutgoingPacketFactory<*>)?.commandName == "wtlogin.login") {
DebugLogger.info("服务器发来了 wtlogin.login. 正在解析 key")
try {
val subCommand = readUShort().toInt()
......
......@@ -5,59 +5,73 @@ import kotlin.jvm.JvmOverloads
/**
* 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, 如 [Bot], 都会通过这个函数构造日志记录器
* 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, 如 [Bot], 都会通过这个函数构造日志记录器.
* 可直接修改这个变量的值来重定向日志输出.
*/
var DefaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger(it) }
/**
* 当前平台的默认的日志记录器.
* 在 _JVM 控制台_ 端的实现为 [println]
* 在 _Android_ 端的实现为 [android.util.Log]
*
* 不应该直接构造这个类的实例. 请使用 [DefaultLogger], 或使用默认的顶层日志记录 [MiraiLogger.Companion]
* **注意:** 请务必将所有的输出定向到日志记录系统, 否则在某些情况下 (如 web 控制台中) 将无法接收到输出
*
* **注意:** 请为日志做好分类, 即不同的模块使用不同的 [MiraiLogger].
* 如, [Bot] 中使用 identity 为 "Bot(qqId)" 的 [MiraiLogger]
* 而 [Bot] 的网络处理中使用 identity 为 "BotNetworkHandler" 的.
*/
expect open class PlatformLogger @JvmOverloads internal constructor(identity: String? = "Mirai") : MiraiLoggerPlatformBase
var DefaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger(it) }
/**
* 给这个 logger 添加一个开关, 用于控制是否记录 log
*
*/
@JvmOverloads
fun MiraiLogger.withSwitch(default: Boolean = true): MiraiLoggerWithSwitch = MiraiLoggerWithSwitch(this, default)
/**
* 日志记录器. 所有的输出均依赖于它.
* 不同的对象可能拥有只属于自己的 logger. 通过 [identity] 来区分.
* 不同的对象可拥有只属于自己的 logger. 通过 [identity] 来区分.
*
* 注意: 如果你需要重新实现日志, 请不要直接实现这个接口, 请继承 [MiraiLoggerPlatformBase]
*
* 注意: 请不要直接实现这个接口, 请继承 [MiraiLoggerPlatformBase]
* 在定义 logger 变量时, 请一直使用 [MiraiLogger] 或者 [MiraiLoggerWithSwitch].
*
* @see MiraiLoggerPlatformBase 平台通用基础实现
* @see SimpleLogger 简易 logger, 它将所有的日志记录操作都转移给 lambda `(String?, Throwable?) -> Unit`
* @see PlatformLogger 各个平台下的默认日志记录实现.
* @see SilentLogger 忽略任何日志记录操作的 logger 实例.
*
* @see MiraiLoggerPlatformBase 平台通用基础实现. 若
*/
interface MiraiLogger {
/**
* 顶层日志记录器.
*
* 顶层日志会导致混乱并难以定位问题. 请自行构造 logger 实例并使用.
* 请参考使用 [DefaultLogger]
*/
@Deprecated(message = "顶层日志会导致混乱并难以定位问题. 请自行构造 logger 实例并使用.", level = DeprecationLevel.WARNING)
companion object : MiraiLogger by DefaultLogger("Mirai")
/**
* 日志的标记. 在 Mirai 中, identity 可为
* - "Bot"
* - "BotNetworkHandler"
* 等.
*
* 它只用于帮助调试或统计. 十分建议清晰定义 identity
*/
val identity: String?
/**
* 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用.
* [follower] 的存在可以让一次日志被多个日志记录器记录.
*
* 例:
* ```kotlin
* val bot = Bot( ... )
* bot.follower = MyOwnLogger()
* 一般不建议直接修改这个属性. 请通过 [plus] 来连接两个日志记录器.
* 如: `val logger = bot.logger + MyOwnLogger()`
* 这样, 当调用 `logger.info()` 时, bot.logger 会首先记录, MyOwnLogger 会随后记录.
*
* bot.info("Hi")
* ```
* 在这个例子中的 `MyOwnLogger` 将可以记录到 "Hi".
* 当然, 多个 logger 也可以加在一起: `val logger = bot.logger + MyOwnLogger() + MyOwnLogger2()`
*/
var follower: MiraiLogger?
/**
* 记录一个 `verbose` 级别的日志.
* 无关紧要的, 经常大量输出的日志应使用它.
*/
fun verbose(any: Any?)
......@@ -65,7 +79,7 @@ interface MiraiLogger {
fun verbose(message: String?, e: Throwable?)
/**
* 记录一个 `debug` 级别的日志.
* 记录一个 _调试_ 级别的日志.
*/
fun debug(any: Any?)
......@@ -74,7 +88,7 @@ interface MiraiLogger {
/**
* 记录一个 `info` 级别的日志.
* 记录一个 _信息_ 级别的日志.
*/
fun info(any: Any?)
......@@ -83,7 +97,7 @@ interface MiraiLogger {
/**
* 记录一个 `warning` 级别的日志.
* 记录一个 _警告_ 级别的日志.
*/
fun warning(any: Any?)
......@@ -92,7 +106,7 @@ interface MiraiLogger {
/**
* 记录一个 `error` 级别的日志.
* 记录一个 _错误_ 级别的日志.
*/
fun error(e: Any?)
......@@ -109,10 +123,9 @@ interface MiraiLogger {
* | base | <-- | follower | <-- | follower | <-- | follower |
* +------+ +----------+ +----------+ +----------+
*
* @see follower
* @return [follower]
*/
operator fun plus(follower: MiraiLogger): MiraiLogger
operator fun <T : MiraiLogger> plus(follower: T): T
/**
* 添加一个 [follower]
......@@ -123,6 +136,15 @@ interface MiraiLogger {
operator fun plusAssign(follower: MiraiLogger)
}
/**
* 当前平台的默认的日志记录器.
* 在 _JVM 控制台_ 端的实现为 [println]
* 在 _Android_ 端的实现为 [android.util.Log]
*
* 不应该直接构造这个类的实例. 请使用 [DefaultLogger], 或使用默认的顶层日志记录 [MiraiLogger.Companion]
*/
expect open class PlatformLogger @JvmOverloads internal constructor(identity: String? = "Mirai") : MiraiLoggerPlatformBase
/**
* 不做任何事情的 logger, keep silent.
*/
......@@ -167,7 +189,11 @@ class SimpleLogger(override val identity: String?, private val logger: (String?,
class MiraiLoggerWithSwitch internal constructor(private val delegate: MiraiLogger, default: Boolean) : MiraiLoggerPlatformBase() {
override val identity: String? get() = delegate.identity
private var switch: Boolean = default
/**
* true 为开启.
*/
@PublishedApi
internal var switch: Boolean = default
fun enable() {
switch = true
......@@ -188,15 +214,53 @@ class MiraiLoggerWithSwitch internal constructor(private val delegate: MiraiLogg
override fun error0(any: Any?) = if (switch) delegate.error(any) else Unit
override fun error0(message: String?, e: Throwable?) = if (switch) delegate.error(message, e) else Unit
inline fun verbose(lazyMessage: () -> String) {
if (switch) verbose(lazyMessage())
}
inline fun verbose(lazyMessage: () -> String, e: Throwable?) {
if (switch) verbose(lazyMessage(), e)
}
inline fun debug(lazyMessage: () -> Any?) {
if (switch) debug(lazyMessage())
}
inline fun debug(lazyMessage: () -> String?, e: Throwable?) {
if (switch) debug(lazyMessage(), e)
}
inline fun info(lazyMessage: () -> Any?) {
if (switch) info(lazyMessage())
}
inline fun info(lazyMessage: () -> String?, e: Throwable?) {
if (switch) info(lazyMessage(), e)
}
inline fun warning(lazyMessage: () -> Any?) {
if (switch) warning(lazyMessage())
}
inline fun warning(lazyMessage: () -> String?, e: Throwable?) {
if (switch) warning(lazyMessage(), e)
}
inline fun error(lazyMessage: () -> Any?) {
if (switch) error(lazyMessage())
}
inline fun error(lazyMessage: () -> String?, e: Throwable?) {
if (switch) error(lazyMessage(), e)
}
}
/**
* 平台日志基类.
* 实现了 [follower] 的调用传递.
*
* 若要自行实现日志记录, 请优先考虑继承 [PlatformLogger].
* 日志基类. 实现了 [follower] 的调用传递.
* 若 Mirai 自带的日志系统无法满足需求, 请继承这个类并实现其抽象函数.
*
* 它不应该被用作变量的类型定义. 只应被继承
* 这个类不应该被用作变量的类型定义. 只应被作为继承对象.
* 在定义 logger 变量时, 请一直使用 [MiraiLogger] 或者 [MiraiLoggerWithSwitch].
*/
abstract class MiraiLoggerPlatformBase : MiraiLogger {
final override var follower: MiraiLogger? = null
......@@ -262,7 +326,7 @@ abstract class MiraiLoggerPlatformBase : MiraiLogger {
protected abstract fun error0(any: Any?)
protected abstract fun error0(message: String?, e: Throwable?)
override fun plus(follower: MiraiLogger): MiraiLogger {
override operator fun <T : MiraiLogger> plus(follower: T): T {
this.follower = follower
return follower
}
......
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