Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
d89f8acc
Commit
d89f8acc
authored
Oct 13, 2019
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Multiplatform
parent
c09d44f4
Changes
104
Show whitespace changes
Inline
Side-by-side
Showing
104 changed files
with
3102 additions
and
2174 deletions
+3102
-2174
dependencies.gradle
dependencies.gradle
+4
-0
mirai-console/src/main/java/net/mamoe/mirai/MiraiServer.kt
mirai-console/src/main/java/net/mamoe/mirai/MiraiServer.kt
+29
-30
mirai-core/build.gradle
mirai-core/build.gradle
+38
-16
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+21
-24
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt
...i-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt
+66
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Mirai.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Mirai.kt
+0
-4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
.../src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
+0
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
...re/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
+6
-8
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
...-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
+11
-19
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt
...core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt
+23
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
...rc/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
+3
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
...mmonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+1
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/FriendEvents.kt
...nMain/kotlin/net.mamoe.mirai/event/events/FriendEvents.kt
+1
-7
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/GroupEvents.kt
...onMain/kotlin/net.mamoe.mirai/event/events/GroupEvents.kt
+2
-10
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/PacketEvents.kt
...nMain/kotlin/net.mamoe.mirai/event/events/PacketEvents.kt
+1
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
.../net.mamoe.mirai/event/internal/InternalEventListeners.kt
+16
-38
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FaceID.kt
...e/src/commonMain/kotlin/net.mamoe.mirai/message/FaceID.kt
+168
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
.../src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
+3
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageType.kt
.../commonMain/kotlin/net.mamoe.mirai/message/MessageType.kt
+2
-4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Messages.kt
...src/commonMain/kotlin/net.mamoe.mirai/message/Messages.kt
+1
-5
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
...n/net.mamoe.mirai/message/internal/MessageDataInternal.kt
+172
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt
...nMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt
+82
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/HandlerClosedException.kt
.../kotlin/net.mamoe.mirai/network/HandlerClosedException.kt
+3
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/LoginSession.kt
...commonMain/kotlin/net.mamoe.mirai/network/LoginSession.kt
+12
-4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
....mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
+421
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt
...otlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt
+3
-39
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt
...mirai/network/protocol/tim/handler/ActionPacketHandler.kt
+25
-37
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/DataPacketSocket.kt
...oe.mirai/network/protocol/tim/handler/DataPacketSocket.kt
+16
-4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt
....mirai/network/protocol/tim/handler/EventPacketHandler.kt
+15
-9
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/PacketHandler.kt
...mamoe.mirai/network/protocol/tim/handler/PacketHandler.kt
+6
-4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt
...ai/network/protocol/tim/handler/TemporaryPacketHandler.kt
+1
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt
...t.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt
+46
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/FriendOnlineStatusChanged.kt
.../network/protocol/tim/packet/FriendOnlineStatusChanged.kt
+33
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/GradeInfo.kt
.../net.mamoe.mirai/network/protocol/tim/packet/GradeInfo.kt
+14
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt
.../net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt
+9
-9
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt
...lin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt
+28
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt
...n/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt
+1
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt
...et.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt
+96
-182
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt
...t.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt
+91
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt
....mirai/network/protocol/tim/packet/UnknownServerPacket.kt
+63
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt
...et.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt
+55
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/AddContact.kt
...oe.mirai/network/protocol/tim/packet/action/AddContact.kt
+24
-28
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/AddFriendResult.kt
...rai/network/protocol/tim/packet/action/AddFriendResult.kt
+0
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt
...otocol/tim/packet/action/ClientSendFriendMessagePacket.kt
+13
-13
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendGroupMessagePacket.kt
...rotocol/tim/packet/action/ClientSendGroupMessagePacket.kt
+52
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt
...otocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt
+11
-11
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientLogin.kt
...oe.mirai/network/protocol/tim/packet/login/ClientLogin.kt
+42
-36
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/LoginResult.kt
...oe.mirai/network/protocol/tim/packet/login/LoginResult.kt
+6
-4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt
...net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt
+26
-25
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt
.../network/protocol/tim/packet/login/ServerLoginResponse.kt
+157
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginSuccessPacket.kt
...ork/protocol/tim/packet/login/ServerLoginSuccessPacket.kt
+2
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt
....mamoe.mirai/network/protocol/tim/packet/login/Session.kt
+116
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt
...et.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt
+25
-26
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/VerificationCode.kt
...rai/network/protocol/tim/packet/login/VerificationCode.kt
+57
-62
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotAccount.kt
...src/commonMain/kotlin/net.mamoe.mirai/utils/BotAccount.kt
+1
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteArrayUtil.kt
.../commonMain/kotlin/net.mamoe.mirai/utils/ByteArrayUtil.kt
+37
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt
...onMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt
+189
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
...rc/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
+28
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/CaptchaResolver.kt
...ommonMain/kotlin/net.mamoe.mirai/utils/CaptchaResolver.kt
+10
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ContactList.kt
...rc/commonMain/kotlin/net.mamoe.mirai/utils/ContactList.kt
+1
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DebugUtil.kt
.../src/commonMain/kotlin/net.mamoe.mirai/utils/DebugUtil.kt
+28
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/IteratableUtil.kt
...commonMain/kotlin/net.mamoe.mirai/utils/IteratableUtil.kt
+12
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoggerTextFormat.kt
...mmonMain/kotlin/net.mamoe.mirai/utils/LoggerTextFormat.kt
+0
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginConfigure.kt
...commonMain/kotlin/net.mamoe.mirai/utils/LoginConfigure.kt
+18
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
...rc/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
+32
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OnlineStatus.kt
...c/commonMain/kotlin/net.mamoe.mirai/utils/OnlineStatus.kt
+13
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PacketBuilderUtil.kt
...monMain/kotlin/net.mamoe.mirai/utils/PacketBuilderUtil.kt
+83
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtil.kt
...c/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtil.kt
+34
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SocketBridge.kt
...c/commonMain/kotlin/net.mamoe.mirai/utils/SocketBridge.kt
+14
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/TEA.kt
...i-core/src/commonMain/kotlin/net.mamoe.mirai/utils/TEA.kt
+25
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt
...ore/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt
+0
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/TypeConvertion.kt
...commonMain/kotlin/net.mamoe.mirai/utils/TypeConvertion.kt
+62
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Util.kt
...-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Util.kt
+0
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Varint.kt
...ore/src/commonMain/kotlin/net.mamoe.mirai/utils/Varint.kt
+17
-43
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventListeneresInternalJvm.kt
.../mamoe/mirai/event/internal/EventListeneresInternalJvm.kt
+15
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.kt
...core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.kt
+0
-166
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/UnsolvedImage.kt
...c/jvmMain/kotlin/net/mamoe/mirai/message/UnsolvedImage.kt
+6
-6
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/NetworkScope.kt
...rc/jvmMain/kotlin/net/mamoe/mirai/network/NetworkScope.kt
+0
-6
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/ClientPacket.kt
...t/mamoe/mirai/network/protocol/tim/packet/ClientPacket.kt
+0
-230
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/Packet.kt
...lin/net/mamoe/mirai/network/protocol/tim/packet/Packet.kt
+0
-22
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt
...oe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt
+67
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/ServerPacket.kt
...t/mamoe/mirai/network/protocol/tim/packet/ServerPacket.kt
+0
-331
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UnknownServerPacket.kt
.../mirai/network/protocol/tim/packet/UnknownServerPacket.kt
+0
-19
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt
...mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt
+12
-47
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/ServerLoginResponseFailedPacket.kt
...tocol/tim/packet/login/ServerLoginResponseFailedPacket.kt
+0
-12
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/ServerLoginResponseKeyExchangePacket.kt
.../tim/packet/login/ServerLoginResponseKeyExchangePacket.kt
+0
-55
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
...packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
+0
-63
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt
...et/login/ServerLoginResponseVerificationCodeInitPacket.kt
+0
-68
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt
...vmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt
+100
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CharImageConverter.kt
...vmMain/kotlin/net/mamoe/mirai/utils/CharImageConverter.kt
+0
-79
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CharImageUtil.kt
...src/jvmMain/kotlin/net/mamoe/mirai/utils/CharImageUtil.kt
+0
-11
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/EventException.java
.../jvmMain/kotlin/net/mamoe/mirai/utils/EventException.java
+0
-35
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ImageNetworkUtils.kt
...jvmMain/kotlin/net/mamoe/mirai/utils/ImageNetworkUtils.kt
+6
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLogger.kt
...e/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLogger.kt
+0
-60
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
...rc/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
+38
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformBridgeJvm.kt
...jvmMain/kotlin/net/mamoe/mirai/utils/PlatformBridgeJvm.kt
+18
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt
...c/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt
+26
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/TEA.kt
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/TEA.kt
+15
-10
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Utils.kt
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Utils.kt
+0
-176
mirai-core/src/jvmTest/kotlin/BadQQFilter.kt
mirai-core/src/jvmTest/kotlin/BadQQFilter.kt
+4
-5
mirai-debug/build.gradle
mirai-debug/build.gradle
+1
-0
mirai-debug/src/main/java/HexComparator.kt
mirai-debug/src/main/java/HexComparator.kt
+35
-1
mirai-debug/src/main/java/PacketDebuger.kt
mirai-debug/src/main/java/PacketDebuger.kt
+17
-15
mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt
mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt
+19
-14
No files found.
dependencies.gradle
View file @
d89f8acc
...
...
@@ -4,6 +4,7 @@ ext {
// kotlin
kotlinJvm
=
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
kotlinCommon
=
"org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
kotlinNative
=
"org.jetbrains.kotlin:kotlin-stdlib-native:$kotlin_version"
// coroutine
coroutine_version
=
"1.3.0"
...
...
@@ -13,6 +14,8 @@ ext {
coroutineAndroid
=
"org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
coroutineJs
=
"org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutine_version"
coroutineIo
=
"org.jetbrains.kotlinx:kotlinx-coroutines-io:0.24.0"
// reflect
reflect
=
"org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
...
...
@@ -25,5 +28,6 @@ ext {
kotlinxIOJvm
=
"org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version"
kotlinxIOCommon
=
"org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version"
kotlinxIOJS
=
"org.jetbrains.kotlinx:kotlinx-io-js:$kotlinx_io_version"
kotlinxIONative
=
"org.jetbrains.kotlinx:kotlinx-io-native:$kotlinx_io_version"
}
mirai-console/src/main/java/net/mamoe/mirai/MiraiServer.kt
View file @
d89f8acc
package
net.mamoe.mirai
import
kotlinx.coroutines.runBlocking
import
net.mamoe.mirai.network.protocol.tim.packet.login.LoginState
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import
net.mamoe.mirai.utils.BotAccount
import
net.mamoe.mirai.utils.LoggerTextFormat
import
net.mamoe.mirai.utils.MiraiLogger
import
net.mamoe.mirai.utils.config.MiraiConfig
import
net.mamoe.mirai.utils.setting.MiraiSettings
import
java.io.File
import
java.io.IOException
import
java.util.concurrent.ExecutionException
/**
* @author Him188moe
*/
/**
* Mirai 服务器.
...
...
@@ -40,11 +39,11 @@ object MiraiServer {
this
.
logger
=
MiraiLogger
logger
.
i
nfo
(
"About to run Mirai ("
+
Mirai
.
VERSION
+
") under "
+
if
(
isUnix
)
"unix"
else
"windows"
)
logger
.
i
nfo
(
"Loading data under "
+
LoggerTextFormat
.
GREEN
+
this
.
parentFolder
)
logger
.
logI
nfo
(
"About to run Mirai ("
+
Mirai
.
VERSION
+
") under "
+
if
(
isUnix
)
"unix"
else
"windows"
)
logger
.
logI
nfo
(
"Loading data under "
+
LoggerTextFormat
.
GREEN
+
this
.
parentFolder
)
val
setting
=
this
.
parentFolder
+
"/Mirai.ini"
logger
.
i
nfo
(
"Selecting setting from "
+
LoggerTextFormat
.
GREEN
+
setting
)
val
setting
=
File
(
this
.
parentFolder
,
"/Mirai.ini"
)
logger
.
logI
nfo
(
"Selecting setting from "
+
LoggerTextFormat
.
GREEN
+
setting
)
/*
if (!setting.exists()) {
...
...
@@ -54,7 +53,7 @@ object MiraiServer {
}
File qqs = new File(this.parentFolder + "/QQ.yml");
getLogger().
i
nfo("Reading QQ accounts from " + LoggerTextFormat.GREEN + qqs);
getLogger().
logI
nfo("Reading QQ accounts from " + LoggerTextFormat.GREEN + qqs);
if (!qqs.exists()) {
this.initQQConfig(qqs);
} else {
...
...
@@ -67,10 +66,10 @@ object MiraiServer {
/*
MiraiSettingMapSection qqs = this.setting.getMapSection("qq");
qqs.forEach((a,p) -> {
this.getLogger().
i
nfo("Finding available ports between " + "1-65536");
this.getLogger().
logI
nfo("Finding available ports between " + "1-65536");
try {
int port = MiraiNetwork.getAvailablePort();
this.getLogger().
i
nfo("Listening on port " + port);
this.getLogger().
logI
nfo("Listening on port " + port);
} catch (IOException e) {
e.printStackTrace();
...
...
@@ -84,18 +83,18 @@ object MiraiServer {
fun
shutdown
()
{
if
(
this
.
enabled
)
{
logger
.
i
nfo
(
"About to shutdown Mirai"
)
logger
.
i
nfo
(
"Data have been saved"
)
logger
.
logI
nfo
(
"About to shutdown Mirai"
)
logger
.
logI
nfo
(
"Data have been saved"
)
}
}
private
fun
initSetting
(
setting
:
File
)
{
logger
.
i
nfo
(
"Thanks for using Mirai"
)
logger
.
i
nfo
(
"initializing Settings"
)
logger
.
logI
nfo
(
"Thanks for using Mirai"
)
logger
.
logI
nfo
(
"initializing Settings"
)
try
{
if
(
setting
.
createNewFile
())
{
logger
.
i
nfo
(
"Mirai Config Created"
)
logger
.
logI
nfo
(
"Mirai Config Created"
)
}
}
catch
(
e
:
IOException
)
{
e
.
printStackTrace
()
...
...
@@ -113,24 +112,24 @@ object MiraiServer {
worker
[
"core_task_pool_worker_amount"
]
=
5
val
plugin
=
this
.
settings
.
getMapSection
(
"plugin"
)
plugin
[
"
d
ebug"
]
=
false
plugin
[
"
logD
ebug"
]
=
false
this
.
settings
.
save
()
logger
.
i
nfo
(
"initialized; changing can be made in setting file: $setting"
)
logger
.
logI
nfo
(
"initialized; changing can be made in setting file: $setting"
)
}
private
fun
initQQConfig
(
qqConfig
:
File
)
{
this
.
qqs
=
MiraiConfig
(
qqConfig
)
MiraiLogger
.
i
nfo
(
"QQ account initialized; changing can be made in Config file: $qqConfig"
)
logger
.
i
nfo
(
"QQ 账户管理初始化完毕"
)
MiraiLogger
.
logI
nfo
(
"QQ account initialized; changing can be made in Config file: $qqConfig"
)
logger
.
logI
nfo
(
"QQ 账户管理初始化完毕"
)
}
private
fun
reload
()
{
this
.
enabled
=
true
MiraiLogger
.
i
nfo
(
LoggerTextFormat
.
GREEN
.
toString
()
+
"Server enabled; Welcome to Mirai"
)
MiraiLogger
.
i
nfo
(
"Mirai Version="
+
Mirai
.
VERSION
)
MiraiLogger
.
logI
nfo
(
LoggerTextFormat
.
GREEN
.
toString
()
+
"Server enabled; Welcome to Mirai"
)
MiraiLogger
.
logI
nfo
(
"Mirai Version="
+
Mirai
.
VERSION
)
MiraiLogger
.
i
nfo
(
"Initializing [Bot]s"
)
MiraiLogger
.
logI
nfo
(
"Initializing [Bot]s"
)
try
{
availableBot
...
...
@@ -142,23 +141,23 @@ object MiraiServer {
/*
this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
getLogger().
i
nfo("Initializing [Bot] " + section.getString("account"));
getLogger().
logI
nfo("Initializing [Bot] " + section.getString("account"));
try {
Bot bot = new Bot(section);
var state = bot.network.login$mirai_core().of();
//bot.network.login$mirai_core().whenComplete((state, e) -> {
if (state == LoginState.SUCCESS) {
Bot.instances.add(bot);
getLogger().
g
reen(" Login Succeed");
getLogger().
logG
reen(" Login Succeed");
} else {
getLogger().
error(" Login Failed with e
rror " + state);
getLogger().
logError(" Login Failed with logE
rror " + state);
bot.close();
}
// }).of();
} catch (Throwable e) {
e.printStackTrace();
getLogger().
e
rror("Could not load QQ bots config!");
getLogger().
logE
rror("Could not load QQ bots config!");
System.exit(1);
}
});*/
...
...
@@ -173,9 +172,9 @@ object MiraiServer {
get
()
{
for
(
it
in
qqList
.
split
(
"\n"
).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
())
{
val
strings
=
it
.
split
(
"----"
).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
()
val
bot
=
Bot
(
BotAccount
(
strings
[
0
].
toLong
(),
strings
[
1
]),
Console
()
)
val
bot
=
Bot
(
BotAccount
(
strings
[
0
].
toLong
(),
strings
[
1
]),
MiraiLogger
)
if
(
runBlocking
{
bot
.
login
()
}
===
Login
State
.
SUCCESS
)
{
if
(
runBlocking
{
bot
.
login
()
}
===
Login
Result
.
SUCCESS
)
{
bot
.
green
(
"Login succeed"
)
return
bot
}
...
...
mirai-core/build.gradle
View file @
d89f8acc
...
...
@@ -4,22 +4,40 @@ apply plugin: "kotlin-multiplatform"
kotlin
{
targets
{
fromPreset
(
presets
.
jvm
,
"jvm"
)
//fromPreset(presets.mingwX64, "mingwX64")
}
jvm
()
/*
mingwX64("mingwX64") {
binaries {
executable {
// Change to specify fully qualified name of your application's entry point:
entryPoint = 'main'
// Specify command-line arguments, if necessary:
runTask?.args('')
}
}
}*/
sourceSets
{
commonMain
{
dependencies
{
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
implementation
rootProject
.
ext
.
kotlinCommon
implementation
rootProject
.
ext
.
reflect
implementation
rootProject
.
ext
.
coroutine
implementation
rootProject
.
ext
.
kotlinJvm
//implementation rootProject.ext.coroutine
implementation
rootProject
.
ext
.
coroutineCommon
implementation
rootProject
.
ext
.
coroutineIo
implementation
rootProject
.
ext
.
atomicFUCommon
implementation
rootProject
.
ext
.
kotlinxIOCommon
}
}
jvmMain
{
apply
plugin:
'java'
...
...
@@ -35,6 +53,23 @@ kotlin {
implementation
'org.ini4j:ini4j:0.5.2'
}
}
/*
mingwX64Main {
dependencies {
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
implementation rootProject.ext.kotlinCommon
implementation rootProject.ext.coroutine
implementation rootProject.ext.coroutineNative
implementation rootProject.ext.kotlinNative
implementation rootProject.ext.reflect
//implementation rootProject.ext.coroutine
implementation rootProject.ext.kotlinxIONative
}
}*/
jvmTest
{
}
...
...
@@ -47,16 +82,3 @@ kotlin {
compileKotlinJvm
{
}
\ No newline at end of file
/*
dependencies {
compile 'com.google.protobuf:protobuf-java:3.5.0'
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M2'
compile 'io.netty:netty-all:4.1.38.Final'
compile 'net.java.dev.jna:jna:5.4.0'
compile 'org.apache.logging.log4j:log4j-core:2.12.1'
compile 'org.yaml:snakeyaml:1.18'
compile 'org.jetbrains.kotlin:kotlin-reflect:1.3.41'
compile 'org.jsoup:jsoup:1.12.1'
compile 'org.ini4j:ini4j:0.5.2'
}*/
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/Bot.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/Bot.kt
View file @
d89f8acc
package
net.mamoe.mirai
import
kotlinx.atomicfu.atomic
import
kotlinx.coroutines.sync.Mutex
import
net.mamoe.mirai.Bot.ContactSystem
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.QQ
...
...
@@ -12,12 +14,12 @@ import net.mamoe.mirai.utils.MiraiLogger
/**
* Mirai 的机器人. 一个机器人实例登录一个 QQ 账号.
* Mirai 为多账号设计, 可同时维护多个机器人.
*
<br></br>
*
* [Bot] 由 3 个模块组成.
* [联系人管理][ContactSystem]: 可通过 [Bot.contacts] 访问
* [网络处理器][TIMBotNetworkHandler]: 可通过 [Bot.network] 访问
* [机器人账号信息][BotAccount]: 可通过 [Bot.account] 访问
*
<br></br>
*
* 若你需要得到机器人的 QQ 账号, 请访问 [Bot.account]
* 若你需要得到服务器上所有机器人列表, 请访问 [Bot.instances]
*
...
...
@@ -28,7 +30,7 @@ import net.mamoe.mirai.utils.MiraiLogger
* a [ContactSystem], which manage contacts such as [QQ] and [Group];
* a [TIMBotNetworkHandler], which manages the connection to the server;
* a [BotAccount], which stores the account information(e.g. qq number the bot)
*
<br></br>
*
* To of all the QQ contacts, access [Bot.account]
* To of all the Robot instance, access [Bot.instances]
*
...
...
@@ -42,7 +44,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
val
contacts
=
ContactSystem
()
val
network
:
BotNetworkHandler
=
TIMBotNetworkHandler
(
this
)
val
network
:
BotNetworkHandler
<
*
>
=
TIMBotNetworkHandler
(
this
)
init
{
instances
.
add
(
this
)
...
...
@@ -59,25 +61,22 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
*/
inner
class
ContactSystem
internal
constructor
()
{
val
groups
=
ContactList
<
Group
>()
private
val
groupsLock
=
Mutex
()
val
qqs
=
ContactList
<
QQ
>()
private
val
qqsLock
=
Mutex
()
fun
getQQ
(
qqNumber
:
Long
):
QQ
{
synchronized
(
this
.
qqs
)
{
if
(!
this
.
qqs
.
containsKey
(
qqNumber
))
{
this
.
qqs
[
qqNumber
]
=
QQ
(
this
@Bot
,
qqNumber
)
}
return
this
.
qqs
[
qqNumber
]
!!
}
}
/**
* 通过群号码获取群对象.
* 注意: 在并发调用时, 这个方法并不是原子的.
*/
fun
getQQ
(
qqNumber
:
Long
):
QQ
=
qqs
.
getOrPut
(
qqNumber
)
{
QQ
(
this
@Bot
,
qqNumber
)
}
/**
* 通过群号码获取群对象.
* 注意: 在并发调用时, 这个方法并不是原子的.
*/
fun
getGroupByNumber
(
groupNumber
:
Long
):
Group
=
groups
.
getOrPut
(
groupNumber
)
{
Group
(
this
@Bot
,
groupNumber
)
}
fun
getGroupByNumber
(
groupNumber
:
Long
):
Group
{
synchronized
(
this
.
groups
)
{
if
(!
this
.
groups
.
containsKey
(
groupNumber
))
{
this
.
groups
[
groupNumber
]
=
Group
(
this
@Bot
,
groupNumber
)
}
return
this
.
groups
[
groupNumber
]
!!
}
}
fun
getGroupById
(
groupId
:
Long
):
Group
{
return
getGroupByNumber
(
Group
.
groupIdToNumber
(
groupId
))
...
...
@@ -86,7 +85,6 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
fun
close
()
{
this
.
network
.
close
()
this
.
contacts
.
groups
.
values
.
forEach
{
it
.
close
()
}
this
.
contacts
.
groups
.
clear
()
this
.
contacts
.
qqs
.
clear
()
}
...
...
@@ -94,8 +92,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
companion
object
{
val
instances
:
MutableList
<
Bot
>
=
mutableListOf
()
private
var
id
=
0
private
val
idLock
=
Any
()
fun
nextId
():
Int
=
synchronized
(
idLock
)
{
id
++
}
private
val
id
=
atomic
(
0
)
fun
nextId
():
Int
=
id
.
addAndGet
(
1
)
}
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/BotHelper.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/BotHelper.kt
View file @
d89f8acc
@
file
:
Suppress
(
"unused"
)
@
file
:
Suppress
(
"unused"
,
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai
import
kotlinx.io.core.readBytes
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.network.protocol.tim.packet.goto
import
net.mamoe.mirai.network.protocol.tim.packet.login.LoginState
import
net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import
net.mamoe.mirai.utils.ContactList
import
net.mamoe.mirai.utils.Log
gerTextFormat
import
net.mamoe.mirai.utils.Log
inConfiguration
import
net.mamoe.mirai.utils.toUHexString
import
java.text.SimpleDateFormat
import
java.util.*
/**
* The mirror of functions in inner classes of [Bot]
...
...
@@ -23,7 +21,10 @@ import java.util.*
//Contacts
fun
Bot
.
getQQ
(
number
:
Long
):
QQ
=
this
.
contacts
.
getQQ
(
number
)
fun
Bot
.
getQQ
(
number
:
UInt
):
QQ
=
getQQ
(
number
.
toLong
())
fun
Bot
.
getGroupByNumber
(
number
:
Long
):
Group
=
this
.
contacts
.
getGroupByNumber
(
number
)
fun
Bot
.
getGroupByNumber
(
number
:
UInt
):
Group
=
getGroupByNumber
(
number
.
toLong
())
fun
Bot
.
getGroupById
(
number
:
Long
):
Group
=
this
.
contacts
.
getGroupById
(
number
)
...
...
@@ -33,9 +34,11 @@ val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
//NetworkHandler
suspend
fun
Bot
.
sendPacket
(
packet
:
ClientPacket
)
=
this
.
network
.
socket
.
sendPacket
(
packet
)
suspend
fun
Bot
.
sendPacket
(
packet
:
ClientPacket
)
=
this
.
network
.
sendPacket
(
packet
)
suspend
fun
Bot
.
login
(
configuration
:
LoginConfiguration
.()
->
Unit
):
LoginResult
=
this
.
network
.
login
(
LoginConfiguration
().
apply
(
configuration
))
suspend
fun
Bot
.
login
():
Login
State
=
this
.
network
.
login
(
)
suspend
fun
Bot
.
login
():
Login
Result
=
this
.
network
.
login
(
LoginConfiguration
.
Default
)
//BotAccount
val
Bot
.
qqNumber
:
Long
get
()
=
this
.
account
.
qqNumber
...
...
@@ -45,31 +48,19 @@ val Bot.qqNumber: Long get() = this.account.qqNumber
fun
Bot
.
log
(
o
:
Any
?)
=
info
(
o
)
fun
Bot
.
println
(
o
:
Any
?)
=
info
(
o
)
fun
Bot
.
info
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
RESET
)
fun
Bot
.
info
(
o
:
Any
?)
=
this
.
logger
.
logInfo
(
o
)
fun
Bot
.
error
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
RED
)
fun
Bot
.
error
(
o
:
Any
?)
=
this
.
logger
.
logError
(
o
)
fun
Bot
.
notice
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
LIGHT_BLUE
)
fun
Bot
.
purple
(
o
:
Any
?)
=
this
.
logger
.
logPurple
(
o
)
fun
Bot
.
purple
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
PURPLE
)
fun
Bot
.
cyan
(
o
:
Any
?)
=
this
.
logger
.
logCyan
(
o
)
fun
Bot
.
green
(
o
:
Any
?)
=
this
.
logger
.
logGreen
(
o
)
fun
Bot
.
cyan
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
LIGHT_CYAN
)
fun
Bot
.
green
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
GREEN
)
fun
Bot
.
debug
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
YELLOW
)
fun
Bot
.
debug
(
o
:
Any
?)
=
this
.
logger
.
logDebug
(
o
)
fun
Bot
.
printPacketDebugging
(
packet
:
ServerPacket
)
{
debug
(
"Packet=$packet"
)
debug
(
"Packet size="
+
packet
.
input
.
goto
(
0
).
readAllBytes
().
size
)
debug
(
"Packet data="
+
packet
.
input
.
goto
(
0
).
readAllBytes
().
toUHexString
())
}
private
fun
print
(
bot
:
Bot
,
value
:
String
?,
color
:
LoggerTextFormat
=
LoggerTextFormat
.
WHITE
)
{
val
s
=
SimpleDateFormat
(
"MM-dd HH:mm:ss"
).
format
(
Date
())
println
(
"$color[Mirai] $s #R${bot.id}: $value"
)
}
private
fun
print
(
value
:
String
?,
color
:
LoggerTextFormat
=
LoggerTextFormat
.
WHITE
)
{
val
s
=
SimpleDateFormat
(
"MM-dd HH:mm:ss"
).
format
(
Date
())
println
(
"$color[Mirai] $s : $value"
)
debug
(
"Packet size="
+
packet
.
input
.
readBytes
().
size
)
debug
(
"Packet data="
+
packet
.
input
.
readBytes
().
toUHexString
())
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/Mirai.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/Mirai.kt
View file @
d89f8acc
package
net.mamoe.mirai
//expect fun s(): String
/**
* @author Him188moe
*/
object
Mirai
{
const
val
VERSION
:
String
=
"1.0.0"
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/contact/Contact.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/contact/Contact.kt
View file @
d89f8acc
File moved
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/contact/Group.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/contact/Group.kt
View file @
d89f8acc
...
...
@@ -4,7 +4,7 @@ import net.mamoe.mirai.Bot
import
net.mamoe.mirai.contact.Group.Companion.groupNumberToId
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.utils.ContactList
import
java.io.Closeable
import
kotlin.jvm.JvmStatic
/**
* 群.
...
...
@@ -21,22 +21,20 @@ import java.io.Closeable
* Java 调用 [groupNumberToId] : `Group.groupNumberToId(number)`
* @author Him188moe
*/
class
Group
(
bot
:
Bot
,
number
:
Long
)
:
Contact
(
bot
,
number
)
,
Closeable
{
class
Group
(
bot
:
Bot
,
number
:
Long
)
:
Contact
(
bot
,
number
)
{
val
groupId
=
groupNumberToId
(
number
)
val
members
=
ContactList
<
QQ
>()
//todo members
val
members
:
ContactList
<
QQ
>
//todo members
get
()
=
throw
UnsupportedOperationException
(
"Not yet supported"
)
override
suspend
fun
sendMessage
(
message
:
MessageChain
)
{
bot
.
network
.
message
.
sendGroupMessage
(
this
,
message
)
bot
.
network
.
event
.
sendGroupMessage
(
this
,
message
)
}
override
suspend
fun
sendXMLMessage
(
message
:
String
)
{
}
override
fun
close
()
{
this
.
members
.
clear
()
}
companion
object
{
@JvmStatic
fun
groupNumberToId
(
number
:
Long
):
Long
{
//求你别出错
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/contact/QQ.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/contact/QQ.kt
View file @
d89f8acc
...
...
@@ -12,34 +12,26 @@ import net.mamoe.mirai.message.MessageChain
* Java 获取 qq 号: `qq.getNumber()`
* Java 获取所属 bot: `qq.getBot()`
*
* A QQ instance helps you to receive
message from or sendPacket message
to.
* A QQ instance helps you to receive
event from or sendPacket event
to.
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
*
* @author Him188moe
*/
class
QQ
(
bot
:
Bot
,
number
:
Long
)
:
Contact
(
bot
,
number
)
{
override
suspend
fun
sendMessage
(
message
:
MessageChain
)
{
bot
.
network
.
message
.
sendFriendMessage
(
this
,
message
)
bot
.
network
.
event
.
sendFriendMessage
(
this
,
message
)
}
override
suspend
fun
sendXMLMessage
(
message
:
String
)
{
}
}
/**
/**
* At(@) this account.
*
* @return an instance of [Message].
*/
fun
at
():
At
{
fun
QQ
.
at
():
At
{
return
At
(
this
)
}
/*
Make that we can use (QQ + QQ2 + QQ3).sendMessage( )
operator fun plus(qq: QQ): QQCombination {
}*/
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/event/Event.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/event/Event.kt
View file @
d89f8acc
...
...
@@ -2,10 +2,16 @@
package
net.mamoe.mirai.event
import
kotlinx.coroutines.CoroutineExceptionHandler
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.event.internal.broadcastInternal
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.utils.log
import
kotlin.coroutines.CoroutineContext
import
kotlin.jvm.JvmOverloads
/**
* 所有事件的基类.
...
...
@@ -13,7 +19,6 @@ import net.mamoe.mirai.event.internal.broadcastInternal
*
* @see [broadcast] 广播事件
* @see [subscribe] 监听事件
* @author Him188moe
*/
abstract
class
Event
{
...
...
@@ -31,7 +36,6 @@ abstract class Event {
*
* @throws UnsupportedOperationException 如果事件没有实现 [Cancellable]
*/
@Throws
(
UnsupportedOperationException
::
class
)
fun
cancel
()
{
cancelled
=
true
}
...
...
@@ -39,8 +43,6 @@ abstract class Event {
/**
* 实现这个接口的事件可以被取消.
*
* @author Him188moe
*/
interface
Cancellable
{
val
cancelled
:
Boolean
...
...
@@ -51,8 +53,23 @@ interface Cancellable {
/**
* 广播一个事件的唯一途径
*/
@Synchronized
@Suppress
(
"UNCHECKED_CAST"
)
suspend
fun
<
E
:
Event
>
E
.
broadcast
():
E
=
withContext
(
EventScope
.
coroutineContext
)
{
this
@
broadcast
.
broadcastInternal
()
}
@JvmOverloads
suspend
fun
<
E
:
Event
>
E
.
broadcast
(
context
:
CoroutineContext
?
=
null
):
E
{
var
ctx
=
EventScope
.
coroutineContext
if
(
context
!=
null
)
{
if
(
context
[
CoroutineExceptionHandler
]
==
null
)
ctx
+=
CoroutineExceptionHandler
{
_
,
e
->
e
.
log
()
}
ctx
+=
context
}
return
withContext
(
ctx
)
{
this
@
broadcast
.
broadcastInternal
()
}
}
/**
* 事件协程作用域.
* 所有的事件 [broadcast] 过程均在此作用域下运行.
*
* 然而, 若在事件处理过程中使用到 [Contact.sendMessage] 等会 [发送数据包][BotNetworkHandler.sendPacket] 的方法,
* 发送过程将会通过 [withContext] 将协程切换到 [BotNetworkHandler.NetworkScope]
*/
object
EventScope
:
CoroutineScope
by
CoroutineScope
(
Dispatchers
.
Default
)
//todo may change
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/event/Subscribers.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/event/Subscribers.kt
View file @
d89f8acc
...
...
@@ -5,6 +5,7 @@ package net.mamoe.mirai.event
import
net.mamoe.mirai.event.internal.Handler
import
net.mamoe.mirai.event.internal.listeners
import
net.mamoe.mirai.event.internal.subscribeInternal
import
kotlin.jvm.Synchronized
import
kotlin.reflect.KClass
enum
class
ListeningStatus
{
...
...
@@ -76,8 +77,8 @@ inline fun <reified E : Event> subscribeAll(noinline listeners: ListenerBuilder<
* }
*
* untilFalse {
* it.reply("你发送了 ${it.
message
}")
* it.
message
eq "停止"
* it.reply("你发送了 ${it.
event
}")
* it.
event
eq "停止"
* }
* }
* ```
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/event/events/BotEvents.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/event/events/BotEvents.kt
View file @
d89f8acc
...
...
@@ -3,9 +3,7 @@ package net.mamoe.mirai.event.events
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.event.Event
/**
* @author Him188moe
*/
abstract
class
BotEvent
(
val
bot
:
Bot
)
:
Event
()
class
BotLoginSucceedEvent
(
bot
:
Bot
)
:
BotEvent
(
bot
)
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/event/events/FriendEvents.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/event/events/FriendEvents.kt
View file @
d89f8acc
...
...
@@ -5,9 +5,7 @@ import net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.MessageChain
/**
* @author Him188moe
*/
abstract
class
FriendEvent
(
bot
:
Bot
,
val
sender
:
QQ
)
:
BotEvent
(
bot
)
/**
...
...
@@ -16,15 +14,11 @@ abstract class FriendEvent(bot: Bot, val sender: QQ) : BotEvent(bot)
* @author Him188moe
*/
class
FriendMessageEvent
(
bot
:
Bot
,
sender
:
QQ
,
val
message
:
MessageChain
)
:
FriendEvent
(
bot
,
sender
)
{
@JvmSynthetic
suspend
inline
fun
reply
(
message
:
Message
)
=
sender
.
sendMessage
(
message
)
@JvmSynthetic
suspend
inline
fun
reply
(
message
:
String
)
=
sender
.
sendMessage
(
message
)
@JvmSynthetic
suspend
inline
fun
reply
(
message
:
List
<
Message
>)
=
sender
.
sendMessage
(
message
)
@JvmSynthetic
suspend
inline
fun
reply
(
message
:
MessageChain
)
=
sender
.
sendMessage
(
message
)
//shortcut
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/event/events/GroupEvents.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/event/events/GroupEvents.kt
View file @
d89f8acc
...
...
@@ -6,24 +6,16 @@ import net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.MessageChain
/**
* @author Him188moe
*/
abstract
class
GroupEvent
(
bot
:
Bot
,
val
group
:
Group
)
:
BotEvent
(
bot
)
/**
* @author Him188moe
*/
class
GroupMessageEvent
(
bot
:
Bot
,
group
:
Group
,
val
sender
:
QQ
,
val
message
:
MessageChain
)
:
GroupEvent
(
bot
,
group
)
{
@JvmSynthetic
suspend
inline
fun
reply
(
message
:
Message
)
=
group
.
sendMessage
(
message
)
@JvmSynthetic
suspend
inline
fun
reply
(
message
:
String
)
=
group
.
sendMessage
(
message
)
@JvmSynthetic
suspend
inline
fun
reply
(
message
:
List
<
Message
>)
=
group
.
sendMessage
(
message
)
@JvmSynthetic
suspend
inline
fun
reply
(
message
:
MessageChain
)
=
group
.
sendMessage
(
message
)
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/event/events/PacketEvents.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/event/events/PacketEvents.kt
View file @
d89f8acc
...
...
@@ -8,9 +8,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
/* Abstract */
/**
* @author Him188moe
*/
sealed
class
PacketEvent
<
out
P
:
Packet
>(
bot
:
Bot
,
open
val
packet
:
P
)
:
BotEvent
(
bot
)
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/event/internal/InternalEventListeners.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/event/internal/InternalEventListeners.kt
View file @
d89f8acc
package
net.mamoe.mirai.event.internal
import
kotlinx.coroutines.
delay
import
net.mamoe.mirai.Bot
import
kotlinx.coroutines.
sync.Mutex
import
kotlinx.coroutines.sync.withLock
import
net.mamoe.mirai.event.Event
import
net.mamoe.mirai.event.ListeningStatus
import
net.mamoe.mirai.event.broadcast
import
net.mamoe.mirai.event.events.ServerPacketReceivedEvent
import
net.mamoe.mirai.event.subscribeAlways
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.network.protocol.tim.packet.dataInputStream
import
net.mamoe.mirai.utils.BotAccount
import
net.mamoe.mirai.utils.Console
import
kotlin.reflect.KClass
import
kotlin.reflect.full.allSuperclasses
import
kotlin.reflect.full.isSuperclassOf
/**
* 监听和广播实现
...
...
@@ -42,14 +33,16 @@ class Handler<E : Event>(val handler: suspend (E) -> ListeningStatus) : Listener
internal
val
<
E
:
Event
>
KClass
<
E
>.
listeners
:
EventListeners
<
E
>
get
()
=
EventListenerManger
.
get
(
this
)
internal
class
EventListeners
<
E
:
Event
>
:
MutableList
<
Listener
<
E
>>
by
mutableListOf
()
internal
class
EventListeners
<
E
:
Event
>
:
MutableList
<
Listener
<
E
>>
by
mutableListOf
()
{
val
lock
=
Mutex
()
}
internal
object
EventListenerManger
{
private
val
registries
:
MutableMap
<
KClass
<
out
Event
>,
EventListeners
<
out
Event
>>
=
mutableMapOf
()
@Suppress
(
"UNCHECKED_CAST"
)
internal
fun
<
E
:
Event
>
get
(
clazz
:
KClass
<
E
>):
EventListeners
<
E
>
{
synchronized
(
clazz
)
{
//
synchronized(clazz) {
if
(
registries
.
containsKey
(
clazz
))
{
return
registries
[
clazz
]
as
EventListeners
<
E
>
}
else
{
...
...
@@ -58,13 +51,13 @@ internal object EventListenerManger {
return
it
}
}
}
//
}
}
}
@Suppress
(
"UNCHECKED_CAST"
)
internal
suspend
fun
<
E
:
Event
>
E
.
broadcastInternal
():
E
{
suspend
fun
callListeners
(
listeners
:
EventListeners
<
in
E
>)
{
suspend
fun
callListeners
(
listeners
:
EventListeners
<
in
E
>)
=
listeners
.
lock
.
withLock
{
val
iterator
=
listeners
.
iterator
()
while
(
iterator
.
hasNext
())
{
if
(
iterator
.
next
().
onEvent
(
this
)
==
ListeningStatus
.
STOPPED
)
{
...
...
@@ -74,24 +67,9 @@ internal suspend fun <E : Event> E.broadcastInternal(): E {
}
callListeners
(
this
::
class
.
listeners
as
EventListeners
<
in
E
>)
this
::
class
.
allSuperclasses
.
forEach
{
//println("super: " + it.simpleName)
if
(
Event
::
class
.
isSuperclassOf
(
it
))
{
callListeners
((
it
as
KClass
<
out
Event
>).
listeners
as
EventListeners
<
in
E
>)
}
}
loopAllListeners
(
this
::
class
)
{
callListeners
(
it
as
EventListeners
<
in
E
>)
}
return
this
}
suspend
fun
main
()
{
ServerPacketReceivedEvent
::
class
.
subscribeAlways
{
println
(
"got it"
)
}
println
(
ServerPacketReceivedEvent
::
class
.
listeners
.
size
)
ServerPacketReceivedEvent
(
Bot
(
BotAccount
(
1
,
""
),
Console
()),
object
:
ServerPacket
(
byteArrayOf
().
dataInputStream
())
{}).
broadcast
()
delay
(
1000
)
}
\ No newline at end of file
internal
expect
inline
fun
<
E
:
Event
>
loopAllListeners
(
clazz
:
KClass
<
E
>,
consumer
:
(
EventListeners
<
in
E
>)
->
Unit
)
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FaceID.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.message
/**
* @author LamGC
*/
@Suppress
(
"EnumEntryName"
,
"unused"
,
"SpellCheckingInspection"
)
enum
class
FaceID
constructor
(
val
id
:
UByte
)
{
unknown
(
0
xffu
),
// TODO: 2019/9/1 添加更多表情
jingya
(
0
u
),
piezui
(
1
u
),
se
(
2
u
),
fadai
(
3
u
),
deyi
(
4
u
),
liulei
(
5
u
),
haixiu
(
6
u
),
bizui
(
7
u
),
shui
(
8
u
),
daku
(
9
u
),
ganga
(
10
u
),
fanu
(
11
u
),
tiaopi
(
12
u
),
ciya
(
13
u
),
weixiao
(
14
u
),
nanguo
(
15
u
),
ku
(
16
u
),
zhuakuang
(
18
u
),
tu
(
19
u
),
touxiao
(
20
u
),
keai
(
21
u
),
baiyan
(
22
u
),
aoman
(
23
u
),
ji_e
(
24
u
),
kun
(
25
u
),
jingkong
(
26
u
),
liuhan
(
27
u
),
hanxiao
(
28
u
),
dabing
(
29
u
),
fendou
(
30
u
),
zhouma
(
31
u
),
yiwen
(
32
u
),
yun
(
34
u
),
zhemo
(
35
u
),
shuai
(
36
u
),
kulou
(
37
u
),
qiaoda
(
38
u
),
zaijian
(
39
u
),
fadou
(
41
u
),
aiqing
(
42
u
),
tiaotiao
(
43
u
),
zhutou
(
46
u
),
yongbao
(
49
u
),
dan_gao
(
53
u
),
shandian
(
54
u
),
zhadan
(
55
u
),
dao
(
56
u
),
zuqiu
(
57
u
),
bianbian
(
59
u
),
kafei
(
60
u
),
fan
(
61
u
),
meigui
(
63
u
),
diaoxie
(
64
u
),
aixin
(
66
u
),
xinsui
(
67
u
),
liwu
(
69
u
),
taiyang
(
74
u
),
yueliang
(
75
u
),
qiang
(
76
u
),
ruo
(
77
u
),
woshou
(
78
u
),
shengli
(
79
u
),
feiwen
(
85
u
),
naohuo
(
86
u
),
xigua
(
89
u
),
lenghan
(
96
u
),
cahan
(
97
u
),
koubi
(
98
u
),
guzhang
(
99
u
),
qiudale
(
100
u
),
huaixiao
(
101
u
),
zuohengheng
(
102
u
),
youhengheng
(
103
u
),
haqian
(
104
u
),
bishi
(
105
u
),
weiqu
(
106
u
),
kuaikule
(
107
u
),
yinxian
(
108
u
),
qinqin
(
109
u
),
xia
(
110
u
),
kelian
(
111
u
),
caidao
(
112
u
),
pijiu
(
113
u
),
lanqiu
(
114
u
),
pingpang
(
115
u
),
shiai
(
116
u
),
piaochong
(
117
u
),
baoquan
(
118
u
),
gouyin
(
119
u
),
quantou
(
120
u
),
chajin
(
121
u
),
aini
(
122
u
),
bu
(
123
u
),
hao
(
124
u
),
zhuanquan
(
125
u
),
ketou
(
126
u
),
huitou
(
127
u
),
tiaosheng
(
128
u
),
huishou
(
129
u
),
jidong
(
130
u
),
jiewu
(
131
u
),
xianwen
(
132
u
),
zuotaiji
(
133
u
),
youtaiji
(
134
u
),
shuangxi
(
136
u
),
bianpao
(
137
u
),
denglong
(
138
u
),
facai
(
139
u
),
K_ge
(
140
u
),
gouwu
(
141
u
),
youjian
(
142
u
),
shuai_qi
(
143
u
),
hecai
(
144
u
),
qidao
(
145
u
),
baojin
(
146
u
),
bangbangtang
(
147
u
),
he_nai
(
148
u
),
xiamian
(
149
u
),
xiangjiao
(
150
u
),
feiji
(
151
u
),
kaiche
(
152
u
),
gaotiezuochetou
(
153
u
),
chexiang
(
154
u
),
gaotieyouchetou
(
155
u
),
duoyun
(
156
u
),
xiayu
(
157
u
),
chaopiao
(
158
u
),
xiongmao
(
159
u
),
dengpao
(
160
u
),
fengche
(
161
u
),
naozhong
(
162
u
),
dasan
(
163
u
),
caiqiu
(
164
u
),
zuanjie
(
165
u
),
shafa
(
166
u
),
zhijin
(
167
u
),
yao
(
168
u
),
shouqiang
(
169
u
),
qingwa
(
170
u
);
override
fun
toString
():
String
{
return
"$name($id)"
}
companion
object
{
fun
ofId
(
id
:
UByte
):
FaceID
{
for
(
value
in
values
())
{
if
(
value
.
id
==
id
)
{
return
value
}
}
return
unknown
}
}
}
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/message/Message.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/message/Message.kt
View file @
d89f8acc
...
...
@@ -13,12 +13,12 @@ import net.mamoe.mirai.contact.QQ
* 这与使用 [String] 的使用非常类似.
*
* 比较 [Message] 与 [String] (使用 infix [Message.eq]):
* `if(
message eq "你好") qq.sendMessage(message
)`
* `if(
event eq "你好") qq.sendMessage(event
)`
*
* 连接 [Message] 与 [Message], [String], (使用 operator [Message.plus]):
* ```
*
message
= PlainText("Hello ")
* qq.sendMessage(
message
+ "world")
*
event
= PlainText("Hello ")
* qq.sendMessage(
event
+ "world")
* ```
*
* 但注意: 不能 `String + Message`. 只能 `Message + String`
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/message/MessageType.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/message/MessageType.kt
View file @
d89f8acc
...
...
@@ -2,11 +2,9 @@
package
net.mamoe.mirai.message
/**
* @author Him188moe
*/
@Suppress
(
"unused"
)
enum
class
MessageType
(
private
val
value
:
UByte
)
{
enum
class
MessageType
(
val
value
:
UByte
)
{
PLAIN_TEXT
(
0
x03u
),
AT
(
0
x06u
),
FACE
(
0
x02u
),
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/message/Messages.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/message/Messages.kt
View file @
d89f8acc
...
...
@@ -10,7 +10,3 @@ fun String.toMessage(): PlainText = PlainText(this)
* 用 `this` 构造 [MessageChain]
*/
fun
Message
.
toChain
():
MessageChain
=
if
(
this
is
MessageChain
)
this
else
MessageChain
(
this
)
\ No newline at end of file
fun
MessageChain
.
containsType
(
clazz
:
Class
<
out
Message
>):
Boolean
=
list
.
any
{
clazz
.
isInstance
(
it
)
}
operator
fun
MessageChain
.
contains
(
sub
:
Class
<
out
Message
>):
Boolean
=
containsType
(
sub
)
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/mirai/message/internal/MessageData
.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal
.kt
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.message.internal
import
kotlinx.io.core.*
import
net.mamoe.mirai.message.*
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.utils.*
import
java.io.DataInputStream
internal
fun
ByteArray
.
parseMessageFace
():
Face
=
dataDecode
(
this
)
{
internal
fun
ByteArray
.
parseMessageFace
():
Face
=
read
{
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0
//00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D
it
.
skip
(
1
)
discardExact
(
1
)
val
id1
=
FaceID
.
ofId
(
it
.
readLVNumber
().
toInt
())
//可能这个是id, 也可能下面那个
it
.
skip
(
it
.
readByte
().
toLong
())
it
.
readLVNumber
()
//某id?
return
@
dataDecode
Face
(
id1
)
val
id1
=
FaceID
.
ofId
(
readLVNumber
().
toInt
().
toUByte
())
//可能这个是id, 也可能下面那个
discardExact
(
readByte
().
toLong
())
readLVNumber
()
//某id?
return
@
read
Face
(
id1
)
}
internal
fun
ByteArray
.
parsePlainText
():
PlainText
=
dataDecode
(
this
)
{
it
.
skip
(
1
)
PlainText
(
it
.
readLVString
())
internal
fun
ByteArray
.
parsePlainText
():
PlainText
=
read
{
discardExact
(
1
)
PlainText
(
readLVString
())
}
internal
fun
ByteArray
.
parseMessageImage0x06
():
Image
=
dataDecode
(
this
)
{
it
.
skip
(
1
)
MiraiLogger
.
d
ebug
(
"好友的图片"
)
MiraiLogger
.
debug
(
this
.
toUHexString
())
val
filenameLength
=
it
.
readShort
()
val
suffix
=
it
.
readString
(
filenameLength
).
substringAfter
(
"."
)
it
.
skip
(
this
.
size
-
37
-
1
-
filenameLength
-
2
)
val
imageId
=
String
(
it
.
readNBytes
(
36
)
)
MiraiLogger
.
d
ebug
(
imageId
)
it
.
skip
(
1
)
//0x41
return
@
dataDecode
Image
(
"{$imageId}.$suffix"
)
internal
fun
ByteArray
.
parseMessageImage0x06
():
Image
=
read
{
discardExact
(
1
)
MiraiLogger
.
logD
ebug
(
"好友的图片"
)
MiraiLogger
.
logDebug
(
this
@
parseMessageImage0x06
.
toUHexString
())
val
filenameLength
=
readShort
()
val
suffix
=
readString
(
filenameLength
).
substringAfter
(
"."
)
discardExact
(
this
@
parseMessageImage0x06
.
size
-
37
-
1
-
filenameLength
-
2
)
val
imageId
=
readString
(
36
)
MiraiLogger
.
logD
ebug
(
imageId
)
discardExact
(
1
)
//0x41
return
@
read
Image
(
"{$imageId}.$suffix"
)
}
internal
fun
ByteArray
.
parseMessageImage0x03
():
Image
=
dataDecode
(
this
)
{
it
.
skip
(
1
)
return
@
dataDecode
Image
(
String
(
it
.
readLVByteArray
()))
internal
fun
ByteArray
.
parseMessageImage0x03
():
Image
=
read
{
discardExact
(
1
)
return
@
read
Image
(
String
(
readLVByteArray
()))
/*
println(String(
it.
readLVByteArray()))
it.
readTLVMap()
return
@dataDecode Image(String(it.
readLVByteArray().cutTail(5).getRight(42)))
println(String(readLVByteArray()))
readTLVMap()
return
Image(String(
readLVByteArray().cutTail(5).getRight(42)))
/
it.skip
(data.size - 47)
val imageId = String(
it.readN
Bytes(42))
it.skip
(1)//0x41
it.skip
(1)//0x42
it.skip
(1)//0x43
it.skip
(1)//0x41
return
@dataDecode
Image(imageId)*/
discardExact
(data.size - 47)
val imageId = String(
read
Bytes(42))
discardExact
(1)//0x41
discardExact
(1)//0x42
discardExact
(1)//0x43
discardExact
(1)//0x41
return Image(imageId)*/
}
internal
fun
ByteArray
.
parseMessageChain
():
MessageChain
=
dataDecode
(
this
)
{
it
.
readMessageChain
()
internal
fun
ByteArray
.
parseMessageChain
():
MessageChain
=
read
{
readMessageChain
()
}
internal
fun
DataInputStream
.
readMessage
():
Message
?
{
internal
fun
ByteReadPacket
.
readMessage
():
Message
?
{
val
messageType
=
this
.
readByte
().
toInt
()
val
sectionLength
=
this
.
readShort
().
toLong
()
//sectionLength: short
val
sectionData
=
this
.
read
NBytes
(
sectionLength
)
val
sectionData
=
this
.
read
Bytes
(
sectionLength
.
toInt
())
//use buffer instead
return
when
(
messageType
)
{
0
x01
->
sectionData
.
parsePlainText
()
0
x02
->
sectionData
.
parseMessageFace
()
...
...
@@ -81,7 +82,7 @@ internal fun DataInputStream.readMessage(): Message? {
println
(
value
.
size
)
println
(
value
.
toUHexString
())
//todo 未知压缩算法
this
.
skip
(
7
)
//几个TLV
this
.
discardExact
(
7
)
//几个TLV
return
PlainText
(
String
(
value
))
}
...
...
@@ -92,21 +93,21 @@ internal fun DataInputStream.readMessage(): Message? {
else
->
{
println
(
"未知的messageType=0x${messageType.toByte().toUHexString()}"
)
println
(
"后文=${this.read
All
Bytes().toUHexString()}"
)
println
(
"后文=${this.readBytes().toUHexString()}"
)
null
}
}
}
fun
DataInputStream
.
readMessageChain
():
MessageChain
{
fun
ByteReadPacket
.
readMessageChain
():
MessageChain
{
val
chain
=
MessageChain
()
var
got
:
Message
?
=
null
do
{
if
(
got
!=
null
)
{
chain
.
concat
(
got
)
}
if
(
this
.
available
()
==
0
)
{
if
(
this
.
remaining
==
0L
)
{
return
chain
}
got
=
this
.
readMessage
()
...
...
@@ -114,34 +115,34 @@ fun DataInputStream.readMessageChain(): MessageChain {
return
chain
}
fun
MessageChain
.
to
ByteArray
():
ByteArray
=
dataEncode
{
result
->
this
@
to
ByteArray
.
list
.
forEach
{
message
->
result
.
write
(
with
(
message
)
{
fun
MessageChain
.
to
Packet
():
ByteReadPacket
=
buildPacket
{
this
@
to
Packet
.
list
.
forEach
{
message
->
writePacket
(
with
(
message
)
{
when
(
this
)
{
is
Face
->
dataEncode
{
section
->
section
.
writeByte
(
MessageType
.
FACE
.
intV
alue
)
is
Face
->
buildPacket
{
writeUByte
(
MessageType
.
FACE
.
v
alue
)
section
.
writeLVByteArray
(
dataEncode
{
child
->
child
.
writeShort
(
1
)
child
.
writeByte
(
this
.
id
.
id
)
writeLVPacket
{
writeShort
(
1
)
writeUByte
(
id
.
id
)
child
.
writeHex
(
"0B 00 08 00 01 00 04 52 CC F5 D0 FF"
)
writeHex
(
"0B 00 08 00 01 00 04 52 CC F5 D0 FF"
)
child
.
writeShort
(
2
)
child
.
writeByte
(
0
x14
)
//??
child
.
writeByte
(
this
.
id
.
id
+
65
)
}
)
writeShort
(
2
)
writeByte
(
0
x14
)
//??
writeUByte
((
id
.
id
+
65
u
).
toUByte
()
)
}
}
is
At
->
throw
UnsupportedOperationException
(
"At is not supported now but is expecting to be supported"
)
is
Image
->
dataEncode
{
section
->
section
.
writeByte
(
MessageType
.
IMAGE
.
intV
alue
)
is
Image
->
buildPacket
{
writeUByte
(
MessageType
.
IMAGE
.
v
alue
)
section
.
writeLVByteArray
(
dataEncode
{
child
->
child
.
writeByte
(
0
x02
)
child
.
writeLVString
(
this
.
imageId
)
child
.
writeHex
(
"04 00 "
+
writeLVPacket
{
writeByte
(
0
x02
)
writeLVString
(
imageId
)
writeHex
(
"04 00 "
+
"04 9B 53 B0 08 "
+
"05 00 "
+
"04 D9 8A 5A 70 "
+
...
...
@@ -149,19 +150,19 @@ fun MessageChain.toByteArray(): ByteArray = dataEncode { result ->
"04 00 00 00 50 "
+
"07 00 "
+
"01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 11 00 00 00 15 00 04 00 00 02 BC 16 00 04 00 00 02 BC 18 00 04 00 00 7D 5E FF 00 5C 15 36 20 39 32 6B 41 31 43 39 62 35 33 62 30 30 38 64 39 38 61 35 61 37 30 20"
)
child
.
writeHex
(
"20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20"
)
child
.
writeBytes
(
this
.
imageId
)
child
.
writeByte
(
0
x41
)
}
)
writeHex
(
"20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20"
)
writeStringUtf8
(
imageId
)
writeByte
(
0
x41
)
}
}
is
PlainText
->
dataEncode
{
section
->
section
.
writeByte
(
MessageType
.
PLAIN_TEXT
.
intV
alue
)
is
PlainText
->
buildPacket
{
writeUByte
(
MessageType
.
PLAIN_TEXT
.
v
alue
)
section
.
writeLVByteArray
(
dataEncode
{
child
->
child
.
writeByte
(
0
x01
)
child
.
writeLVString
(
this
.
stringValue
)
}
)
writeLVPacket
{
writeByte
(
0
x01
)
writeLVString
(
stringValue
)
}
}
else
->
throw
UnsupportedOperationException
(
"${this::class.simpleName} is not supported"
)
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/BotNetworkHandler.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/BotNetworkHandler.kt
View file @
d89f8acc
package
net.mamoe.mirai.network
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.cancel
import
kotlinx.io.core.Closeable
import
net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocket
import
net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.LoginHandler
import
net.mamoe.mirai.network.protocol.tim.handler.ActionPacketHandler
import
net.mamoe.mirai.network.protocol.tim.handler.DataPacketSocket
import
net.mamoe.mirai.network.protocol.tim.handler.MessagePacketHandler
import
net.mamoe.mirai.network.protocol.tim.handler.TemporaryPacketHandler
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.Packet
import
net.mamoe.mirai.network.protocol.tim.packet.ServerEventPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.network.protocol.tim.packet.login.LoginState
import
net.mamoe.mirai.network.protocol.tim.handler.*
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRefreshmentRequestPacket
import
net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import
net.mamoe.mirai.utils.LoginConfiguration
import
net.mamoe.mirai.utils.MiraiDatagramChannel
/**
* Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务.
...
...
@@ -20,42 +20,49 @@ import net.mamoe.mirai.network.protocol.tim.packet.login.LoginState
* - [BotSocket]: 处理数据包底层的发送([ByteArray])
* - [PacketHandler]: 制作 [ClientPacket] 并传递给 [BotSocket] 发送; 分析 [ServerPacket] 并处理
*
* 其中, [PacketHandler] 由 4 个子模块构成:
* - [DebugPacketHandler] 输出 [Packet.toString]
* - [LoginHandler] 处理 touch/login/verification code 相关
* - [MessagePacketHandler] 处理消息相关(群消息/好友消息)([ServerEventPacket])
* 其中, [PacketHandler] 由 3 个子模块构成:
* - [LoginHandler] 处理 sendTouch/login/verification code 相关
* - [EventPacketHandler] 处理消息相关(群消息/好友消息)([ServerEventPacket])
* - [ActionPacketHandler] 处理动作相关(踢人/加入群/好友列表等)
*
* A BotNetworkHandler is used to connect with Tencent servers.
*
* @author Him188moe
*/
interface
BotNetworkHandler
{
interface
BotNetworkHandler
<
Socket
:
DataPacketSocket
>
:
Closeable
{
/**
* 网络层处理器. 用于编码/解码 [Packet], 发送/接受 [ByteArray]
* [BotNetworkHandler] 的协程作用域.
* 所有 [BotNetworkHandler] 的协程均启动在此作用域下.
*
* [BotNetworkHandler] 的协程包含:
* - UDP 包接收: [MiraiDatagramChannel.read]
* - 心跳 Job [ClientHeartbeatPacket]
* - SKey 刷新 [ClientSKeyRefreshmentRequestPacket]
* - 所有数据包处理和发送
*
*
java 调用方式: `botNetWorkHandler.getSocket()`
*
[BotNetworkHandler.close] 时将会 [取消][CoroutineScope.cancel] 所有此作用域下的协程
*/
val
socket
:
DataPacketSocket
val
NetworkScope
:
CoroutineScope
var
socket
:
Socket
/**
* 消息处理. 如发送好友消息, 接受群消息并触发事件
*
* java 调用方式: `botNetWorkHandler.getMessage()`
* 事件处理. 如发送好友消息, 接受群消息并触发事件
*/
val
message
:
Message
PacketHandler
val
event
:
Event
PacketHandler
/**
* 动作处理. 如发送好友请求, 处理别人发来的好友请求等
*
* java 调用方式: `botNetWorkHandler.getAction()`
*/
val
action
:
ActionPacketHandler
/**
*
尝试登录
*
[PacketHandler] 列表
*/
suspend
fun
login
():
LoginState
val
packetHandlers
:
PacketHandlerList
/**
* 尝试登录. 将会依次尝试登录到可用的服务器. 在任一服务器登录完成后返回登录结果
*/
suspend
fun
login
(
configuration
:
LoginConfiguration
):
LoginResult
/**
* 添加一个临时包处理器
...
...
@@ -64,5 +71,12 @@ interface BotNetworkHandler {
*/
suspend
fun
addHandler
(
temporaryPacketHandler
:
TemporaryPacketHandler
<
*
>)
fun
close
()
/**
* 发送数据包
*/
suspend
fun
sendPacket
(
packet
:
ClientPacket
)
override
fun
close
()
{
NetworkScope
.
cancel
(
"handler closed"
,
HandlerClosedException
())
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/HandlerClosedException.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.network
class
HandlerClosedException
:
Exception
()
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/LoginSession.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/LoginSession.kt
View file @
d89f8acc
package
net.mamoe.mirai.network
import
kotlinx.coroutines.CompletableDeferred
import
kotlinx.coroutines.CoroutineScope
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.network.protocol.tim.handler.DataPacketSocket
import
net.mamoe.mirai.network.protocol.tim.handler.TemporaryPacketHandler
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.utils.getGTK
import
kotlin.jvm.JvmSynthetic
/**
* 登录会话. 当登录完成后, 客户端会拿到 sessionKey.
...
...
@@ -16,8 +18,9 @@ import net.mamoe.mirai.utils.getGTK
*/
class
LoginSession
(
val
bot
:
Bot
,
val
sessionKey
:
ByteArray
,
val
socket
:
DataPacketSocket
val
sessionKey
:
ByteArray
,
//TODO 协议抽象? 可能并不是所有协议均需要 sessionKey
val
socket
:
DataPacketSocket
,
val
scope
:
CoroutineScope
)
{
/**
...
...
@@ -28,6 +31,7 @@ class LoginSession(
/**
* Web api 使用
*/
@ExperimentalStdlibApi
var
sKey
:
String
=
""
set
(
value
)
{
field
=
value
...
...
@@ -39,6 +43,7 @@ class LoginSession(
*/
var
gtk
:
Int
=
0
val
isOpen
:
Boolean
get
()
=
socket
.
isOpen
/**
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket].
...
...
@@ -56,7 +61,7 @@ class LoginSession(
* @param P 期待的包
* @param handlerTemporary 处理器.
*/
@JvmSynthetic
//
@JvmSynthetic
suspend
inline
fun
<
reified
P
:
ServerPacket
>
expectPacket
(
handlerTemporary
:
TemporaryPacketHandler
<
P
>.()
->
Unit
):
CompletableDeferred
<
Unit
>
{
val
deferred
=
CompletableDeferred
<
Unit
>()
this
.
bot
.
network
.
addHandler
(
TemporaryPacketHandler
(
P
::
class
,
deferred
,
this
).
also
(
handlerTemporary
))
...
...
@@ -89,3 +94,6 @@ class LoginSession(
return
deferred
}
}
suspend
fun
LoginSession
.
distributePacket
(
packet
:
ServerPacket
)
=
this
.
socket
.
distributePacket
(
packet
)
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/TIMBotNetworkHandler.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/TIMBotNetworkHandler.kt
View file @
d89f8acc
package
net.mamoe.mirai.network.protocol.tim
import
kotlinx.coroutines.*
import
kotlinx.coroutines.sync.Mutex
import
kotlinx.coroutines.sync.withLock
import
kotlinx.io.core.*
import
net.mamoe.mirai.*
import
net.mamoe.mirai.event.EventScope
import
net.mamoe.mirai.event.ListeningStatus
...
...
@@ -14,93 +13,170 @@ import net.mamoe.mirai.event.events.ServerPacketReceivedEvent
import
net.mamoe.mirai.event.subscribe
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.NetworkScope
import
net.mamoe.mirai.network.protocol.tim.handler.*
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.network.protocol.tim.packet.login.*
import
net.mamoe.mirai.utils.*
import
java.io.File
import
java.net.DatagramPacket
import
java.net.DatagramSocket
import
java.net.InetSocketAddress
import
java.util.*
import
java.util.concurrent.atomic.AtomicBoolean
import
javax.imageio.ImageIO
/**
* [BotNetworkHandler] 的
内部实现, 该类不会有帮助理解的注解, 请查看 [BotNetworkHandler] 以获取帮助
* [BotNetworkHandler] 的
TIM PC 协议实现
*
* @see BotNetworkHandler
* @author Him188moe
*/
@Suppress
(
"EXPERIMENTAL_API_USAGE"
)
//to simplify code
internal
class
TIMBotNetworkHandler
(
private
val
bot
:
Bot
)
:
BotNetworkHandler
{
override
val
socket
:
BotSocket
=
BotSocket
()
internal
class
TIMBotNetworkHandler
(
private
val
bot
:
Bot
)
:
BotNetworkHandler
<
TIMBotNetworkHandler
.
BotSocket
>
{
override
val
NetworkScope
:
CoroutineScope
=
CoroutineScope
(
Dispatchers
.
Default
)
override
lateinit
var
socket
:
BotSocket
lateinit
var
loginHandler
:
Login
Handler
override
lateinit
var
event
:
EventPacket
Handler
override
lateinit
var
message
:
MessagePacketHandler
override
lateinit
var
action
:
ActionPacketHandler
val
packetHandlers
:
PacketHandlerList
=
PacketHandlerList
()
override
val
packetHandlers
:
PacketHandlerList
=
PacketHandlerList
()
internal
val
temporaryPacketHandlers
=
Collections
.
synchronizedList
(
mutableListOf
<
TemporaryPacketHandler
<
*
>>()
)
internal
val
temporaryPacketHandlers
=
mutableListOf
<
TemporaryPacketHandler
<
*
>>(
)
private
var
heartbeatJob
:
Job
?
=
null
override
suspend
fun
addHandler
(
temporaryPacketHandler
:
TemporaryPacketHandler
<
*
>)
{
temporaryPacketHandler
.
send
(
action
.
session
)
temporaryPacketHandlers
.
add
(
temporaryPacketHandler
)
temporaryPacketHandler
.
send
(
action
.
session
)
}
override
suspend
fun
login
():
LoginState
{
return
loginInternal
(
LinkedList
(
TIMProtocol
.
SERVER_IP
))
}
override
suspend
fun
login
(
configuration
:
LoginConfiguration
):
LoginResult
{
TIMProtocol
.
SERVER_IP
.
forEach
{
bot
.
logger
.
logInfo
(
"Connecting server $it"
)
this
.
socket
=
BotSocket
(
it
,
configuration
)
//嵌套进 login 会导致 kotlin internal CompilationException
private
suspend
fun
loginInternal
(
ipQueue
:
LinkedList
<
String
>):
LoginState
{
this
.
socket
.
close
()
val
ip
=
ipQueue
.
poll
()
?:
return
LoginState
.
UNKNOWN
//所有服务器均返回 UNKNOWN
loginResult
=
CompletableDeferred
()
return
socket
.
touch
(
ip
).
let
{
state
->
if
(
state
==
LoginState
.
UNKNOWN
||
state
==
LoginState
.
TIMEOUT
)
{
loginInternal
(
ipQueue
)
//超时或未知, 重试连接下一个服务器
}
else
{
state
val
state
=
socket
.
resendTouch
()
if
(
state
!=
LoginResult
.
TIMEOUT
)
{
return
state
}
bot
.
logger
.
logPurple
(
"Timeout. Retrying next server"
)
socket
.
close
()
}
return
LoginResult
.
TIMEOUT
}
internal
var
loginResult
:
CompletableDeferred
<
LoginResult
>
=
CompletableDeferred
()
//private | internal
private
fun
onLoggedIn
(
sessionKey
:
ByteArray
)
{
val
session
=
LoginSession
(
bot
,
sessionKey
,
socket
)
message
=
MessagePacketHandler
(
session
)
require
(
packetHandlers
.
size
==
0
)
{
"Already logged in"
}
val
session
=
LoginSession
(
bot
,
sessionKey
,
socket
,
NetworkScope
)
event
=
EventPacketHandler
(
session
)
action
=
ActionPacketHandler
(
session
)
packetHandlers
.
add
(
message
.
asNode
())
packetHandlers
.
add
(
event
.
asNode
())
packetHandlers
.
add
(
action
.
asNode
())
}
private
lateinit
var
sessionKey
:
ByteArray
override
fun
close
()
{
super
.
close
()
this
.
heartbeatJob
?.
cancel
(
CancellationException
(
"handler closed"
))
this
.
heartbeatJob
=
null
if
(!
this
.
loginResult
.
isCompleted
&&
!
this
.
loginResult
.
isCancelled
)
{
this
.
loginResult
.
cancel
(
CancellationException
(
"socket closed"
))
}
this
.
packetHandlers
.
forEach
{
it
.
instance
.
close
()
}
this
.
socket
.
close
()
}
override
suspend
fun
sendPacket
(
packet
:
ClientPacket
)
=
socket
.
sendPacket
(
packet
)
internal
inner
class
BotSocket
(
override
val
serverIp
:
String
,
val
configuration
:
LoginConfiguration
)
:
DataPacketSocket
{
override
val
channel
:
MiraiDatagramChannel
=
MiraiDatagramChannel
(
serverIp
,
8000
)
override
val
isOpen
:
Boolean
get
()
=
channel
.
isOpen
private
lateinit
var
loginHandler
:
LoginHandler
private
suspend
fun
processReceive
()
{
while
(
channel
.
isOpen
)
{
val
buffer
=
IoBuffer
.
Pool
.
borrow
()
try
{
channel
.
read
(
buffer
)
//JVM: withContext(IO)
}
catch
(
e
:
ClosedChannelException
)
{
}
catch
(
e
:
Exception
)
{
e
.
log
()
continue
}
if
(!
buffer
.
canRead
()
||
buffer
.
readRemaining
==
0
)
{
//size==0
buffer
.
release
(
IoBuffer
.
Pool
)
continue
}
NetworkScope
.
launch
{
try
{
//Ensure the packet is consumed totally so that all buffers are released
ByteReadPacket
(
buffer
,
IoBuffer
.
Pool
).
use
{
distributePacket
(
it
.
parseServerPacket
(
buffer
.
readRemaining
))
}
}
catch
(
e
:
Exception
)
{
e
.
log
()
}
}
}
}
internal
suspend
fun
resendTouch
():
LoginResult
{
if
(
::
loginHandler
.
isInitialized
)
loginHandler
.
close
()
loginHandler
=
LoginHandler
()
val
expect
=
expectPacket
<
ServerTouchResponsePacket
>()
NetworkScope
.
launch
{
processReceive
()
}
NetworkScope
.
launch
{
if
(
withTimeoutOrNull
(
configuration
.
touchTimeoutMillis
)
{
expect
.
join
()
}
==
null
)
{
loginResult
.
complete
(
LoginResult
.
TIMEOUT
)
}
}
sendPacket
(
ClientTouchPacket
(
bot
.
qqNumber
,
this
.
serverIp
))
return
loginResult
.
await
()
}
private
inline
fun
<
reified
P
:
ServerPacket
>
expectPacket
():
CompletableDeferred
<
P
>
{
val
receiving
=
CompletableDeferred
<
P
>()
subscribe
<
ServerPacketReceivedEvent
>
{
if
(
it
.
packet
is
P
&&
it
.
bot
===
bot
)
{
receiving
.
complete
(
it
.
packet
)
ListeningStatus
.
STOPPED
}
else
ListeningStatus
.
LISTENING
}
return
receiving
}
internal
inner
class
BotSocket
:
DataPacketSocket
{
override
suspend
fun
distributePacket
(
packet
:
ServerPacket
)
{
try
{
packet
.
decode
()
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
bot
.
printPacketDebugging
(
packet
)
return
packet
.
close
()
throw
e
}
//removeIf is not inline
packet
.
use
{
//coz removeIf is not inline
with
(
temporaryPacketHandlers
.
iterator
())
{
while
(
hasNext
())
{
if
(
next
().
onPacketReceived
(
action
.
session
,
packet
))
{
...
...
@@ -109,10 +185,11 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
}
};
//For d
ebug
//For logD
ebug
{
if
(!
packet
.
javaClass
.
name
.
endsWith
(
"Encrypted"
)
&&
!
packet
.
javaClass
.
name
.
endsWith
(
"Raw"
))
{
bot
.
notice
(
"Packet received: $packet"
)
val
name
=
packet
::
class
.
simpleName
if
(
name
!=
null
&&
!
name
.
endsWith
(
"Encrypted"
)
&&
!
name
.
endsWith
(
"Raw"
))
{
bot
.
cyan
(
"Packet received: $packet"
)
}
}()
...
...
@@ -132,115 +209,41 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
it
.
instance
.
onPacketReceived
(
packet
)
}
}
private
var
socket
:
DatagramSocket
?
=
null
internal
var
serverIP
:
String
=
""
set
(
value
)
{
field
=
value
restartSocket
()
}
internal
lateinit
var
loginResult
:
CompletableDeferred
<
LoginState
>
@Synchronized
private
fun
restartSocket
()
{
socket
?.
close
()
socket
=
DatagramSocket
(
0
)
socket
!!
.
connect
(
InetSocketAddress
(
serverIP
,
8000
))
NetworkScope
.
launch
{
while
(
socket
?.
isConnected
==
true
)
{
val
packet
=
DatagramPacket
(
ByteArray
(
2048
),
2048
)
kotlin
.
runCatching
{
withContext
(
Dispatchers
.
IO
)
{
socket
?.
receive
(
packet
)
}
}
.
onSuccess
{
NetworkScope
.
launch
{
distributePacket
(
ServerPacket
.
ofByteArray
(
packet
.
data
.
removeZeroTail
()))
}
}.
onFailure
{
if
(
it
.
message
==
"Socket closed"
||
it
.
message
==
"socket closed"
)
{
return
@
launch
}
it
.
printStackTrace
()
}
}
}
}
internal
suspend
fun
touch
(
serverAddress
:
String
):
LoginState
{
bot
.
info
(
"Connecting server: $serverAddress"
)
restartSocket
()
if
(
this
@TIMBotNetworkHandler
::
loginHandler
.
isInitialized
)
{
loginHandler
.
close
()
}
loginHandler
=
LoginHandler
()
this
.
loginResult
=
CompletableDeferred
()
serverIP
=
serverAddress
//bot.waitForPacket(ServerTouchResponsePacket::class, timeoutMillis) {
// loginResult?.complete(LoginState.TIMEOUT)
//}
val
received
=
AtomicBoolean
(
false
)
ServerPacketReceivedEvent
::
class
.
subscribe
{
if
(
it
.
packet
is
ServerTouchResponsePacket
&&
it
.
bot
===
bot
)
{
received
.
set
(
true
)
ListeningStatus
.
STOPPED
}
else
ListeningStatus
.
LISTENING
}
NetworkScope
.
launch
{
delay
(
2000
)
if
(!
received
.
get
())
{
loginResult
.
complete
(
LoginState
.
TIMEOUT
)
}
}
sendPacket
(
ClientTouchPacket
(
bot
.
qqNumber
,
serverIP
))
return
withContext
(
Dispatchers
.
IO
)
{
loginResult
.
await
()
}
}
override
suspend
fun
sendPacket
(
packet
:
ClientPacket
)
=
withContext
(
NetworkScope
.
coroutineContext
)
{
checkNotNull
(
socket
)
{
"network closed"
}
if
(
socket
!!
.
isClosed
)
{
return
@
withContext
}
packet
.
encodePacket
()
check
(
channel
.
isOpen
)
{
"channel is not open"
}
if
(
BeforePacketSendEvent
(
bot
,
packet
).
broadcast
().
cancelled
)
{
return
@
withContext
}
val
data
=
packet
.
toByteArray
()
withContext
(
Dispatchers
.
IO
)
{
socket
!!
.
send
(
DatagramPacket
(
data
,
data
.
size
))
packet
.
packet
.
use
{
build
->
val
buffer
=
IoBuffer
.
Pool
.
borrow
()
try
{
build
.
readAvailable
(
buffer
)
channel
.
send
(
buffer
)
//JVM: withContext(IO)
}
catch
(
e
:
Exception
)
{
e
.
log
()
return
@
withContext
}
finally
{
buffer
.
release
(
IoBuffer
.
Pool
)
}
}
bot
.
cyan
(
"Packet sent: $packet"
)
EventScope
.
launch
{
PacketSentEvent
(
bot
,
packet
).
broadcast
()
}
bot
.
green
(
"Packet sent: $packet"
)
Unit
EventScope
.
launch
{
PacketSentEvent
(
bot
,
packet
).
broadcast
()
}
}
override
val
owner
:
Bot
get
()
=
this
@TIMBotNetworkHandler
.
bot
override
fun
close
()
{
this
.
socket
?.
close
()
if
(
this
::
loginResult
.
isInitialized
)
{
if
(!
this
.
loginResult
.
isCompleted
&&
!
this
.
loginResult
.
isCancelled
)
{
this
.
loginResult
.
cancel
(
CancellationException
(
"socket closed"
))
if
(
::
loginHandler
.
isInitialized
)
loginHandler
.
close
()
this
.
channel
.
close
()
}
}
}
override
fun
isClosed
():
Boolean
=
this
.
socket
?.
isClosed
?:
true
}
companion
object
{
val
captchaLock
=
Mutex
()
}
/**
* 处理登录过程
...
...
@@ -255,40 +258,48 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
/**
* 0828_decr_key
*/
private
lateinit
var
sessionResponseDecryptionKey
:
ByteArray
private
lateinit
var
sessionResponseDecryptionKey
:
IoBuffer
private
var
captchaSectionId
:
Int
=
1
private
var
captchaCache
:
ByteArray
?
=
byteArrayOf
()
//每次包只发一部分验证码来
private
var
heartbeatJob
:
Job
?
=
null
private
var
captchaCache
:
IoBuffer
?
=
null
get
()
{
if
(
field
==
null
)
field
=
IoBuffer
.
Pool
.
borrow
()
return
field
}
set
(
value
)
{
if
(
value
==
null
)
{
field
?.
release
(
IoBuffer
.
Pool
)
}
field
=
value
}
suspend
fun
onPacketReceived
(
packet
:
ServerPacket
)
{
when
(
packet
)
{
is
ServerTouchResponsePacket
->
{
if
(
packet
.
serverIP
!=
null
)
{
//redirection
socket
.
serverIP
=
packet
.
serverIP
!!
//connect(packet.serverIP!!)
socket
.
sendPacket
(
ClientServerRedirectionPacket
(
packet
.
serverIP
!!
,
bot
.
qqNumber
))
socket
.
close
()
socket
=
BotSocket
(
packet
.
serverIP
!!
,
socket
.
configuration
)
bot
.
logger
.
logPurple
(
"Redirecting to ${packet.serverIP}"
)
loginResult
.
complete
(
socket
.
resendTouch
())
}
else
{
//password submission
this
.
loginIP
=
packet
.
loginIP
this
.
loginTime
=
packet
.
loginTime
this
.
token0825
=
packet
.
token0825
println
(
"token0825="
+
this
.
token0825
.
toUHexString
())
socket
.
sendPacket
(
ClientPasswordSubmissionPacket
(
bot
.
qqNumber
,
bot
.
account
.
password
,
packet
.
loginTime
,
packet
.
loginIP
,
this
.
privateKey
,
packet
.
token0825
))
socket
.
sendPacket
(
ClientPasswordSubmissionPacket
(
bot
.
qqNumber
,
bot
.
account
.
password
,
packet
.
loginTime
,
packet
.
loginIP
,
this
.
privateKey
,
packet
.
token0825
,
socket
.
configuration
.
randomDeviceName
))
}
}
is
ServerLoginResponseFailedPacket
->
{
socket
.
loginResult
.
complete
(
packet
.
loginState
)
loginResult
.
complete
(
packet
.
loginResult
)
bot
.
close
()
return
}
is
ServerCaptchaCorrectPacket
->
{
this
.
privateKey
=
getRandomByteArray
(
16
)
this
.
privateKey
=
getRandomByteArray
(
16
)
//似乎是必须的
this
.
token00BA
=
packet
.
token00BA
socket
.
sendPacket
(
ClientLoginResendPacket3105
(
bot
.
qqNumber
,
bot
.
account
.
password
,
this
.
loginTime
,
this
.
loginIP
,
this
.
privateKey
,
this
.
token0825
,
this
.
token00BA
))
socket
.
sendPacket
(
ClientLoginResendPacket3105
(
bot
.
qqNumber
,
bot
.
account
.
password
,
this
.
loginTime
,
this
.
loginIP
,
this
.
privateKey
,
this
.
token0825
,
packet
.
token00BA
,
socket
.
configuration
.
randomDeviceName
))
}
is
ServerLoginResponseVerificationCodeInitPacket
->
{
...
...
@@ -298,7 +309,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
if
(
packet
.
unknownBoolean
==
true
)
{
this
.
captchaSectionId
=
1
socket
.
sendPacket
(
ClientVerificationCodeTransmissionRequestPacket
(
1
,
bot
.
qqNumber
,
this
.
token0825
,
this
.
captchaSectionId
++,
this
.
token00BA
))
socket
.
sendPacket
(
ClientVerificationCodeTransmissionRequestPacket
(
1
,
bot
.
qqNumber
,
this
.
token0825
,
this
.
captchaSectionId
++,
packet
.
token00BA
))
}
}
...
...
@@ -306,47 +317,29 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
if
(
packet
is
ServerCaptchaWrongPacket
)
{
bot
.
error
(
"验证码错误, 请重新输入"
)
captchaSectionId
=
1
this
.
captchaCache
=
byteArrayOf
()
this
.
captchaCache
=
null
}
this
.
captchaCache
=
this
.
captchaCache
!!
+
packet
.
captchaSectionN
this
.
captchaCache
!!
.
writeFully
(
packet
.
captchaSectionN
)
this
.
token00BA
=
packet
.
token00BA
if
(
packet
.
transmissionCompleted
)
{
//todo 验证码多样化处理
val
code
=
captchaLock
.
withLock
{
withContext
(
Dispatchers
.
IO
)
{
bot
.
notice
(
ImageIO
.
read
(
captchaCache
!!
.
inputStream
()).
createCharImg
())
}
bot
.
notice
(
"需要验证码登录, 验证码为 4 字母"
)
try
{
File
(
System
.
getProperty
(
"user.dir"
)
+
"/temp/Captcha.png"
)
.
also
{
withContext
(
Dispatchers
.
IO
)
{
it
.
createNewFile
()
}
}
.
writeBytes
(
this
.
captchaCache
!!
)
bot
.
notice
(
"若看不清字符图片, 请查看 Mirai 目录下 /temp/Captcha.png"
)
}
catch
(
e
:
Exception
)
{
bot
.
notice
(
"无法写出验证码文件, 请尝试查看以上字符图片"
)
}
val
code
=
solveCaptcha
(
captchaCache
!!
)
if
(
code
==
null
)
{
this
.
captchaCache
=
null
bot
.
notice
(
"若要更换验证码, 请直接回车"
)
Scanner
(
System
.
`in`
).
nextLine
()
}
if
(
code
.
isEmpty
()
||
code
.
length
!=
4
)
{
this
.
captchaCache
=
byteArrayOf
()
this
.
captchaSectionId
=
1
socket
.
sendPacket
(
ClientVerificationCodeRefreshPacket
(
packet
.
packetIdLast
+
1
,
bot
.
qqNumber
,
token0825
))
}
else
{
socket
.
sendPacket
(
ClientVerificationCodeSubmitPacket
(
packet
.
packetIdLast
+
1
,
bot
.
qqNumber
,
token0825
,
code
,
packet
.
verificationToken
))
}
}
else
{
socket
.
sendPacket
(
ClientVerificationCodeTransmissionRequestPacket
(
packet
.
packetIdLast
+
1
,
bot
.
qqNumber
,
token0825
,
captchaSectionId
++,
token00BA
))
socket
.
sendPacket
(
ClientVerificationCodeTransmissionRequestPacket
(
packet
.
packetIdLast
+
1
,
bot
.
qqNumber
,
token0825
,
captchaSectionId
++,
packet
.
token00BA
))
}
}
is
ServerLoginResponseSuccessPacket
->
{
this
.
sessionResponseDecryptionKey
=
packet
.
sessionResponseDecryptionKey
socket
.
sendPacket
(
ClientSessionRequestPacket
(
bot
.
qqNumber
,
socket
.
serverI
P
,
packet
.
token38
,
packet
.
token88
,
packet
.
encryptionKey
))
socket
.
sendPacket
(
ClientSessionRequestPacket
(
bot
.
qqNumber
,
socket
.
serverI
p
,
packet
.
token38
,
packet
.
token88
,
packet
.
encryptionKey
))
}
//是ClientPasswordSubmissionPacket之后服务器回复的
...
...
@@ -358,10 +351,10 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
if
(
packet
.
flag
==
ServerLoginResponseKeyExchangePacket
.
Flag
.
`
08
36
31
03
`
)
{
this
.
privateKey
=
packet
.
privateKeyUpdate
socket
.
sendPacket
(
ClientLoginResendPacket3104
(
bot
.
qqNumber
,
bot
.
account
.
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
packet
.
tokenUnknown
?:
t
his
.
token00BA
,
packet
.
tlv0006
))
?:
t
oken00BA
,
socket
.
configuration
.
randomDeviceName
,
packet
.
tlv0006
))
}
else
{
socket
.
sendPacket
(
ClientLoginResendPacket3106
(
bot
.
qqNumber
,
bot
.
account
.
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
packet
.
tokenUnknown
?:
token00BA
,
packet
.
tlv0006
))
?:
token00BA
,
socket
.
configuration
.
randomDeviceName
,
packet
.
tlv0006
))
}
}
...
...
@@ -369,13 +362,15 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
sessionKey
=
packet
.
sessionKey
heartbeatJob
=
NetworkScope
.
launch
{
while
(
socket
.
isOpen
)
{
delay
(
90000
)
socket
.
sendPacket
(
ClientHeartbeatPacket
(
bot
.
qqNumber
,
sessionKey
))
}
}
socket
.
loginResult
.
complete
(
LoginState
.
SUCCESS
)
loginResult
.
complete
(
LoginResult
.
SUCCESS
)
loginHandler
.
changeOnlineStatus
(
ClientLogin
Status
.
ONLINE
)
//required
setOnlineStatus
(
Online
Status
.
ONLINE
)
//required
}
is
ServerLoginSuccessPacket
->
{
...
...
@@ -384,11 +379,12 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
//登录成功后会收到大量上次的消息, 忽略掉 todo 优化
NetworkScope
.
launch
{
delay
(
3000
)
message
.
ignoreMessage
=
false
event
.
ignoreMessage
=
false
}
onLoggedIn
(
sessionKey
)
this
.
close
()
//The LoginHandler is useless since then
}
...
...
@@ -400,10 +396,11 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
is
ServerTouchResponsePacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
())
is
ServerHeartbeatResponsePacket
,
is
UnknownServerPacket
->
{
//ignored
is
ServerHeartbeatResponsePacket
->
{
}
is
UnknownServerPacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
(
sessionKey
))
else
->
{
}
...
...
@@ -411,16 +408,14 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
}
@Suppress
(
"MemberVisibilityCanBePrivate"
)
suspend
fun
changeOnlineStatus
(
status
:
ClientLogin
Status
)
{
suspend
fun
setOnlineStatus
(
status
:
Online
Status
)
{
socket
.
sendPacket
(
ClientChangeOnlineStatusPacket
(
bot
.
qqNumber
,
sessionKey
,
status
))
}
fun
close
()
{
this
.
captchaCache
=
null
this
.
heartbeatJob
?.
cancel
(
CancellationException
(
"handler closed"
))
this
.
heartbeatJob
=
null
if
(
::
sessionResponseDecryptionKey
.
isInitialized
)
this
.
sessionResponseDecryptionKey
.
release
(
IoBuffer
.
Pool
)
}
}
}
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/TIMProtocol.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/TIMProtocol.kt
View file @
d89f8acc
...
...
@@ -2,17 +2,9 @@
package
net.mamoe.mirai.network.protocol.tim
import
net.mamoe.mirai.utils.TEA
import
net.mamoe.mirai.utils.hexToBytes
import
net.mamoe.mirai.utils.toUHexString
import
java.net.InetAddress
import
java.util.*
import
java.util.stream.Collectors
import
net.mamoe.mirai.utils.solveIpAddress
/**
* @author Him188moe
*/
object
TIMProtocol
{
val
SERVER_IP
:
List
<
String
>
=
{
//add("183.60.56.29")
...
...
@@ -25,7 +17,7 @@ object TIMProtocol {
"sz8.tencent.com"
,
"sz9.tencent.com"
,
"sz2.tencent.com"
).
forEach
{
list
.
add
(
InetAddress
.
getByName
(
it
).
hostAddress
)
}
).
forEach
{
list
.
add
(
solveIpAddress
(
it
)
)
}
list
.
toList
()
}()
...
...
@@ -45,7 +37,7 @@ object TIMProtocol {
const
val
constantData2
=
"00 00 04 53 00 00 00 01 00 00 15 85 "
/**
* Touch 发出时写入, 并用于加密, 接受
t
ouch response 时解密.
* Touch 发出时写入, 并用于加密, 接受
sendT
ouch response 时解密.
*/
const
val
touchKey
=
"A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
//16
...
...
@@ -66,11 +58,6 @@ object TIMProtocol {
*/
const
val
key0836
=
"EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA"
//16
@JvmStatic
fun
main
(
args
:
Array
<
String
>)
{
println
(
TEA
.
decrypt
(
publicKey
.
hexToBytes
(),
key0836
).
toUHexString
())
}
/**
* 并非常量. 是 publicKey 与 key0836 的算法计算结果
*/
...
...
@@ -100,27 +87,4 @@ object TIMProtocol {
*/
const
val
messageConst1
=
"00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
// TIM最新 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91
private
val
hexToByteArrayCacheMap
:
MutableMap
<
Int
,
ByteArray
>
=
mutableMapOf
()
fun
hexToBytes
(
hex
:
String
):
ByteArray
{
hex
.
hashCode
().
let
{
id
->
if
(
hexToByteArrayCacheMap
.
containsKey
(
id
))
{
return
hexToByteArrayCacheMap
[
id
]
!!
.
clone
()
}
else
{
hexToUBytes
(
hex
).
toByteArray
().
let
{
hexToByteArrayCacheMap
[
id
]
=
it
.
clone
()
return
it
}
}
}
}
fun
hexToUBytes
(
hex
:
String
):
UByteArray
=
Arrays
.
stream
(
hex
.
split
(
" "
.
toRegex
()).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
())
.
map
{
value
->
value
.
trim
{
it
<=
' '
}
}
.
map
{
s
->
s
.
toUByte
(
16
)
}
.
collect
(
Collectors
.
toList
()).
toUByteArray
()
}
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/handler/ActionPacketHandler.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/handler/ActionPacketHandler.kt
View file @
d89f8acc
package
net.mamoe.mirai.network.protocol.tim.handler
import
kotlinx.coroutines.runBlocking
import
kotlinx.coroutines.CompletableDeferred
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.delay
import
kotlinx.coroutines.launch
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.protocol.tim.packet.ClientAccountInfoRequestPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerAccountInfoResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerEventPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.network.protocol.tim.packet.action.AddFriendResult
import
net.mamoe.mirai.network.protocol.tim.packet.action.ClientAddFriendPacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ClientCanAddFriendPacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ServerCanAddFriendResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.image.ServerTryGetImageIDFailedPacket
import
net.mamoe.mirai.network.protocol.tim.packet.image.ServerTryGetImageIDResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.image.ServerTryGetImageIDSuccessPacket
import
net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRefreshmentRequestPacket
import
net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRequestPacket
import
net.mamoe.mirai.network.protocol.tim.packet.login.ServerSKeyResponsePacket
import
net.mamoe.mirai.task.MiraiThreadPool
import
net.mamoe.mirai.utils.getGTK
import
java.awt.image.BufferedImage
import
java.util.*
import
java.util.concurrent.CompletableFuture
import
java.util.concurrent.ScheduledFuture
import
java.util.concurrent.TimeUnit
import
java.util.function.Supplier
import
net.mamoe.mirai.utils.hexToBytes
/**
* 动作: 获取好友列表, 点赞, 踢人等.
...
...
@@ -32,12 +23,13 @@ import java.util.function.Supplier
* @author Him188moe
*/
class
ActionPacketHandler
(
session
:
LoginSession
)
:
PacketHandler
(
session
)
{
private
val
addFriendSessions
=
Collections
.
synchronizedCollection
(
mutableListOf
<
AddFriendSession
>()
)
private
val
uploadImageSessions
=
Collections
.
synchronizedCollection
(
mutableListOf
<
UploadImageSession
>()
)
private
val
addFriendSessions
=
mutableListOf
<
AddFriendSession
>(
)
private
val
uploadImageSessions
=
mutableListOf
<
UploadImageSession
>(
)
private
var
sKeyRefresher
Future
:
ScheduledFuture
<
*
>
?
=
null
private
var
sKeyRefresher
Job
:
Job
?
=
null
@ExperimentalStdlibApi
override
suspend
fun
onPacketReceived
(
packet
:
ServerPacket
)
{
when
(
packet
)
{
is
ServerCanAddFriendResponsePacket
->
{
...
...
@@ -65,11 +57,13 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
session
.
sKey
=
packet
.
sKey
session
.
cookies
=
"uin=o"
+
session
.
bot
.
account
.
qqNumber
+
";skey="
+
session
.
sKey
+
";"
sKeyRefresherFuture
=
MiraiThreadPool
.
instance
.
scheduleWithFixedDelay
({
runBlocking
{
sKeyRefresherJob
=
session
.
scope
.
launch
{
while
(
session
.
isOpen
)
{
delay
(
1800000
)
session
.
socket
.
sendPacket
(
ClientSKeyRefreshmentRequestPacket
(
session
.
bot
.
account
.
qqNumber
,
session
.
sessionKey
))
}
}
,
1800000
,
1800000
,
TimeUnit
.
MILLISECONDS
)
}
session
.
gtk
=
getGTK
(
session
.
sKey
)
}
...
...
@@ -82,15 +76,9 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
}
}
suspend
fun
addFriend
(
qqNumber
:
Long
,
message
:
Supplier
<
String
>)
{
addFriend
(
qqNumber
,
lazy
{
message
.
get
()
})
}
@JvmSynthetic
suspend
fun
addFriend
(
qqNumber
:
Long
,
message
:
Lazy
<
String
>
=
lazyOf
(
""
)):
CompletableFuture
<
AddFriendResult
>
{
val
future
=
CompletableFuture
<
AddFriendResult
>()
//@JvmSynthetic
suspend
fun
addFriend
(
qqNumber
:
Long
,
message
:
Lazy
<
String
>
=
lazyOf
(
""
)):
CompletableDeferred
<
AddFriendResult
>
{
val
future
=
CompletableDeferred
<
AddFriendResult
>()
val
session
=
AddFriendSession
(
qqNumber
,
future
,
message
)
// uploadImageSessions.add(session)
session
.
sendAddRequest
()
...
...
@@ -108,14 +96,14 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
}
override
fun
close
()
{
this
.
sKeyRefresher
Future
?.
cancel
(
true
)
this
.
sKeyRefresher
Future
=
null
this
.
sKeyRefresher
Job
?.
cancel
(
)
this
.
sKeyRefresher
Job
=
null
}
private
inner
class
UploadImageSession
(
private
val
group
:
Long
,
private
val
future
:
Completable
Future
<
AddFriendResult
>,
private
val
image
:
BufferedImage
private
val
future
:
Completable
Deferred
<
AddFriendResult
>
//
private val image: BufferedImage
)
{
lateinit
var
id
:
ByteArray
...
...
@@ -167,7 +155,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
private
inner
class
AddFriendSession
(
private
val
qq
:
Long
,
private
val
future
:
Completable
Future
<
AddFriendResult
>,
private
val
future
:
Completable
Deferred
<
AddFriendResult
>,
private
val
message
:
Lazy
<
String
>
)
{
lateinit
var
id
:
ByteArray
...
...
@@ -180,7 +168,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
when
(
packet
)
{
is
ServerCanAddFriendResponsePacket
->
{
if
(!(
packet
.
idByteArray
[
2
]
==
id
[
0
]
&&
packet
.
idByteArray
[
3
]
==
id
[
1
]
))
{
if
(!(
packet
.
idByteArray
.
contentEquals
(
id
)
))
{
return
}
...
...
@@ -211,7 +199,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
suspend
fun
sendAddRequest
()
{
session
.
socket
.
sendPacket
(
ClientCanAddFriendPacket
(
session
.
bot
.
account
.
qqNumber
,
qq
,
session
.
sessionKey
).
also
{
this
.
id
=
it
.
packetIdLast
})
session
.
socket
.
sendPacket
(
ClientCanAddFriendPacket
(
session
.
bot
.
account
.
qqNumber
,
qq
,
session
.
sessionKey
).
also
{
this
.
id
=
it
.
idHex
.
hexToBytes
()
})
}
fun
close
()
{
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/handler/DataPacketSocket.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/handler/DataPacketSocket.kt
View file @
d89f8acc
package
net.mamoe.mirai.network.protocol.tim.handler
import
kotlinx.io.core.Closeable
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.event.events.ServerPacketReceivedEvent
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.utils.MiraiDatagramChannel
/**
* 网络接口.
...
...
@@ -14,9 +16,21 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
*
* @author Him188moe
*/
interface
DataPacketSocket
{
interface
DataPacketSocket
:
Closeable
{
val
owner
:
Bot
val
serverIp
:
String
/**
* UDP 通道
*/
val
channel
:
MiraiDatagramChannel
/**
* 是否开启
*/
val
isOpen
:
Boolean
/**
* 分发数据包给 [PacketHandler]
*/
...
...
@@ -31,7 +45,5 @@ interface DataPacketSocket {
*/
suspend
fun
sendPacket
(
packet
:
ClientPacket
)
fun
isClosed
():
Boolean
fun
close
()
override
fun
close
()
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/handler/Message
PacketHandler.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/Event
PacketHandler.kt
View file @
d89f8acc
...
...
@@ -9,14 +9,13 @@ import net.mamoe.mirai.getGroupByNumber
import
net.mamoe.mirai.getQQ
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.protocol.tim.packet.ServerFriendMessageEventPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerGroupMessageEventPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerGroupUploadFileEventPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.network.distributePacket
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.network.protocol.tim.packet.action.ClientSendFriendMessagePacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ClientSendGroupMessagePacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendFriendMessageResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendGroupMessageResponsePacket
import
net.mamoe.mirai.utils.MiraiLogger
/**
* 处理消息事件, 承担消息发送任务.
...
...
@@ -24,10 +23,10 @@ import net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendGroupMessage
* @author Him188moe
*/
@Suppress
(
"EXPERIMENTAL_API_USAGE"
)
class
Message
PacketHandler
(
session
:
LoginSession
)
:
PacketHandler
(
session
)
{
class
Event
PacketHandler
(
session
:
LoginSession
)
:
PacketHandler
(
session
)
{
internal
var
ignoreMessage
:
Boolean
=
true
override
suspend
fun
onPacketReceived
(
packet
:
ServerPacket
)
{
override
suspend
fun
onPacketReceived
(
packet
:
ServerPacket
)
:
Unit
=
with
(
session
)
{
when
(
packet
)
{
is
ServerGroupUploadFileEventPacket
->
{
//todo
...
...
@@ -36,21 +35,28 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
is
ServerFriendMessageEventPacket
->
{
if
(
ignoreMessage
)
return
FriendMessageEvent
(
session
.
bot
,
session
.
bot
.
getQQ
(
packet
.
qq
),
packet
.
message
).
broadcast
()
FriendMessageEvent
(
bot
,
bot
.
getQQ
(
packet
.
qq
),
packet
.
message
).
broadcast
()
}
is
ServerGroupMessageEventPacket
->
{
if
(
ignoreMessage
)
return
if
(
packet
.
qq
==
session
.
bot
.
account
.
qqNumber
)
return
if
(
packet
.
qq
.
toLong
()
==
bot
.
account
.
qqNumber
)
return
GroupMessageEvent
(
session
.
bot
,
session
.
bot
.
getGroupByNumber
(
packet
.
groupNumber
),
session
.
bot
.
getQQ
(
packet
.
qq
),
packet
.
message
).
broadcast
()
GroupMessageEvent
(
bot
,
bot
.
getGroupByNumber
(
packet
.
groupNumber
),
bot
.
getQQ
(
packet
.
qq
),
packet
.
message
).
broadcast
()
}
is
ServerSendFriendMessageResponsePacket
,
is
ServerSendGroupMessageResponsePacket
->
{
//ignored
}
is
ServerFieldOnlineStatusChangedPacket
.
Encrypted
->
distributePacket
(
packet
.
decrypt
(
sessionKey
))
is
ServerFieldOnlineStatusChangedPacket
->
{
MiraiLogger
.
logInfo
(
"${packet.qq.toLong()} 登录状态改变为 ${packet.status}"
)
//TODO
}
else
->
{
//ignored
}
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/handler/PacketHandler.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/handler/PacketHandler.kt
View file @
d89f8acc
...
...
@@ -2,6 +2,7 @@ package net.mamoe.mirai.network.protocol.tim.handler
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
kotlin.reflect.KClass
/**
* 数据包(接受/发送)处理器
...
...
@@ -17,17 +18,18 @@ abstract class PacketHandler(
}
class
PacketHandlerNode
<
T
:
PacketHandler
>(
val
clazz
:
Class
<
T
>,
val
clazz
:
K
Class
<
T
>,
val
instance
:
T
)
fun
PacketHandler
.
asNode
():
PacketHandlerNode
<
PacketHandler
>
{
return
PacketHandlerNode
(
this
.
javaClass
,
this
)
fun
<
T
:
PacketHandler
>
T
.
asNode
():
PacketHandlerNode
<
T
>
{
@Suppress
(
"UNCHECKED_CAST"
)
return
PacketHandlerNode
(
this
::
class
as
KClass
<
T
>,
this
)
}
class
PacketHandlerList
:
MutableList
<
PacketHandlerNode
<
*
>>
by
mutableListOf
()
{
fun
<
T
:
PacketHandler
>
get
(
clazz
:
Class
<
T
>):
T
{
operator
fun
<
T
:
PacketHandler
>
get
(
clazz
:
K
Class
<
T
>):
T
{
this
.
forEach
{
if
(
it
.
clazz
==
clazz
)
{
@Suppress
(
"UNCHECKED_CAST"
)
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt
View file @
d89f8acc
...
...
@@ -4,7 +4,6 @@ import kotlinx.coroutines.CompletableDeferred
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
java.util.concurrent.CompletableFuture
import
kotlin.reflect.KClass
/**
...
...
@@ -20,7 +19,7 @@ import kotlin.reflect.KClass
*
* @see LoginSession.expectPacket
*/
open
class
TemporaryPacketHandler
<
P
:
ServerPacket
>(
class
TemporaryPacketHandler
<
P
:
ServerPacket
>(
private
val
expectationClass
:
KClass
<
P
>,
private
val
deferred
:
CompletableDeferred
<
Unit
>,
private
val
fromSession
:
LoginSession
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.io.core.*
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.utils.writeHex
//TODO 将序列 ID 从包 ID 中独立出来
abstract
class
ClientPacket
:
Packet
(),
Closeable
{
/**
* Encode this packet.
*
* Before sending the packet, a [tail][TIMProtocol.tail] is added.
*/
protected
abstract
fun
encode
(
builder
:
BytePacketBuilder
)
companion
object
{
@Suppress
(
"PrivatePropertyName"
)
private
val
UninitializedByteReadPacket
=
ByteReadPacket
(
IoBuffer
.
Empty
,
IoBuffer
.
EmptyPool
)
}
/**
* 务必 [ByteReadPacket.close] 或 [close] 或使用 [Closeable.use]
*/
var
packet
:
ByteReadPacket
=
UninitializedByteReadPacket
get
()
{
if
(
field
===
UninitializedByteReadPacket
)
build
()
return
field
}
private
fun
build
():
ByteReadPacket
{
packet
=
buildPacket
{
writeHex
(
TIMProtocol
.
head
)
writeHex
(
TIMProtocol
.
ver
)
writeHex
(
idHex
)
encode
(
this
)
writeHex
(
TIMProtocol
.
tail
)
}
return
packet
}
override
fun
toString
():
String
=
packetToString
()
override
fun
close
()
=
if
(
this
.
packet
===
UninitializedByteReadPacket
)
Unit
else
this
.
packet
.
close
()
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/FriendOnlineStatusChanged.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readUByte
import
kotlinx.io.core.readUInt
import
net.mamoe.mirai.utils.OnlineStatus
import
kotlin.properties.Delegates
/**
* 好友在线状态改变
*/
@PacketId
(
"00 81"
)
class
ServerFieldOnlineStatusChangedPacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
var
qq
:
UInt
by
Delegates
.
notNull
()
lateinit
var
status
:
OnlineStatus
override
fun
decode
()
=
with
(
input
)
{
qq
=
readUInt
()
discardExact
(
8
)
status
=
OnlineStatus
.
ofId
(
readUByte
())
}
//在线 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
class
Encrypted
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerFieldOnlineStatusChangedPacket
=
ServerFieldOnlineStatusChangedPacket
(
this
.
decryptBy
(
sessionKey
)).
setId
(
this
.
idHex
)
}
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/GradeInfo.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/GradeInfo.kt
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
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
import
java.io.DataInputStream
import
net.mamoe.mirai.utils.encryptAndWrite
import
net.mamoe.mirai.utils.writeHex
import
net.mamoe.mirai.utils.writeQQ
import
net.mamoe.mirai.utils.writeRandom
/**
* 获取升级天数等.
...
...
@@ -14,13 +22,13 @@ class ClientAccountInfoRequestPacket(
private
val
qq
:
Long
,
private
val
sessionKey
:
ByteArray
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
writeRandom
(
2
)
//part of packet id
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeRandom
(
2
)
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer2
)
this
.
encryptAndWrite
(
sessionKey
)
{
write
Byte
(
0
x88
)
write
UByte
(
0
x88
.
toUByte
()
)
writeQQ
(
qq
)
writeByte
(
0
x00
)
}
...
...
@@ -28,7 +36,7 @@ class ClientAccountInfoRequestPacket(
}
@PacketId
(
"00 5C"
)
class
ServerAccountInfoResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
class
ServerAccountInfoResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
//等级
//升级剩余活跃天数
//ignored
...
...
@@ -37,7 +45,7 @@ class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(inp
}
@PacketId
(
"00 5C"
)
class
Encrypted
(
inputStream
:
DataInputStream
)
:
ServerPacket
(
inputStream
)
{
class
Encrypted
(
inputStream
:
ByteReadPacket
)
:
ServerPacket
(
inputStream
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerAccountInfoResponsePacket
=
ServerAccountInfoResponsePacket
(
this
.
decryptBy
(
sessionKey
)).
setId
(
this
.
idHex
)
}
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/Heartbeat.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/Heartbeat.kt
View file @
d89f8acc
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
java.io.DataInputStream
import
java.io.IOException
import
net.mamoe.mirai.utils.*
/**
* @author Him188moe
*/
@PacketId
(
"00 58"
)
class
ClientHeartbeatPacket
(
private
val
qq
:
Long
,
private
val
sessionKey
:
ByteArray
)
:
ClientPacket
()
{
@Throws
(
IOException
::
class
)
override
fun
encode
()
{
this
.
writeRandom
(
2
)
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
getRandomByteArray
(
2
).
toUHexString
()
}
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer
)
this
.
encryptAndWrite
(
sessionKey
)
{
...
...
@@ -24,4 +24,4 @@ class ClientHeartbeatPacket(
}
}
class
ServerHeartbeatResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
\ No newline at end of file
class
ServerHeartbeatResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
net.mamoe.mirai.utils.hexToUBytes
abstract
class
Packet
{
open
val
idHex
:
String
by
lazy
{
this
::
class
.
annotations
.
filterIsInstance
<
PacketId
>().
firstOrNull
()
?.
value
?.
trim
()
?:
""
}
open
val
fixedId
:
String
by
lazy
{
when
(
this
.
idHex
.
length
)
{
0
->
"__ __ __ __"
2
->
this
.
idHex
+
" __ __ __"
5
->
this
.
idHex
+
" __ __"
7
->
this
.
idHex
+
" __"
else
->
this
.
idHex
}
}
open
val
idByteArray
:
ByteArray
by
lazy
{
idHex
.
hexToUBytes
().
toByteArray
()
}
}
internal
expect
fun
Packet
.
packetToString
():
String
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/PacketId.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/PacketId.kt
View file @
d89f8acc
package
net.mamoe.mirai.network.protocol.tim.packet
/**
* @author Him188moe
*/
@Retention
(
AnnotationRetention
.
RUNTIME
)
@Target
(
AnnotationTarget
.
CLASS
,
AnnotationTarget
.
FILE
)
annotation
class
PacketId
(
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/ServerEvent.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/ServerEvent.kt
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.io.core.*
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.message.internal.readMessageChain
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.utils.dataDecode
import
net.mamoe.mirai.utils.hexToBytes
import
net.mamoe.mirai.utils.toUHexString
import
net.mamoe.mirai.utils.toUInt
import
java.io.DataInputStream
import
net.mamoe.mirai.utils.*
import
kotlin.properties.Delegates
data class
EventPacketIdentity
(
val
from
:
UInt
,
//对于好友消息, 这个是发送人
val
to
:
UInt
,
//对于好友消息, 这个是bot
internal
val
uniqueId
:
IoBuffer
//8
)
{
override
fun
toString
():
String
=
"EPI(from=$from, to=$to)"
}
fun
BytePacketBuilder
.
writeEventPacketIdentity
(
identity
:
EventPacketIdentity
)
=
with
(
identity
)
{
writeUInt
(
from
)
writeUInt
(
to
)
writeFully
(
uniqueId
)
}
/**
* Packet id: `00 CE` or `00 17`
*
* @author Him188moe
*/
abstract
class
ServerEventPacket
(
input
:
DataInputStream
,
val
packetId
:
ByteArray
,
val
eventIdentity
:
ByteArray
)
:
ServerPacket
(
input
)
{
@PacketId
(
"00 17"
)
class
Raw
(
input
:
DataInputStream
,
private
val
packetId
:
ByteArray
)
:
ServerPacket
(
input
)
{
fun
distribute
():
ServerEventPacket
{
val
eventIdentity
=
this
.
input
.
readNBytes
(
16
)
val
type
=
this
.
input
.
goto
(
18
).
readNBytes
(
2
)
abstract
class
ServerEventPacket
(
input
:
ByteReadPacket
,
packetId
:
ByteArray
,
val
eventIdentity
:
EventPacketIdentity
)
:
ServerPacket
(
input
)
{
override
val
idByteArray
:
ByteArray
=
packetId
override
var
idHex
:
String
=
packetId
.
toUHexString
()
class
Raw
(
input
:
ByteReadPacket
,
private
val
packetId
:
ByteArray
)
:
ServerPacket
(
input
)
{
fun
distribute
():
ServerEventPacket
=
with
(
input
)
{
val
eventIdentity
=
EventPacketIdentity
(
from
=
readUInt
(),
to
=
readUInt
(),
uniqueId
=
readIoBuffer
(
8
)
)
readBytes
(
2
).
takeIf
{
it
[
0
].
toUInt
()
!=
0
x1Fu
&&
it
[
1
].
toUInt
()
!=
0
x40u
}
?.
debugPrint
(
"type前面2个byte"
)
val
type
=
readBytes
(
2
)
return
when
(
type
.
toUHexString
())
{
"00 C4"
->
{
if
(
this
.
input
.
goto
(
33
).
readBoolean
())
{
ServerAndroidOnlineEventPacket
(
this
.
input
,
packetId
,
eventIdentity
)
discardExact
(
13
)
if
(
readBoolean
())
{
ServerAndroidOnlineEventPacket
(
input
,
packetId
,
eventIdentity
)
}
else
{
ServerAndroidOfflineEventPacket
(
this
.
input
,
packetId
,
eventIdentity
)
ServerAndroidOfflineEventPacket
(
input
,
packetId
,
eventIdentity
)
}
}
"00 2D"
->
ServerGroupUploadFileEventPacket
(
this
.
input
,
packetId
,
eventIdentity
)
"00 2D"
->
ServerGroupUploadFileEventPacket
(
input
,
packetId
,
eventIdentity
)
"00 52"
->
ServerGroupMessageEventPacket
(
this
.
input
,
packetId
,
eventIdentity
)
"00 52"
->
ServerGroupMessageEventPacket
(
input
,
packetId
,
eventIdentity
)
"00 A6"
->
ServerFriendMessageEventPacket
(
this
.
input
,
packetId
,
eventIdentity
)
"00 A6"
->
ServerFriendMessageEventPacket
(
input
,
packetId
,
eventIdentity
)
//"02 10", "00 12" -> ServerUnknownEventPacket(
this.
input, packetId, eventIdentity)
//"02 10", "00 12" -> ServerUnknownEventPacket(input, packetId, eventIdentity)
else
->
UnknownServerEventPacket
(
this
.
input
,
packetId
,
eventIdentity
)
}.
setId
(
this
.
idHex
)
else
->
UnknownServerEventPacket
(
input
,
packetId
,
eventIdentity
)
}.
setId
(
idHex
)
}
@PacketId
(
"00 17"
)
class
Encrypted
(
input
:
DataInputStream
,
private
val
packetId
:
ByteArray
)
:
ServerPacket
(
input
)
{
class
Encrypted
(
input
:
ByteReadPacket
,
private
val
packetId
:
ByteArray
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
Raw
=
Raw
(
this
.
decryptBy
(
sessionKey
),
packetId
).
setId
(
this
.
idHex
)
}
}
@PacketId
(
""
)
inner
class
ResponsePacket
(
val
qq
:
Long
,
val
bot
:
Long
,
val
sessionKey
:
ByteArray
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
write
(
packetId
)
//packet id 4bytes
override
val
idHex
:
String
=
this
@ServerEventPacket
.
idHex
override
val
idByteArray
:
ByteArray
=
this
@ServerEventPacket
.
idByteArray
override
val
fixedId
:
String
=
idHex
this
.
writeQQ
(
qq
)
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
bot
)
this
.
writeHex
(
TIMProtocol
.
fixVer2
)
this
.
encryptAndWrite
(
sessionKey
)
{
write
(
eventIdentity
)
write
EventPacketIdentity
(
eventIdentity
)
}
}
override
fun
getFixedId
():
String
{
return
packetId
.
toUHexString
()
}
}
}
/**
* Unknown event
*/
class
UnknownServerEventPacket
(
input
:
DataInputStream
,
packetId
:
ByteArray
,
eventIdentity
:
ByteArra
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
{
class
UnknownServerEventPacket
(
input
:
ByteReadPacket
,
packetId
:
ByteArray
,
eventIdentity
:
EventPacketIdentit
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
{
override
fun
decode
()
{
super
.
decode
()
println
(
"UnknownServerEventPacket data: "
+
this
.
input
.
goto
(
0
).
readAllBytes
().
toUHexString
())
println
(
"UnknownServerEventPacket data: "
+
this
.
input
.
readBytes
().
toUHexString
())
}
}
/**
* Android 客户端上线
*/
class
ServerAndroidOnlineEventPacket
(
input
:
DataInputStream
,
packetId
:
ByteArray
,
eventIdentity
:
ByteArra
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
class
ServerAndroidOnlineEventPacket
(
input
:
ByteReadPacket
,
packetId
:
ByteArray
,
eventIdentity
:
EventPacketIdentit
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
/**
* Android 客户端下线
*/
class
ServerAndroidOfflineEventPacket
(
input
:
DataInputStream
,
packetId
:
ByteArray
,
eventIdentity
:
ByteArra
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
class
ServerAndroidOfflineEventPacket
(
input
:
ByteReadPacket
,
packetId
:
ByteArray
,
eventIdentity
:
EventPacketIdentit
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
/**
* 群文件上传
*/
class
ServerGroupUploadFileEventPacket
(
input
:
DataInputStream
,
packetId
:
ByteArray
,
eventIdentity
:
ByteArra
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
{
class
ServerGroupUploadFileEventPacket
(
input
:
ByteReadPacket
,
packetId
:
ByteArray
,
eventIdentity
:
EventPacketIdentit
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
{
private
lateinit
var
xmlMessage
:
String
override
fun
decode
()
{
xmlMessage
=
String
(
this
.
input
.
goto
(
65
).
readNBytes
(
this
.
input
.
goto
(
60
).
readShort
().
toInt
()))
this
.
input
.
discardExact
(
60
)
val
size
=
this
.
input
.
readShort
().
toInt
()
this
.
input
.
discardExact
(
3
)
xmlMessage
=
this
.
input
.
readString
(
size
)
}
//todo test
}
@Suppress
(
"EXPERIMENTAL_API_USAGE"
)
class
ServerGroupMessageEventPacket
(
input
:
DataInputStream
,
packetId
:
ByteArray
,
eventIdentity
:
ByteArra
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
{
var
groupNumber
:
Long
=
0
var
qq
:
Long
=
0
class
ServerGroupMessageEventPacket
(
input
:
ByteReadPacket
,
packetId
:
ByteArray
,
eventIdentity
:
EventPacketIdentit
y
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
{
var
groupNumber
:
UInt
by
Delegates
.
notNull
()
var
qq
:
UInt
by
Delegates
.
notNull
()
lateinit
var
senderName
:
String
lateinit
var
message
:
MessageChain
enum
class
MessageType
{
NORMAL
,
XML
,
AT
,
FACE
,
//qq自带表情 [face107.gif]
PLAIN_TEXT
,
//纯文本
IMAGE
,
//自定义图片 {F50C5235-F958-6DF7-4EFA-397736E125A4}.gif
ANONYMOUS
,
//匿名用户发出的消息
OTHER
,
}
override
fun
decode
()
{
println
(
this
.
input
.
goto
(
0
).
readAllBytes
().
toUHexString
())
groupNumber
=
this
.
input
.
goto
(
51
).
readInt
().
toLong
()
qq
=
this
.
input
.
goto
(
56
).
readNBytes
(
4
).
toUInt
().
toLong
()
override
fun
decode
()
=
with
(
input
)
{
discardExact
(
31
)
groupNumber
=
readUInt
()
discardExact
(
1
)
qq
=
readUInt
()
this
.
input
.
goto
(
10
8
)
this
.
input
.
readLVByteArray
()
input
.
skip
(
2
)
//2个0x00
message
=
input
.
readMessageChain
()
discardExact
(
4
8
)
readLVByteArray
()
discardExact
(
2
)
//2个0x00
message
=
readMessageChain
()
val
map
=
input
.
readTLVMap
(
true
)
if
(
map
.
containsKey
(
18
))
{
this
.
senderName
=
dataDecode
(
map
.
getValue
(
18
))
{
val
tlv
=
it
.
readTLVMap
(
true
)
tlv
.
printTLVMap
()
val
map
=
readTLVMap
(
true
)
map
.
printTLVMap
(
"父map"
)
if
(
map
.
containsKey
(
0
x18
))
{
senderName
=
map
.
getValue
(
0
x18
).
read
{
val
tlv
=
readTLVMap
(
true
)
tlv
.
printTLVMap
(
"子map"
)
when
{
tlv
.
containsKey
(
0
x01
)
->
String
(
tlv
.
getValue
(
0
x01
))
...
...
@@ -147,128 +152,37 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
}
}
}
/*
messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) {
0x13 -> MessageType.NORMAL
0x0E -> MessageType.XML
0x06 -> MessageType.AT
0x01 -> MessageType.PLAIN_TEXT
0x02 -> MessageType.FACE
0x03 -> MessageType.IMAGE
0x19 -> MessageType.ANONYMOUS
else -> {
MiraiLogger.debug("ServerGroupMessageEventPacket id=$id")
MessageType.OTHER
}
}*/
/*
when (messageType) {
MessageType.NORMAL -> {
val gzippedMessage = this.input.goto(110 + fontLength + 16).readNBytes(this.input.goto(110 + fontLength + 3).readShort().toInt() - 11)
ByteArrayOutputStream().let {
GZIPInputStream(gzippedMessage.inputStream()).transferTo(it)
message = String(it.toByteArray())
}
}
MessageType.XML -> {
val gzippedMessage = this.input.goto(110 + fontLength + 9).readNBytes(this.input.goto(110 + fontLength + 3).readShort().toInt() - 4)
ByteArrayOutputStream().let {
GZIPInputStream(gzippedMessage.inputStream()).transferTo(it)
message = String(it.toByteArray())
}
}
MessageType.FACE -> {
val faceId = this.input.goto(110 + fontLength + 8).readByte()
message = "[face${faceId}.gif]"
}
MessageType.AT, MessageType.OTHER, MessageType.PLAIN_TEXT, MessageType.IMAGE, MessageType.ANONYMOUS -> {
var messageLength: Int = this.input.goto(110 + fontLength + 6).readShort().toInt()
message = String(this.input.goto(110 + fontLength + 8).readNBytes(messageLength))
val oeLength: Int
if (this.input.readByte().toInt() == 6) {
oeLength = this.input.readShort().toInt()
this.input.skip(4)
val messageLength2 = this.input.readShort().toInt()
val message2 = String(this.input.readNBytes(messageLength2))
message += message2
messageLength += messageLength2
} else {
oeLength = this.input.readShort().toInt()
}
//读取 nick, ignore.
/*
when (this.input.goto(110 + fontLength + 3 + oeLength).readByteAt().toInt()) {
12 -> {
this.input.skip(4)//maybe 5?
}
19 -> {
}
0x0E -> {
}
else -> {
}
}*/
}
}*/
}
}
fun
main
()
{
println
(
String
(
"E7 BE A4"
.
hexToBytes
()))
println
(
"."
.
toByteArray
().
toUByteArray
().
toUHexString
())
//长文本 22 96 29 7B B4 DF 94 AA 00 01 9F 8E 09 18 85 5B 1F 40 00 52 00 00 00 1B 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 01 01 22 96 29 7B 01 3E 03 3F A2 00 03 7E F3 5D 7B 97 57 00 00 F3 32 00 B8 00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 5D 7B 97 56 7F D0 53 BB 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 12 01 00 0F E9 95 BF E6 96 87 E6 9C AC E6 B6 88 E6 81 AF 0E 00 0E 01 00 04 00 00 00 09 07 00 04 00 00 00 01 19 00 35 01 00 32 AA 02 2F 50 03 60 00 68 00 9A 01 26 08 09 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 B8 03 00 C0 03 00 D0 03 00 E8 03 00 12 00 25 05 00 04 00 00 00 01 08 00 04 00 00 00 01 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08
val
packet
=
ServerGroupMessageEventPacket
((
""
+
"22 96 29 7B B4 DF 94 AA 00 09 8F 37 0A 65 07 2E 1F 40 00 52 00 00 00 1B 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 01 01 22 96 29 7B 01 3E 03 3F A2 00 03 7F 67 5D 7B AE D7 00 00 F3 36 02 E7 00 02 02 00 1B 10 00 00 00 00 4D 53 47 00 00 00 00 00 5D 7B AE D6 F4 91 87 BE 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 03 00 CB 02 00 2A 7B 37 41 41 34 42 33 41 41 2D 38 43 33 43 2D 30 46 34 35 2D 32 44 39 42 2D 37 46 33 30 32 41 30 41 43 45 41 41 7D 2E 6A 70 67 04 00 04 83 81 3B E2 05 00 04 B8 8B 33 79 06 00 04 00 00 00 50 07 00 01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 00 00 00 00 15 00 04 00 00 00 41 16 00 04 00 00 00 34 18 00 04 00 00 03 73 FF 00 5C 15 36 20 39 32 6B 41 31 43 38 33 38 31 33 62 65 32 62 38 38 62 33 33 37 39 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 37 41 41 34 42 33 41 41 2D 38 43 33 43 2D 30 46 34 35 2D 32 44 39 42 2D 37 46 33 30 32 41 30 41 43 45 41 41 7D 2E 6A 70 67 41 01 00 09 01 00 06 E7 89 9B E9 80 BC 03 00 77 02 00 2A 7B 37 41 41 34 42 33 41 41 2D 38 43 33 43 2D 30 46 34 35 2D 32 44 39 42 2D 37 46 33 30 32 41 30 41 43 45 41 41 7D 2E 6A 70 67 04 00 04 83 81 3B E2 05 00 04 B8 8B 33 79 06 00 04 00 00 00 50 07 00 01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 00 00 00 00 15 00 04 00 00 00 41 16 00 04 00 00 00 34 18 00 04 00 00 03 73 FF 00 08 15 37 20 20 38 41 41 41 02 00 14 01 00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0 03 00 CE 02 00 2A 7B 31 46 42 34 43 32 35 45 2D 42 34 46 45 2D 31 32 45 34 2D 46 33 42 42 2D 38 31 39 31 33 37 42 44 39 39 30 39 7D 2E 6A 70 67 04 00 04 B8 27 4B C6 05 00 04 79 5C B1 A3 06 00 04 00 00 00 50 07 00 01 41 08 00 00 09 00 01 01 0B 00 00 14 00 04 03 00 00 00 15 00 04 00 00 00 4E 16 00 04 00 00 00 23 18 00 04 00 00 02 A2 FF 00 5F 15 36 20 39 35 6B 44 31 41 62 38 32 37 34 62 63 36 37 39 35 63 62 31 61 33 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 31 46 42 34 43 32 35 45 2D 42 34 46 45 2D 31 32 45 34 2D 46 33 42 42 2D 38 31 39 31 33 37 42 44 39 39 30 39 7D 2E 6A 70 67 41 42 43 41 0E 00 07 01 00 04 00 00 00 09 19 00 38 01 00 35 AA 02 32 50 03 60 00 68 00 9A 01 29 08 09 20 BF 02 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 B8 03 00 C0 03 00 D0 03 00 E8 03 00 12 00 25 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08 05 00 04 00 00 00 01 08 00 04 00 00 00 01"
+
""
).
hexToBytes
().
dataInputStream
(),
byteArrayOf
(),
byteArrayOf
())
packet
.
decode
()
println
(
packet
)
}
//牛逼[图片]牛逼[图片] 22 96 29 7B B4 DF 94 AA 00 08 74 A4 09 18 8D CC 1F 40 00 52 00 00 00 1B 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 01 01 22 96 29 7B 01 3E 03 3F A2 00 03 7F 64 5D 7B AC BD 00 00 F3 36 02 03 00 02 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 5D 7B AC BD 12 73 DB A2 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 03 00 CB 02 00 2A 7B 37 41 41 34 42 33 41 41 2D 38 43 33 43 2D 30 46 34 35 2D 32 44 39 42 2D 37 46 33 30 32 41 30 41 43 45 41 41 7D 2E 6A 70 67 04 00 04 B4 52 77 F1 05 00 04 BC EB 03 B7 06 00 04 00 00 00 50 07 00 01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 00 00 00 00 15 00 04 00 00 00 41 16 00 04 00 00 00 34 18 00 04 00 00 03 73 FF 00 5C 15 36 20 39 32 6B 41 31 43 62 34 35 32 37 37 66 31 62 63 65 62 30 33 62 37 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 37 41 41 34 42 33 41 41 2D 38 43 33 43 2D 30 46 34 35 2D 32 44 39 42 2D 37 46 33 30 32 41 30 41 43 45 41 41 7D 2E 6A 70 67 41 01 00 09 01 00 06 E7 89 9B E9 80 BC 03 00 77 02 00 2A 7B 37 41 41 34 42 33 41 41 2D 38 43 33 43 2D 30 46 34 35 2D 32 44 39 42 2D 37 46 33 30 32 41 30 41 43 45 41 41 7D 2E 6A 70 67 04 00 04 B4 52 77 F1 05 00 04 BC EB 03 B7 06 00 04 00 00 00 50 07 00 01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 00 00 00 00 15 00 04 00 00 00 41 16 00 04 00 00 00 34 18 00 04 00 00 03 73 FF 00 08 15 37 20 20 38 41 41 41 0E 00 0E 01 00 04 00 00 00 09 07 00 04 00 00 00 01 19 00 35 01 00 32 AA 02 2F 50 03 60 00 68 00 9A 01 26 08 09 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 B8 03 00 C0 03 00 D0 03 00 E8 03 00 12 00 25 05 00 04 00 00 00 01 08 00 04 00 00 00 01 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08
//牛逼[图片]牛逼 22 96 29 7B B4 DF 94 AA 00 0B C1 0A 09 18 89 93 1F 40 00 52 00 00 00 1B 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 01 01 22 96 29 7B 01 3E 03 3F A2 00 03 7E F5 5D 7B 97 E7 00 00 F3 32 01 8D 00 02 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 5D 7B 97 E6 FA BE 7F DC 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 03 00 CF 02 00 2A 7B 39 44 32 44 45 39 31 41 2D 33 39 38 39 2D 39 35 35 43 2D 44 35 42 34 2D 37 46 41 32 37 38 39 37 38 36 30 39 7D 2E 6A 70 67 04 00 04 97 15 7F 03 05 00 04 79 5C B1 A3 06 00 04 00 00 00 50 07 00 01 41 08 00 00 09 00 01 01 0B 00 00 14 00 04 03 00 00 00 15 00 04 00 00 00 3C 16 00 04 00 00 00 40 18 00 04 00 00 03 CC FF 00 60 15 36 20 39 36 6B 45 31 41 39 37 31 35 37 66 30 33 37 39 35 63 62 31 61 33 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 39 44 32 44 45 39 31 41 2D 33 39 38 39 2D 39 35 35 43 2D 44 35 42 34 2D 37 46 41 32 37 38 39 37 38 36 30 39 7D 2E 6A 70 67 31 32 31 32 41 01 00 09 01 00 06 E7 89 9B E9 80 BC 0E 00 0E 01 00 04 00 00 00 09 07 00 04 00 00 00 01 19 00 35 01 00 32 AA 02 2F 50 03 60 00 68 00 9A 01 26 08 09 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 B8 03 00 C0 03 00 D0 03 00 E8 03 00 12 00 25 05 00 04 00 00 00 01 08 00 04 00 00 00 01 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08
class
ServerFriendMessageEventPacket
(
input
:
DataInputStream
,
packetId
:
ByteArray
,
eventIdentity
:
ByteArray
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
{
var
qq
:
Long
=
0
lateinit
var
message
:
MessageChain
class
ServerFriendMessageEventPacket
(
input
:
ByteReadPacket
,
packetId
:
ByteArray
,
eventIdentity
:
EventPacketIdentity
)
:
ServerEventPacket
(
input
,
packetId
,
eventIdentity
)
{
val
group
:
UInt
get
()
=
eventIdentity
.
from
val
qq
:
UInt
get
()
=
eventIdentity
.
to
override
fun
decode
()
{
input
.
goto
(
0
)
println
(
"ServerFriendMessageEventPacket.input="
+
input
.
readAllBytes
().
toUHexString
())
input
.
goto
(
0
)
lateinit
var
message
:
MessageChain
qq
=
input
.
readUIntAt
(
0
).
toLong
()
//00 00 00 25 00 08 00 02 00 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 01 38 03 3E 03 3F A2 76 E4 B8 DD E7 86 74 F2 64 55 AD 9A EB 2F B9 DF F1 7F 8C 28 00 0B 78 14 5D A2 F5 CB 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D A2 F5 CA 9D 26 CB 5E 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 E4 BD A0 E5 A5 BD 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00
val
l1
=
input
.
readShortAt
(
22
)
input
.
goto
(
93
+
l1
)
input
.
readLVByteArray
()
//font
input
.
skip
(
2
)
//2个0x00
message
=
input
.
readMessageChain
()
override
fun
decode
()
=
with
(
input
)
{
input
.
discardExact
(
2
)
val
l1
=
readShort
()
discardExact
(
l1
.
toInt
())
discardExact
(
69
)
readLVByteArray
()
//font
discardExact
(
2
)
//2个0x00
message
=
readMessageChain
()
val
map
:
Map
<
Int
,
ByteArray
>
=
input
.
readTLVMap
(
true
).
withDefault
{
byteArrayOf
()
}
println
(
map
.
getValue
(
18
))
val
map
:
Map
<
Int
,
ByteArray
>
=
readTLVMap
(
true
).
withDefault
{
byteArrayOf
()
}
println
(
"map.getValue(18)="
+
map
.
getValue
(
18
))
//19 00 38 01 00 35 AA 02 32 50 03 60 00 68 00 9A 01 29 08 09 20 BF 02 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 B8 03 00 C0 03 00 D0 03 00 E8 03 00 12 00 25 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08 05 00 04 00 00 00 01 08 00 04 00 00 00 01
/*
val offset = unknownLength0 + fontLength//57
message
= MessageChain(PlainText(let {
event
= MessageChain(PlainText(let {
val length = input.readShortAt(101 + offset)//
input.goto(103 + offset).readString(length.toInt())
}))*/
...
...
@@ -318,9 +232,9 @@ B1 89 BE 09 8F 00 1A E5 00 0B 03 A2 09 90 BB 7A 1F 40 00 A6 00 00 00 20 00 05 00
backup
class ServerFriendMessageEventPacket(input:
DataInputStream, packetId: ByteArray, eventIdentity: ByteArra
y) : ServerEventPacket(input, packetId, eventIdentity) {
class ServerFriendMessageEventPacket(input:
ByteReadPacket, packetId: ByteArray, eventIdentity: EventPacketIdentit
y) : ServerEventPacket(input, packetId, eventIdentity) {
var qq: Long = 0
lateinit var
message
: String
lateinit var
event
: String
...
...
@@ -330,7 +244,7 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
val msgLength = input.readShortAt(22)
val fontLength = input.readShortAt(93+msgLength)
val offset = msgLength+fontLength
message
= if(input.readByteAt(97+offset).toUHexString() == "02"){
event
= if(input.readByteAt(97+offset).toUHexString() == "02"){
"[face" + input.goto(103+offset).readByteAt(1).toInt().toString() + ".gif]"
//.gif
}else {
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Closeable
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.core.readBytes
import
net.mamoe.mirai.utils.*
abstract
class
ServerPacket
(
val
input
:
ByteReadPacket
)
:
Packet
(),
Closeable
{
override
var
idHex
:
String
=
EMPTY_ID_HEX
get
()
{
if
(
field
===
EMPTY_ID_HEX
)
{
idHex
=
(
this
::
class
.
annotations
.
firstOrNull
{
it
::
class
==
PacketId
::
class
} as?
PacketId
)
?.
value
?.
trim
()
?:
""
}
return
field
}
var
encoded
:
Boolean
=
false
open
fun
decode
()
{
}
override
fun
close
()
=
this
.
input
.
close
()
companion
object
{
private
const
val
EMPTY_ID_HEX
=
"EMPTY_ID_HEX"
}
override
fun
toString
():
String
=
this
.
packetToString
()
fun
getFixedId
(
id
:
String
):
String
=
when
(
id
.
length
)
{
0
->
"__ __ __ __"
2
->
"$id __ __ __"
5
->
"$id __ __"
7
->
"$id __"
else
->
id
}
fun
decryptBy
(
key
:
ByteArray
):
ByteReadPacket
{
return
ByteReadPacket
(
decryptAsByteArray
(
key
))
}
fun
decryptBy
(
key
:
IoBuffer
):
ByteReadPacket
{
return
ByteReadPacket
(
decryptAsByteArray
(
key
))
}
fun
decryptBy
(
keyHex
:
String
):
ByteReadPacket
{
return
this
.
decryptBy
(
keyHex
.
hexToBytes
())
}
fun
decryptBy
(
key1
:
ByteArray
,
key2
:
ByteArray
):
ByteReadPacket
{
return
TEA
.
decrypt
(
this
.
decryptAsByteArray
(
key1
),
key2
).
toReadPacket
()
}
fun
decryptBy
(
key1
:
String
,
key2
:
ByteArray
):
ByteReadPacket
{
return
this
.
decryptBy
(
key1
.
hexToBytes
(),
key2
)
}
fun
decryptBy
(
key1
:
String
,
key2
:
IoBuffer
):
ByteReadPacket
{
return
this
.
decryptBy
(
key1
.
hexToBytes
(),
key2
.
readBytes
())
}
fun
decryptBy
(
key1
:
ByteArray
,
key2
:
String
):
ByteReadPacket
{
return
this
.
decryptBy
(
key1
,
key2
.
hexToBytes
())
}
fun
decryptBy
(
keyHex1
:
String
,
keyHex2
:
String
):
ByteReadPacket
{
return
this
.
decryptBy
(
keyHex1
.
hexToBytes
(),
keyHex2
.
hexToBytes
())
}
fun
decryptAsByteArray
(
key
:
ByteArray
):
ByteArray
{
return
TEA
.
decrypt
(
input
.
readRemainingBytes
().
cutTail
(
1
),
key
)
}
fun
decryptAsByteArray
(
keyHex
:
String
):
ByteArray
=
this
.
decryptAsByteArray
(
keyHex
.
hexToBytes
())
fun
decryptAsByteArray
(
buffer
:
IoBuffer
):
ByteArray
=
this
.
decryptAsByteArray
(
buffer
.
readBytes
())
}
fun
<
P
:
ServerPacket
>
P
.
setId
(
idHex
:
String
):
P
{
this
.
idHex
=
idHex
return
this
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt
0 → 100644
View file @
d89f8acc
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.toUHexString
class
UnknownServerPacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
override
fun
decode
()
{
val
raw
=
this
.
input
.
readBytes
()
MiraiLogger
.
logDebug
(
"UnknownServerPacket data: "
+
raw
.
toUHexString
())
}
class
Encrypted
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
UnknownServerPacket
=
UnknownServerPacket
(
this
.
decryptBy
(
sessionKey
)).
setId
(
this
.
idHex
)
}
override
fun
toString
():
String
{
return
LoggerTextFormat
.
LIGHT_RED
.
toString
()
+
super
.
toString
()
}
}
/*
ID: 00 17
长度 95
76 E4 B8 DD //1994701021
76 E4 B8 DD //1994701021
00 0B B9 A9 09 90 BB 54 1F //类似Event的uniqueId?
40 02
10 00 00 00
18 00
08 00
02 00
01 00
09 00
06 41 4B DA 4C 00 00
00 0A
00 04
01 00 00 00 00 00
00 06 00 00
00 0E
08 02
1A 02 08 49 0A 0C 08 A2 FF 8C F0
03 10 CA EB 8B ED 05
或者
长度63
00 00 27 10 76 E4 B8 DD
00 09 ED 26 64 73 0E CA 1F 40
00 12 00 00
00 08
00 0A
00 04
01 00 00
00 02
值都是一样的.
*/
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.readBytes
import
net.mamoe.mirai.utils.gotoWhere
import
net.mamoe.mirai.utils.toReadPacket
expect
class
PlatformImage
expect
class
ClientTryGetImageIDPacket
(
botNumber
:
Long
,
sessionKey
:
ByteArray
,
groupNumberOrQQNumber
:
Long
,
image
:
PlatformImage
)
:
ClientPacket
abstract
class
ServerTryGetImageIDResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
class
Encrypted
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerTryGetImageIDResponsePacket
{
val
data
=
this
.
decryptAsByteArray
(
sessionKey
)
println
(
data
.
size
)
println
(
data
.
size
)
if
(
data
.
size
==
209
)
{
return
ServerTryGetImageIDSuccessPacket
(
data
.
toReadPacket
()).
setId
(
this
.
idHex
)
}
return
ServerTryGetImageIDFailedPacket
(
data
.
toReadPacket
())
}
}
}
/**
* 服务器未存有图片, 返回一个 key 用于客户端上传
*/
class
ServerTryGetImageIDSuccessPacket
(
input
:
ByteReadPacket
)
:
ServerTryGetImageIDResponsePacket
(
input
)
{
lateinit
var
uKey
:
ByteArray
override
fun
decode
()
{
this
.
input
.
gotoWhere
(
ubyteArrayOf
(
0
x42u
,
0
x80u
,
0
x01u
))
uKey
=
this
.
input
.
readBytes
(
128
)
}
}
/**
* 服务器已经存有这个图片
*/
class
ServerTryGetImageIDFailedPacket
(
input
:
ByteReadPacket
)
:
ServerTryGetImageIDResponsePacket
(
input
)
{
override
fun
decode
()
{
}
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/action/AddContact.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/action/AddContact.kt
View file @
d89f8acc
...
...
@@ -2,12 +2,15 @@
package
net.mamoe.mirai.network.protocol.tim.packet.action
import
kotlinx.io.core.BytePacketBuilder
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.readBytes
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.
*
import
net.mamoe.mirai.
utils.getRandomByteArray
import
net.mamoe.mirai.
utils.toUHexString
import
java.io.DataInputStream
import
java.util
.*
import
net.mamoe.mirai.network.protocol.tim.packet.
ClientPacket
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.setId
import
net.mamoe.mirai.utils
.*
/**
* 向服务器检查是否可添加某人为好友
...
...
@@ -20,15 +23,11 @@ class ClientCanAddFriendPacket(
val
qq
:
Long
,
val
sessionKey
:
ByteArray
)
:
ClientPacket
()
{
val
packetIdLast
=
getRandomByteArray
(
2
)
override
fun
getFixedId
():
String
{
return
idHex
+
" "
+
packetIdLast
.
toUHexString
()
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
getRandomByteArray
(
2
).
toUHexString
()
}
override
fun
encode
()
{
this
.
write
(
packetIdLast
)
//id, 2bytes
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
bot
)
this
.
writeHex
(
TIMProtocol
.
fixVer2
)
this
.
encryptAndWrite
(
sessionKey
)
{
...
...
@@ -38,7 +37,7 @@ class ClientCanAddFriendPacket(
}
@PacketId
(
"00 A7"
)
class
ServerCanAddFriendResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
class
ServerCanAddFriendResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
lateinit
var
state
:
State
enum
class
State
{
...
...
@@ -50,7 +49,8 @@ class ServerCanAddFriendResponsePacket(input: DataInputStream) : ServerPacket(in
override
fun
decode
()
{
val
data
=
input
.
goto
(
0
).
readAllBytes
()
input
.
readBytes
()
val
data
=
input
.
readRemainingBytes
()
if
(
data
.
size
==
99
)
{
state
=
State
.
ALREADY_ADDED
return
...
...
@@ -61,13 +61,13 @@ class ServerCanAddFriendResponsePacket(input: DataInputStream) : ServerPacket(in
0
x99u
->
State
.
ALREADY_ADDED
0
x03u
,
0
x04u
->
State
.
FAILED
else
->
throw
IllegalArgumentException
(
Arrays
.
toString
(
data
))
else
->
throw
IllegalArgumentException
(
data
.
contentToString
(
))
}
}
@PacketId
(
"00 A7"
)
class
Encrypted
(
inputStream
:
DataInputStream
)
:
ServerPacket
(
inputStream
)
{
class
Encrypted
(
inputStream
:
ByteReadPacket
)
:
ServerPacket
(
inputStream
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerCanAddFriendResponsePacket
{
return
ServerCanAddFriendResponsePacket
(
this
.
decryptBy
(
sessionKey
)).
setId
(
this
.
idHex
)
}
...
...
@@ -84,15 +84,11 @@ class ClientAddFriendPacket(
val
qq
:
Long
,
val
sessionKey
:
ByteArray
)
:
ClientPacket
()
{
val
packetIdLast
=
getRandomByteArray
(
2
)
override
fun
getFixedId
():
String
{
return
idHex
+
" "
+
packetIdLast
.
toUHexString
()
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
getRandomByteArray
(
2
).
toUHexString
()
}
override
fun
encode
()
{
this
.
write
(
packetIdLast
)
//id, 2bytes
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
bot
)
this
.
writeHex
(
TIMProtocol
.
fixVer2
)
this
.
encryptAndWrite
(
sessionKey
)
{
...
...
@@ -104,17 +100,17 @@ class ClientAddFriendPacket(
}
class
ServerAddFriendResponsePacket
(
input
:
DataInputStream
)
:
ServerAddContactResponsePacket
(
input
)
class
ServerAddFriendResponsePacket
(
input
:
ByteReadPacket
)
:
ServerAddContactResponsePacket
(
input
)
class
ServerAddGroupResponsePacket
(
input
:
DataInputStream
)
:
ServerAddContactResponsePacket
(
input
)
class
ServerAddGroupResponsePacket
(
input
:
ByteReadPacket
)
:
ServerAddContactResponsePacket
(
input
)
/**
* 添加好友/群的回复
*/
abstract
class
ServerAddContactResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
abstract
class
ServerAddContactResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
class
Raw
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
class
Raw
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
override
fun
decode
()
{
...
...
@@ -125,7 +121,7 @@ abstract class ServerAddContactResponsePacket(input: DataInputStream) : ServerPa
TODO
()
}
class
Encrypted
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
class
Encrypted
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
Raw
=
Raw
(
this
.
decryptBy
(
sessionKey
))
}
}
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/action/AddFriendResult.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/action/AddFriendResult.kt
View file @
d89f8acc
File moved
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt
View file @
d89f8acc
package
net.mamoe.mirai.network.protocol.tim.packet.action
import
kotlinx.io.core.*
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.message.internal.to
ByteArray
import
net.mamoe.mirai.message.internal.to
Packet
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.utils.dataEncode
import
java.io.DataInputStream
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.PacketId
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.utils.*
/**
* @author Him188moe
*/
@PacketId
(
"00 CD"
)
class
ClientSendFriendMessagePacket
(
private
val
botQQ
:
Long
,
...
...
@@ -17,8 +17,8 @@ class ClientSendFriendMessagePacket(
private
val
sessionKey
:
ByteArray
,
private
val
message
:
MessageChain
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
writeRandom
(
2
)
//part of packet id
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeRandom
(
2
)
this
.
writeQQ
(
botQQ
)
this
.
writeHex
(
TIMProtocol
.
fixVer2
)
...
...
@@ -30,7 +30,7 @@ class ClientSendFriendMessagePacket(
writeHex
(
"37 0F"
)
//TIM最新: 38 03
writeQQ
(
botQQ
)
writeQQ
(
targetQQ
)
write
(
md5
(
dataEncode
{
md5Key
->
md5Key
.
writeQQ
(
targetQQ
);
md5Key
.
write
(
sessionKey
)
}
))
write
Fully
(
md5
(
buildPacket
{
writeQQ
(
targetQQ
);
writeFully
(
sessionKey
)
}.
readBytes
()
))
writeHex
(
"00 0B"
)
writeRandom
(
2
)
writeTime
()
...
...
@@ -54,11 +54,11 @@ class ClientSendFriendMessagePacket(
writeHex
(
TIMProtocol
.
messageConst1
)
//... 85 E9 BB 91
writeZero
(
2
)
write
(
message
.
toByteArray
())
write
Packet
(
message
.
toPacket
())
/*
//Plain text
val bytes =
message.toByteArray
()
val bytes =
event.toPacket
()
it.writeByte(0x01)
it.writeShort(bytes.size + 3)
it.writeByte(0x01)
...
...
@@ -69,4 +69,4 @@ class ClientSendFriendMessagePacket(
}
@PacketId
(
"00 CD"
)
class
ServerSendFriendMessageResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
\ No newline at end of file
class
ServerSendFriendMessageResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/action/ClientSendGroupMessagePacket.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/action/ClientSendGroupMessagePacket.kt
View file @
d89f8acc
package
net.mamoe.mirai.network.protocol.tim.packet.action
import
kotlinx.io.core.BytePacketBuilder
import
kotlinx.io.core.ByteReadPacket
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.message.internal.to
ByteArray
import
net.mamoe.mirai.message.internal.to
Packet
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.utils.dataEncode
import
net.mamoe.mirai.utils.toUHexString
import
java.io.DataInputStream
/**
* @author Him188moe
*/
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.PacketId
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.utils.*
@PacketId
(
"00 02"
)
class
ClientSendGroupMessagePacket
(
private
val
botQQ
:
Long
,
...
...
@@ -18,39 +18,35 @@ class ClientSendGroupMessagePacket(
private
val
sessionKey
:
ByteArray
,
private
val
message
:
MessageChain
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
writeRandom
(
2
)
//part of packet id
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeRandom
(
2
)
this
.
writeQQ
(
botQQ
)
this
.
writeHex
(
TIMProtocol
.
fixVer2
)
this
.
encryptAndWrite
(
sessionKey
)
{
val
bytes
=
message
.
toByteArray
()
writeByte
(
0
x2A
)
writeGroup
(
groupId
)
writeLV
ByteArray
(
dataEncode
{
child
->
child
.
writeHex
(
"00 01 01"
)
child
.
writeHex
(
"00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00"
)
writeLV
Packet
{
writeHex
(
"00 01 01"
)
writeHex
(
"00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00"
)
child
.
writeTime
()
child
.
writeRandom
(
4
)
child
.
writeHex
(
"00 00 00 00 09 00 86"
)
child
.
writeHex
(
TIMProtocol
.
messageConst1
)
child
.
writeZero
(
2
)
writeTime
()
writeRandom
(
4
)
writeHex
(
"00 00 00 00 09 00 86"
)
writeHex
(
TIMProtocol
.
messageConst1
)
writeZero
(
2
)
//messages
child
.
write
(
bytes
)
})
writePacket
(
message
.
toPacket
())
}
/*it.writeByte(0x01)
it.writeShort(bytes.size + 3)
it.writeByte(0x01)
it.writeShort(bytes.size)
it.write(bytes)*/
println
(
toByteArray
().
toUHexString
())
}
}
}
@PacketId
(
"00 02"
)
class
ServerSendGroupMessageResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
\ No newline at end of file
class
ServerSendGroupMessageResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt
View file @
d89f8acc
...
...
@@ -2,32 +2,32 @@
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.*
import
net.mamoe.mirai.utils.ClientLoginStatus
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.PacketId
import
net.mamoe.mirai.utils.*
/**
* 改变在线状态: "我在线上", "隐身" 等
*
* @author Him188moe
*/
@PacketId
(
"00 EC"
)
class
ClientChangeOnlineStatusPacket
(
private
val
qq
:
Long
,
private
val
sessionKey
:
ByteArray
,
private
val
loginStatus
:
ClientLoginStatus
private
val
loginStatus
:
OnlineStatus
)
:
ClientPacket
()
{
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
getRandomByteArray
(
2
).
toUHexString
()
}
override
fun
encode
()
{
this
.
writeRandom
(
2
)
//part of packet id
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer2
)
this
.
encryptAndWrite
(
sessionKey
)
{
writeHex
(
"01 00"
)
write
Byte
(
loginStatus
.
id
.
toInt
()
)
write
UByte
(
loginStatus
.
id
)
writeHex
(
"00 01 00 01 00 04 00 00 00 00"
)
}
}
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/login/ClientLogin.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/login/ClientLogin.kt
View file @
d89f8acc
package
net.mamoe.mirai.network.protocol.tim.packet.login
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.*
import
net.mamoe.mirai.utils.TEA
import
net.mamoe.mirai.utils.Tested
import
net.mamoe.mirai.utils.hexToBytes
import
java.io.DataOutputStream
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import
net.mamoe.mirai.network.protocol.tim.packet.PacketId
import
net.mamoe.mirai.utils.*
/**
* Password submission (0836_622)
*
* @author Him188moe
*/
@PacketId
(
"08 36 31 03"
)
@Tested
...
...
@@ -21,10 +20,11 @@ class ClientPasswordSubmissionPacket(
private
val
loginTime
:
Int
,
private
val
loginIP
:
String
,
private
val
privateKey
:
ByteArray
,
//16 random by client
private
val
token0825
:
ByteArray
//56 from server
private
val
token0825
:
ByteArray
,
//56 from server
private
val
randomDeviceName
:
Boolean
)
:
ClientPacket
()
{
override
fun
encode
()
{
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
passwordSubmissionTLV1
)
...
...
@@ -36,7 +36,7 @@ class ClientPasswordSubmissionPacket(
//TODO shareKey 极大可能为 publicKey, key0836 计算得到
this
.
encryptAndWrite
(
TIMProtocol
.
shareKey
.
hexToBytes
())
{
writePart1
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
)
writePart1
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
randomDeviceName
)
writePart2
()
}
}
...
...
@@ -46,16 +46,13 @@ class ClientPasswordSubmissionPacket(
//但为简化处理, 特固定这个 id
@PacketId
(
"08 36 31 04"
)
class
ClientLoginResendPacket3104
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
,
token0825
:
ByteArray
,
token00BA
:
ByteArray
,
tlv0006
:
ByteArray
?
=
null
)
:
ClientLoginResendPacket
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
token00BA
,
tlv0006
)
class
ClientLoginResendPacket3104
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
,
token0825
:
ByteArray
,
token00BA
:
ByteArray
,
randomDeviceName
:
Boolean
,
tlv0006
:
IoBuffer
?
=
null
)
:
ClientLoginResendPacket
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
token00BA
,
randomDeviceName
,
tlv0006
)
@PacketId
(
"08 36 31 05"
)
class
ClientLoginResendPacket3105
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
,
token0825
:
ByteArray
,
token00BA
:
ByteArray
)
:
ClientLoginResendPacket
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
token00BA
,
null
)
class
ClientLoginResendPacket3105
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
,
token0825
:
ByteArray
,
token00BA
:
ByteArray
,
randomDeviceName
:
Boolean
,
tlv0006
:
IoBuffer
?
=
null
)
:
ClientLoginResendPacket
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
token00BA
,
randomDeviceName
,
tlv0006
)
@PacketId
(
"08 36 31 06"
)
class
ClientLoginResendPacket3106
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
,
token0825
:
ByteArray
,
token00BA
:
ByteArray
,
tlv0006
:
ByteArray
?
=
null
)
:
ClientLoginResendPacket
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
token00BA
,
tlv0006
)
class
ClientLoginResendPacket3106
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
,
token0825
:
ByteArray
,
token00BA
:
ByteArray
,
randomDeviceName
:
Boolean
,
tlv0006
:
IoBuffer
?
=
null
)
:
ClientLoginResendPacket
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
token00BA
,
randomDeviceName
,
tlv0006
)
open
class
ClientLoginResendPacket
constructor
(
...
...
@@ -66,9 +63,10 @@ open class ClientLoginResendPacket constructor(
private
val
privateKey
:
ByteArray
,
private
val
token0825
:
ByteArray
,
private
val
token00BA
:
ByteArray
,
private
val
tlv0006
:
ByteArray
?
=
null
private
val
randomDeviceName
:
Boolean
=
false
,
private
val
tlv0006
:
IoBuffer
?
=
null
)
:
ClientPacket
()
{
override
fun
encode
()
{
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
passwordSubmissionTLV1
)
...
...
@@ -79,13 +77,14 @@ open class ClientLoginResendPacket constructor(
this
.
writeHex
(
TIMProtocol
.
key0836
)
//16
this
.
encryptAndWrite
(
TIMProtocol
.
shareKey
.
hexToBytes
())
{
writePart1
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
tlv0006
)
writePart1
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
randomDeviceName
,
tlv0006
)
writeHex
(
"01 10"
)
//tag
writeHex
(
"00 3C"
)
//length
writeHex
(
"00 01"
)
//tag
writeHex
(
"00 38"
)
//length
write
(
token00BA
)
//value
writeHex
(
"01 10"
)
writeHex
(
"00 3C"
)
writeHex
(
"00 01"
)
writeHex
(
"00 38"
)
writeFully
(
token00BA
)
writePart2
()
}
...
...
@@ -93,32 +92,40 @@ open class ClientLoginResendPacket constructor(
}
/**
* @author Him188moe
*/
private
fun
DataOutputStream
.
writePart1
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
,
token0825
:
ByteArray
,
tlv0006
:
ByteArray
?
=
null
)
{
private
fun
BytePacketBuilder
.
writePart1
(
qq
:
Long
,
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
this
.
write
(
token0825
)
//length
this
.
write
Fully
(
token0825
)
//length
this
.
writeHex
(
"03 0F"
)
//tag
this
.
writeDeviceName
(
fals
e
)
this
.
writeDeviceName
(
randomDeviceNam
e
)
this
.
writeHex
(
"00 05 00 06 00 02"
)
this
.
writeQQ
(
qq
)
this
.
writeHex
(
"00 06"
)
//tag
this
.
writeHex
(
"00 78"
)
//length
if
(
tlv0006
!=
null
)
{
this
.
write
(
tlv0006
)
MiraiLogger
.
logDebug
(
"tlv0006!=null"
)
this
.
writeFully
(
tlv0006
)
}
else
{
MiraiLogger
.
logDebug
(
"tlv0006==null"
)
this
.
writeTLV0006
(
qq
,
password
,
loginTime
,
loginIP
,
privateKey
)
}
//fix
this
.
writeHex
(
TIMProtocol
.
passwordSubmissionTLV2
)
this
.
writeHex
(
"00 1A"
)
//tag
this
.
writeHex
(
"00 40"
)
//length
this
.
write
(
TEA
.
encrypt
(
TIMProtocol
.
passwordSubmissionTLV2
.
hexToBytes
(),
privateKey
))
this
.
write
Fully
(
TEA
.
encrypt
(
TIMProtocol
.
passwordSubmissionTLV2
.
hexToBytes
(),
privateKey
))
this
.
writeHex
(
TIMProtocol
.
constantData1
)
this
.
writeHex
(
TIMProtocol
.
constantData2
)
this
.
writeQQ
(
qq
)
...
...
@@ -133,7 +140,7 @@ private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: I
}
private
fun
DataOutputStream
.
writePart2
()
{
private
fun
BytePacketBuilder
.
writePart2
()
{
this
.
writeHex
(
"03 12"
)
//tag
this
.
writeHex
(
"00 05"
)
//length
...
...
@@ -161,9 +168,8 @@ private fun DataOutputStream.writePart2() {
//value
this
.
writeHex
(
"E9 AA 2B 4D 26 4C 76 18 FE 59 D5 A9 82 6A 0C 04 B4 49 50 D7 9B B1 FE 5D 97 54 8D 82 F3 22 C2 48 B9 C9 22 69 CA 78 AD 3E 2D E9 C9 DF A8 9E 7D 8C 8D 6B DF 4C D7 34 D0 D3"
)
this
.
writeHex
(
"00 14"
)
//length
writeCRC32
()
this
.
writeHex
(
"00 14"
)
this
.
writeCRC32
()
}
mirai-core/src/
jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/LoginState
.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/LoginResult
.kt
View file @
d89f8acc
package
net.mamoe.mirai.network.protocol.tim.packet.login
/**
* @author Him188moe
*/
enum
class
LoginState
{
enum
class
LoginResult
{
/**
* 登录成功
*/
...
...
@@ -39,6 +36,11 @@ enum class LoginState {
*/
UNKNOWN
,
/**
* 未知. 更换服务器或等几分钟再登录可能解决.
*/
INTERNAL_ERROR
,
/**
* 超时
*/
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/login/SKey.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/login/SKey.kt
View file @
d89f8acc
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.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
java.io.DataInputStream
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
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.setId
import
net.mamoe.mirai.utils.*
/**
* SKey 用于 http api
*
* @author Him188moe
*/
@PacketId
(
"00 1D"
)
class
ClientSKeyRequestPacket
(
private
val
qq
:
Long
,
private
val
sessionKey
:
ByteArray
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
writeRandom
(
2
)
//part of packet id
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
getRandomByteArray
(
2
).
toUHexString
()
}
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer2
)
this
.
encryptAndWrite
(
sessionKey
)
{
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
writeQQ
(
qq
)
writeHex
(
TIMProtocol
.
fixVer2
)
encryptAndWrite
(
sessionKey
)
{
writeHex
(
"33 00 05 00 08 74 2E 71 71 2E 63 6F 6D 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 0C 6A 75 62 61 6F 2E 71 71 2E 63 6F 6D 00 09 6B 65 2E 71 71 2E 63 6F 6D"
)
}
}
}
/**
* @author Him188moe
*/
@PacketId
(
"00 1D"
)
class
ClientSKeyRefreshmentRequestPacket
(
private
val
qq
:
Long
,
private
val
sessionKey
:
ByteArray
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
writeRandom
(
2
)
//part of packet id
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
getRandomByteArray
(
2
).
toUHexString
()
}
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
encryptAndWrite
(
sessionKey
)
{
writeHex
(
"33 00 05 00 08 74 2E 71 71 2E 63 6F 6D 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 0C 6A 75 62 61 6F 2E 71 71 2E 63 6F 6D 00 09 6B 65 2E 71 71 2E 63 6F 6D"
)
...
...
@@ -45,18 +48,16 @@ class ClientSKeyRefreshmentRequestPacket(
}
}
/**
* @author Him188moe
*/
class
ServerSKeyResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
class
ServerSKeyResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
lateinit
var
sKey
:
String
override
fun
decode
()
{
this
.
sKey
=
String
(
this
.
input
.
goto
(
4
).
readNBytes
(
10
))
override
fun
decode
()
=
with
(
input
)
{
discardExact
(
4
)
sKey
=
this
.
readString
(
10
)
//todo test
MiraiLogger
.
logDebug
(
"SKey=$sKey"
)
}
class
Encrypted
(
inputStream
:
DataInputStream
)
:
ServerPacket
(
inputStream
)
{
class
Encrypted
(
inputStream
:
ByteReadPacket
)
:
ServerPacket
(
inputStream
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerSKeyResponsePacket
=
ServerSKeyResponsePacket
(
this
.
decryptBy
(
sessionKey
)).
setId
(
this
.
idHex
)
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
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.setId
import
net.mamoe.mirai.utils.*
import
kotlin.properties.Delegates
sealed
class
ServerLoginResponsePacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
class
ServerLoginResponseFailedPacket
(
val
loginResult
:
LoginResult
,
input
:
ByteReadPacket
)
:
ServerLoginResponsePacket
(
input
)
/**
* 服务器进行加密后返回 privateKey
*
* @author NaturalHG
*/
@PacketId
(
"08 36 31 03"
)
class
ServerLoginResponseKeyExchangePacket
(
input
:
ByteReadPacket
,
val
flag
:
Flag
)
:
ServerLoginResponsePacket
(
input
)
{
enum
class
Flag
{
`
08
36
31
03
`
,
OTHER
,
}
lateinit
var
tlv0006
:
IoBuffer
//120bytes
var
tokenUnknown
:
ByteArray
?
=
null
lateinit
var
privateKeyUpdate
:
ByteArray
//16bytes
@Tested
override
fun
decode
()
{
this
.
input
.
discardExact
(
5
)
//01 00 1E 00 10
privateKeyUpdate
=
this
.
input
.
readBytes
(
0
x10
)
//22
this
.
input
.
discardExact
(
4
)
//00 06 00 78
tlv0006
=
this
.
input
.
readIoBuffer
(
0
x78
)
when
(
flag
)
{
Flag
.
`
08
36
31
03
`
->
{
//TODO 在解析时分类而不是在这里
this
.
input
.
discardExact
(
8
)
//01 10 00 3C 00 01 00 38
tokenUnknown
=
this
.
input
.
readBytes
(
56
)
//println(tokenUnknown!!.toUHexString())
}
Flag
.
OTHER
->
{
//do nothing in this packet.
//[this.token] will be set in [BotNetworkHandler]
//token
}
}
}
class
Encrypted
(
input
:
ByteReadPacket
,
private
val
flag
:
Flag
)
:
ServerPacket
(
input
)
{
@Tested
fun
decrypt
(
privateKey
:
ByteArray
):
ServerLoginResponseKeyExchangePacket
=
ServerLoginResponseKeyExchangePacket
(
this
.
decryptBy
(
TIMProtocol
.
shareKey
,
privateKey
),
flag
).
setId
(
this
.
idHex
)
}
}
enum
class
Gender
(
val
id
:
Boolean
)
{
MALE
(
false
),
FEMALE
(
true
);
}
/**
* @author NaturalHG
*/
class
ServerLoginResponseSuccessPacket
(
input
:
ByteReadPacket
)
:
ServerLoginResponsePacket
(
input
)
{
lateinit
var
sessionResponseDecryptionKey
:
IoBuffer
//16 bytes|
lateinit
var
token38
:
IoBuffer
//56
lateinit
var
token88
:
IoBuffer
//136
lateinit
var
encryptionKey
:
IoBuffer
//16
lateinit
var
nickname
:
String
var
age
:
Short
by
Delegates
.
notNull
()
lateinit
var
gender
:
Gender
@Tested
override
fun
decode
()
=
with
(
input
)
{
discardExact
(
7
)
//00 01 09 00 70 00 01
encryptionKey
=
readIoBuffer
(
16
)
//C6 72 C7 73 70 01 46 A2 11 88 AC E4 92 7B BF 90
discardExact
(
2
)
//00 38
token38
=
readIoBuffer
(
56
)
discardExact
(
60
)
//00 20 01 60 C5 A1 39 7A 12 8E BC 34 C3 56 70 E3 1A ED 20 67 ED A9 DB 06 C1 70 81 3C 01 69 0D FF 63 DA 00 00 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6
discardExact
(
when
(
val
flag
=
readBytes
(
2
).
toUHexString
())
{
"01 07"
->
0
"00 33"
->
28
"01 10"
->
64
else
->
throw
IllegalStateException
(
flag
)
})
discardExact
(
23
+
3
)
//01 D3 00 01 00 16 00 00 00 01 00 00 00 64 00 00 0D DE 00 09 3A 80 00
discardExact
(
2
)
//00 02
sessionResponseDecryptionKey
=
readIoBuffer
(
16
)
discardExact
(
2
)
token88
=
readIoBuffer
(
136
)
discardExact
(
299
)
//2E 72 7A 50 41 54 5B 62 7D 47 5D 37 41 53 47 51 00 78 00 01 5D A2 DB 79 00 70 72 E7 D3 4E 6F D8 D1 DD F2 67 04 1D 23 4D E9 A7 AB 89 7A B7 E6 4B C0 79 60 3B 4F AA 31 C5 24 51 C1 4B 4F A4 32 74 BA FE 8E 06 DB 54 25 A2 56 91 E8 66 BB 23 29 EB F7 13 7B 94 1E AF B2 40 4E 69 5C 8C 35 04 D1 25 1F 60 93 F3 40 71 0B 61 60 F1 B6 A9 7A E8 B1 DA 0E 16 A2 F1 2D 69 5A 01 20 7A AB A7 37 68 D2 1A B0 4D 35 D1 E1 35 64 F6 90 2B 00 83 01 24 5B 4E 69 3D 45 54 6B 29 5E 73 23 2D 4E 42 3F 00 70 00 01 5D A2 DB 79 00 68 FD 10 8A 39 51 09 C6 69 CE 09 A4 52 8C 53 D3 B6 87 E1 7B 7E 4E 52 6D BA 9C C4 6E 6D DE 09 99 67 B4 BD 56 71 14 5A 54 01 68 1C 3C AA 0D 76 0B 86 5A C1 F1 BC 5E 0A ED E3 8C 57 86 35 D8 A5 F8 16 01 24 8B 57 56 8C A6 31 6F 65 73 03 DA ED 21 FA 6B 79 32 2B 09 01 E8 D2 D8 F0 7B F1 60 C2 7F 53 5D F6 53 50 8A 43 E2 23 2E 52 7B 60 39 56 67 2D 6A 23 43 4B 60 55 68 35 01 08 00 23 00 01 00 1F 00 17 02 5B
val
nickLength
=
readUByte
().
toInt
()
nickname
=
readString
(
nickLength
)
//后文
//00 05 00 04 00 00 00 01 01 15 00 10 49 83 5C D9 93 6C 8D FE 09 18 99 37 99 80 68 92
discardExact
(
4
)
//02 13 80 02
age
=
readShort
()
//00 05
discardExact
(
4
)
//00 04 00 00
discardExact
(
2
)
//00 01
gender
=
if
(
readBoolean
())
Gender
.
FEMALE
else
Gender
.
MALE
}
class
Encrypted
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
privateKey
:
ByteArray
):
ServerLoginResponseSuccessPacket
=
ServerLoginResponseSuccessPacket
(
this
.
decryptBy
(
TIMProtocol
.
shareKey
,
privateKey
)).
setId
(
this
.
idHex
)
}
}
/**
* 收到这个包意味着需要验证码登录, 并且能得到验证码图片文件的一部分
*
* @author Him188moe
*/
class
ServerLoginResponseVerificationCodeInitPacket
(
input
:
ByteReadPacket
)
:
ServerLoginResponsePacket
(
input
)
{
lateinit
var
verifyCodePart1
:
IoBuffer
lateinit
var
token00BA
:
ByteArray
var
unknownBoolean
:
Boolean
?
=
null
@Tested
override
fun
decode
()
{
this
.
input
.
discardExact
(
78
)
//println(this.input.readRemainingBytes().toUHexString())
val
verifyCodeLength
=
this
.
input
.
readShort
()
//2bytes
this
.
verifyCodePart1
=
this
.
input
.
readIoBuffer
(
verifyCodeLength
)
this
.
input
.
discardExact
(
1
)
this
.
unknownBoolean
=
this
.
input
.
readByte
().
toInt
()
==
1
this
.
input
.
discardExact
(
this
.
input
.
remaining
-
60
)
this
.
token00BA
=
this
.
input
.
readBytes
(
40
)
}
class
Encrypted
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
fun
decrypt
():
ServerLoginResponseVerificationCodeInitPacket
=
ServerLoginResponseVerificationCodeInitPacket
(
this
.
decryptAsByteArray
(
TIMProtocol
.
shareKey
).
toReadPacket
()).
setId
(
this
.
idHex
)
}
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/login/ServerLoginSuccessPacket.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/login/ServerLoginSuccessPacket.kt
View file @
d89f8acc
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.ServerPacket
import
java.io.DataInputStream
/**
* Congratulations!
...
...
@@ -10,4 +10,4 @@ import java.io.DataInputStream
* @author Him188moe
*/
@PacketId
(
"00 EC"
)
class
ServerLoginSuccessPacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
\ No newline at end of file
class
ServerLoginSuccessPacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/login/Session.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/login/Session.kt
View file @
d89f8acc
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.dataEncode
import
java.io.DataInputStream
import
java.net.InetAddress
/**
* @author Him188moe
*/
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
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.setId
import
net.mamoe.mirai.utils.*
@PacketId
(
"08 28 04 34"
)
class
ClientSessionRequestPacket
(
private
val
qq
:
Long
,
private
val
serverIp
:
String
,
private
val
token38
:
ByteArray
,
private
val
token88
:
ByteArray
,
private
val
encryptionKey
:
ByteArray
private
val
token38
:
IoBuffer
,
private
val
token88
:
IoBuffer
,
private
val
encryptionKey
:
IoBuffer
)
:
ClientPacket
()
{
override
fun
encode
()
{
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
"02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A"
)
this
.
writeHex
(
"00 38"
)
this
.
write
(
token38
)
this
.
write
Fully
(
token38
)
this
.
encryptAndWrite
(
encryptionKey
)
{
writeHex
(
"00 07 00 88"
)
write
(
token88
)
write
Fully
(
token88
)
writeHex
(
"00 0C 00 16 00 02 00 00 00 00 00 00 00 00 00 00"
)
writeIP
(
serverIp
)
writeHex
(
"1F 40 00 00 00 00 00 15 00 30 00 01"
)
//fix1
...
...
@@ -56,44 +54,51 @@ class ClientSessionRequestPacket(
writeHex
(
"68"
)
writeHex
(
"00 00 00 00 00 2D 00 06 00 01"
)
writeIP
(
InetAddress
.
getLocalHost
().
hostAddress
)
//todo random to avoid being banned?
writeIP
(
localIpAddress
()
)
//todo random to avoid being banned?
}
}
}
/**
* @author Him188moe
*/
@PacketId
(
"08 28 04 34"
)
class
ServerSessionKeyResponsePacket
(
inputStream
:
DataInputStream
,
private
val
dataLength
:
In
t
)
:
ServerPacket
(
inputStream
)
{
class
ServerSessionKeyResponsePacket
(
inputStream
:
ByteReadPacke
t
)
:
ServerPacket
(
inputStream
)
{
lateinit
var
sessionKey
:
ByteArray
lateinit
var
tlv0105
:
Byte
Array
override
fun
decode
()
{
when
(
dataLength
)
{
407
->
{
input
.
goto
(
25
)
sessionKey
=
input
.
read
N
Bytes
(
16
)
lateinit
var
tlv0105
:
Byte
ReadPacket
@Tested
override
fun
decode
()
=
with
(
input
)
{
when
(
val
dataLength
=
remaining
)
{
407
L
->
{
input
.
discardExact
(
25
)
//todo test
sessionKey
=
input
.
readBytes
(
16
)
}
439
->
{
input
.
goto
(
63
)
sessionKey
=
input
.
read
N
Bytes
(
16
)
439
L
->
{
input
.
discardExact
(
63
)
sessionKey
=
input
.
readBytes
(
16
)
}
512
,
527
->
{
input
.
goto
(
63
)
sessionKey
=
input
.
readNBytes
(
16
)
tlv0105
=
dataEncode
{
it
.
writeHex
(
"01 05 00 88 00 01 01 02 00 40 02 01 03 3C 01 03 00 00"
)
input
.
goto
(
dataLength
-
122
)
it
.
write
(
input
.
readNBytes
(
56
))
it
.
writeHex
(
"00 40 02 02 03 3C 01 03 00 00"
)
input
.
goto
(
dataLength
-
55
)
it
.
write
(
input
.
readNBytes
(
56
))
502L
,
//?
512L
,
527L
->
{
input
.
discardExact
(
63
)
//00 00 0D 00 06 00 01 00 00 00 00 00 1F 00 22 00 01 D7 EC FC 38 1B 74 6F 91 42 00 B9 DB 69 32 43 EC 8C 02 DC E0 07 35 58 8C 6C FE 43 5D AA 6A 88 E0 00 14 00 04 00 01 00 3C 01 0C 00 73 00 01
sessionKey
=
input
.
readBytes
(
16
)
tlv0105
=
buildPacket
{
writeHex
(
"01 05 00 88 00 01 01 02 00 40 02 01 03 3C 01 03 00 00"
)
input
.
discardExact
(
input
.
remaining
-
122
-
1
)
writeFully
(
input
.
readIoBuffer
(
56
))
writeHex
(
"00 40 02 02 03 3C 01 03 00 00"
)
input
.
discardExact
(
11
)
writeFully
(
input
.
readIoBuffer
(
56
))
}
//todo 这个 tlv0105似乎可以保存起来然后下次登录时使用.
/*
Discarded(63) =00 00 0D 00 06 00 01 00 00 00 00 00 1F 00 22 00 01 F7 AB 01 4B 23 B5 47 FC 79 02 09 E0 19 EF 61 91 14 AD 8F 38 2E 8B D7 47 39 DE FE 84 A7 E5 6E 3D 00 14 00 04 00 01 00 3C 01 0C 00 73 00 01
sessionKey=7E 8C 1D AC 52 64 B8 D0 9A 55 3A A6 DF 53 88 C8
Discarded(301) =76 E4 B8 DD AB 53 02 2B 53 F1 5D A2 DA CB 00 00 00 B4 03 3D 97 B4 D1 3D 97 B4 C7 00 00 00 07 00 30 D4 E2 53 73 2E 00 F6 3F 8E 45 9F 2E 74 63 39 99 B4 AC 3B 40 C8 9A EE B0 62 A8 E1 39 FE 8E 75 EC 28 6C 03 E6 3B 5F F5 6D 50 7D 1E 29 EC 3D 47 85 08 02 04 08 08 08 08 08 04 00 05 01 0E 12 AC F6 01 0E 00 56 00 01 00 52 13 80 42 00 00 02 02 00 00 18 AB 52 CF 5B E8 CD 95 CC 3F 5C A7 BA C9 C1 5D DD F8 E2 6E 0D A3 DF F8 76 00 20 D3 87 6B 1F F2 2B C7 53 38 60 F3 AD 07 82 8B F6 62 3C E0 DB 66 BC AD D0 68 D0 30 9D 8A 41 E7 75 00 0C 00 00 00 01 00 00 00 00 00 00 00 40 00 2F 00 2A 00 01 8F FE 4F BB B2 63 C7 69 C3 F1 3C DC A1 E8 77 A3 DD 97 FA 00 36 04 40 EF 11 7A 31 02 4E 10 13 94 02 28 00 00 00 00 00 00 01 0D 00 2C 00 01 00 28 EF CB 22 58 6F AE DC F5 CC CE 45 EE 6D CA E7 EF 06 3F 60 B5 8A 22 D5 9E 37 FA 92 9F A9 11 68 F0 2A 25 4A 45 C3 D4 56 CF 01 05 00 8A 00 01 02 02 00 41 01 00 01 03 3C 01 03 00 00 FB
56长度=39 89 04 81 64 6B C0 71 B5 6E B0 DF 7D D4 C0 7E 97 83 BC 9F 31 39 39 C3 95 93 D9 CD 48 00 1D 0D 18 52 87 21 B2 C1 B1 AD EF 96 82 D6 D4 57 EA 48 5A 27 8C 14 6F E2 83 00
Discarded(11) =41 01 00 02 03 3C 01 03 00 00 86
*/
}
else
->
throw
IllegalArgumentException
(
dataLength
.
toString
())
...
...
@@ -104,9 +109,8 @@ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val d
}
class
Encrypted
(
inputStream
:
DataInputStream
)
:
ServerPacket
(
inputStream
)
{
fun
decrypt
(
sessionResponseDecryptionKey
:
ByteArray
):
ServerSessionKeyResponsePacket
=
this
.
decryptAsByteArray
(
sessionResponseDecryptionKey
).
let
{
ServerSessionKeyResponsePacket
(
it
.
dataInputStream
(),
it
.
size
).
setId
(
this
.
idHex
)
}
class
Encrypted
(
inputStream
:
ByteReadPacket
)
:
ServerPacket
(
inputStream
)
{
fun
decrypt
(
sessionResponseDecryptionKey
:
IoBuffer
):
ServerSessionKeyResponsePacket
=
ServerSessionKeyResponsePacket
(
this
.
decryptBy
(
sessionResponseDecryptionKey
)).
setId
(
this
.
idHex
)
}
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/login/Touch.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/login/Touch.kt
View file @
d89f8acc
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
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.
*
import
net.mamoe.mirai.
utils.hexToBytes
import
net.mamoe.mirai.
utils.toUHexString
import
java.io.DataInputStream
import
java.io.IOException
import
net.mamoe.mirai.network.protocol.tim.packet.
ClientPacket
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.setId
import
net.mamoe.mirai.utils.*
/**
* The packet received when logging in, used to redirect server address
...
...
@@ -17,7 +21,7 @@ import java.io.IOException
*/
@Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
@PacketId
(
"08 25 31 01"
)
class
ServerTouchResponsePacket
(
inputStream
:
DataInputStream
)
:
ServerPacket
(
inputStream
)
{
class
ServerTouchResponsePacket
(
inputStream
:
ByteReadPacket
)
:
ServerPacket
(
inputStream
)
{
var
serverIP
:
String
?
=
null
var
loginTime
:
Int
=
0
...
...
@@ -30,28 +34,28 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
}
override
fun
decode
()
{
when
(
val
id
=
input
.
readByte
().
toUByte
().
toInt
())
{
0
xFE
->
{
input
.
skip
(
94
)
serverIP
=
input
.
readIP
()
override
fun
decode
()
=
with
(
input
)
{
when
(
val
id
=
readByte
().
toUByte
().
toInt
())
{
0
xFE
->
{
//todo 在 packet 解析时分类而不是在这里分类
discardExact
(
94
)
serverIP
=
readIP
()
}
0
x00
->
{
input
.
skip
(
4
)
token0825
=
input
.
readN
Bytes
(
56
)
input
.
skip
(
6
)
discardExact
(
4
)
token0825
=
read
Bytes
(
56
)
discardExact
(
6
)
loginTime
=
input
.
readInt
()
loginIP
=
input
.
readIP
()
loginTime
=
readInt
()
loginIP
=
readIP
()
}
else
->
{
throw
IllegalStateException
(
arrayOf
(
id
.
toUByte
()).
toUByteArray
().
toUHexString
())
throw
IllegalStateException
(
id
.
toByte
().
toUHexString
())
}
}
}
class
Encrypted
(
private
val
type
:
Type
,
inputStream
:
DataInputStream
)
:
ServerPacket
(
inputStream
)
{
class
Encrypted
(
private
val
type
:
Type
,
inputStream
:
ByteReadPacket
)
:
ServerPacket
(
inputStream
)
{
fun
decrypt
():
ServerTouchResponsePacket
=
ServerTouchResponsePacket
(
decryptBy
(
when
(
type
)
{
Type
.
TYPE_08_25_31_02
->
TIMProtocol
.
redirectionKey
.
hexToBytes
()
...
...
@@ -61,16 +65,13 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
}
/**
* The packet to
t
ouch server, that is, to start the connection to the server.
* The packet to
sendT
ouch server, that is, to start the connection to the server.
*
* @author Him188moe
*/
@PacketId
(
"08 25 31 01"
)
class
ClientTouchPacket
(
private
val
qq
:
Long
,
private
val
serverIp
:
String
)
:
ClientPacket
()
{
@Throws
(
IOException
::
class
)
override
fun
encode
()
{
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer
)
this
.
writeHex
(
TIMProtocol
.
touchKey
)
...
...
@@ -94,13 +95,11 @@ class ClientTouchPacket(private val qq: Long, private val serverIp: String) : Cl
*/
@PacketId
(
"08 25 31 02"
)
class
ClientServerRedirectionPacket
(
private
val
serverIP
:
String
,
private
val
qq
:
Long
)
:
ClientPacket
()
{
override
fun
encode
()
{
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer
)
this
.
writeHex
(
TIMProtocol
.
redirectionKey
)
this
.
encryptAndWrite
(
TIMProtocol
.
redirectionKey
)
{
this
.
writeHex
(
TIMProtocol
.
constantData1
)
this
.
writeHex
(
TIMProtocol
.
constantData2
)
...
...
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/network/protocol/tim/packet/login/VerificationCode.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/network/protocol/tim/packet/login/VerificationCode.kt
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
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
java.io.DataInputStream
import
net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
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.setId
import
net.mamoe.mirai.utils.*
/**
* 客户端请求验证码图片数据的第几部分
*/
@PacketId
(
"00 BA 31"
)
class
ClientVerificationCodeTransmissionRequestPacket
(
private
val
packetId
:
Int
,
private
val
packetId
Last
:
Int
,
//ubyte
private
val
qq
:
Long
,
private
val
token0825
:
ByteArray
,
private
val
verificationSequence
:
Int
,
private
val
token00BA
:
ByteArray
)
:
ClientPacket
()
{
@Tested
override
fun
encode
()
{
this
.
writeByte
(
packetId
)
//part of packet id
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
packetIdLast
.
toByte
().
toUHexString
()
}
@Tested
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer
)
this
.
writeHex
(
TIMProtocol
.
key00BA
)
...
...
@@ -27,13 +34,13 @@ class ClientVerificationCodeTransmissionRequestPacket(
writeHex
(
"00 02 00 00 08 04 01 E0"
)
writeHex
(
TIMProtocol
.
constantData2
)
writeHex
(
"00 00 38"
)
write
(
token0825
)
write
Fully
(
token0825
)
writeHex
(
"01 03 00 19"
)
writeHex
(
TIMProtocol
.
publicKey
)
writeHex
(
"13 00 05 00 00 00 00"
)
write
Byte
(
verificationSequence
)
write
UByte
(
verificationSequence
.
toUByte
()
)
writeHex
(
"00 28"
)
write
(
token00BA
)
write
Fully
(
token00BA
)
writeHex
(
"00 10"
)
writeHex
(
TIMProtocol
.
key00BAFix
)
}
...
...
@@ -45,19 +52,21 @@ class ClientVerificationCodeTransmissionRequestPacket(
*/
@PacketId
(
"00 BA 32"
)
class
ClientVerificationCodeSubmitPacket
(
private
val
packetIdLast
:
Int
,
private
val
packetIdLast
:
Int
,
//ubyte
private
val
qq
:
Long
,
private
val
token0825
:
ByteArray
,
private
val
captcha
:
String
,
private
val
verificationToken
:
ByteArray
private
val
verificationToken
:
IoBuffer
)
:
ClientPacket
()
{
init
{
require
(
captcha
.
length
==
4
)
{
"captcha.length must == 4"
}
}
override
fun
encode
()
{
this
.
writeByte
(
packetIdLast
)
//part of packet id
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
packetIdLast
.
toByte
().
toUHexString
()
}
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer
)
this
.
writeHex
(
TIMProtocol
.
key00BA
)
...
...
@@ -65,25 +74,21 @@ class ClientVerificationCodeSubmitPacket(
writeHex
(
"00 02 00 00 08 04 01 E0"
)
writeHex
(
TIMProtocol
.
constantData2
)
writeHex
(
"01 00 38"
)
write
(
token0825
)
write
Fully
(
token0825
)
writeHex
(
"01 03"
)
writeShort
(
25
)
writeHex
(
TIMProtocol
.
publicKey
)
//25
writeHex
(
"14 00 05 00 00 00 00 00 04"
)
write
(
captcha
.
toUpperCase
().
toByteArray
())
write
StringUtf8
(
captcha
.
toUpperCase
())
writeHex
(
"00 38"
)
write
(
verificationToken
)
write
Fully
(
verificationToken
)
writeShort
(
16
)
writeHex
(
TIMProtocol
.
key00BAFix
)
//16
}
}
override
fun
getFixedId
():
String
{
return
this
.
idHex
+
" "
+
packetIdLast
}
}
/**
...
...
@@ -95,9 +100,11 @@ class ClientVerificationCodeRefreshPacket(
private
val
qq
:
Long
,
private
val
token0825
:
ByteArray
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
writeByte
(
packetIdLast
)
//part of packet id
override
val
idHex
:
String
by
lazy
{
super
.
idHex
+
" "
+
packetIdLast
.
toByte
().
toUHexString
()
}
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
TIMProtocol
.
fixVer
)
this
.
writeHex
(
TIMProtocol
.
key00BA
)
...
...
@@ -105,24 +112,20 @@ class ClientVerificationCodeRefreshPacket(
writeHex
(
"00 02 00 00 08 04 01 E0"
)
writeHex
(
TIMProtocol
.
constantData2
)
writeHex
(
"00 00 38"
)
write
(
token0825
)
write
Fully
(
token0825
)
writeHex
(
"01 03 00 19"
)
writeHex
(
TIMProtocol
.
publicKey
)
writeHex
(
"13 00 05 00 00 00 00 00 00 00 00 10"
)
writeHex
(
TIMProtocol
.
key00BAFix
)
}
}
override
fun
getFixedId
():
String
{
return
this
.
idHex
+
" "
+
packetIdLast
}
}
/**
* 验证码输入错误, 同时也会给一部分验证码
*/
@PacketId
(
"00 BA 32"
)
class
ServerCaptchaWrongPacket
(
input
:
DataInputStream
,
dataSize
:
Int
,
packetId
:
ByteArray
)
:
ServerCaptchaTransmissionPacket
(
input
,
dataSize
,
packetId
)
class
ServerCaptchaWrongPacket
(
input
:
ByteReadPacket
,
packetIdLast
:
Int
)
:
ServerCaptchaTransmissionPacket
(
input
,
packetIdLast
)
/**
* 服务器发送验证码图片文件一部分过来
...
...
@@ -130,38 +133,34 @@ class ServerCaptchaWrongPacket(input: DataInputStream, dataSize: Int, packetId:
* @author Him188moe
*/
@PacketId
(
"00 BA 31"
)
open
class
ServerCaptchaTransmissionPacket
(
input
:
DataInputStream
,
private
val
dataSize
:
Int
,
private
val
packetId
:
ByteArray
)
:
ServerCaptchaPacket
(
input
)
{
open
class
ServerCaptchaTransmissionPacket
(
input
:
ByteReadPacket
,
val
packetIdLast
:
Int
)
:
ServerCaptchaPacket
(
input
)
{
lateinit
var
captchaSectionN
:
ByteArray
lateinit
var
verificationToken
:
ByteArray
//56bytes
lateinit
var
captchaSectionN
:
IoBuffer
lateinit
var
verificationToken
:
IoBuffer
//56bytes
var
transmissionCompleted
:
Boolean
=
false
//验证码是否已经传输完成
lateinit
var
token00BA
:
ByteArray
//40 bytes
var
packetIdLast
:
Int
=
0
override
fun
decode
()
{
this
.
verificationToken
=
this
.
input
.
readNBytesAt
(
10
,
56
)
override
fun
decode
()
=
with
(
input
)
{
input
.
discardExact
(
10
)
//13 00 05 01 00 00 01 23 00 38
verificationToken
=
readIoBuffer
(
56
)
val
length
=
this
.
input
.
readShortAt
(
66
)
this
.
captchaSectionN
=
this
.
input
.
readNBytes
(
length
)
val
length
=
readShort
(
)
captchaSectionN
=
readIoBuffer
(
length
)
this
.
input
.
skip
(
1
)
val
byte
=
this
.
input
.
readByteAt
(
69
+
length
).
toInt
()
this
.
transmissionCompleted
=
byte
==
0
this
.
token00BA
=
this
.
input
.
readNBytesAt
(
dataSize
-
56
-
2
,
40
)
this
.
packetIdLast
=
packetId
[
3
].
toInt
()
}
discardExact
(
1
)
val
byte
=
readByte
().
toInt
()
transmissionCompleted
=
byte
==
0
override
fun
getFixedId
():
String
{
return
this
.
idHex
+
" "
+
packetIdLast
discardExact
(
remaining
-
56
-
2
)
token00BA
=
readBytes
(
40
)
}
}
/*
fun main() {
val data = "13 00 05 01 00 00 01 23 00 38 59 32 29 5A 3E 3D 2D FC F5 22 EB 9E 2D FB 9C 4F AA 06 C8 32 3D F0 3C 2C 2B BA 8D 05 C4 9B C1 74 3B 70 F1 99 90 BB 6E 3E 6F 74 48 97 D3 61 B7 04 C0 A3 F1 DF 40 A4 DC 2B 00 A2 01 2D BB BB E8 FE B8 AF B3 6F 39 7C EA E2 5B 91 BE DB 59 38 CF 58 BC F2 88 F1 09 CF 92 E9 F7 FB 13 76 C5 68 29 23 3F 8E 43 16 2E 50 D7 FA 4D C1 F7 67 EF 27 FB C6 F1 A7 25 A4 BC 45 39 3A EA B2 A5 38 02 FF 4B C9 FF EB BD 89 E5 5D B9 4A 2A BE 5F 52 F1 EB 09 29 CB 3E 66 CF EF 97 89 47 BB 6B E0 7B 4A 3E A1 BC 3F FB F2 0A 83 CB E3 EA B9 43 E1 26 88 03 0B A7 E0 B2 AD 7F 83 CC DA 74 85 83 72 08 EC D2 F9 95 05 15 05 96 F7 1C FF 00 82 C3 90 22 A4 BA 90 D5 00 00 00 00 49 45 4E 44 AE 42 60 82 03 00 00 28 EA 32 5A 85 C8 D2 73 B3 40 39 77 85 65 98 00 FE 03 A2 A5 95 B4 2F E6 79 7A DE 5A 03 10 C8 3D BF 6D 3D 8B 51 84 C2 6D 49 00 10 92 AA 69 FB C6 3D 60 5A 7A A4 AC 7A B0 71 00 36".hexToBytes()
ServerVerificationCodeTransmissionPacket(data.
dataInputStream
(), data.size, "00 BA 31 01".hexToBytes()).let {
ServerVerificationCodeTransmissionPacket(data.
toReadPacket
(), data.size, "00 BA 31 01".hexToBytes()).let {
it.dataDecode()
println(it.toString())
}
...
...
@@ -173,35 +172,31 @@ fun main() {
* @author Him188moe
*/
@PacketId
(
"00 BA 32"
)
class
ServerCaptchaCorrectPacket
(
input
:
DataInputStream
)
:
ServerCaptchaPacket
(
input
)
{
class
ServerCaptchaCorrectPacket
(
input
:
ByteReadPacket
)
:
ServerCaptchaPacket
(
input
)
{
lateinit
var
token00BA
:
ByteArray
//56 bytes
override
fun
decode
()
{
token00BA
=
this
.
input
.
readNBytesAt
(
10
,
56
)
override
fun
decode
()
=
with
(
input
)
{
discardExact
(
10
)
//14 00 05 00 00 00 00 00 00 38
token00BA
=
readBytes
(
56
)
}
}
abstract
class
ServerCaptchaPacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
abstract
class
ServerCaptchaPacket
(
input
:
ByteReadPacket
)
:
ServerPacket
(
input
)
{
@PacketId
(
"00 BA"
)
class
Encrypted
(
input
:
DataInputStream
,
private
val
id
:
String
)
:
ServerPacket
(
input
)
{
class
Encrypted
(
input
:
ByteReadPacket
,
override
var
idHex
:
String
)
:
ServerPacket
(
input
)
{
fun
decrypt
():
ServerCaptchaPacket
{
val
data
=
this
.
decryptAsByteArray
(
TIMProtocol
.
key00BA
)
if
(
id
.
startsWith
(
"00 BA 32"
))
{
if
(
id
Hex
.
startsWith
(
"00 BA 32"
))
{
return
when
(
data
.
size
)
{
66
,
95
->
ServerCaptchaCorrectPacket
(
data
.
dataInputStream
())
//66 -> ServerVerificationCodeUnknownPacket(data.
dataInputStream
())
else
->
return
ServerCaptchaWrongPacket
(
data
.
dataInputStream
(),
data
.
size
,
this
.
input
.
readNBytesAt
(
3
,
4
))
95
->
ServerCaptchaCorrectPacket
(
data
.
toReadPacket
())
//66 -> ServerVerificationCodeUnknownPacket(data.
toReadPacket
())
else
->
return
ServerCaptchaWrongPacket
(
data
.
toReadPacket
(),
idHex
.
substringAfterLast
(
" "
).
toInt
(
16
))
}.
setId
(
this
.
idHex
)
}
return
ServerCaptchaTransmissionPacket
(
data
.
dataInputStream
(),
data
.
size
,
this
.
input
.
readNBytesAt
(
3
,
4
)).
setId
(
this
.
idHex
)
return
ServerCaptchaTransmissionPacket
(
data
.
toReadPacket
(),
idHex
.
substringAfterLast
(
" "
).
toInt
(
16
)).
setId
(
this
.
idHex
)
}
override
fun
getFixedId
():
String
=
this
.
getFixedId
(
id
)
}
}
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/utils/BotAccount.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/utils/BotAccount.kt
View file @
d89f8acc
package
net.mamoe.mirai.utils
/**
* @author Him188moe
*/
data class
BotAccount
(
val
qqNumber
:
Long
,
//实际上是 UInt
val
password
:
String
//todo 不保存 password?
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteArrayUtil.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.utils
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.String
import
kotlin.jvm.JvmOverloads
@JvmOverloads
fun
ByteArray
.
toHexString
(
separator
:
String
=
" "
):
String
=
this
.
joinToString
(
separator
)
{
var
ret
=
it
.
toString
(
16
).
toUpperCase
()
if
(
ret
.
length
==
1
)
{
ret
=
"0$ret"
}
return
@
joinToString
ret
}
@JvmOverloads
fun
ByteArray
.
toUHexString
(
separator
:
String
=
" "
):
String
=
this
.
toUByteArray
().
toUHexString
(
separator
)
fun
ByteArray
.
stringOf
():
String
=
String
(
this
)
//@JvmSynthetic TODO 等待 kotlin 修复 bug 后添加这个注解
@JvmOverloads
fun
UByteArray
.
toUHexString
(
separator
:
String
=
" "
):
String
=
this
.
joinToString
(
separator
)
{
var
ret
=
it
.
toString
(
16
).
toUpperCase
()
if
(
ret
.
length
==
1
)
{
ret
=
"0$ret"
}
return
@
joinToString
ret
}
fun
ByteArray
.
toReadPacket
()
=
ByteReadPacket
(
this
)
fun
<
R
>
ByteArray
.
read
(
t
:
ByteReadPacket
.()
->
R
):
R
=
this
.
toReadPacket
().
run
(
t
)
fun
ByteArray
.
cutTail
(
length
:
Int
):
ByteArray
=
this
.
copyOfRange
(
0
,
this
.
size
-
length
)
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.utils
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.action.ServerCanAddFriendResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendFriendMessageResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendGroupMessageResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.login.*
fun
ByteReadPacket
.
readRemainingBytes
(
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
):
IoBuffer
=
IoBuffer
.
Pool
.
borrow
().
also
{
this
.
readFully
(
it
,
n
)
}
fun
ByteReadPacket
.
readIoBuffer
(
n
:
Number
)
=
this
.
readIoBuffer
(
n
.
toInt
())
//必须消耗完 packet
fun
ByteReadPacket
.
parseServerPacket
(
size
:
Int
):
ServerPacket
{
//TODO 优化
discardExact
(
3
)
val
idHex
=
readInt
().
toUHexString
(
" "
)
discardExact
(
7
)
//4 for qq number, 3 for 0x00 0x00 0x00. 但更可能是应该 discard 8
return
when
(
idHex
)
{
"08 25 31 01"
->
ServerTouchResponsePacket
.
Encrypted
(
ServerTouchResponsePacket
.
Type
.
TYPE_08_25_31_01
,
this
)
"08 25 31 02"
->
ServerTouchResponsePacket
.
Encrypted
(
ServerTouchResponsePacket
.
Type
.
TYPE_08_25_31_02
,
this
)
"08 36 31 03"
,
"08 36 31 04"
,
"08 36 31 05"
,
"08 36 31 06"
->
{
when
(
size
)
{
271
,
207
->
return
ServerLoginResponseKeyExchangePacket
.
Encrypted
(
this
,
when
(
idHex
)
{
"08 36 31 03"
->
ServerLoginResponseKeyExchangePacket
.
Flag
.
`
08
36
31
03
`
else
->
ServerLoginResponseKeyExchangePacket
.
Flag
.
OTHER
}).
setId
(
idHex
)
871
->
return
ServerLoginResponseVerificationCodeInitPacket
.
Encrypted
(
this
).
setId
(
idHex
)
}
if
(
size
>
700
)
{
return
ServerLoginResponseSuccessPacket
.
Encrypted
(
this
).
setId
(
idHex
)
}
println
(
size
)
return
ServerLoginResponseFailedPacket
(
when
(
size
)
{
135
->
{
//包数据错误. 目前怀疑是 tlv0006
this
.
readRemainingBytes
().
cutTail
(
1
).
decryptBy
(
TIMProtocol
.
shareKey
).
read
{
discardExact
(
51
)
MiraiLogger
.
logError
(
"Internal logError: "
+
readLVString
())
//抱歉,请重新输入密码。
}
LoginResult
.
INTERNAL_ERROR
}
//可能是包数据错了. 账号没有被ban, 用TIM官方可以登录
319
,
351
->
LoginResult
.
WRONG_PASSWORD
//135 -> LoginState.RETYPE_PASSWORD
63
,
279
->
LoginResult
.
BLOCKED
263
->
LoginResult
.
UNKNOWN_QQ_NUMBER
551
,
487
->
LoginResult
.
DEVICE_LOCK
343
,
359
->
LoginResult
.
TAKEN_BACK
else
->
LoginResult
.
UNKNOWN
/*
//unknown
63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown logError)")
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown logError)")
else -> throw IllegalArgumentException(bytes.size.toString())*/
},
this
).
setId
(
idHex
)
}
"08 28 04 34"
->
ServerSessionKeyResponsePacket
.
Encrypted
(
this
)
else
->
when
(
idHex
.
substring
(
0
,
5
))
{
"00 EC"
->
ServerLoginSuccessPacket
(
this
)
"00 1D"
->
ServerSKeyResponsePacket
.
Encrypted
(
this
)
"00 5C"
->
ServerAccountInfoResponsePacket
.
Encrypted
(
this
)
"00 58"
->
ServerHeartbeatResponsePacket
(
this
)
"00 BA"
->
ServerCaptchaPacket
.
Encrypted
(
this
,
idHex
)
"00 CE"
,
"00 17"
->
ServerEventPacket
.
Raw
.
Encrypted
(
this
,
idHex
.
hexToBytes
())
"00 81"
->
ServerFieldOnlineStatusChangedPacket
.
Encrypted
(
this
)
"00 CD"
->
ServerSendFriendMessageResponsePacket
(
this
)
"00 02"
->
ServerSendGroupMessageResponsePacket
(
this
)
"00 A7"
->
ServerCanAddFriendResponsePacket
(
this
)
"03 88"
->
ServerTryGetImageIDResponsePacket
.
Encrypted
(
this
)
else
->
UnknownServerPacket
.
Encrypted
(
this
)
}
}.
setId
(
idHex
)
}
fun
ByteReadPacket
.
readIP
():
String
=
buildString
(
4
+
3
)
{
repeat
(
4
)
{
val
byte
=
readUByte
()
this
.
append
(
byte
.
toString
())
if
(
it
!=
3
)
this
.
append
(
"."
)
}
}
fun
ByteReadPacket
.
readLVString
():
String
=
String
(
this
.
readLVByteArray
())
fun
ByteReadPacket
.
readLVByteArray
():
ByteArray
=
this
.
readBytes
(
this
.
readShort
().
toInt
())
fun
ByteReadPacket
.
readTLVMap
(
expectingEOF
:
Boolean
=
false
):
Map
<
Int
,
ByteArray
>
{
val
map
=
mutableMapOf
<
Int
,
ByteArray
>()
var
type
:
UByte
try
{
type
=
readUByte
()
}
catch
(
e
:
EOFException
)
{
if
(
expectingEOF
)
{
return
map
}
throw
e
}
while
(
type
!=
UByte
.
MAX_VALUE
)
{
map
[
type
.
toInt
()]
=
this
.
readLVByteArray
()
try
{
type
=
readUByte
()
}
catch
(
e
:
EOFException
)
{
if
(
expectingEOF
)
{
return
map
}
throw
e
}
}
return
map
}
fun
Map
<
Int
,
ByteArray
>.
printTLVMap
(
name
:
String
)
=
debugPrintln
(
"TLVMap $name= "
+
this
.
mapValues
{
(
_
,
value
)
->
value
.
toUHexString
()
})
fun
ByteReadPacket
.
readString
(
length
:
Number
):
String
=
String
(
this
.
readBytes
(
length
.
toInt
()))
private
const
val
TRUE_BYTE_VALUE
:
Byte
=
1
fun
ByteReadPacket
.
readBoolean
():
Boolean
=
this
.
readByte
()
==
TRUE_BYTE_VALUE
fun
ByteReadPacket
.
readLVNumber
():
Number
{
return
when
(
this
.
readShort
().
toInt
())
{
1
->
this
.
readByte
()
2
->
this
.
readShort
()
4
->
this
.
readInt
()
8
->
this
.
readLong
()
else
->
throw
UnsupportedOperationException
()
}
}
//添加@JvmSynthetic 导致 idea 无法检查这个文件的错误
//@JvmSynthetic
@Deprecated
(
"Low efficiency"
,
ReplaceWith
(
""
))
fun
ByteReadPacket
.
gotoWhere
(
matcher
:
UByteArray
):
ByteReadPacket
{
return
this
.
gotoWhere
(
matcher
.
toByteArray
())
}
/**
* 去往下一个含这些连续字节的位置
*/
@Deprecated
(
"Low efficiency"
,
ReplaceWith
(
""
))
fun
ByteReadPacket
.
gotoWhere
(
matcher
:
ByteArray
):
ByteReadPacket
{
require
(
matcher
.
isNotEmpty
())
loop
@
do
{
val
byte
=
this
.
readByte
()
if
(
byte
==
matcher
[
0
])
{
//todo mark here
for
(
i
in
1
until
matcher
.
size
)
{
val
b
=
this
.
readByte
()
if
(
b
!=
matcher
[
i
])
{
continue
@
loop
//todo goto mark
}
}
return
this
}
}
while
(
true
)
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlinx.io.core.BytePacketBuilder
import
kotlinx.io.core.toByteArray
import
kotlinx.io.core.writeFully
private
const
val
GTK_BASE_VALUE
:
Int
=
5381
@ExperimentalStdlibApi
internal
fun
getGTK
(
sKey
:
String
):
Int
{
var
value
=
GTK_BASE_VALUE
for
(
c
in
sKey
.
toCharArray
())
{
value
+=
(
value
shl
5
)
+
c
.
toInt
()
}
value
=
value
and
Int
.
MAX_VALUE
return
value
}
@Tested
fun
BytePacketBuilder
.
writeCRC32
()
=
writeCRC32
(
getRandomByteArray
(
16
))
fun
BytePacketBuilder
.
writeCRC32
(
key
:
ByteArray
)
{
writeFully
(
key
)
//key
writeInt
(
crc32
(
key
))
}
fun
md5
(
str
:
String
):
ByteArray
=
md5
(
str
.
toByteArray
())
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/CaptchaResolver.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlinx.io.core.IoBuffer
/**
* 让用户处理验证码
*
* @return 用户输入得到的验证码. 非 null 时一定 `length==4`.
*/
internal
expect
suspend
fun
solveCaptcha
(
captchaBuffer
:
IoBuffer
):
String
?
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/utils/ContactList.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/utils/ContactList.kt
View file @
d89f8acc
...
...
@@ -2,7 +2,5 @@ package net.mamoe.mirai.utils
import
net.mamoe.mirai.contact.Contact
/**
* @author Him188moe
*/
class
ContactList
<
C
:
Contact
>
:
MutableMap
<
Long
,
C
>
by
mutableMapOf
()
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DebugUtil.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlinx.io.core.Input
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.core.readBytes
internal
object
DebugLogger
:
MiraiLogger
by
PlatformLogger
(
"Packet Debug"
)
internal
fun
debugPrintln
(
any
:
Any
?)
=
DebugLogger
.
logPurple
(
any
)
@Deprecated
(
"Debugging Warning"
,
ReplaceWith
(
""
))
internal
fun
ByteArray
.
debugPrint
(
name
:
String
):
ByteArray
{
DebugLogger
.
logPurple
(
name
+
"="
+
this
.
toUHexString
())
return
this
}
@Deprecated
(
"Debugging Warning"
,
ReplaceWith
(
""
))
internal
fun
IoBuffer
.
debugPrint
(
name
:
String
):
IoBuffer
{
val
readBytes
=
this
.
readBytes
()
DebugLogger
.
logPurple
(
name
+
"="
+
readBytes
.
toUHexString
())
return
readBytes
.
toIoBuffer
()
}
@Deprecated
(
"Debugging Warning"
,
ReplaceWith
(
""
))
internal
fun
Input
.
debugDiscardExact
(
n
:
Number
,
name
:
String
=
""
)
{
DebugLogger
.
logPurple
(
"Discarded($n) $name="
+
this
.
readBytes
(
n
.
toInt
()).
toUHexString
())
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/IteratableUtil.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
inline
fun
<
T
>
MutableIterable
<
T
>.
removeIfInlined
(
predicate
:
(
T
)
->
Boolean
)
=
iterator
().
removeIfInlined
(
predicate
)
inline
fun
<
T
>
MutableIterator
<
T
>.
removeIfInlined
(
predicate
:
(
T
)
->
Boolean
)
{
while
(
this
.
hasNext
())
{
if
(
predicate
(
this
.
next
()))
{
this
.
remove
()
}
}
}
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/utils/LoggerTextFormat.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/utils/LoggerTextFormat.kt
View file @
d89f8acc
File moved
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginConfigure.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
net.mamoe.mirai.network.protocol.tim.packet.login.ServerTouchResponsePacket
class
LoginConfiguration
{
/**
* 等待 [ServerTouchResponsePacket] 的时间
*/
var
touchTimeoutMillis
:
Long
=
2000
var
randomDeviceName
:
Boolean
=
false
companion
object
{
val
Default
=
LoginConfiguration
()
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlin.jvm.JvmOverloads
interface
MiraiLogger
{
companion
object
:
MiraiLogger
by
PlatformLogger
(
"[TOP Level]"
)
var
identity
:
String
?
fun
logInfo
(
any
:
Any
?)
=
log
(
any
)
fun
log
(
e
:
Throwable
)
fun
log
(
any
:
Any
?)
fun
logError
(
any
:
Any
?)
fun
logDebug
(
any
:
Any
?)
fun
logCyan
(
any
:
Any
?)
fun
logPurple
(
any
:
Any
?)
fun
logGreen
(
any
:
Any
?)
fun
logBlue
(
any
:
Any
?)
}
expect
class
PlatformLogger
@JvmOverloads
constructor
(
identity
:
String
?
=
null
)
:
MiraiLogger
fun
Throwable
.
log
()
=
MiraiLogger
.
log
(
this
)
fun
Throwable
.
printStacktrace
()
=
this
.
log
()
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/mirai/utils/ClientLogin
Status.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.mirai/utils/Online
Status.kt
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_UNSIGNED_LITERALS"
,
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.utils
/**
...
...
@@ -7,12 +8,21 @@ package net.mamoe.mirai.utils
* @author Him188moe
* @see net.mamoe.mirai.network.protocol.tim.packet.login.ClientChangeOnlineStatusPacket
*/
enum
class
ClientLoginStatus
(
// TODO: 2019/8/31 add more ClientLoginStatus
enum
class
OnlineStatus
(
val
id
:
UByte
//1 ubyte
)
{
/**
* 我在线上
*/
ONLINE
(
0
x0Au
)
ONLINE
(
0
x0Au
),
/**
* 忙碌
*/
BUSY
(
0
x32u
);
companion
object
{
fun
ofId
(
id
:
UByte
):
OnlineStatus
=
values
().
first
{
it
.
id
==
id
}
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PacketBuilderUtil.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.utils
import
kotlinx.io.core.*
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
kotlin.random.Random
import
kotlin.random.nextInt
fun
BytePacketBuilder
.
writeZero
(
count
:
Int
)
=
repeat
(
count
)
{
this
.
writeByte
(
0
)
}
fun
BytePacketBuilder
.
writeRandom
(
length
:
Int
)
=
repeat
(
length
)
{
this
.
writeByte
(
Random
.
Default
.
nextInt
(
255
).
toByte
())
}
fun
BytePacketBuilder
.
writeQQ
(
qq
:
Long
)
=
this
.
writeUInt
(
qq
.
toUInt
())
fun
BytePacketBuilder
.
writeGroup
(
groupIdOrGroupNumber
:
Long
)
=
this
.
writeFully
(
groupIdOrGroupNumber
.
toUInt
().
toByteArray
())
fun
BytePacketBuilder
.
writeLVByteArray
(
byteArray
:
ByteArray
)
{
this
.
writeShort
(
byteArray
.
size
.
toShort
())
this
.
writeFully
(
byteArray
)
}
fun
BytePacketBuilder
.
writeLVPacket
(
packet
:
ByteReadPacket
)
{
this
.
writeShort
(
packet
.
remaining
.
toShort
())
this
.
writePacket
(
packet
)
}
fun
BytePacketBuilder
.
writeLVPacket
(
builder
:
BytePacketBuilder
.()
->
Unit
)
=
this
.
writeLVPacket
(
BytePacketBuilder
().
apply
(
builder
).
use
{
it
.
build
()
})
@Suppress
(
"DEPRECATION"
)
fun
BytePacketBuilder
.
writeLVString
(
str
:
String
)
=
this
.
writeLVByteArray
(
str
.
toByteArray
())
@Suppress
(
"DEPRECATION"
)
fun
BytePacketBuilder
.
writeLVHex
(
hex
:
String
)
=
this
.
writeLVByteArray
(
hex
.
hexToBytes
())
fun
BytePacketBuilder
.
writeIP
(
ip
:
String
)
=
writeFully
(
ip
.
trim
().
split
(
"."
).
map
{
it
.
toUByte
()
}.
toUByteArray
())
fun
BytePacketBuilder
.
writeTime
()
=
this
.
writeInt
(
currentTime
.
toInt
())
fun
BytePacketBuilder
.
writeHex
(
uHex
:
String
)
=
this
.
writeFully
(
uHex
.
hexToUBytes
())
fun
BytePacketBuilder
.
encryptAndWrite
(
key
:
IoBuffer
,
encoder
:
BytePacketBuilder
.()
->
Unit
)
=
encryptAndWrite
(
key
.
readBytes
(),
encoder
)
fun
BytePacketBuilder
.
encryptAndWrite
(
key
:
ByteArray
,
encoder
:
BytePacketBuilder
.()
->
Unit
)
=
writeFully
(
TEA
.
encrypt
(
BytePacketBuilder
().
apply
(
encoder
).
use
{
it
.
build
().
readBytes
()
},
key
))
fun
BytePacketBuilder
.
encryptAndWrite
(
keyHex
:
String
,
encoder
:
BytePacketBuilder
.()
->
Unit
)
=
encryptAndWrite
(
keyHex
.
hexToBytes
(),
encoder
)
fun
BytePacketBuilder
.
writeTLV0006
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
)
{
val
firstMD5
=
md5
(
password
)
val
secondMD5
=
md5
(
firstMD5
+
byteArrayOf
(
0
,
0
,
0
,
0
)
+
qq
.
toUInt
().
toByteArray
())
this
.
encryptAndWrite
(
secondMD5
)
{
writeRandom
(
4
)
writeHex
(
"00 02"
)
writeQQ
(
qq
)
writeHex
(
TIMProtocol
.
constantData2
)
writeHex
(
"00 00 01"
)
writeFully
(
firstMD5
)
writeInt
(
loginTime
)
writeByte
(
0
)
writeZero
(
4
*
3
)
writeIP
(
loginIP
)
writeZero
(
8
)
writeHex
(
"00 10"
)
//这两个hex是passwordSubmissionTLV2的末尾
writeHex
(
"15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B"
)
//16
writeFully
(
privateKey
)
}
}
@Tested
fun
BytePacketBuilder
.
writeDeviceName
(
random
:
Boolean
)
{
val
deviceName
:
String
=
if
(
random
)
{
"DESKTOP-"
+
String
(
ByteArray
(
7
)
{
(
if
(
Random
.
nextBoolean
())
Random
.
nextInt
(
'A'
.
toInt
()
..
'Z'
.
toInt
())
else
Random
.
nextInt
(
'1'
.
toInt
()
..
'9'
.
toInt
())).
toByte
()
})
}
else
{
deviceName
}
this
.
writeShort
((
deviceName
.
length
+
2
).
toShort
())
this
.
writeShort
(
deviceName
.
length
.
toShort
())
this
.
writeStringUtf8
(
deviceName
)
//TODO TEST?
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtil.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
/**
* 时间戳
*/
expect
val
currentTime
:
Long
/**
* 设备名
*/
expect
val
deviceName
:
String
/**
* CRC32 算法
*/
expect
fun
crc32
(
key
:
ByteArray
):
Int
/**
* MD5 算法
*
* @return 16 bytes
*/
expect
fun
md5
(
byteArray
:
ByteArray
):
ByteArray
/**
* Hostname 解析 IP 地址
*/
expect
fun
solveIpAddress
(
hostname
:
String
):
String
/**
* Localhost 解析
*/
expect
fun
localIpAddress
():
String
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SocketBridge.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlinx.io.core.Closeable
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.errors.IOException
expect
class
MiraiDatagramChannel
(
serverHost
:
String
,
serverPort
:
Short
)
:
Closeable
{
suspend
fun
read
(
buffer
:
IoBuffer
):
Int
suspend
fun
send
(
buffer
:
IoBuffer
):
Int
val
isOpen
:
Boolean
}
expect
class
ClosedChannelException
:
IOException
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/TEA.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlinx.io.core.IoBuffer
import
kotlin.jvm.JvmStatic
expect
object
TEA
{
internal
fun
doOption
(
data
:
ByteArray
,
key
:
ByteArray
,
encrypt
:
Boolean
):
ByteArray
@JvmStatic
fun
encrypt
(
source
:
ByteArray
,
key
:
ByteArray
):
ByteArray
@JvmStatic
fun
decrypt
(
source
:
ByteArray
,
key
:
ByteArray
):
ByteArray
@JvmStatic
fun
decrypt
(
source
:
ByteArray
,
key
:
IoBuffer
):
ByteArray
@JvmStatic
fun
decrypt
(
source
:
ByteArray
,
keyHex
:
String
):
ByteArray
}
fun
ByteArray
.
decryptBy
(
key
:
ByteArray
):
ByteArray
=
TEA
.
decrypt
(
this
,
key
)
fun
ByteArray
.
decryptBy
(
key
:
IoBuffer
):
ByteArray
=
TEA
.
decrypt
(
this
,
key
)
fun
ByteArray
.
decryptBy
(
key
:
String
):
ByteArray
=
TEA
.
decrypt
(
this
,
key
)
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/utils/Tested.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/utils/Tested.kt
View file @
d89f8acc
File moved
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/TypeConvertion.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_UNSIGNED_LITERALS"
,
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.utils
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.core.writeFully
import
kotlin.jvm.Synchronized
import
kotlin.random.Random
import
kotlin.random.nextInt
/**
* 255 -> 00 00 00 FF
*/
fun
Int
.
toByteArray
():
ByteArray
=
byteArrayOf
(
(
this
.
ushr
(
24
)
and
0
xFF
).
toByte
(),
(
this
.
ushr
(
16
)
and
0
xFF
).
toByte
(),
(
this
.
ushr
(
8
)
and
0
xFF
).
toByte
(),
(
this
.
ushr
(
0
)
and
0
xFF
).
toByte
()
)
/**
* 255u -> 00 00 00 FF
*/
fun
UInt
.
toByteArray
():
ByteArray
=
byteArrayOf
(
(
this
.
shr
(
24
)
and
255
u
).
toByte
(),
(
this
.
shr
(
16
)
and
255
u
).
toByte
(),
(
this
.
shr
(
8
)
and
255
u
).
toByte
(),
(
this
.
shr
(
0
)
and
255
u
).
toByte
()
)
fun
Int
.
toUHexString
(
separator
:
String
=
" "
):
String
=
this
.
toByteArray
().
toUHexString
(
separator
)
fun
Byte
.
toUHexString
():
String
=
this
.
toUByte
().
toString
(
16
).
toUpperCase
()
fun
String
.
hexToBytes
():
ByteArray
=
HexCache
.
hexToBytes
(
this
)
fun
String
.
hexToUBytes
():
UByteArray
=
HexCache
.
hexToUBytes
(
this
)
fun
String
.
hexToInt
():
Int
=
hexToBytes
().
toUInt
().
toInt
()
fun
getRandomByteArray
(
length
:
Int
):
ByteArray
=
ByteArray
(
length
)
{
Random
.
nextInt
(
0
..
255
).
toByte
()
}
fun
ByteArray
.
toUInt
():
UInt
=
this
[
0
].
toUInt
().
and
(
255
u
).
shl
(
24
)
+
this
[
1
].
toUInt
().
and
(
255
u
).
shl
(
16
)
+
this
[
2
].
toUInt
().
and
(
255
u
).
shl
(
8
)
+
this
[
3
].
toUInt
().
and
(
255
u
).
shl
(
0
)
fun
ByteArray
.
toIoBuffer
():
IoBuffer
=
IoBuffer
.
Pool
.
borrow
().
let
{
it
.
writeFully
(
this
);
it
}
internal
object
HexCache
{
private
val
hexToByteArrayCacheMap
:
MutableMap
<
Int
,
ByteArray
>
=
mutableMapOf
()
@Synchronized
internal
fun
hexToBytes
(
hex
:
String
):
ByteArray
=
hex
.
hashCode
().
let
{
id
->
if
(
hexToByteArrayCacheMap
.
containsKey
(
id
))
{
return
hexToByteArrayCacheMap
[
id
]
!!
.
copyOf
()
}
else
{
hexToUBytes
(
hex
).
toByteArray
().
let
{
hexToByteArrayCacheMap
[
id
]
=
it
.
copyOf
()
return
it
}
}
}
internal
fun
hexToUBytes
(
hex
:
String
):
UByteArray
=
hex
.
split
(
" "
).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
()
.
map
{
value
->
value
.
trim
{
it
<=
' '
}
}
.
map
{
s
->
s
.
toUByte
(
16
)
}
.
toUByteArray
()
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Util.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.utils
//todo
\ No newline at end of file
mirai-core/src/
jvmMain/kotlin/net/mamoe/
mirai/utils/Varint.kt
→
mirai-core/src/
commonMain/kotlin/net.mamoe.
mirai/utils/Varint.kt
View file @
d89f8acc
...
...
@@ -3,11 +3,10 @@
package
net.mamoe.mirai.utils
import
java.io.DataInputStream
import
java.io.DataOutputStream
import
java.io.IOException
import
java.io.InputStream
import
kotlinx.io.core.BytePacketBuilder
import
kotlinx.io.core.ByteReadPacket
import
kotlin.experimental.or
import
kotlin.jvm.JvmName
/**
* Tool class for VarInt or VarLong operations.
...
...
@@ -23,7 +22,7 @@ fun encodeZigZag32(signedInt: Int): Long {
}
@JvmSynthetic
//
@JvmSynthetic
fun
decodeZigZag32
(
uint
:
UInt
):
Int
{
return
decodeZigZag32
(
uint
.
toLong
())
}
...
...
@@ -41,57 +40,47 @@ fun decodeZigZag64(signedLong: Long): Long {
}
@Throws
(
IOException
::
class
)
fun
DataInputStream
.
readVarInt
():
Int
{
fun
ByteReadPacket
.
readVarInt
():
Int
{
return
decodeZigZag32
(
this
.
readUnsignedVarInt
())
}
@Throws
(
IOException
::
class
)
fun
DataInputStream
.
readUnsignedVarInt
():
UInt
{
fun
ByteReadPacket
.
readUnsignedVarInt
():
UInt
{
return
read
(
this
,
5
).
toUInt
()
}
@Throws
(
IOException
::
class
)
fun
DataInputStream
.
readVarLong
():
Long
{
fun
ByteReadPacket
.
readVarLong
():
Long
{
return
decodeZigZag64
(
readUnsignedVarLong
().
toLong
())
}
@Throws
(
IOException
::
class
)
fun
DataInputStream
.
readUnsignedVarLong
():
ULong
{
fun
ByteReadPacket
.
readUnsignedVarLong
():
ULong
{
return
read
(
this
,
10
).
toULong
()
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeVarInt
(
signedInt
:
Int
)
{
fun
BytePacketBuilder
.
writeVarInt
(
signedInt
:
Int
)
{
this
.
writeUVarInt
(
encodeZigZag32
(
signedInt
))
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeUVarInt
(
uint
:
UInt
)
{
fun
BytePacketBuilder
.
writeUVarInt
(
uint
:
UInt
)
{
return
writeUVarInt
(
uint
.
toLong
())
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeUVarInt
(
uint
:
Long
)
{
fun
BytePacketBuilder
.
writeUVarInt
(
uint
:
Long
)
{
this
.
write0
(
uint
)
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeVarLong
(
signedLong
:
Long
)
{
fun
BytePacketBuilder
.
writeVarLong
(
signedLong
:
Long
)
{
this
.
writeUVarLong
(
encodeZigZag64
(
signedLong
))
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeUVarLong
(
ulong
:
Long
)
{
fun
BytePacketBuilder
.
writeUVarLong
(
ulong
:
Long
)
{
this
.
write0
(
ulong
)
}
@Throws
(
IOException
::
class
)
private
fun
DataOutputStream
.
write0
(
long
:
Long
)
{
private
fun
BytePacketBuilder
.
write0
(
long
:
Long
)
{
var
value
=
long
do
{
var
temp
=
(
value
and
127
).
toByte
()
...
...
@@ -99,34 +88,19 @@ private fun DataOutputStream.write0(long: Long) {
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
this
.
writeByte
(
temp
.
toInt
()
)
this
.
writeByte
(
temp
)
}
while
(
value
!=
0L
)
}
@Throws
(
IOException
::
class
)
private
fun
read
(
stream
:
DataInputStream
,
maxSize
:
Int
):
Long
{
private
fun
read
(
stream
:
ByteReadPacket
,
maxSize
:
Int
):
Long
{
var
value
:
Long
=
0
var
size
=
0
var
b
=
stream
.
readByte
().
toInt
()
while
(
b
and
0
x80
==
0
x80
)
{
value
=
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
++
*
7
)
require
(
size
<
maxSize
)
{
"VarLong too big"
}
require
(
size
<
maxSize
)
{
"VarLong too big
ger(expecting maxSize=$maxSize)
"
}
b
=
stream
.
readByte
().
toInt
()
}
return
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
*
7
)
}
\ No newline at end of file
@Throws
(
IOException
::
class
)
private
fun
read
(
stream
:
InputStream
,
maxSize
:
Int
):
Long
{
var
value
:
Long
=
0
var
size
=
0
var
b
=
stream
.
read
()
while
(
b
and
0
x80
==
0
x80
)
{
value
=
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
++
*
7
)
require
(
size
<
maxSize
)
{
"VarLong too big"
}
b
=
stream
.
read
()
}
return
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
*
7
)
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventListeneresInternalJvm.kt
0 → 100644
View file @
d89f8acc
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
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
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/FaceID.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.message
/**
* @author LamGC
*/
@Suppress
(
"EnumEntryName"
,
"unused"
,
"SpellCheckingInspection"
)
enum
class
FaceID
constructor
(
val
id
:
Int
)
{
unknown
(
0
xff
),
// TODO: 2019/9/1 添加更多表情
jingya
(
0
),
piezui
(
1
),
se
(
2
),
fadai
(
3
),
deyi
(
4
),
liulei
(
5
),
haixiu
(
6
),
bizui
(
7
),
shui
(
8
),
daku
(
9
),
ganga
(
10
),
fanu
(
11
),
tiaopi
(
12
),
ciya
(
13
),
weixiao
(
14
),
nanguo
(
15
),
ku
(
16
),
zhuakuang
(
18
),
tu
(
19
),
touxiao
(
20
),
keai
(
21
),
baiyan
(
22
),
aoman
(
23
),
ji_e
(
24
),
kun
(
25
),
jingkong
(
26
),
liuhan
(
27
),
hanxiao
(
28
),
dabing
(
29
),
fendou
(
30
),
zhouma
(
31
),
yiwen
(
32
),
yun
(
34
),
zhemo
(
35
),
shuai
(
36
),
kulou
(
37
),
qiaoda
(
38
),
zaijian
(
39
),
fadou
(
41
),
aiqing
(
42
),
tiaotiao
(
43
),
zhutou
(
46
),
yongbao
(
49
),
dan_gao
(
53
),
shandian
(
54
),
zhadan
(
55
),
dao
(
56
),
zuqiu
(
57
),
bianbian
(
59
),
kafei
(
60
),
fan
(
61
),
meigui
(
63
),
diaoxie
(
64
),
aixin
(
66
),
xinsui
(
67
),
liwu
(
69
),
taiyang
(
74
),
yueliang
(
75
),
qiang
(
76
),
ruo
(
77
),
woshou
(
78
),
shengli
(
79
),
feiwen
(
85
),
naohuo
(
86
),
xigua
(
89
),
lenghan
(
96
),
cahan
(
97
),
koubi
(
98
),
guzhang
(
99
),
qiudale
(
100
),
huaixiao
(
101
),
zuohengheng
(
102
),
youhengheng
(
103
),
haqian
(
104
),
bishi
(
105
),
weiqu
(
106
),
kuaikule
(
107
),
yinxian
(
108
),
qinqin
(
109
),
xia
(
110
),
kelian
(
111
),
caidao
(
112
),
pijiu
(
113
),
lanqiu
(
114
),
pingpang
(
115
),
shiai
(
116
),
piaochong
(
117
),
baoquan
(
118
),
gouyin
(
119
),
quantou
(
120
),
chajin
(
121
),
aini
(
122
),
bu
(
123
),
hao
(
124
),
zhuanquan
(
125
),
ketou
(
126
),
huitou
(
127
),
tiaosheng
(
128
),
huishou
(
129
),
jidong
(
130
),
jiewu
(
131
),
xianwen
(
132
),
zuotaiji
(
133
),
youtaiji
(
134
),
shuangxi
(
136
),
bianpao
(
137
),
denglong
(
138
),
facai
(
139
),
K_ge
(
140
),
gouwu
(
141
),
youjian
(
142
),
shuai_qi
(
143
),
hecai
(
144
),
qidao
(
145
),
baojin
(
146
),
bangbangtang
(
147
),
he_nai
(
148
),
xiamian
(
149
),
xiangjiao
(
150
),
feiji
(
151
),
kaiche
(
152
),
gaotiezuochetou
(
153
),
chexiang
(
154
),
gaotieyouchetou
(
155
),
duoyun
(
156
),
xiayu
(
157
),
chaopiao
(
158
),
xiongmao
(
159
),
dengpao
(
160
),
fengche
(
161
),
naozhong
(
162
),
dasan
(
163
),
caiqiu
(
164
),
zuanjie
(
165
),
shafa
(
166
),
zhijin
(
167
),
yao
(
168
),
shouqiang
(
169
),
qingwa
(
170
);
override
fun
toString
():
String
{
return
"$name($id)"
}
companion
object
{
fun
ofId
(
id
:
Int
):
FaceID
{
for
(
value
in
values
())
{
if
(
value
.
id
==
id
)
{
return
value
}
}
return
unknown
}
}
}
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/UnsolvedImage.kt
View file @
d89f8acc
...
...
@@ -5,13 +5,13 @@ import kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.protocol.tim.packet.image.ClientTryGetImageIDPacket
import
net.mamoe.mirai.network.protocol.tim.packet.image.ServerTryGetImageIDFailedPacket
import
net.mamoe.mirai.network.protocol.tim.packet.image.ServerTryGetImageIDResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.image.ServerTryGetImageIDSuccessPacket
import
net.mamoe.mirai.network.protocol.tim.packet.md5
import
net.mamoe.mirai.network.protocol.tim.packet.ClientTryGetImageIDPacketJvm
import
net.mamoe.mirai.network.protocol.tim.packet.ServerTryGetImageIDFailedPacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerTryGetImageIDResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.ServerTryGetImageIDSuccessPacket
import
net.mamoe.mirai.qqNumber
import
net.mamoe.mirai.utils.ImageNetworkUtils
import
net.mamoe.mirai.utils.md5
import
net.mamoe.mirai.utils.toByteArray
import
net.mamoe.mirai.utils.toUHexString
import
java.awt.image.BufferedImage
...
...
@@ -32,7 +32,7 @@ class UnsolvedImage(private val filename: String, val image: BufferedImage) {
suspend
fun
upload
(
session
:
LoginSession
,
contact
:
Contact
):
CompletableDeferred
<
Unit
>
{
return
session
.
expectPacket
<
ServerTryGetImageIDResponsePacket
>
{
toSend
{
ClientTryGetImageIDPacket
(
session
.
bot
.
qqNumber
,
session
.
sessionKey
,
contact
.
number
,
image
)
}
toSend
{
ClientTryGetImageIDPacket
Jvm
(
session
.
bot
.
qqNumber
,
session
.
sessionKey
,
contact
.
number
,
image
)
}
onExpect
{
when
(
it
)
{
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/NetworkScope.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.network
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
object
NetworkScope
:
CoroutineScope
by
CoroutineScope
(
Dispatchers
.
Default
)
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/ClientPacket.kt
deleted
100644 → 0
View file @
c09d44f4
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.PacketNameFormatter.adjustName
import
net.mamoe.mirai.utils.*
import
java.io.DataOutputStream
import
java.io.IOException
import
java.net.InetAddress
import
java.security.MessageDigest
/**
* @author Him188moe
*/
abstract
class
ClientPacket
:
ByteArrayDataOutputStream
(),
Packet
{
val
idHex
:
String
private
var
encoded
:
Boolean
=
false
init
{
val
annotation
=
this
.
javaClass
.
getAnnotation
(
PacketId
::
class
.
java
)
idHex
=
annotation
.
value
.
trim
()
try
{
this
.
writeHex
(
TIMProtocol
.
head
)
this
.
writeHex
(
TIMProtocol
.
ver
)
this
.
writePacketId
()
}
catch
(
e
:
IOException
)
{
throw
RuntimeException
(
e
)
}
}
@Throws
(
IOException
::
class
)
fun
writePacketId
()
{
this
.
writeHex
(
this
@ClientPacket
.
idHex
)
}
/**
* Encode this packet.
*
*
* Before sending the packet, a [tail][TIMProtocol.tail] is added.
*/
@Throws
(
IOException
::
class
)
protected
abstract
fun
encode
()
fun
encodePacket
()
{
if
(
encoded
)
{
return
}
encode
()
writeHex
(
TIMProtocol
.
tail
)
}
@Throws
(
IOException
::
class
)
fun
encodeToByteArray
():
ByteArray
{
encodePacket
()
return
toByteArray
()
}
open
fun
getFixedId
():
String
=
when
(
this
.
idHex
.
length
)
{
0
->
"__ __ __ __"
2
->
this
.
idHex
+
" __ __ __"
5
->
this
.
idHex
+
" __ __"
7
->
this
.
idHex
+
" __"
else
->
this
.
idHex
}
override
fun
toString
():
String
{
return
adjustName
(
this
.
javaClass
.
simpleName
+
"(${this.getFixedId()})"
)
+
this
.
getAllDeclaredFields
().
filterNot
{
it
.
name
==
"idHex"
||
it
.
name
==
"idByteArray"
||
it
.
name
==
"encoded"
}.
joinToString
(
", "
,
"{"
,
"}"
)
{
it
.
trySetAccessible
();
it
.
name
+
"="
+
it
.
get
(
this
).
let
{
value
->
when
(
value
)
{
null
->
null
is
ByteArray
->
value
.
toUHexString
()
is
UByteArray
->
value
.
toUHexString
()
else
->
value
.
toString
()
}
}
}
}
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeIP
(
ip
:
String
)
{
for
(
s
in
ip
.
trim
().
split
(
"\\."
.
toRegex
()).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
())
{
this
.
writeByte
(
s
.
toInt
())
}
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeTime
()
{
this
.
writeInt
(
System
.
currentTimeMillis
().
toInt
())
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeHex
(
uHex
:
String
)
{
for
(
s
in
uHex
.
trim
().
split
(
" "
.
toRegex
()).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
())
{
if
(
s
.
isEmpty
())
{
continue
}
this
.
writeByte
(
s
.
toUByte
(
16
).
toInt
())
}
}
fun
DataOutputStream
.
encryptAndWrite
(
byteArray
:
ByteArray
,
key
:
ByteArray
)
{
this
.
write
(
TEA
.
encrypt
(
byteArray
,
key
))
}
fun
DataOutputStream
.
encryptAndWrite
(
key
:
ByteArray
,
encoder
:
ByteArrayDataOutputStream
.()
->
Unit
)
{
this
.
write
(
TEA
.
encrypt
(
ByteArrayDataOutputStream
().
apply
(
encoder
).
use
{
it
.
toByteArray
()
},
key
))
}
fun
DataOutputStream
.
encryptAndWrite
(
keyHex
:
String
,
encoder
:
ByteArrayDataOutputStream
.()
->
Unit
)
{
this
.
encryptAndWrite
(
keyHex
.
hexToBytes
(),
encoder
)
}
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeTLV0006
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
privateKey
:
ByteArray
)
{
val
firstMD5
=
md5
(
password
)
val
secondMD5
=
md5
(
firstMD5
+
"00 00 00 00"
.
hexToBytes
()
+
qq
.
toUInt
().
toByteArray
())
this
.
encryptAndWrite
(
secondMD5
)
{
writeRandom
(
4
)
writeHex
(
"00 02"
)
writeQQ
(
qq
)
writeHex
(
TIMProtocol
.
constantData2
)
writeHex
(
"00 00 01"
)
write
(
firstMD5
)
writeInt
(
loginTime
)
writeByte
(
0
)
writeZero
(
4
*
3
)
writeIP
(
loginIP
)
writeZero
(
8
)
writeHex
(
"00 10"
)
//这两个hex是passwordSubmissionTLV2的末尾
writeHex
(
"15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B"
)
//16
write
(
privateKey
)
}
}
@Tested
fun
DataOutputStream
.
writeCRC32
()
=
writeCRC32
(
getRandomByteArray
(
16
))
fun
DataOutputStream
.
writeCRC32
(
key
:
ByteArray
)
{
write
(
key
)
//key
writeInt
(
getCrc32
(
key
))
}
@Tested
fun
DataOutputStream
.
writeDeviceName
(
random
:
Boolean
=
false
)
{
val
deviceName
:
String
=
if
(
random
)
{
String
(
getRandomByteArray
(
10
))
}
else
{
InetAddress
.
getLocalHost
().
hostName
}
this
.
writeShort
(
deviceName
.
length
+
2
)
this
.
writeShort
(
deviceName
.
length
)
this
.
writeBytes
(
deviceName
)
}
/**
* 255 -> 00 00 00 FF
*/
fun
Int
.
toByteArray
():
ByteArray
=
byteArrayOf
(
(
this
.
ushr
(
24
)
and
0
xFF
).
toByte
(),
(
this
.
ushr
(
16
)
and
0
xFF
).
toByte
(),
(
this
.
ushr
(
8
)
and
0
xFF
).
toByte
(),
(
this
.
ushr
(
0
)
and
0
xFF
).
toByte
()
)
/**
* 255u -> 00 00 00 FF
*/
fun
UInt
.
toByteArray
():
ByteArray
=
byteArrayOf
(
(
this
.
shr
(
24
)
and
255
u
).
toByte
(),
(
this
.
shr
(
16
)
and
255
u
).
toByte
(),
(
this
.
shr
(
8
)
and
255
u
).
toByte
(),
(
this
.
shr
(
0
)
and
255
u
).
toByte
()
)
fun
Int
.
toUHexString
(
separator
:
String
=
" "
):
String
=
this
.
toByteArray
().
toUHexString
(
separator
)
fun
md5
(
str
:
String
):
ByteArray
=
MessageDigest
.
getInstance
(
"MD5"
).
digest
(
str
.
toByteArray
())
fun
md5
(
byteArray
:
ByteArray
):
ByteArray
=
MessageDigest
.
getInstance
(
"MD5"
).
digest
(
byteArray
)
fun
DataOutputStream
.
writeZero
(
count
:
Int
)
{
repeat
(
count
)
{
this
.
writeByte
(
0
)
}
}
fun
DataOutputStream
.
writeRandom
(
length
:
Int
)
{
repeat
(
length
)
{
this
.
writeByte
((
Math
.
random
()
*
255
).
toInt
())
}
}
fun
DataOutputStream
.
writeQQ
(
qq
:
Long
)
{
this
.
write
(
qq
.
toUInt
().
toByteArray
())
}
fun
DataOutputStream
.
writeGroup
(
groupIdOrGroupNumber
:
Long
)
{
this
.
write
(
groupIdOrGroupNumber
.
toUInt
().
toByteArray
())
}
fun
DataOutputStream
.
writeUByte
(
uByte
:
UByte
)
{
this
.
write
(
uByte
.
toInt
())
}
fun
DataOutputStream
.
writeLVByteArray
(
byteArray
:
ByteArray
)
{
this
.
writeShort
(
byteArray
.
size
)
this
.
write
(
byteArray
)
}
fun
DataOutputStream
.
writeLVString
(
str
:
String
)
{
this
.
writeLVByteArray
(
str
.
toByteArray
())
}
fun
DataOutputStream
.
writeLVHex
(
hex
:
String
)
{
this
.
writeLVByteArray
(
hex
.
hexToBytes
())
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/Packet.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.network.protocol.tim.packet
/**
* @author Him188moe
*/
interface
Packet
internal
object
PacketNameFormatter
{
@JvmStatic
private
var
longestNameLength
:
Int
=
43
@JvmStatic
fun
adjustName
(
name
:
String
):
String
{
if
(
name
.
length
>
longestNameLength
)
{
longestNameLength
=
name
.
length
return
name
}
return
" "
.
repeat
(
longestNameLength
-
name
.
length
)
+
name
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt
0 → 100644
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.core.internal.DangerousInternalIoApi
import
net.mamoe.mirai.utils.toUHexString
import
java.lang.reflect.Field
internal
object
PacketNameFormatter
{
@JvmStatic
private
var
longestNameLength
:
Int
=
43
@JvmStatic
fun
adjustName
(
name
:
String
):
String
{
if
(
name
.
length
>
longestNameLength
)
{
longestNameLength
=
name
.
length
return
name
}
return
" "
.
repeat
(
longestNameLength
-
name
.
length
)
+
name
}
}
private
object
IgnoreIdList
:
List
<
String
>
by
listOf
(
"idHex"
,
"fixedId"
,
"idByteArray"
,
"encoded"
,
"packet"
,
"Companion"
,
"EMPTY_ID_HEX"
,
"input"
,
"output"
,
"UninitializedByteReadPacket"
)
@UseExperimental
(
DangerousInternalIoApi
::
class
)
internal
actual
fun
Packet
.
packetToString
():
String
=
PacketNameFormatter
.
adjustName
(
this
::
class
.
simpleName
+
"(${this.fixedId})"
)
+
this
::
class
.
java
.
allDeclaredFields
.
filterNot
{
it
.
name
in
IgnoreIdList
||
"delegate"
in
it
.
name
||
"$"
in
it
.
name
}
.
joinToString
(
", "
,
"{"
,
"}"
)
{
it
.
isAccessible
=
true
it
.
name
+
"="
+
it
.
get
(
this
).
let
{
value
->
when
(
value
)
{
null
->
null
is
ByteArray
->
value
.
toUHexString
()
is
UByteArray
->
value
.
toUHexString
()
is
ByteReadPacket
->
"[ByteReadPacket(${value.remaining})]"
//is ByteReadPacket -> value.copy().readBytes().toUHexString()
is
IoBuffer
->
"[IoBuffer(${value.readRemaining})]"
else
->
value
.
toString
()
}
}
}
private
val
Class
<*>.
allDeclaredFields
:
List
<
Field
>
get
()
{
val
list
=
mutableListOf
<
Field
>()
var
clazz
:
Class
<
*
>
=
this
do
{
list
.
addAll
(
clazz
.
declaredFields
)
}
while
(
clazz
.
let
{
clazz
=
it
.
superclass
;
clazz
.
kotlin
!=
Any
::
class
})
return list
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/ServerPacket.kt
deleted
100644 → 0
View file @
c09d44f4
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
net.mamoe.mirai.network.protocol.tim.packet.PacketNameFormatter.adjustName
import
net.mamoe.mirai.network.protocol.tim.packet.action.ServerCanAddFriendResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendFriendMessageResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.ServerSendGroupMessageResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.image.ServerTryGetImageIDResponsePacket
import
net.mamoe.mirai.network.protocol.tim.packet.login.*
import
net.mamoe.mirai.utils.*
import
java.io.DataInputStream
import
java.io.EOFException
/**
* @author Him188moe
*/
abstract
class
ServerPacket
(
val
input
:
DataInputStream
)
:
Packet
{
var
idHex
:
String
var
idByteArray
:
ByteArray
//fixed 4 size
var
encoded
:
Boolean
=
false
init
{
idHex
=
try
{
val
annotation
=
this
.
javaClass
.
getAnnotation
(
PacketId
::
class
.
java
)
annotation
.
value
.
trim
()
}
catch
(
e
:
NullPointerException
)
{
""
}
idByteArray
=
if
(
idHex
.
isEmpty
())
{
byteArrayOf
(
0
,
0
,
0
,
0
)
}
else
{
idHex
.
hexToBytes
()
}
}
fun
<
P
:
ServerPacket
>
P
.
setId
(
idHex
:
String
):
P
{
this
.
idHex
=
idHex
return
this
}
open
fun
decode
()
{
}
companion
object
{
fun
ofByteArray
(
bytes
:
ByteArray
):
ServerPacket
{
val
stream
=
bytes
.
dataInputStream
()
stream
.
skip
(
3
)
val
idHex
=
stream
.
readInt
().
toUHexString
(
" "
)
return
when
(
idHex
)
{
"08 25 31 01"
->
ServerTouchResponsePacket
.
Encrypted
(
ServerTouchResponsePacket
.
Type
.
TYPE_08_25_31_01
,
stream
)
"08 25 31 02"
->
ServerTouchResponsePacket
.
Encrypted
(
ServerTouchResponsePacket
.
Type
.
TYPE_08_25_31_02
,
stream
)
"08 36 31 03"
,
"08 36 31 04"
,
"08 36 31 05"
,
"08 36 31 06"
->
{
when
(
bytes
.
size
)
{
271
,
207
->
return
ServerLoginResponseKeyExchangePacket
.
Encrypted
(
stream
,
when
(
idHex
)
{
"08 36 31 03"
->
ServerLoginResponseKeyExchangePacket
.
Flag
.
`
08
36
31
03
`
else
->
ServerLoginResponseKeyExchangePacket
.
Flag
.
OTHER
}).
apply
{
this
.
idHex
=
idHex
}
871
->
return
ServerLoginResponseVerificationCodeInitPacket
.
Encrypted
(
stream
).
apply
{
this
.
idHex
=
idHex
}
}
if
(
bytes
.
size
>
700
)
{
return
ServerLoginResponseSuccessPacket
.
Encrypted
(
stream
).
apply
{
this
.
idHex
=
idHex
}
}
println
(
bytes
.
size
)
return
ServerLoginResponseFailedPacket
(
when
(
bytes
.
size
)
{
135
->
LoginState
.
UNKNOWN
//账号已经在另一台电脑登录??
319
,
351
->
LoginState
.
WRONG_PASSWORD
//135 -> LoginState.RETYPE_PASSWORD
63
,
279
->
LoginState
.
BLOCKED
263
->
LoginState
.
UNKNOWN_QQ_NUMBER
551
,
487
->
LoginState
.
DEVICE_LOCK
359
->
LoginState
.
TAKEN_BACK
else
->
LoginState
.
UNKNOWN
/*
//unknown
63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
else -> throw IllegalArgumentException(bytes.size.toString())*/
},
stream
).
apply
{
this
.
idHex
=
idHex
}
}
"08 28 04 34"
->
ServerSessionKeyResponsePacket
.
Encrypted
(
stream
)
else
->
when
(
idHex
.
substring
(
0
,
5
))
{
"00 EC"
->
ServerLoginSuccessPacket
(
stream
)
"00 1D"
->
ServerSKeyResponsePacket
.
Encrypted
(
stream
)
"00 5C"
->
ServerAccountInfoResponsePacket
.
Encrypted
(
stream
)
"00 58"
->
ServerHeartbeatResponsePacket
(
stream
)
"00 BA"
->
ServerCaptchaPacket
.
Encrypted
(
stream
,
idHex
)
"00 CE"
,
"00 17"
->
ServerEventPacket
.
Raw
.
Encrypted
(
stream
,
idHex
.
hexToBytes
())
"00 81"
->
UnknownServerPacket
(
stream
)
"00 CD"
->
ServerSendFriendMessageResponsePacket
(
stream
)
"00 02"
->
ServerSendGroupMessageResponsePacket
(
stream
)
"00 A7"
->
ServerCanAddFriendResponsePacket
(
stream
)
"03 88"
->
ServerTryGetImageIDResponsePacket
.
Encrypted
(
stream
)
else
->
UnknownServerPacket
(
stream
)
}
}.
apply
{
this
.
idHex
=
idHex
}
}
}
override
fun
toString
():
String
{
return
adjustName
(
this
.
javaClass
.
simpleName
+
"(${this.getFixedId()})"
)
+
this
.
getAllDeclaredFields
().
filterNot
{
it
.
name
==
"idHex"
||
it
.
name
==
"idByteArray"
||
it
.
name
==
"encoded"
}.
joinToString
(
", "
,
"{"
,
"}"
)
{
it
.
trySetAccessible
();
it
.
name
+
"="
+
it
.
get
(
this
).
let
{
value
->
when
(
value
)
{
is
ByteArray
->
value
.
toUHexString
()
is
UByteArray
->
value
.
toUHexString
()
else
->
value
?.
toString
()
}
}
}
}
open
fun
getFixedId
():
String
=
getFixedId
(
this
.
idHex
)
fun
getFixedId
(
id
:
String
):
String
=
when
(
id
.
length
)
{
0
->
"__ __ __ __"
2
->
"$id __ __ __"
5
->
"$id __ __"
7
->
"$id __"
else
->
id
}
fun
decryptBy
(
key
:
ByteArray
):
DataInputStream
{
return
decryptAsByteArray
(
key
).
dataInputStream
()
}
fun
decryptBy
(
keyHex
:
String
):
DataInputStream
{
return
this
.
decryptBy
(
keyHex
.
hexToBytes
())
}
fun
decryptBy
(
key1
:
ByteArray
,
key2
:
ByteArray
):
DataInputStream
{
return
TEA
.
decrypt
(
this
.
decryptAsByteArray
(
key1
),
key2
).
dataInputStream
()
}
fun
decryptBy
(
key1
:
String
,
key2
:
ByteArray
):
DataInputStream
{
return
this
.
decryptBy
(
key1
.
hexToBytes
(),
key2
)
}
fun
decryptBy
(
key1
:
ByteArray
,
key2
:
String
):
DataInputStream
{
return
this
.
decryptBy
(
key1
,
key2
.
hexToBytes
())
}
fun
decryptBy
(
keyHex1
:
String
,
keyHex2
:
String
):
DataInputStream
{
return
this
.
decryptBy
(
keyHex1
.
hexToBytes
(),
keyHex2
.
hexToBytes
())
}
fun
decryptAsByteArray
(
key
:
ByteArray
):
ByteArray
{
input
.
goto
(
14
)
return
TEA
.
decrypt
(
input
.
readAllBytes
().
cutTail
(
1
),
key
)
}
fun
decryptAsByteArray
(
keyHex
:
String
):
ByteArray
=
this
.
decryptAsByteArray
(
keyHex
.
hexToBytes
())
}
fun
DataInputStream
.
readIP
():
String
{
var
buff
=
""
for
(
i
in
0
..
3
)
{
val
byte
=
readUnsignedByte
()
buff
+=
byte
.
toString
()
if
(
i
!=
3
)
buff
+=
"."
}
return
buff
}
fun
DataInputStream
.
readLVString
():
String
{
return
String
(
this
.
readLVByteArray
())
}
fun
DataInputStream
.
readLVByteArray
():
ByteArray
{
return
this
.
readNBytes
(
this
.
readShort
().
toInt
())
}
fun
DataInputStream
.
readTLVMap
(
expectingEOF
:
Boolean
=
false
):
Map
<
Int
,
ByteArray
>
{
val
map
=
mutableMapOf
<
Int
,
ByteArray
>()
var
type
:
Int
try
{
type
=
readUnsignedByte
()
}
catch
(
e
:
EOFException
)
{
if
(
expectingEOF
)
{
return
map
}
throw
e
}
while
(
type
!=
0
xff
)
{
map
[
type
]
=
this
.
readLVByteArray
()
try
{
type
=
readUnsignedByte
()
}
catch
(
e
:
EOFException
)
{
if
(
expectingEOF
)
{
return
map
}
throw
e
}
}
return
map
}
fun
Map
<
Int
,
ByteArray
>.
printTLVMap
()
{
println
(
this
.
mapValues
{
(
_
,
value
)
->
value
.
toUHexString
()
})
}
fun
DataInputStream
.
readString
(
length
:
Number
):
String
{
return
String
(
this
.
readNBytes
(
length
))
}
fun
ByteArray
.
dataInputStream
():
DataInputStream
=
DataInputStream
(
this
.
inputStream
())
/**
* Reset and skip(position)
*/
fun
<
N
:
Number
>
DataInputStream
.
goto
(
position
:
N
):
DataInputStream
{
this
.
reset
()
this
.
skip
(
position
.
toLong
())
return
this
}
fun
<
N
:
Number
>
DataInputStream
.
readNBytesAt
(
position
:
N
,
length
:
Int
):
ByteArray
{
this
.
goto
(
position
)
return
this
.
readNBytes
(
length
)
}
fun
<
N
:
Number
>
DataInputStream
.
readNBytes
(
length
:
N
):
ByteArray
{
return
this
.
readNBytes
(
length
.
toInt
())
}
fun
DataInputStream
.
readLVNumber
():
Number
{
return
when
(
this
.
readShort
().
toInt
())
{
1
->
this
.
readByte
()
2
->
this
.
readShort
()
4
->
this
.
readInt
()
8
->
this
.
readLong
()
else
->
throw
UnsupportedOperationException
()
}
}
fun
DataInputStream
.
readNBytesIn
(
range
:
IntRange
):
ByteArray
{
this
.
goto
(
range
.
first
)
return
this
.
readNBytes
(
range
.
last
-
range
.
first
+
1
)
}
fun
<
N
:
Number
>
DataInputStream
.
readIntAt
(
position
:
N
):
Int
{
this
.
goto
(
position
)
return
this
.
readInt
()
}
fun
<
N
:
Number
>
DataInputStream
.
readUIntAt
(
position
:
N
):
UInt
{
this
.
goto
(
position
)
return
this
.
readNBytes
(
4
).
toUInt
()
}
fun
DataInputStream
.
readUInt
():
UInt
{
return
this
.
readNBytes
(
4
).
toUInt
()
}
fun
<
N
:
Number
>
DataInputStream
.
readByteAt
(
position
:
N
):
Byte
{
this
.
goto
(
position
)
return
this
.
readByte
()
}
fun
<
N
:
Number
>
DataInputStream
.
readShortAt
(
position
:
N
):
Short
{
this
.
goto
(
position
)
return
this
.
readShort
()
}
//添加@JvmSynthetic 导致 idea 无法检查这个文件的错误
//@JvmSynthetic
fun
DataInputStream
.
gotoWhere
(
matcher
:
UByteArray
):
DataInputStream
{
return
this
.
gotoWhere
(
matcher
.
toByteArray
())
}
/**
* 去往下一个含这些连续字节的位置
*/
@Throws
(
EOFException
::
class
)
fun
DataInputStream
.
gotoWhere
(
matcher
:
ByteArray
):
DataInputStream
{
require
(
matcher
.
isNotEmpty
())
loop
@
do
{
val
byte
=
this
.
readByte
()
if
(
byte
==
matcher
[
0
])
{
//todo mark here
for
(
i
in
1
until
matcher
.
size
)
{
val
b
=
this
.
readByte
()
if
(
b
!=
matcher
[
i
])
{
continue
@
loop
//todo goto mark
}
}
return
this
}
}
while
(
true
)
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UnknownServerPacket.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.network.protocol.tim.packet
import
net.mamoe.mirai.utils.LoggerTextFormat
import
net.mamoe.mirai.utils.MiraiLogger
import
net.mamoe.mirai.utils.toUHexString
import
java.io.DataInputStream
/**
* @author Him188moe
*/
class
UnknownServerPacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
override
fun
decode
()
{
MiraiLogger
.
debug
(
"UnknownServerPacket data: "
+
this
.
input
.
goto
(
0
).
readAllBytes
().
toUHexString
())
}
override
fun
toString
():
String
{
return
LoggerTextFormat
.
LIGHT_RED
.
toString
()
+
super
.
toString
()
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/
image/UploadGroupImage
.kt
→
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/
UploadImageJvm
.kt
View file @
d89f8acc
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet
.image
package
net.mamoe.mirai.network.protocol.tim.packet
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.utils.toByteArra
y
import
net.mamoe.mirai.utils.
writeUVarInt
import
kotlinx.io.core.BytePacketBuilder
import
kotlinx.io.core.writeFull
y
import
net.mamoe.mirai.utils.
*
import
java.awt.image.BufferedImage
import
java.io.DataInputStream
actual
typealias
PlatformImage
=
BufferedImage
actual
typealias
ClientTryGetImageIDPacket
=
ClientTryGetImageIDPacketJvm
/**
* 请求上传图片. 将发送图片的 md5, size.
...
...
@@ -17,13 +20,13 @@ import java.io.DataInputStream
* @author Him188moe
*/
@PacketId
(
"03 88"
)
class
ClientTryGetImageIDPacket
(
class
ClientTryGetImageIDPacket
Jvm
(
private
val
botNumber
:
Long
,
private
val
sessionKey
:
ByteArray
,
private
val
groupNumberOrQQNumber
:
Long
,
private
val
image
:
Buffered
Image
private
val
image
:
Platform
Image
)
:
ClientPacket
()
{
override
fun
encode
()
{
override
fun
encode
(
builder
:
BytePacketBuilder
)
=
with
(
builder
)
{
this
.
writeRandom
(
2
)
this
.
writeQQ
(
botNumber
)
...
...
@@ -55,7 +58,7 @@ class ClientTryGetImageIDPacket(
writeHex
(
"22"
)
writeHex
(
"10"
)
write
(
md5
(
byteArray
))
write
Fully
(
md5
(
byteArray
))
writeHex
(
"28"
)
writeUVarInt
(
byteArray
.
size
.
toUInt
())
//E2 0D
...
...
@@ -92,41 +95,3 @@ class ClientTryGetImageIDPacket(
}
}
}
\ No newline at end of file
abstract
class
ServerTryGetImageIDResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
class
Encrypted
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerTryGetImageIDResponsePacket
{
val
data
=
this
.
decryptAsByteArray
(
sessionKey
)
println
(
data
.
size
)
println
(
data
.
size
)
if
(
data
.
size
==
209
)
{
return
ServerTryGetImageIDSuccessPacket
(
data
.
dataInputStream
()).
setId
(
this
.
idHex
)
}
return
ServerTryGetImageIDFailedPacket
(
data
.
dataInputStream
())
}
}
}
/**
* 服务器未存有图片, 返回一个 key 用于客户端上传
*/
class
ServerTryGetImageIDSuccessPacket
(
input
:
DataInputStream
)
:
ServerTryGetImageIDResponsePacket
(
input
)
{
lateinit
var
uKey
:
ByteArray
override
fun
decode
()
{
this
.
input
.
gotoWhere
(
ubyteArrayOf
(
0
x42u
,
0
x80u
,
0
x01u
))
uKey
=
this
.
input
.
readNBytes
(
128
)
}
}
/**
* 服务器已经存有这个图片
*/
class
ServerTryGetImageIDFailedPacket
(
input
:
DataInputStream
)
:
ServerTryGetImageIDResponsePacket
(
input
)
{
override
fun
decode
()
{
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/ServerLoginResponseFailedPacket.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.network.protocol.tim.packet.login
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
java.io.DataInputStream
/**
* @author Him188moe
*/
class
ServerLoginResponseFailedPacket
(
val
loginState
:
LoginState
,
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
override
fun
decode
()
{
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/ServerLoginResponseKeyExchangePacket.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.network.protocol.tim.packet.login
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.goto
import
net.mamoe.mirai.utils.Tested
import
java.io.DataInputStream
/**
* 服务器进行加密后返回 privateKey
*
* @author NaturalHG
*/
@PacketId
(
"08 36 31 03"
)
class
ServerLoginResponseKeyExchangePacket
(
input
:
DataInputStream
,
val
flag
:
Flag
)
:
ServerPacket
(
input
)
{
enum
class
Flag
{
`
08
36
31
03
`
,
OTHER
,
}
lateinit
var
tlv0006
:
ByteArray
//120bytes
var
tokenUnknown
:
ByteArray
?
=
null
lateinit
var
privateKeyUpdate
:
ByteArray
//16bytes
@Tested
override
fun
decode
()
{
this
.
input
.
skip
(
5
)
privateKeyUpdate
=
this
.
input
.
readNBytes
(
16
)
//22
//this.input.skip(2)//25
this
.
input
.
goto
(
25
)
tlv0006
=
this
.
input
.
readNBytes
(
120
)
when
(
flag
)
{
Flag
.
`
08
36
31
03
`
->
{
tokenUnknown
=
this
.
input
.
goto
(
153
).
readNBytes
(
56
)
//println(tokenUnknown!!.toUHexString())
}
Flag
.
OTHER
->
{
//do nothing in this packet.
//[this.token] will be set in [BotNetworkHandler]
//token
}
}
}
class
Encrypted
(
input
:
DataInputStream
,
private
val
flag
:
Flag
)
:
ServerPacket
(
input
)
{
@Tested
fun
decrypt
(
privateKey
:
ByteArray
):
ServerLoginResponseKeyExchangePacket
{
return
ServerLoginResponseKeyExchangePacket
(
this
.
decryptBy
(
TIMProtocol
.
shareKey
,
privateKey
),
flag
).
setId
(
this
.
idHex
)
}
}
}
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
deleted
100644 → 0
View file @
c09d44f4
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet.login
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.network.protocol.tim.packet.goto
import
net.mamoe.mirai.network.protocol.tim.packet.readNBytesAt
import
net.mamoe.mirai.network.protocol.tim.packet.readString
import
net.mamoe.mirai.utils.Tested
import
net.mamoe.mirai.utils.toUHexString
import
java.io.DataInputStream
/**
* @author NaturalHG
*/
class
ServerLoginResponseSuccessPacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
lateinit
var
sessionResponseDecryptionKey
:
ByteArray
//16 bytes|
lateinit
var
nickname
:
String
lateinit
var
token38
:
ByteArray
lateinit
var
token88
:
ByteArray
lateinit
var
encryptionKey
:
ByteArray
@Tested
override
fun
decode
()
{
this
.
input
.
skip
(
7
)
//8
this
.
encryptionKey
=
this
.
input
.
readNBytes
(
16
)
//24
this
.
input
.
skip
(
2
)
//26
this
.
token38
=
this
.
input
.
readNBytes
(
56
)
//82
this
.
input
.
skip
(
60L
)
//142
val
msgLength
=
when
(
val
id
=
this
.
input
.
readNBytes
(
2
).
toUByteArray
().
toUHexString
())
{
"01 07"
->
0
"00 33"
->
28
"01 10"
->
64
else
->
throw
IllegalStateException
(
id
)
}
this
.
sessionResponseDecryptionKey
=
this
.
input
.
readNBytesAt
(
171
+
msgLength
,
16
)
this
.
token88
=
this
.
input
.
readNBytesAt
(
189
+
msgLength
,
136
)
val
nickLength
=
this
.
input
.
goto
(
624
+
msgLength
).
readByte
().
toInt
()
this
.
nickname
=
this
.
input
.
readString
(
nickLength
)
//this.age = this.input.goto(packetDataLength - 28).readShortAt()
//this.gender = this.input.goto(packetDataLength - 32).readByteAt().toInt()
}
class
Encrypted
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
privateKey
:
ByteArray
):
ServerLoginResponseSuccessPacket
{
input
.
goto
(
14
)
return
ServerLoginResponseSuccessPacket
(
this
.
decryptBy
(
TIMProtocol
.
shareKey
,
privateKey
)).
setId
(
this
.
idHex
)
}
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt
deleted
100644 → 0
View file @
c09d44f4
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet.login
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import
net.mamoe.mirai.network.protocol.tim.packet.dataInputStream
import
net.mamoe.mirai.network.protocol.tim.packet.goto
import
net.mamoe.mirai.utils.Tested
import
net.mamoe.mirai.utils.hexToUBytes
import
java.io.DataInputStream
/**
* 收到这个包意味着需要验证码登录, 并且能得到验证码图片文件的一部分
*
* @author Him188moe
*/
class
ServerLoginResponseVerificationCodeInitPacket
(
input
:
DataInputStream
,
private
val
packetLength
:
Int
)
:
ServerPacket
(
input
)
{
lateinit
var
verifyCodePart1
:
ByteArray
lateinit
var
token00BA
:
ByteArray
var
unknownBoolean
:
Boolean
?
=
null
@Tested
override
fun
decode
()
{
val
verifyCodeLength
=
this
.
input
.
goto
(
78
).
readShort
()
//2bytes
this
.
verifyCodePart1
=
this
.
input
.
readNBytes
(
verifyCodeLength
.
toInt
())
this
.
input
.
skip
(
1
)
this
.
unknownBoolean
=
this
.
input
.
readByte
().
toInt
()
==
1
this
.
token00BA
=
this
.
input
.
goto
(
packetLength
-
60
).
readNBytes
(
40
)
}
class
Encrypted
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
override
fun
decode
()
{
}
fun
decrypt
():
ServerLoginResponseVerificationCodeInitPacket
=
this
.
decryptAsByteArray
(
TIMProtocol
.
shareKey
).
let
{
ServerLoginResponseVerificationCodeInitPacket
(
it
.
dataInputStream
(),
it
.
size
).
setId
(
this
.
idHex
)
}
}
}
fun
main
()
{
val
data
=
"FB 01 04 03 33 00 01 00 BA 02 03 2C 13 00 05 01 00 00 01 23 00 38 D5 01 05 8B 67 4D 52 5A FA 92 DB 99 18 D4 F0 72 03 E0 17 71 7C 8A 45 74 1F C3 2D F8 61 96 0D 93 0D 8C 51 95 70 F8 F9 CB B9 2D 5D BC 4F 5D 89 5F E7 59 8C E4 E5 A2 04 56 02 BC 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 82 00 00 00 35 08 03 00 00 00 BA 12 C3 02 00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00 00 01 73 52 47 42 00 AE CE 1C E9 00 00 00 45 50 4C 54 45 FE F6 ED E2 F1 DF F3 FF F2 11 77 48 FE FE F3 F1 F9 EA D7 FD E7 F8 F9 EC FC EF E7 E8 FF EE 2D 69 48 2A 8A 5D 29 7A 52 F0 ED E1 A9 C7 B1 65 96 79 AB E0 C2 C3 F0 D5 42 7D 5C 4A 99 72 89 AA 93 51 73 5C 6E BA 94 42 BD 7A 0B 00 00 09 C5 49 44 41 54 58 C3 AC 99 8B 76 AB 3A 12 44 91 D0 1B 10 08 04 FF FF A9 B3 5B 60 C7 AF 38 77 EE 0C EB C4 76 6C 07 95 BA AB AB AB 75 BA EE 1F 5D DE CB 63 08 C1 C7 A8 AD BC B6 31 46 6B 3B A3 79 94 E7 F3 63 79 E4 D9 98 89 37 7D F7 FF BB 3C 10 82 6F B7 F7 2A B6 5B 07 EF B5 16 60 DE 1B 63 3A 1F 7E BE 6D 0C 50 EC 09 E1 FE 76 B8 3F FC 6B 0C 1D 5B 37 1F 3E E1 AE 61 9A D8 B6 07 93 B7 56 4D 66 22 06 9D FC EE 5F BF F9 3F 81 90 BB 99 F3 49 E2 1D DA 43 38 13 44 14 8C 9D B4 8E E7 65 40 11 E4 BB FE 8C 5B 78 41 F1 AF 01 48 2E 08 6D 8B AD F1 BA 05 F9 C4 E0 25 E6 31 BA EB 2A 90 C3 74 26 FC 1E 80 CF 14 91 44 7F FC 39 FF D4 37 10 82 C0 B3 B0 67 BF 2E 5E 5B F4 46 E5 3A 5F D7 BE A6 54 17 2D 7C 0D D7 DA E1 7E 93 C7 E5 DB 9E 6E 9F FD A3 14 9C 7F 23 97 55 FA DC 6F 74 8A CC 04 49 03 3F DE 4E 5C 4A 95 9C 53 8D 7A EA 82 F9 10 77 7F EE E5 43 28 C2 9F 00 BA 5B C0 59 A4 CE DB BA AE DB BC 14 D5 2A 82 A5 83 E0 30 92 19 A8 99 36 A7 15 7C BC DF F6 BE D7 86 C0 7F C9 C5 ED 93 C7 1F B9 E0 56 CB AC 61 F5 5D 2E 00 F0 93 D2 5A 9D B6 B0 3F 4B 4D 5C B9 0F C1 EE 29 46 65 4E 40 46 E2 E4 1B 91 C3 A5 2E FE D3 7A 3C 84 0B E3 F3 E2 57 16 2F 08 B9 E6 5A 73 29 65 29 4E B9 25 CF 6B 9A 97 48 20 B2 35 46 1B 6D AD D6 9A 57 69 45 B2 1A 00 90 05 51 8D C7 24 F8 1B B9 FE A6 41 B8 83 6C 59 05 84 9F 44 0F 85 05 9A 97 5A 97 BC A6 6D 81 FE 59 DE 2F 4B 5E E4 DF B2 A4 19 AA 06 D9 FE F9 33 4D 7E 6A 40 FC 97 34 BF 84 E4 81 81 ED E9 DC 85 32 56 47 E5 A4 F0 2D 6F 4D 2A BA 65 4B 73 89 B6 58 5E D7 35 8D 69 E4 4A 6B 76 50 C1 5C 3A D9 59 11 CF 37 99 FA 48 88 70 7F F4 9F 22 12 F2 24 91 3E 2B BF 28 A5 34 68 C0 50 A3 55 DD A4 E3 9C 6E 85 99 95 B6 24 2E 18 D9 3C 5C B1 4D AA 2F 08 E1 75 F1 F0 6B 49 FC BC E3 8D 00 01 00 28 42 E6 18 57 D4 B1 4D AE 51 27 D5 EF A2 38 91 39 15 37 6C 5A FE 75 93 49 DB FC 57 3C 12 3F 26 D9 16 1D 83 45 8B 78 39 D8 01 15 00 10 F6 F0 50 03 74 BB 18 91 D3 55 8D 7F BB 53 15 7A"
.
hexToUBytes
().
toByteArray
()
ServerLoginResponseVerificationCodeInitPacket
(
data
.
dataInputStream
(),
data
.
size
).
let
{
it
.
decode
();
println
(
it
)
}
}
/*
data
FB 01 04 03 33 00 01 00 BA 02 03 2C 13 00 05 01 00 00 01 23 00 38 D5 01 05 8B 67 4D 52 5A FA 92 DB 99 18 D4 F0 72 03 E0 17 71 7C 8A 45 74 1F C3 2D F8 61 96 0D 93 0D 8C 51 95 70 F8 F9 CB B9 2D 5D BC 4F 5D 89 5F E7 59 8C E4 E5 A2 04 56 02 BC 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 82 00 00 00 35 08 03 00 00 00 BA 12 C3 02 00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00 00 01 73 52 47 42 00 AE CE 1C E9 00 00 00 45 50 4C 54 45 FE F6 ED E2 F1 DF F3 FF F2 11 77 48 FE FE F3 F1 F9 EA D7 FD E7 F8 F9 EC FC EF E7 E8 FF EE 2D 69 48 2A 8A 5D 29 7A 52 F0 ED E1 A9 C7 B1 65 96 79 AB E0 C2 C3 F0 D5 42 7D 5C 4A 99 72 89 AA 93 51 73 5C 6E BA 94 42 BD 7A 0B 00 00 09 C5 49 44 41 54 58 C3 AC 99 8B 76 AB 3A 12 44 91 D0 1B 10 08 04 FF FF A9 B3 5B 60 C7 AF 38 77 EE 0C EB C4 76 6C 07 95 BA AB AB AB 75 BA EE 1F 5D DE CB 63 08 C1 C7 A8 AD BC B6 31 46 6B 3B A3 79 94 E7 F3 63 79 E4 D9 98 89 37 7D F7 FF BB 3C 10 82 6F B7 F7 2A B6 5B 07 EF B5 16 60 DE 1B 63 3A 1F 7E BE 6D 0C 50 EC 09 E1 FE 76 B8 3F FC 6B 0C 1D 5B 37 1F 3E E1 AE 61 9A D8 B6 07 93 B7 56 4D 66 22 06 9D FC EE 5F BF F9 3F 81 90 BB 99 F3 49 E2 1D DA 43 38 13 44 14 8C 9D B4 8E E7 65 40 11 E4 BB FE 8C 5B 78 41 F1 AF 01 48 2E 08 6D 8B AD F1 BA 05 F9 C4 E0 25 E6 31 BA EB 2A 90 C3 74 26 FC 1E 80 CF 14 91 44 7F FC 39 FF D4 37 10 82 C0 B3 B0 67 BF 2E 5E 5B F4 46 E5 3A 5F D7 BE A6 54 17 2D 7C 0D D7 DA E1 7E 93 C7 E5 DB 9E 6E 9F FD A3 14 9C 7F 23 97 55 FA DC 6F 74 8A CC 04 49 03 3F DE 4E 5C 4A 95 9C 53 8D 7A EA 82 F9 10 77 7F EE E5 43 28 C2 9F 00 BA 5B C0 59 A4 CE DB BA AE DB BC 14 D5 2A 82 A5 83 E0 30 92 19 A8 99 36 A7 15 7C BC DF F6 BE D7 86 C0 7F C9 C5 ED 93 C7 1F B9 E0 56 CB AC 61 F5 5D 2E 00 F0 93 D2 5A 9D B6 B0 3F 4B 4D 5C B9 0F C1 EE 29 46 65 4E 40 46 E2 E4 1B 91 C3 A5 2E FE D3 7A 3C 84 0B E3 F3 E2 57 16 2F 08 B9 E6 5A 73 29 65 29 4E B9 25 CF 6B 9A 97 48 20 B2 35 46 1B 6D AD D6 9A 57 69 45 B2 1A 00 90 05 51 8D C7 24 F8 1B B9 FE A6 41 B8 83 6C 59 05 84 9F 44 0F 85 05 9A 97 5A 97 BC A6 6D 81 FE 59 DE 2F 4B 5E E4 DF B2 A4 19 AA 06 D9 FE F9 33 4D 7E 6A 40 FC 97 34 BF 84 E4 81 81 ED E9 DC 85 32 56 47 E5 A4 F0 2D 6F 4D 2A BA 65 4B 73 89 B6 58 5E D7 35 8D 69 E4 4A 6B 76 50 C1 5C 3A D9 59 11 CF 37 99 FA 48 88 70 7F F4 9F 22 12 F2 24 91 3E 2B BF 28 A5 34 68 C0 50 A3 55 DD A4 E3 9C 6E 85 99 95 B6 24 2E 18 D9 3C 5C B1 4D AA 2F 08 E1 75 F1 F0 6B 49 FC BC E3 8D 00 01 00 28 42 E6 18 57 D4 B1 4D AE 51 27 D5 EF A2 38 91 39 15 37 6C 5A FE 75 93 49 DB FC 57 3C 12 3F 26 D9 16 1D 83 45 8B 78 39 D8 01 15 00 10 F6 F0 50 03 74 BB 18 91 D3 55 8D 7F BB 53 15 7A
length 700
verify code
89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 82 00 00 00 35 08 03 00 00 00 BA 12 C3 02 00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00 00 01 73 52 47 42 00 AE CE 1C E9 00 00 00 45 50 4C 54 45 FE F6 ED E2 F1 DF F3 FF F2 11 77 48 FE FE F3 F1 F9 EA D7 FD E7 F8 F9 EC FC EF E7 E8 FF EE 2D 69 48 2A 8A 5D 29 7A 52 F0 ED E1 A9 C7 B1 65 96 79 AB E0 C2 C3 F0 D5 42 7D 5C 4A 99 72 89 AA 93 51 73 5C 6E BA 94 42 BD 7A 0B 00 00 09 C5 49 44 41 54 58 C3 AC 99 8B 76 AB 3A 12 44 91 D0 1B 10 08 04 FF FF A9 B3 5B 60 C7 AF 38 77 EE 0C EB C4 76 6C 07 95 BA AB AB AB 75 BA EE 1F 5D DE CB 63 08 C1 C7 A8 AD BC B6 31 46 6B 3B A3 79 94 E7 F3 63 79 E4 D9 98 89 37 7D F7 FF BB 3C 10 82 6F B7 F7 2A B6 5B 07 EF B5 16 60 DE 1B 63 3A 1F 7E BE 6D 0C 50 EC 09 E1 FE 76 B8 3F FC 6B 0C 1D 5B 37 1F 3E E1 AE 61 9A D8 B6 07 93 B7 56 4D 66 22 06 9D FC EE 5F BF F9 3F 81 90 BB 99 F3 49 E2 1D DA 43 38 13 44 14 8C 9D B4 8E E7 65 40 11 E4 BB FE 8C 5B 78 41 F1 AF 01 48 2E 08 6D 8B AD F1 BA 05 F9 C4 E0 25 E6 31 BA EB 2A 90 C3 74 26 FC 1E 80 CF 14 91 44 7F FC 39 FF D4 37 10 82 C0 B3 B0 67 BF 2E 5E 5B F4 46 E5 3A 5F D7 BE A6 54 17 2D 7C 0D D7 DA E1 7E 93 C7 E5 DB 9E 6E 9F FD A3 14 9C 7F 23 97 55 FA DC 6F 74 8A CC 04 49 03 3F DE 4E 5C 4A 95 9C 53 8D 7A EA 82 F9 10 77 7F EE E5 43 28 C2 9F 00 BA 5B C0 59 A4 CE DB BA AE DB BC 14 D5 2A 82 A5 83 E0 30 92 19 A8 99 36 A7 15 7C BC DF F6 BE D7 86 C0 7F C9 C5 ED 93 C7 1F B9 E0 56 CB AC 61 F5 5D 2E 00 F0 93 D2 5A 9D B6 B0 3F 4B 4D 5C B9 0F C1 EE 29 46 65 4E 40 46 E2 E4 1B 91 C3 A5 2E FE D3 7A 3C 84 0B E3 F3 E2 57 16 2F 08 B9 E6 5A 73 29 65 29 4E B9 25 CF 6B 9A 97 48 20 B2 35 46 1B 6D AD D6 9A 57 69 45 B2 1A 00 90 05 51 8D C7 24 F8 1B B9 FE A6 41 B8 83 6C 59 05 84 9F 44 0F 85 05 9A 97 5A 97 BC A6 6D 81 FE 59 DE 2F 4B 5E E4 DF B2 A4 19 AA 06 D9 FE F9 33 4D 7E 6A 40 FC 97 34 BF 84 E4 81 81 ED E9 DC 85 32 56 47 E5 A4 F0 2D 6F 4D 2A BA 65 4B 73 89 B6 58 5E D7 35 8D 69 E4 4A 6B 76 50 C1 5C 3A D9 59 11 CF 37 99 FA 48 88 70 7F F4 9F 22 12 F2 24 91 3E 2B BF 28 A5 34 68 C0 50 A3 55 DD A4 E3 9C 6E 85 99 95 B6 24 2E 18 D9 3C 5C B1 4D AA 2F 08 E1 75 F1 F0 6B 49 FC BC E3 8D
token00ba
42 E6 18 57 D4 B1 4D AE 51 27 D5 EF A2 38 91 39 15 37 6C 5A FE 75 93 49 DB FC 57 3C 12 3F 26 D9 16 1D 83 45 8B 78 39 D8
*/
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.sync.Mutex
import
kotlinx.coroutines.sync.withLock
import
kotlinx.coroutines.withContext
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.core.readBytes
import
java.awt.Image
import
java.awt.image.BufferedImage
import
java.io.File
import
javax.imageio.ImageIO
import
kotlin.math.max
import
kotlin.math.min
/**
* 让用户处理验证码
*
* @return 用户输入得到的验证码
*/
internal
actual
suspend
fun
solveCaptcha
(
captchaBuffer
:
IoBuffer
):
String
?
=
captchaLock
.
withLock
{
val
captcha
=
captchaBuffer
.
readBytes
()
withContext
(
Dispatchers
.
IO
)
{
MiraiLogger
.
logCyan
(
ImageIO
.
read
(
captcha
.
inputStream
()).
createCharImg
())
}
MiraiLogger
.
logCyan
(
"需要验证码登录, 验证码为 4 字母"
)
try
{
File
(
System
.
getProperty
(
"user.dir"
)
+
"/temp/Captcha.png"
)
.
also
{
withContext
(
Dispatchers
.
IO
)
{
it
.
createNewFile
();
it
.
writeBytes
(
captcha
)
}
}
MiraiLogger
.
logCyan
(
"若看不清字符图片, 请查看 Mirai 目录下 /temp/Captcha.png"
)
}
catch
(
e
:
Exception
)
{
MiraiLogger
.
logCyan
(
"无法写出验证码文件, 请尝试查看以上字符图片"
)
}
MiraiLogger
.
logCyan
(
"若要更换验证码, 请直接回车"
)
readLine
()
?.
takeUnless
{
it
.
isEmpty
()
||
it
.
length
!=
4
}
}
private
val
captchaLock
=
Mutex
()
/**
* @author NaturalHG
*/
@JvmOverloads
internal
fun
BufferedImage
.
createCharImg
(
outputWidth
:
Int
=
100
,
ignoreRate
:
Double
=
0.95
):
String
{
/*
* resize Image
* */
val
newHeight
=
(
this
.
height
*
(
outputWidth
.
toDouble
()
/
this
.
width
)).
toInt
()
val
tmp
=
this
.
getScaledInstance
(
outputWidth
,
newHeight
,
Image
.
SCALE_SMOOTH
)
val
image
=
BufferedImage
(
outputWidth
,
newHeight
,
BufferedImage
.
TYPE_INT_ARGB
)
val
g2d
=
image
.
createGraphics
()
g2d
.
drawImage
(
tmp
,
0
,
0
,
null
)
fun
gray
(
rgb
:
Int
):
Int
{
val
r
=
rgb
and
0
xff0000
shr
16
val
g
=
rgb
and
0
x00ff00
shr
8
val
b
=
rgb
and
0
x0000ff
return
(
r
*
30
+
g
*
59
+
b
*
11
+
50
)
/
100
}
fun
grayCompare
(
g1
:
Int
,
g2
:
Int
):
Boolean
=
min
(
g1
,
g2
).
toDouble
()
/
max
(
g1
,
g2
)
>=
ignoreRate
val
background
=
gray
(
image
.
getRGB
(
0
,
0
))
return
buildString
(
capacity
=
height
)
{
val
lines
=
mutableListOf
<
StringBuilder
>()
var
minXPos
=
outputWidth
var
maxXPos
=
0
for
(
y
in
0
until
image
.
height
)
{
val
builderLine
=
StringBuilder
()
for
(
x
in
0
until
image
.
width
)
{
val
gray
=
gray
(
image
.
getRGB
(
x
,
y
))
if
(
grayCompare
(
gray
,
background
))
{
builderLine
.
append
(
" "
)
}
else
{
builderLine
.
append
(
"#"
)
if
(
x
<
minXPos
)
{
minXPos
=
x
}
if
(
x
>
maxXPos
)
{
maxXPos
=
x
}
}
}
if
(
builderLine
.
toString
().
isBlank
())
{
continue
}
lines
.
add
(
builderLine
)
}
for
(
line
in
lines
)
{
append
(
line
.
substring
(
minXPos
,
maxXPos
)).
append
(
"\n"
)
}
}
}
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CharImageConverter.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.utils
import
java.awt.Image
import
java.awt.image.BufferedImage
import
java.util.*
import
java.util.concurrent.Callable
import
kotlin.math.max
import
kotlin.math.min
/**
* Convert IMAGE into Chars that could shows in terminal
*
* @author NaturalHG
*/
class
CharImageConverter
@JvmOverloads
constructor
(
/**
* width should depends on the width of the terminal
*/
private
var
image
:
BufferedImage
?,
private
val
width
:
Int
,
private
val
ignoreRate
:
Double
=
0.95
)
:
Callable
<
String
>
{
override
fun
call
():
String
{
/*
* resize Image
* */
val
newHeight
=
(
this
.
image
!!
.
height
*
(
width
.
toDouble
()
/
this
.
image
!!
.
width
)).
toInt
()
val
tmp
=
image
!!
.
getScaledInstance
(
width
,
newHeight
,
Image
.
SCALE_SMOOTH
)
val
dimg
=
BufferedImage
(
width
,
newHeight
,
BufferedImage
.
TYPE_INT_ARGB
)
val
g2d
=
dimg
.
createGraphics
()
g2d
.
drawImage
(
tmp
,
0
,
0
,
null
)
this
.
image
=
dimg
val
background
=
gray
(
image
!!
.
getRGB
(
0
,
0
))
val
builder
=
StringBuilder
()
val
lines
=
ArrayList
<
StringBuilder
>(
this
.
image
!!
.
height
)
var
minXPos
=
this
.
width
var
maxXPos
=
0
for
(
y
in
0
until
image
!!
.
height
)
{
val
builderLine
=
StringBuilder
()
for
(
x
in
0
until
image
!!
.
width
)
{
val
gray
=
gray
(
image
!!
.
getRGB
(
x
,
y
))
if
(
grayCompare
(
gray
,
background
))
{
builderLine
.
append
(
" "
)
}
else
{
builderLine
.
append
(
"#"
)
if
(
x
<
minXPos
)
{
minXPos
=
x
}
if
(
x
>
maxXPos
)
{
maxXPos
=
x
}
}
}
if
(
builderLine
.
toString
().
isBlank
())
{
continue
}
lines
.
add
(
builderLine
)
}
for
(
line
in
lines
)
{
builder
.
append
(
line
.
substring
(
minXPos
,
maxXPos
)).
append
(
"\n"
)
}
return
builder
.
toString
()
}
private
fun
gray
(
rgb
:
Int
):
Int
{
val
R
=
rgb
and
0
xff0000
shr
16
val
G
=
rgb
and
0
x00ff00
shr
8
val
B
=
rgb
and
0
x0000ff
return
(
R
*
30
+
G
*
59
+
B
*
11
+
50
)
/
100
}
fun
grayCompare
(
g1
:
Int
,
g2
:
Int
):
Boolean
{
return
min
(
g1
,
g2
).
toDouble
()
/
max
(
g1
,
g2
)
>=
ignoreRate
}
}
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CharImageUtil.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.utils
import
java.awt.image.BufferedImage
/**
* @author NaturalHG
*/
@JvmOverloads
fun
BufferedImage
.
createCharImg
(
sizeWeight
:
Int
=
100
):
String
{
return
CharImageConverter
(
this
,
sizeWeight
).
call
()
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/EventException.java
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.utils
;
/**
* @author NaturalHG
*/
public
class
EventException
extends
RuntimeException
{
private
final
Throwable
cause
;
public
EventException
(
Throwable
throwable
)
{
cause
=
throwable
;
}
public
EventException
()
{
cause
=
null
;
}
public
EventException
(
Throwable
cause
,
String
message
)
{
super
(
message
);
this
.
cause
=
cause
;
}
public
EventException
(
String
message
)
{
super
(
message
);
cause
=
null
;
}
@Override
public
Throwable
getCause
()
{
return
cause
;
}
}
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ImageNetworkUtils.kt
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
java.awt.image.BufferedImage
import
java.io.ByteArrayOutputStream
import
java.io.IOException
import
java.net.HttpURLConnection
import
java.net.URL
import
javax.imageio.ImageIO
/**
* @author NaturalHG
...
...
@@ -29,3 +32,6 @@ object ImageNetworkUtils {
return
conn
.
responseCode
==
200
}
}
fun
BufferedImage
.
toByteArray
():
ByteArray
=
ByteArrayOutputStream
().
use
{
ImageIO
.
write
(
this
,
"JPG"
,
it
);
it
.
toByteArray
()
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLogger.kt
deleted
100644 → 0
View file @
c09d44f4
package
net.mamoe.mirai.utils
import
java.text.SimpleDateFormat
import
java.util.*
/**
* @author Him188moe
*/
interface
MiraiLogger
{
companion
object
:
MiraiLogger
by
defaultLogger
()
var
identity
:
String
fun
info
(
any
:
Any
?)
=
log
(
any
)
fun
log
(
any
:
Any
?)
fun
error
(
any
:
Any
?)
fun
debug
(
any
:
Any
?)
fun
cyan
(
any
:
Any
?)
fun
purple
(
any
:
Any
?)
fun
green
(
any
:
Any
?)
fun
blue
(
any
:
Any
?)
}
/**
* 由 mirai-console 或 mirai-web 等模块实现
*/
var
defaultLogger
:
()
->
MiraiLogger
=
{
Console
()
}
val
DEBUGGING
:
Boolean
by
lazy
{
//avoid inspections
true
}
open
class
Console
(
override
var
identity
:
String
=
"[Unknown]"
)
:
MiraiLogger
{
override
fun
green
(
any
:
Any
?)
=
print
(
any
.
toString
(),
LoggerTextFormat
.
GREEN
)
override
fun
purple
(
any
:
Any
?)
=
print
(
any
.
toString
(),
LoggerTextFormat
.
LIGHT_PURPLE
)
override
fun
blue
(
any
:
Any
?)
=
print
(
any
.
toString
(),
LoggerTextFormat
.
BLUE
)
override
fun
cyan
(
any
:
Any
?)
=
print
(
any
.
toString
(),
LoggerTextFormat
.
LIGHT_CYAN
)
override
fun
error
(
any
:
Any
?)
=
print
(
any
.
toString
(),
LoggerTextFormat
.
RED
)
override
fun
log
(
any
:
Any
?)
=
print
(
any
.
toString
(),
LoggerTextFormat
.
LIGHT_GRAY
)
override
fun
debug
(
any
:
Any
?)
{
if
(
DEBUGGING
)
{
print
(
any
.
toString
(),
LoggerTextFormat
.
YELLOW
)
}
}
fun
print
(
value
:
String
?,
color
:
LoggerTextFormat
=
LoggerTextFormat
.
YELLOW
)
{
val
s
=
SimpleDateFormat
(
"MM-dd HH:mm:ss"
).
format
(
Date
())
println
(
"$color$identity $s : $value"
)
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
java.text.SimpleDateFormat
import
java.util.*
actual
typealias
PlatformLogger
=
Console
open
class
Console
@JvmOverloads
constructor
(
override
var
identity
:
String
?
=
null
)
:
MiraiLogger
{
override
fun
logGreen
(
any
:
Any
?)
=
println
(
any
.
toString
(),
LoggerTextFormat
.
GREEN
)
override
fun
logPurple
(
any
:
Any
?)
=
println
(
any
.
toString
(),
LoggerTextFormat
.
LIGHT_PURPLE
)
override
fun
logBlue
(
any
:
Any
?)
=
println
(
any
.
toString
(),
LoggerTextFormat
.
BLUE
)
override
fun
logCyan
(
any
:
Any
?)
=
println
(
any
.
toString
(),
LoggerTextFormat
.
LIGHT_CYAN
)
override
fun
logError
(
any
:
Any
?)
=
println
(
any
.
toString
(),
LoggerTextFormat
.
RED
)
override
fun
log
(
e
:
Throwable
)
=
e
.
printStackTrace
()
override
fun
log
(
any
:
Any
?)
=
println
(
any
.
toString
())
//kotlin println
override
fun
logDebug
(
any
:
Any
?)
{
if
(
DEBUGGING
)
{
println
(
any
.
toString
(),
LoggerTextFormat
.
YELLOW
)
}
}
private
fun
println
(
value
:
String
?,
color
:
LoggerTextFormat
)
{
val
time
=
SimpleDateFormat
(
"HH:mm:ss"
).
format
(
Date
())
if
(
identity
==
null
)
{
println
(
"$color$time : $value"
)
}
else
{
println
(
"$color$identity $time : $value"
)
}
}
}
private
val
DEBUGGING
:
Boolean
by
lazy
{
//avoid inspections
true
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformBridgeJvm.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
java.net.InetAddress
import
java.security.MessageDigest
import
java.util.zip.CRC32
actual
val
currentTime
:
Long
=
System
.
currentTimeMillis
()
actual
val
deviceName
:
String
=
InetAddress
.
getLocalHost
().
hostName
actual
fun
crc32
(
key
:
ByteArray
):
Int
=
CRC32
().
let
{
it
.
update
(
key
);
it
.
value
.
toInt
()
}
actual
fun
md5
(
byteArray
:
ByteArray
):
ByteArray
=
MessageDigest
.
getInstance
(
"MD5"
).
digest
(
byteArray
)
actual
fun
solveIpAddress
(
hostname
:
String
):
String
=
InetAddress
.
getByName
(
hostname
).
hostAddress
actual
fun
localIpAddress
():
String
=
InetAddress
.
getLocalHost
().
hostAddress
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt
0 → 100644
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
kotlinx.io.core.Closeable
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.nio.read
import
java.net.InetSocketAddress
import
java.nio.channels.DatagramChannel
import
java.nio.channels.ReadableByteChannel
actual
class
MiraiDatagramChannel
actual
constructor
(
serverHost
:
String
,
serverPort
:
Short
)
:
Closeable
{
private
val
serverAddress
:
InetSocketAddress
=
InetSocketAddress
(
serverHost
,
serverPort
.
toInt
())
private
val
channel
:
DatagramChannel
=
DatagramChannel
.
open
().
connect
(
serverAddress
)
actual
suspend
fun
read
(
buffer
:
IoBuffer
)
=
withContext
(
Dispatchers
.
IO
)
{
(
channel
as
ReadableByteChannel
).
read
(
buffer
)
}
actual
suspend
fun
send
(
buffer
:
IoBuffer
)
=
withContext
(
Dispatchers
.
IO
)
{
buffer
.
readDirect
{
channel
.
send
(
it
,
serverAddress
)
}
}
override
fun
close
()
{
channel
.
close
()
}
actual
val
isOpen
:
Boolean
get
()
=
channel
.
isOpen
}
actual
typealias
ClosedChannelException
=
java
.
nio
.
channels
.
ClosedChannelException
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/TEA.kt
View file @
d89f8acc
package
net.mamoe.mirai.utils
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.core.readBytes
import
java.nio.ByteBuffer
import
java.util.*
import
kotlin.experimental.and
...
...
@@ -10,10 +12,10 @@ import kotlin.experimental.xor
*
* @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java
*/
object
TEA
{
actual
object
TEA
{
private
const
val
UINT32_MASK
=
0
xffffffffL
private
fun
doOption
(
data
:
ByteArray
,
key
:
ByteArray
,
encrypt
:
Boolean
):
ByteArray
{
internal
actual
fun
doOption
(
data
:
ByteArray
,
key
:
ByteArray
,
encrypt
:
Boolean
):
ByteArray
{
val
mRandom
=
Random
()
lateinit
var
mOutput
:
ByteArray
lateinit
var
mInBlock
:
ByteArray
...
...
@@ -162,7 +164,7 @@ object TEA {
}
fun
decrypt
(
cipherText
:
ByteArray
,
offset
:
Int
,
len
:
Int
):
ByteArray
?
{
require
(!(
len
%
8
!=
0
||
len
<
16
))
{
"data must len % 8 == 0 && len >= 16"
}
require
(!(
len
%
8
!=
0
||
len
<
16
))
{
"data must len % 8 == 0 && len >= 16
but given $len
"
}
mIV
=
decode
(
cipherText
,
offset
)
mIndexPos
=
(
mIV
[
0
]
and
7
).
toInt
()
var
plen
=
len
-
mIndexPos
-
10
...
...
@@ -240,20 +242,23 @@ object TEA {
}
}
fun
encrypt
(
source
:
ByteArray
,
key
:
ByteArray
):
ByteArray
{
@JvmStatic
actual
fun
encrypt
(
source
:
ByteArray
,
key
:
ByteArray
):
ByteArray
{
return
doOption
(
source
,
key
,
true
)
}
@
Suppress
(
"unused"
)
fun
encrypt
(
source
:
ByteArray
,
keyHex
:
String
):
ByteArray
{
return
encrypt
(
source
,
keyHex
.
hexToBytes
()
)
@
JvmStatic
actual
fun
decrypt
(
source
:
ByteArray
,
key
:
ByteArray
):
ByteArray
{
return
doOption
(
source
,
key
,
false
)
}
fun
decrypt
(
source
:
ByteArray
,
key
:
ByteArray
):
ByteArray
{
return
doOption
(
source
,
key
,
false
)
@JvmStatic
actual
fun
decrypt
(
source
:
ByteArray
,
key
:
IoBuffer
):
ByteArray
{
return
doOption
(
source
,
key
.
readBytes
(),
false
)
}
fun
decrypt
(
source
:
ByteArray
,
keyHex
:
String
):
ByteArray
{
@JvmStatic
actual
fun
decrypt
(
source
:
ByteArray
,
keyHex
:
String
):
ByteArray
{
return
decrypt
(
source
,
keyHex
.
hexToBytes
())
}
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/Utils.kt
deleted
100644 → 0
View file @
c09d44f4
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.utils
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.dataInputStream
import
java.awt.image.BufferedImage
import
java.io.ByteArrayOutputStream
import
java.io.DataInputStream
import
java.io.DataOutputStream
import
java.io.File
import
java.lang.reflect.Field
import
java.util.*
import
java.util.zip.CRC32
import
java.util.zip.GZIPInputStream
import
java.util.zip.GZIPOutputStream
import
javax.imageio.ImageIO
import
kotlin.random.Random
import
kotlin.random.nextInt
/**
* @author Him188moe
* @author NaturalHG
*/
//@JvmSynthetic
fun
ByteArray
.
toHexString
():
String
=
toHexString
(
" "
)
fun
ByteArray
.
toHexString
(
separator
:
String
=
" "
):
String
=
this
.
joinToString
(
separator
)
{
var
ret
=
it
.
toString
(
16
).
toUpperCase
()
if
(
ret
.
length
==
1
)
{
ret
=
"0$ret"
}
return
@
joinToString
ret
}
fun
ByteArray
.
toUHexString
(
separator
:
String
=
" "
):
String
=
this
.
toUByteArray
().
toUHexString
(
separator
)
//@JvmSynthetic
fun
ByteArray
.
toUHexString
():
String
=
this
.
toUByteArray
().
toUHexString
()
//@JvmSynthetic
fun
UByteArray
.
toUHexString
(
separator
:
String
=
" "
):
String
{
return
this
.
joinToString
(
separator
)
{
var
ret
=
it
.
toString
(
16
).
toUpperCase
()
if
(
ret
.
length
==
1
)
{
ret
=
"0$ret"
}
return
@
joinToString
ret
}
}
//@JvmSynthetic
fun
UByteArray
.
toUHexString
():
String
=
this
.
toUHexString
(
" "
)
fun
Byte
.
toUHexString
():
String
=
this
.
toUByte
().
toString
(
16
)
fun
String
.
hexToBytes
():
ByteArray
=
TIMProtocol
.
hexToBytes
(
this
)
fun
String
.
hexToUBytes
():
UByteArray
=
TIMProtocol
.
hexToUBytes
(
this
)
fun
String
.
hexToInt
():
Int
=
hexToBytes
().
toUInt
().
toInt
()
fun
ByteArray
.
toUInt
():
UInt
=
this
[
0
].
toUInt
().
and
(
255
u
).
shl
(
24
)
+
this
[
1
].
toUInt
().
and
(
255
u
).
shl
(
16
)
+
this
[
2
].
toUInt
().
and
(
255
u
).
shl
(
8
)
+
this
[
3
].
toUInt
().
and
(
255
u
).
shl
(
0
)
open
class
ByteArrayDataOutputStream
:
DataOutputStream
(
ByteArrayOutputStream
())
{
open
fun
toByteArray
():
ByteArray
=
(
out
as
ByteArrayOutputStream
).
toByteArray
()
open
fun
toUByteArray
():
UByteArray
=
(
out
as
ByteArrayOutputStream
).
toByteArray
().
toUByteArray
()
}
fun
dataEncode
(
t
:
(
ByteArrayDataOutputStream
)
->
Unit
):
ByteArray
=
ByteArrayDataOutputStream
().
also
(
t
).
toByteArray
()
fun
<
R
>
dataDecode
(
byteArray
:
ByteArray
,
t
:
(
DataInputStream
)
->
R
):
R
=
byteArray
.
dataInputStream
().
let
(
t
)
fun
<
R
>
ByteArray
.
decode
(
t
:
(
DataInputStream
)
->
R
):
R
=
this
.
dataInputStream
().
let
(
t
)
fun
ByteArray
.
decryptBy
(
key
:
ByteArray
):
ByteArray
=
TEA
.
decrypt
(
this
,
key
)
fun
ByteArray
.
decryptBy
(
key
:
String
):
ByteArray
=
TEA
.
decrypt
(
this
,
key
)
fun
DataInputStream
.
skip
(
n
:
Number
)
{
this
.
skip
(
n
.
toLong
())
}
fun
getRandomByteArray
(
length
:
Int
):
ByteArray
=
List
(
length
)
{
Random
.
Default
.
nextInt
(
0
..
255
).
toByte
()
}.
toByteArray
()
operator
fun
File
.
plus
(
child
:
String
):
File
=
File
(
this
,
child
)
private
const
val
GTK_BASE_VALUE
:
Int
=
5381
internal
fun
getGTK
(
sKey
:
String
):
Int
{
var
value
=
GTK_BASE_VALUE
for
(
c
in
sKey
.
toCharArray
())
{
value
+=
(
value
shl
5
)
+
c
.
toInt
()
}
value
=
value
and
Int
.
MAX_VALUE
return
value
}
internal
fun
getCrc32
(
key
:
ByteArray
):
Int
=
CRC32
().
let
{
it
.
update
(
key
);
it
.
value
.
toInt
()
}
/**
* 获取类的所有字段(类成员变量), 包括父类的和私有的. <br></br>
* 相当于将这个类和它所有父类的 [Class.getDeclaredFields] 都合并成一个 [List] <br></br>
* 不会排除重名的字段. <br></br>
*
* @return field list
*/
@Throws
(
SecurityException
::
class
)
fun
Any
.
getAllDeclaredFields
():
List
<
Field
>
{
var
clazz
:
Class
<
*
>
=
this
.
javaClass
val
list
=
LinkedList
<
Field
>()
loop
@
do
{
if
(!
clazz
.
name
.
contains
(
"net.mamoe"
))
{
break
@
loop
}
list
.
addAll
(
clazz
.
declaredFields
.
filter
{
(
it
.
name
==
"Companion"
||
it
.
name
==
"input"
).
not
()
}.
toList
())
if
(
clazz
.
superclass
==
null
)
{
break
}
clazz
=
clazz
.
superclass
}
while
(
clazz
!=
Object
::
javaClass
)
return
list
}
private
const
val
ZERO_BYTE
:
Byte
=
0
fun
ByteArray
.
removeZeroTail
():
ByteArray
{
var
i
=
this
.
size
-
1
while
(
this
[
i
]
==
ZERO_BYTE
)
{
--
i
}
return
this
.
copyOfRange
(
0
,
i
+
1
)
}
fun
BufferedImage
.
toByteArray
(
formatName
:
String
=
"PNG"
):
ByteArray
{
return
dataEncode
{
ImageIO
.
write
(
this
,
formatName
,
it
)
}
}
object
GZip
{
fun
uncompress
(
bytes
:
ByteArray
):
ByteArray
=
dataEncode
{
GZIPInputStream
(
bytes
.
inputStream
()).
transferTo
(
it
)
}
fun
compress
(
bytes
:
ByteArray
):
ByteArray
=
ByteArrayOutputStream
().
let
{
GZIPOutputStream
(
it
).
write
(
bytes
)
return
it
.
toByteArray
()
}
}
fun
ByteArray
.
cutTail
(
length
:
Int
):
ByteArray
=
this
.
copyOfRange
(
0
,
this
.
size
-
length
)
fun
ByteArray
.
getRight
(
length
:
Int
):
ByteArray
=
this
.
copyOfRange
(
this
.
size
-
length
,
this
.
size
)
\ No newline at end of file
mirai-core/src/jvmTest/kotlin/BadQQFilter.kt
View file @
d89f8acc
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.
launch
import
kotlinx.coroutines.
GlobalScope
import
kotlinx.coroutines.runBlocking
import
kotlinx.coroutines.withContext
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.login
import
net.mamoe.mirai.network.NetworkScope
import
net.mamoe.mirai.network.protocol.tim.packet.login.LoginState
import
net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import
net.mamoe.mirai.utils.BotAccount
import
net.mamoe.mirai.utils.Console
import
java.util.*
...
...
@@ -36,7 +35,7 @@ const val qqList = "" +
suspend
fun
main
()
{
val
goodBotList
=
Collections
.
synchronizedList
(
mutableListOf
<
Bot
>())
withContext
(
Network
Scope
.
coroutineContext
)
{
withContext
(
Global
Scope
.
coroutineContext
)
{
qqList
.
split
(
"\n"
)
.
filterNot
{
it
.
isEmpty
()
}
.
map
{
it
.
split
(
"----"
)
}
...
...
@@ -54,7 +53,7 @@ suspend fun main() {
withContext
(
Dispatchers
.
IO
)
{
bot
.
login
()
}.
let
{
state
->
if
(
state
==
Login
State
.
SUCCESS
)
{
if
(
state
==
Login
Result
.
SUCCESS
)
{
goodBotList
.
add
(
bot
)
}
}
...
...
mirai-debug/build.gradle
View file @
d89f8acc
...
...
@@ -7,6 +7,7 @@ dependencies {
compile
rootProject
.
ext
.
coroutineCommon
compile
rootProject
.
ext
.
kotlinJvm
compile
rootProject
.
ext
.
kotlinxIOJvm
}
tasks
.
withType
(
JavaCompile
)
{
...
...
mirai-debug/src/main/java/HexComparator.kt
View file @
d89f8acc
@
file
:
Suppress
(
"ObjectPropertyName"
,
"unused"
,
"NonAsciiCharacters"
,
"MayBeConstant"
)
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.toUHexString
import
net.mamoe.mirai.utils.toUHexString
import
java.awt.Toolkit
import
java.awt.datatransfer.DataFlavor
...
...
@@ -246,6 +245,41 @@ object HexComparator {
}
fun
colorize
(
hex1s
:
String
):
String
{
val
builder
=
StringBuilder
()
val
hex1
=
hex1s
.
trim
{
it
<=
' '
}.
replace
(
"\n"
,
""
).
split
(
" "
).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
()
val
constMatcher1
=
ConstMatcher
(
hex1s
)
val
numberLine
=
StringBuilder
()
val
hex1ConstName
=
StringBuilder
()
val
hex1b
=
StringBuilder
()
buildConstNameChain
(
hex1s
.
length
,
constMatcher1
,
hex1ConstName
)
for
(
i
in
hex1
.
indices
)
{
var
h1
:
String
?
=
null
val
matchedConstName
=
constMatcher1
.
getMatchedConstName
(
i
)
if
(
matchedConstName
!=
null
)
{
h1
=
BLUE
+
hex1
[
i
]
}
if
(
h1
==
null
)
{
h1
=
hex1
[
i
]
h1
=
GREEN
+
h1
}
numberLine
.
append
(
UNKNOWN
).
append
(
getFixedNumber
(
i
)).
append
(
" "
)
hex1b
.
append
(
" "
).
append
(
h1
).
append
(
" "
)
}
return
builder
.
append
(
"\n"
)
.
append
(
numberLine
).
append
(
"\n"
)
.
append
(
hex1ConstName
).
append
(
"\n"
)
.
append
(
hex1b
).
append
(
"\n"
)
.
toString
()
}
private
fun
getFixedNumber
(
number
:
Int
):
String
{
if
(
number
<
10
)
{
...
...
mirai-debug/src/main/java/PacketDebuger.kt
View file @
d89f8acc
...
...
@@ -3,6 +3,8 @@
import
jpcap.JpcapCaptor
import
jpcap.packet.IPPacket
import
jpcap.packet.UDPPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readBytes
import
net.mamoe.mirai.message.internal.readMessageChain
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.ServerEventPacket
...
...
@@ -93,7 +95,7 @@ object Main {
//println("--------------")
//println("接收数据包")
//println("raw packet = " + data.toUHexString())
packetReceived
(
ServerPacket
.
ofByteArray
(
data
)
)
packetReceived
(
data
.
read
{
this
.
parseServerPacket
(
data
.
size
)
}
)
}
fun
packetReceived
(
packet
:
ServerPacket
)
{
...
...
@@ -106,8 +108,8 @@ object Main {
is
UnknownServerEventPacket
->
{
println
(
"--------------"
)
println
(
"未知事件ID="
+
packet
.
packetId
.
toUHexString
()
)
println
(
"未知事件: "
+
packet
.
input
.
read
All
Bytes
().
toUHexString
())
println
(
"未知事件ID="
+
packet
.
idHex
)
println
(
"未知事件: "
+
packet
.
input
.
readBytes
().
toUHexString
())
}
is
ServerEventPacket
->
{
...
...
@@ -124,41 +126,41 @@ object Main {
}
}
fun
dataSent
(
rawPacket
:
ByteArray
)
=
rawPacket
.
cutTail
(
1
).
decode
{
packet
->
fun
dataSent
(
rawPacket
:
ByteArray
)
=
rawPacket
.
cutTail
(
1
).
read
{
println
(
"---------------------------"
)
packet
.
skip
(
3
)
//head
val
idHex
=
packet
.
readN
Bytes
(
4
).
toUHexString
()
discardExact
(
3
)
//head
val
idHex
=
read
Bytes
(
4
).
toUHexString
()
println
(
"发出包ID = $idHex"
)
packet
.
skip
(
TIMProtocol
.
fixVer2
.
hexToBytes
().
size
+
1
+
5
-
3
+
1
)
discardExact
(
TIMProtocol
.
fixVer2
.
hexToBytes
().
size
+
1
+
5
-
3
+
1
)
val
encryptedBody
=
packet
.
readAll
Bytes
()
val
encryptedBody
=
readRemaining
Bytes
()
println
(
"body = ${encryptedBody.toUHexString()}"
)
encryptedBody
.
decode
{
data
->
encryptedBody
.
read
{
when
(
idHex
.
substring
(
0
,
5
))
{
"00 CD"
->
{
println
(
"好友消息"
)
val
raw
=
data
.
readAll
Bytes
()
val
raw
=
readRemaining
Bytes
()
println
(
"解密前数据: "
+
raw
.
toUHexString
())
val
messageData
=
raw
.
decryptBy
(
sessionKey
)
println
(
"解密结果: "
+
messageData
.
toUHexString
())
println
(
"尝试解消息"
)
messageData
.
decode
{
it
.
skip
(
messageData
.
read
{
discardExact
(
4
+
4
+
12
+
2
+
4
+
4
+
16
+
2
+
2
+
4
+
2
+
16
+
4
+
4
+
7
+
15
+
2
+
1
)
val
chain
=
it
.
readMessageChain
()
val
chain
=
readMessageChain
()
println
(
chain
)
}
}
"03 88"
->
{
println
(
"上传图片-获取图片ID"
)
d
ata
.
skip
(
8
)
val
body
=
data
.
readAll
Bytes
().
decryptBy
(
sessionKey
)
d
iscardExact
(
8
)
val
body
=
readRemaining
Bytes
().
decryptBy
(
sessionKey
)
println
(
body
.
toUHexString
())
}
}
...
...
mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt
View file @
d89f8acc
package
demo1
import
kotlinx.coroutines.delay
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.event.events.FriendMessageEvent
import
net.mamoe.mirai.event.events.GroupMessageEvent
import
net.mamoe.mirai.event.subscribeAll
import
net.mamoe.mirai.event.subscribeAlways
import
net.mamoe.mirai.event.subscribeOnce
import
net.mamoe.mirai.event.subscribeUntilFalse
import
net.mamoe.mirai.login
import
net.mamoe.mirai.message.Image
import
net.mamoe.mirai.message.PlainText
import
net.mamoe.mirai.network.protocol.tim.packet.login.Login
State
import
net.mamoe.mirai.network.protocol.tim.packet.login.Login
Result
import
net.mamoe.mirai.utils.BotAccount
import
net.mamoe.mirai.utils.Console
import
net.mamoe.mirai.utils.MiraiLogger
import
kotlin.system.exitProcess
/**
* @author Him188moe
*/
suspend
fun
main
()
{
val
bot
=
Bot
(
BotAccount
(
//填写你的账号
qqNumber
=
1994701
1
21
,
password
=
"
xiaoqqq
"
qqNumber
=
1994701
0
21
,
password
=
"
asdhim188666
"
),
Console
())
bot
.
login
().
let
{
if
(
it
!=
LoginState
.
SUCCESS
)
{
MiraiLogger
.
error
(
"Login failed: "
+
it
.
name
)
bot
.
login
{
touchTimeoutMillis
=
2000
randomDeviceName
=
true
}.
let
{
if
(
it
!=
LoginResult
.
SUCCESS
)
{
MiraiLogger
.
logError
(
"Login failed: "
+
it
.
name
)
exitProcess
(
0
)
}
}
//提供泛型以监听事件
subscribe
Always
<
FriendMessageEvent
>
{
//获取第一个纯文本消息
subscribe
Once
<
FriendMessageEvent
>
{
//获取第一个纯文本消息
, 获取不到会抛出 NoSuchElementException
val
firstText
=
it
.
message
.
first
<
PlainText
>()
//获取第一个图片
val
firstImage
=
it
.
message
.
first
<
Image
>()
val
firstImage
=
it
.
message
.
first
OrNull
<
Image
>()
when
{
it
.
message
eq
"你好"
->
it
.
reply
(
"你好!"
)
...
...
@@ -53,13 +55,13 @@ suspend fun main() {
}
}
/*it.
message
eq "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
/*it.
event
eq "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, Group(session.bot, 580266363)).of()
})*/
it
.
message
eq
"发图片群2"
->
Group
(
bot
,
580266363
).
sendMessage
(
Image
(
"{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"
))
/* it.
message
eq "发图片" -> sendFriendMessage(it.sender, PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
/* it.
event
eq "发图片" -> sendFriendMessage(it.sender, PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, it.sender).of()
})*/
it
.
message
eq
"发图片2"
->
it
.
reply
(
PlainText
(
"test"
)
+
Image
(
"{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"
))
...
...
@@ -82,6 +84,9 @@ suspend fun main() {
}
}
//由于使用的是协程, main函数执行完后就会结束程序.
delay
(
Long
.
MAX_VALUE
)
//永远等待, 以测试事件
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment