1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-02-27 04:30:08 +08:00

Message receiver is working!

This commit is contained in:
Him188moe 2019-08-31 23:21:56 +08:00
parent aa6e95624b
commit 6350638e40
10 changed files with 360 additions and 78 deletions

View File

@ -1,7 +1,5 @@
package net.mamoe.mirai.network
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import net.mamoe.mirai.MiraiServer
import net.mamoe.mirai.network.packet.client.ClientPacket
import net.mamoe.mirai.network.packet.client.login.*
@ -12,11 +10,12 @@ import net.mamoe.mirai.network.packet.client.touch.ServerHeartbeatResponsePacket
import net.mamoe.mirai.network.packet.client.writeHex
import net.mamoe.mirai.network.packet.client.writeRandom
import net.mamoe.mirai.network.packet.server.ServerPacket
import net.mamoe.mirai.network.packet.server.event.*
import net.mamoe.mirai.network.packet.server.login.*
import net.mamoe.mirai.network.packet.server.security.*
import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacket
import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacketEncrypted
import net.mamoe.mirai.task.MiraiTaskManager
import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.util.*
import net.mamoe.mirai.utils.MiraiLogger
import java.io.ByteArrayInputStream
@ -24,7 +23,7 @@ import java.io.FileOutputStream
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
import kotlin.system.exitProcess
import java.util.concurrent.TimeUnit
/**
@ -51,8 +50,14 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
Thread {
while (true) {
val dp1 = DatagramPacket(ByteArray(2048), 2048)
socket.receive(dp1)
GlobalScope.async {
try {
socket.receive(dp1)
} catch (e: Exception) {
if (e.message == "socket closed") {
return@Thread
}
}
MiraiThreadPool.getInstance().submit {
var i = dp1.data.size - 1;
while (dp1.data[i] == zeroByte) {
--i
@ -62,7 +67,7 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
}
}
}.start()
}
@ -105,7 +110,10 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
@ExperimentalUnsignedTypes
internal fun onPacketReceived(packet: ServerPacket) {
packet.decode()
println("Packet received: $packet")
MiraiLogger info "Packet received: $packet"
if (packet is ServerEventPacket) {
sendPacket(ClientMessageResponsePacket(this.number, packet.packetId, this.sessionKey, packet.eventIdentity))
}
when (packet) {
is ServerTouchResponsePacket -> {
if (packet.serverIP != null) {//redirection
@ -122,7 +130,7 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
}
is ServerLoginResponseFailedPacket -> {
println("Login failed: " + packet.state.toString())
MiraiLogger error "Login failed: " + packet.state.toString()
return
}
@ -185,11 +193,16 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
}
}
is ServerVerificationCodePacket -> {
this.sequence++
}
is ServerSessionKeyResponsePacket -> {
this.sessionKey = packet.sessionKey
MiraiTaskManager.getInstance().repeatingTask({
MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey))
}, 90000)
}, 90000, 90000, TimeUnit.MILLISECONDS)
this.tlv0105 = packet.tlv0105
sendPacket(ClientLoginStatusPacket(this.number, this.sessionKey, ClientLoginStatus.ONLINE))
}
@ -202,9 +215,10 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
this.sKey = packet.sKey
this.cookies = "uin=o" + this.number + ";skey=" + this.sKey + ";"
MiraiTaskManager.getInstance().repeatingTask({
MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientRefreshSKeyRequestPacket(this.number, this.sessionKey))
}, 1800000)
}, 1800000, 1800000, TimeUnit.MILLISECONDS)
this.gtk = getGTK(sKey)
sendPacket(ClientAccountInfoRequestPacket(this.number, this.sessionKey))
}
@ -217,6 +231,22 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
}
is ServerMessageEventPacketRaw -> onPacketReceived(packet.analyze())
is ServerFriendMessageEventPacket -> {
//friend message
}
is ServerGroupMessageEventPacket -> {
//group message
}
is ServerUnknownEventPacket -> {
//unknown message event
}
is ServerVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt(this.token00BA))
is ServerLoginResponseVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt())
is ServerLoginResponseResendPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
is ServerLoginResponseSuccessPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
@ -224,6 +254,8 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
is ServerTouchResponsePacketEncrypted -> onPacketReceived(packet.decrypt())
is ServerSKeyResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
is ServerAccountInfoResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
is ServerMessageEventPacketRawEncoded -> onPacketReceived(packet.decrypt(this.sessionKey))
else -> throw IllegalArgumentException(packet.toString())
}
@ -232,21 +264,17 @@ class RobotNetworkHandler(val number: Int, private val password: String) {
@ExperimentalUnsignedTypes
fun sendPacket(packet: ClientPacket) {
MiraiThreadPool.getInstance().submit {
try {
packet.encode()
packet.writeHex(Protocol.tail)
try {
packet.encode()
} catch (e: Exception) {
e.printStackTrace()
}
packet.writeHex(Protocol.tail)
val data = packet.toByteArray()
try {
socket.send(DatagramPacket(data, data.size))
MiraiLogger info "Packet sent: ${data.toUByteArray().toUHexString()}"
} catch (e: Exception) {
e.printStackTrace()
exitProcess(1)
val data = packet.toByteArray()
socket.send(DatagramPacket(data, data.size))
MiraiLogger info "Packet sent: $packet"
} catch (e: Throwable) {
e.printStackTrace()
}
}
}
}

View File

@ -6,7 +6,6 @@ import net.mamoe.mirai.network.packet.client.*
import net.mamoe.mirai.network.packet.server.ServerPacket
import net.mamoe.mirai.network.packet.server.dataInputStream
import net.mamoe.mirai.network.packet.server.goto
import net.mamoe.mirai.network.packet.server.security.ServerSKeyResponsePacket
import net.mamoe.mirai.util.TEACryptor
import java.io.DataInputStream
@ -47,9 +46,9 @@ class ServerAccountInfoResponsePacketEncrypted(inputStream: DataInputStream) : S
}
fun decrypt(sessionKey: ByteArray): ServerSKeyResponsePacket {
fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket {
this.input goto 14
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
return ServerSKeyResponsePacket(TEACryptor.decrypt(data, sessionKey).dataInputStream());
return ServerAccountInfoResponsePacket(TEACryptor.decrypt(data, sessionKey).dataInputStream());
}
}

View File

@ -4,6 +4,7 @@ import net.mamoe.mirai.network.packet.Packet
import net.mamoe.mirai.network.packet.client.session.ServerAccountInfoResponsePacketEncrypted
import net.mamoe.mirai.network.packet.client.toHexString
import net.mamoe.mirai.network.packet.client.touch.ServerHeartbeatResponsePacket
import net.mamoe.mirai.network.packet.server.event.ServerMessageEventPacketRawEncoded
import net.mamoe.mirai.network.packet.server.login.*
import net.mamoe.mirai.network.packet.server.security.ServerLoginSuccessPacket
import net.mamoe.mirai.network.packet.server.security.ServerSKeyResponsePacketEncrypted
@ -11,6 +12,7 @@ import net.mamoe.mirai.network.packet.server.security.ServerSessionKeyResponsePa
import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacket
import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacketEncrypted
import net.mamoe.mirai.util.getAllDeclaredFields
import net.mamoe.mirai.util.hexToBytes
import net.mamoe.mirai.util.toUHexString
import java.io.DataInputStream
@ -31,6 +33,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
stream.skip(3)
return when (val idHex = stream.readInt().toHexString(" ")) {
"08 25 31 01" -> ServerTouchResponsePacketEncrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, stream)
@ -76,7 +79,12 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
"00 5C" -> ServerAccountInfoResponsePacketEncrypted(stream)
"00 58" -> ServerHeartbeatResponsePacket(stream)
//"00 CE" ->
"00 BA" -> ServerVerificationCodePacketEncrypted(stream)
"00 CE", "00 17" -> ServerMessageEventPacketRawEncoded(stream, idHex.hexToBytes())
else -> throw IllegalArgumentException(idHex)
}
@ -120,6 +128,10 @@ fun DataInputStream.readIP(): String {
return buff
}
fun DataInputStream.readShortVarString(): String {
return String(this.readNBytes(this.readShort().toInt()))
}
fun DataInputStream.readVarString(length: Int): String {
return String(this.readNBytes(length))
}

View File

@ -1,30 +1,92 @@
package net.mamoe.mirai.network.packet.server.event
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.client.ClientPacket
import net.mamoe.mirai.network.packet.client.encryptAndWrite
import net.mamoe.mirai.network.packet.client.writeHex
import net.mamoe.mirai.network.packet.client.writeQQ
import net.mamoe.mirai.network.packet.server.ServerPacket
import net.mamoe.mirai.network.packet.server.dataInputStream
import net.mamoe.mirai.network.packet.server.goto
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.util.toUHexString
import java.io.DataInputStream
/**
* @author Him188moe
* 告知服务器已经收到数据
*/
class ServerMessageEventPacket(input: DataInputStream) : ServerPacket(input) {
@PacketId("")//随后写入
@ExperimentalUnsignedTypes
open class ClientMessageResponsePacket(
private val qq: Int,
private val packetIdFromServer: ByteArray,
private val sessionKey: ByteArray,
private val eventIdentity: ByteArray
) : ClientPacket() {
override fun encode() {
this.write(packetIdFromServer)
this.writeQQ(qq)
this.writeHex(Protocol._fixVer)
this.encryptAndWrite(sessionKey) {
it.write(eventIdentity)
}
}
}
/**
* 群聊和好友消息分析
*/
@PacketId("00 17")
class ServerMessageEventPacketRaw(
input: DataInputStream,
private val dataLength: Int,
private val packetId: ByteArray
) : ServerPacket(input) {
lateinit var type: ByteArray;
lateinit var eventIdentity: ByteArray;
override fun decode() {
eventIdentity = this.input.readNBytes(16)
type = this.input.goto(18).readNBytes(2)
}
fun analyze(): ServerEventPacket = when (val typeHex = type.toUHexString()) {
"00 C4" -> {
if (this.input.goto(33).readBoolean()) {
ServerAndroidOnlineEventPacket(this.input, packetId, eventIdentity)
} else {
ServerAndroidOfflineEventPacket(this.input, packetId, eventIdentity)
}
}
"00 2D" -> ServerGroupUploadFileEventPacket(this.input, packetId, eventIdentity)
"00 52" -> ServerGroupMessageEventPacket(this.input, packetId, eventIdentity)
"00 A6" -> ServerFriendMessageEventPacket(this.input, packetId, eventIdentity)
"02 10" -> ServerUnknownEventPacket(this.input, packetId, eventIdentity)
else -> throw IllegalArgumentException(typeHex)
}
}
class ServerUnknownEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
@PacketId("00 17")
class ServerMessageEventPacketRawEncoded(input: DataInputStream, val packetId: ByteArray) : ServerPacket(input) {
override fun decode() {
}
}
@ExperimentalUnsignedTypes
class ClientGroupMessageResponsePacket : ClientMessageResponsePacket() {
}
/**
* 告知服务器已经收到数据
*/
@ExperimentalUnsignedTypes
open class ClientMessageResponsePacket : ClientPacket() {
override fun encode() {
fun decrypt(sessionKey: ByteArray): ServerMessageEventPacketRaw {
this.input goto 14
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
return ServerMessageEventPacketRaw(TEACryptor.decrypt(data, sessionKey).dataInputStream(), data.size, packetId);
}
}

View File

@ -0,0 +1,137 @@
package net.mamoe.mirai.network.packet.server.event
import net.mamoe.mirai.network.packet.server.ServerPacket
import net.mamoe.mirai.network.packet.server.goto
import net.mamoe.mirai.utils.MiraiLogger
import java.io.ByteArrayOutputStream
import java.io.DataInputStream
import java.util.zip.GZIPInputStream
/**
* @author Him188moe
*/
open class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, val eventIdentity: ByteArray) : ServerPacket(input) {
override fun decode() {
}
}
/**
* Android 客户端上线
*/
class ServerAndroidOnlineEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
/**
* Android 客户端上线
*/
class ServerAndroidOfflineEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
/**
* 群文件上传
*/
class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
lateinit var message: String
override fun decode() {
message = String(this.input.goto(64).readNBytes(this.input.goto(60).readShort().toInt()))
}//todo test
}
class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
var group: Int = 0
var qq: Int = 0
lateinit var message: String
lateinit var messageType: MessageType
enum class MessageType {
NORMAL,
XML,
AT,
IMAGE,
OTHER,
}
override fun decode() {
group = this.input.goto(51).readInt()
qq = this.input.goto(56).readInt()
val fontLength = this.input.goto(108).readShort()
println(this.toString())
messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) {
19 -> MessageType.NORMAL
14 -> MessageType.XML
2 -> MessageType.IMAGE
6 -> MessageType.AT
else -> 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.IMAGE -> {
val faceId = this.input.goto(110 + fontLength + 8).readByte()
message = "[face${faceId}.gif]"
}
MessageType.AT, MessageType.OTHER -> {
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).readByte().toInt()) {
12 -> {
this.input.skip(4)//maybe 5?
}
19 -> {
}
0x0E -> {
}
else -> {
}
}*/
}
}
MiraiLogger info this.toString()
}
}
class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
override fun decode() {
}
}

View File

@ -0,0 +1,44 @@
package net.mamoe.mirai.network.packet.server.login
import net.mamoe.mirai.network.packet.server.ServerPacket
import net.mamoe.mirai.network.packet.server.dataInputStream
import net.mamoe.mirai.network.packet.server.goto
import net.mamoe.mirai.util.TEACryptor
import java.io.DataInputStream
/**
* @author Him188moe
*/
class ServerVerificationCodePacket(input: DataInputStream) : ServerPacket(input) {
lateinit var verifyCode: ByteArray
lateinit var verifyToken: ByteArray
var unknownBoolean: Boolean? = null
lateinit var token00BA: ByteArray
var count: Int = 0
@ExperimentalUnsignedTypes
override fun decode() {
TODO()
val verifyCodeLength = this.input.goto(78).readShort()//2bytes
this.verifyCode = 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 ServerVerificationCodePacketEncrypted(input: DataInputStream) : ServerPacket(input) {
override fun decode() {
}
fun decrypt(token00BA: ByteArray): ServerVerificationCodePacket {
this.input goto 14
val data = TEACryptor.decrypt(token00BA, this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) });
return ServerVerificationCodePacket(data.dataInputStream())
}
}

View File

@ -53,11 +53,6 @@ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, val dataLengt
}
}
/**
* Encrypted using [0828_rec_decr_key], decrypting in RobotNetworkHandler
*
* @author Him188moe
*/
class ServerSessionKeyResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
override fun decode() {

View File

@ -10,14 +10,11 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class MiraiTaskManager {
public final class MiraiTaskManager {
private static MiraiTaskManager instance;
private static MiraiTaskManager instance = new MiraiTaskManager();
public static MiraiTaskManager getInstance(){
if(MiraiTaskManager.instance == null){
MiraiTaskManager.instance = new MiraiTaskManager();
}
public static MiraiTaskManager getInstance() {
return MiraiTaskManager.instance;
}
@ -34,19 +31,19 @@ public class MiraiTaskManager {
}
/**
基础Future处理
* 基础Future处理
*/
public void execute(Runnable runnable){
public void execute(Runnable runnable) {
this.execute(runnable, MiraiTaskExceptionHandler.byDefault());
}
public void execute(Runnable runnable, MiraiTaskExceptionHandler handler){
public void execute(Runnable runnable, MiraiTaskExceptionHandler handler) {
this.pool.execute(() ->
{
try{
try {
runnable.run();
}catch (Exception e){
} catch (Exception e) {
handler.onHandle(e);
}
});
@ -69,13 +66,13 @@ public class MiraiTaskManager {
}
/**
异步任务
* 异步任务
*/
public <D> void ansycTask(Callable<D> callable, Consumer<D> callback){
this.ansycTask(callable,callback, MiraiTaskExceptionHandler.byDefault());
public <D> void ansycTask(Callable<D> callable, Consumer<D> callback) {
this.ansycTask(callable, callback, MiraiTaskExceptionHandler.byDefault());
}
public <D> void ansycTask(Callable<D> callable, Consumer<D> callback, MiraiTaskExceptionHandler handler){
public <D> void ansycTask(Callable<D> callable, Consumer<D> callback, MiraiTaskExceptionHandler handler) {
this.pool.execute(() -> {
try {
callback.accept(callable.call());
@ -86,7 +83,7 @@ public class MiraiTaskManager {
}
/**
定时任务
* 定时任务
*/
public void repeatingTask(Runnable runnable, long intervalMillis) {
@ -102,12 +99,13 @@ public class MiraiTaskManager {
}
public void repeatingTask(Runnable runnable, long intervalMillis, int times, MiraiTaskExceptionHandler handler) {
AtomicInteger integer = new AtomicInteger(times-1);
AtomicInteger integer = new AtomicInteger(times - 1);
this.repeatingTask(
runnable, intervalMillis, a -> integer.getAndDecrement() > 0, handler
);
}
public <D extends Runnable> void repeatingTask(D runnable, long intervalMillis, Predicate<D> shouldContinue, MiraiTaskExceptionHandler handler) {
new Thread(() -> {
do {
@ -129,7 +127,7 @@ public class MiraiTaskManager {
public void deletingTask(Runnable runnable, long intervalMillis) {
new Thread(() -> {
try{
try {
Thread.sleep(intervalMillis);
} catch (InterruptedException e) {
e.printStackTrace();

View File

@ -1,19 +1,23 @@
package net.mamoe.mirai.task;
import java.io.Closeable;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledThreadPoolExecutor;
public final class MiraiThreadPool extends ThreadPoolExecutor implements Closeable {
public final class MiraiThreadPool extends ScheduledThreadPoolExecutor implements Closeable {
private static MiraiThreadPool instance = new MiraiThreadPool();
protected MiraiThreadPool(){
super(0,
public static MiraiThreadPool getInstance() {
return instance;
}
MiraiThreadPool() {
super(0);
/*super(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>()
);
);*/
}

View File

@ -11,6 +11,10 @@ object MiraiLogger {
this.print(o.toString())
}
infix fun error(o: Any?) {
this.print(o.toString(), LoggerTextFormat.RED)
}
infix fun log(o: Any?) = info(o)
infix fun println(o: Any?) = info(o)
@ -27,11 +31,10 @@ object MiraiLogger {
this.print(e.cause.toString())*/
}
private fun print(value: String?) {
private fun print(value: String?, color: LoggerTextFormat = LoggerTextFormat.BLUE) {
val s = SimpleDateFormat("MM-dd HH:mm:ss").format(Date())
kotlin.io.println(LoggerTextFormat.BLUE.toString() + "[Mirai] $s : $value")
kotlin.io.println("$color[Mirai] $s : $value")
}
}