This commit is contained in:
Him188moe 2019-09-14 00:23:25 +08:00
parent ff0acc87e3
commit b488bee0f4
24 changed files with 366 additions and 178 deletions

View File

@ -213,21 +213,7 @@ public final class MiraiServer {
String qqList = String qqList =
"2573990098----qq123456789\n" + "3034551466----zxcvbnm\n";
"3303923865----q123456789\n" +
"3349933294----q123456789\n" +
"3303708824----q123456789\n" +
"3227036647----q123456789\n" +
"3451394431----q123456789\n" +
"3533243484----q123456789\n" +
"3364512686----q123456789\n" +
"3137567463----q123456789\n" +
"3414786399----q123456789\n" +
"3347405939----q123456789\n" +
"3544089622----q123456789\n" +
"3108512993----q123456789\n" +
"2985563549----q123456789\n" +
"3463531892----q123456789\n";
private Bot getAvailableBot() throws ExecutionException, InterruptedException { private Bot getAvailableBot() throws ExecutionException, InterruptedException {
for (String it : qqList.split("\n")) { for (String it : qqList.split("\n")) {

View File

@ -10,7 +10,7 @@ import java.util.Objects;
* @author Him188moe * @author Him188moe
*/ */
public abstract class BotEvent extends MiraiEvent { public abstract class BotEvent extends MiraiEvent {
private final Bot bot; public final Bot bot;
public BotEvent(@NotNull Bot bot) { public BotEvent(@NotNull Bot bot) {
this.bot = Objects.requireNonNull(bot); this.bot = Objects.requireNonNull(bot);

View File

@ -10,25 +10,25 @@ import org.jetbrains.annotations.NotNull;
* @author Him188moe * @author Him188moe
*/ */
public final class GroupMessageEvent extends GroupEvent { public final class GroupMessageEvent extends GroupEvent {
private final QQ sender; public final QQ sender;
private final MessageChain messageChain; public final MessageChain chain;
private final String messageString; public final String message;
public GroupMessageEvent(@NotNull Bot bot, @NotNull Group group, @NotNull QQ sender, @NotNull MessageChain messageChain) { public GroupMessageEvent(@NotNull Bot bot, @NotNull Group group, @NotNull QQ sender, @NotNull MessageChain chain) {
super(bot, group); super(bot, group);
this.sender = sender; this.sender = sender;
this.messageChain = messageChain; this.chain = chain;
this.messageString = messageChain.toString(); this.message = chain.toString();
} }
@NotNull @NotNull
public MessageChain getMessageChain() { public MessageChain getChain() {
return messageChain; return chain;
} }
@NotNull @NotNull
public String getMessageString() { public String getMessage() {
return messageString; return message;
} }
@NotNull @NotNull

View File

@ -11,15 +11,15 @@ import java.util.Objects;
* @author Him188moe * @author Him188moe
*/ */
public abstract class FriendEvent extends BotEvent { public abstract class FriendEvent extends BotEvent {
private final QQ qq; public final QQ sender;
public FriendEvent(@NotNull Bot bot, @NotNull QQ qq) { public FriendEvent(@NotNull Bot bot, @NotNull QQ sender) {
super(bot); super(bot);
this.qq = Objects.requireNonNull(qq); this.sender = Objects.requireNonNull(sender);
} }
@NotNull @NotNull
public QQ getQQ() { public QQ getSender() {
return qq; return sender;
} }
} }

View File

@ -11,15 +11,15 @@ import java.util.Objects;
* @author Him188moe * @author Him188moe
*/ */
public final class FriendMessageEvent extends FriendEvent { public final class FriendMessageEvent extends FriendEvent {
private final MessageChain messageChain; public final MessageChain message;
public FriendMessageEvent(@NotNull Bot bot, @NotNull QQ sender, @NotNull MessageChain messageChain) { public FriendMessageEvent(@NotNull Bot bot, @NotNull QQ sender, @NotNull MessageChain message) {
super(bot, sender); super(bot, sender);
this.messageChain = Objects.requireNonNull(messageChain); this.message = Objects.requireNonNull(message);
} }
@NotNull @NotNull
public MessageChain message() { public MessageChain message() {
return messageChain; return message;
} }
} }

View File

@ -166,7 +166,7 @@ public enum FaceID {
return value; return value;
} }
} }
return null; return FaceID.unknown;
} }

View File

@ -74,7 +74,7 @@ abstract class Message {
/** /**
* 比较两个 Message 的内容是否相等. : * 比较两个 Message 的内容是否相等. :
* - [PlainText] 比较 [PlainText.text] * - [PlainText] 比较 [PlainText.text]
* - [Image] 比较 [Image.imageID] * - [Image] 比较 [Image.imageId]
*/ */
abstract infix fun valueEquals(another: Message): Boolean abstract infix fun valueEquals(another: Message): Boolean

View File

@ -10,13 +10,13 @@ package net.mamoe.mirai.message
*/ */
object MessageId { object MessageId {
const val AT: Int = 0x00//todo 不知道是多少 const val AT: Int = 0x06
const val FACE: Int = 0x00//todo 不知道是多少 const val FACE: Int = 0x02
const val TEXT: Int = 0x01 const val TEXT: Int = 0x01
const val IMAGE: Int = 0x06 const val IMAGE: Int = 0x03
const val CHAIN: Int = 0xff//仅用于 equals. Packet 中不存在 Chain 概念 const val CHAIN: Int = 0xff//仅用于 equals. Packet 中不存在 Chain 概念
} }

View File

@ -3,8 +3,10 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.FaceID import net.mamoe.mirai.message.FaceID
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId import net.mamoe.mirai.message.MessageId
import net.mamoe.mirai.network.packet.readLVNumber
import net.mamoe.mirai.network.packet.writeHex import net.mamoe.mirai.network.packet.writeHex
import net.mamoe.mirai.network.packet.writeLVByteArray import net.mamoe.mirai.network.packet.writeLVByteArray
import net.mamoe.mirai.utils.lazyDecode
import net.mamoe.mirai.utils.lazyEncode import net.mamoe.mirai.utils.lazyEncode
/** /**
@ -41,5 +43,18 @@ class Face(val id: FaceID) : Message() {
} }
return this.id == another.id return this.id == another.id
} }
companion object {
fun ofByteArray(data: ByteArray): Face = lazyDecode(data) {
//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)
val id1 = FaceID.ofId(it.readLVNumber().toInt())//可能这个是id, 也可能下面那个
it.skip(it.readByte().toLong())
it.readLVNumber()//某id?
return@lazyDecode Face(id1)
}
}
} }

View File

@ -2,30 +2,33 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId import net.mamoe.mirai.message.MessageId
import net.mamoe.mirai.network.packet.writeHex import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.writeLVByteArray import net.mamoe.mirai.utils.lazyDecode
import net.mamoe.mirai.network.packet.writeLVString
import net.mamoe.mirai.utils.lazyEncode import net.mamoe.mirai.utils.lazyEncode
import net.mamoe.mirai.utils.skip
import net.mamoe.mirai.utils.toUHexString
/** /**
* 图片消息. * 图片消息.
* 由接收消息时构建, 可直接发送 * 由接收消息时构建, 可直接发送
* *
* @param imageId 类似 `{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg`. 群的是大写id, 好友的是小写id
*
* @author Him188moe * @author Him188moe
*/ */
open class Image internal constructor(val imageID: String) : Message() { open class Image internal constructor(val imageId: String) : Message() {
override val type: Int = MessageId.IMAGE override val type: Int = MessageId.IMAGE
override fun toStringImpl(): String { override fun toStringImpl(): String {
return imageID return imageId
} }
override fun toByteArray(): ByteArray = lazyEncode { section -> override fun toByteArray(): ByteArray = lazyEncode { section ->
section.writeByte(0x03)//todo 可能是 0x03? section.writeByte(MessageId.IMAGE)
section.writeLVByteArray(lazyEncode { child -> section.writeLVByteArray(lazyEncode { child ->
child.writeByte(0x02) child.writeByte(0x02)
child.writeLVString(this.imageID) child.writeLVString(this.imageId)
child.writeHex("04 00 " + child.writeHex("04 00 " +
"04 9B 53 B0 08 " + "04 9B 53 B0 08 " +
"05 00 " + "05 00 " +
@ -35,16 +38,51 @@ open class Image internal constructor(val imageID: String) : Message() {
"07 00 " + "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") "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.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.writeBytes(this.imageId)
child.writeByte(0x41) child.writeByte(0x41)
}) })
} }
override fun valueEquals(another: Message): Boolean { override fun valueEquals(another: Message): Boolean {
if (another is Image) { if (another is Image) {
return this.imageID == another.imageID return this.imageId == another.imageId
} }
return false return false
} }
companion object {
@JvmStatic
fun ofByteArray0x06(data: ByteArray): Image = lazyDecode(data) {
it.skip(1)
println("好友的图片")
println(data.toUHexString())
val filenameLength = it.readShort()
val suffix = it.readString(filenameLength).substringAfter(".")
it.skip(data.size - 37 - 1 - filenameLength - 2)
val imageId = String(it.readNBytes(36))
println(imageId)
it.skip(1)//0x41
return@lazyDecode Image("{$imageId}.$suffix")
}
@JvmStatic
fun ofByteArray0x03(data: ByteArray): Image = lazyDecode(data) {
it.skip(1)
return@lazyDecode Image(String(it.readLVByteArray()))
/*
println(String(it.readLVByteArray()))
it.readTLVMap()
return@lazyDecode Image(String(it.readLVByteArray().cutTail(5).getRight(42)))
/
it.skip(data.size - 47)
val imageId = String(it.readNBytes(42))
it.skip(1)//0x41
it.skip(1)//0x42
it.skip(1)//0x43
it.skip(1)//0x41
return@lazyDecode Image(imageId)*/
}
}
} }

View File

@ -2,8 +2,10 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId import net.mamoe.mirai.message.MessageId
import net.mamoe.mirai.network.packet.readLVString
import net.mamoe.mirai.network.packet.writeLVByteArray import net.mamoe.mirai.network.packet.writeLVByteArray
import net.mamoe.mirai.network.packet.writeLVString import net.mamoe.mirai.network.packet.writeLVString
import net.mamoe.mirai.utils.lazyDecode
import net.mamoe.mirai.utils.lazyEncode import net.mamoe.mirai.utils.lazyEncode
/** /**
@ -31,4 +33,11 @@ class PlainText(private val text: String) : Message() {
} }
return this.text == another.text return this.text == another.text
} }
companion object {
fun ofByteArray(data: ByteArray): PlainText = lazyDecode(data) {
it.skip(1)
PlainText(it.readLVString())
}
}
} }

View File

@ -3,9 +3,9 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.network.LoginSession import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.image.ClientTryGetImageIDPacket import net.mamoe.mirai.network.packet.image.ClientTryGetImageIDPacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageFailedPacket import net.mamoe.mirai.network.packet.image.ServerTryGetImageIDFailedPacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket import net.mamoe.mirai.network.packet.image.ServerTryGetImageIDResponsePacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageSuccessPacket import net.mamoe.mirai.network.packet.image.ServerTryGetImageIDSuccessPacket
import net.mamoe.mirai.network.packet.md5 import net.mamoe.mirai.network.packet.md5
import net.mamoe.mirai.utils.ImageNetworkUtils import net.mamoe.mirai.utils.ImageNetworkUtils
import net.mamoe.mirai.utils.toByteArray import net.mamoe.mirai.utils.toByteArray
@ -27,16 +27,16 @@ class UnsolvedImage(filename: String, val image: BufferedImage) : Image(getImage
constructor(url: URL) : this(File(url.file)) constructor(url: URL) : this(File(url.file))
fun upload(session: LoginSession, contact: Contact): CompletableFuture<Unit> { fun upload(session: LoginSession, contact: Contact): CompletableFuture<Unit> {
return session.expectPacket<ServerTryUploadGroupImageResponsePacket> { return session.expectPacket<ServerTryGetImageIDResponsePacket> {
toSend { ClientTryGetImageIDPacket(session.bot.account.qqNumber, session.sessionKey, session.bot.account.qqNumber, image) } toSend { ClientTryGetImageIDPacket(session.bot.account.qqNumber, session.sessionKey, contact.number, image) }
expect { expect {
when (it) { when (it) {
is ServerTryUploadGroupImageFailedPacket -> { is ServerTryGetImageIDFailedPacket -> {
//已经存在于服务器了 //已经存在于服务器了
} }
is ServerTryUploadGroupImageSuccessPacket -> { is ServerTryGetImageIDSuccessPacket -> {
val data = image.toByteArray() val data = image.toByteArray()
if (!ImageNetworkUtils.postImage(it.uKey.toUHexString(), data.size, session.bot.account.qqNumber, contact.number, data)) { if (!ImageNetworkUtils.postImage(it.uKey.toUHexString(), data.size, session.bot.account.qqNumber, contact.number, data)) {
throw RuntimeException("cannot upload image") throw RuntimeException("cannot upload image")
@ -56,8 +56,8 @@ class UnsolvedImage(filename: String, val image: BufferedImage) : Image(getImage
return "{" + md5.copyOfRange(0, 4).toUHexString("") + "-" return "{" + md5.copyOfRange(0, 4).toUHexString("") + "-"
.plus(md5.copyOfRange(4, 6).toUHexString("")) + "-" .plus(md5.copyOfRange(4, 6).toUHexString("")) + "-"
.plus(md5.copyOfRange(6, 8).toUHexString("")) + "-" .plus(md5.copyOfRange(6, 8).toUHexString("")) + "-"
.plus(md5.copyOfRange(8, 12).toUHexString("")) + "-" .plus(md5.copyOfRange(8, 10).toUHexString("")) + "-"
.plus(md5.copyOfRange(12, 16).toUHexString("")) + "}." + if (filename.endsWith(".jpeg")) "jpg" else filename.substringAfter(".", "jpg") .plus(md5.copyOfRange(10, 16).toUHexString("")) + "}." + if (filename.endsWith(".jpeg")) "jpg" else filename.substringAfter(".", "jpg")
} }
} }
} }

View File

@ -70,7 +70,7 @@ object Protocol {
* 发送/接受消息中的一个const (?) * 发送/接受消息中的一个const (?)
* length=15 * length=15
*/ */
const val friendMessageConst1 = "00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91" const val messageConst1 = "00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf() private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf()

View File

@ -6,9 +6,9 @@ import net.mamoe.mirai.network.packet.action.AddFriendResult
import net.mamoe.mirai.network.packet.action.ClientAddFriendPacket import net.mamoe.mirai.network.packet.action.ClientAddFriendPacket
import net.mamoe.mirai.network.packet.action.ClientCanAddFriendPacket import net.mamoe.mirai.network.packet.action.ClientCanAddFriendPacket
import net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket import net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageFailedPacket import net.mamoe.mirai.network.packet.image.ServerTryGetImageIDFailedPacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket import net.mamoe.mirai.network.packet.image.ServerTryGetImageIDResponsePacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageSuccessPacket import net.mamoe.mirai.network.packet.image.ServerTryGetImageIDSuccessPacket
import net.mamoe.mirai.task.MiraiThreadPool import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.getGTK import net.mamoe.mirai.utils.getGTK
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
@ -39,15 +39,15 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
it.onPacketReceived(packet) it.onPacketReceived(packet)
} }
} }
is ServerTryUploadGroupImageSuccessPacket -> { is ServerTryGetImageIDSuccessPacket -> {
// ImageNetworkUtils.postImage(packet.uKey.toUHexString(), ) // ImageNetworkUtils.postImage(packet.uKey.toUHexString(), )
} }
is ServerTryUploadGroupImageFailedPacket -> { is ServerTryGetImageIDFailedPacket -> {
} }
is ServerTryUploadGroupImageResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey)) is ServerTryGetImageIDResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey)) is ServerAccountInfoResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerAccountInfoResponsePacket -> { is ServerAccountInfoResponsePacket -> {

View File

@ -2,18 +2,21 @@ package net.mamoe.mirai.network.handler
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.events.group.GroupMessageEvent
import net.mamoe.mirai.event.events.qq.FriendMessageEvent import net.mamoe.mirai.event.events.qq.FriendMessageEvent
import net.mamoe.mirai.event.hookWhile import net.mamoe.mirai.event.hookWhile
import net.mamoe.mirai.message.defaults.Image
import net.mamoe.mirai.message.defaults.MessageChain import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.message.defaults.PlainText import net.mamoe.mirai.message.defaults.PlainText
import net.mamoe.mirai.message.defaults.UnsolvedImage
import net.mamoe.mirai.network.LoginSession import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.ServerFriendMessageEventPacket
import net.mamoe.mirai.network.packet.ServerGroupMessageEventPacket
import net.mamoe.mirai.network.packet.ServerGroupUploadFileEventPacket
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.action.ClientSendFriendMessagePacket import net.mamoe.mirai.network.packet.action.ClientSendFriendMessagePacket
import net.mamoe.mirai.network.packet.action.ClientSendGroupMessagePacket import net.mamoe.mirai.network.packet.action.ClientSendGroupMessagePacket
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import java.io.File
/** /**
* 处理消息事件, 承担消息发送任务. * 处理消息事件, 承担消息发送任务.
@ -31,21 +34,39 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
return@hookWhile false return@hookWhile false
} }
when { when {
it.message() valueEquals "你好" -> it.qq.sendMessage("你好!") it.message valueEquals "你好" -> it.sender.sendMessage("你好!")
it.message().toString().startsWith("复读") -> it.qq.sendMessage(it.message()) it.message.toString().startsWith("复读") -> it.sender.sendMessage(it.message())
it.message().toString().startsWith("发群") -> { it.message.toString().startsWith("发群") -> {
it.message().list.toMutableList().let { messages -> it.message().list.toMutableList().let { messages ->
messages.removeAt(0) messages.removeAt(0)
sendGroupMessage(Group(session.bot, 580266363), MessageChain(messages)) sendGroupMessage(Group(session.bot, 580266363), MessageChain(messages))
} }
} }
it.message() valueEquals "发图片" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image -> /*it.message valueEquals "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, it.qq).get() image.upload(session, Group(session.bot, 580266363)).get()
}) })*/
it.message valueEquals "发图片群2" -> sendGroupMessage(Group(session.bot, 580266363), Image("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg").toChain())
/* it.message valueEquals "发图片" -> sendFriendMessage(it.sender, PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, it.sender).get()
})*/
it.message valueEquals "发图片2" -> sendFriendMessage(it.sender, PlainText("test") + Image("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"))
} }
return@hookWhile true return@hookWhile true
} }
GroupMessageEvent::class.hookWhile {
if (session.socket.isClosed()) {
return@hookWhile false
}
when {
it.message.contains("复读") -> it.group.sendMessage(it.chain)
}
return@hookWhile true
}
} }
override fun onPacketReceived(packet: ServerPacket) { override fun onPacketReceived(packet: ServerPacket) {
@ -55,20 +76,17 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
} }
is ServerFriendMessageEventPacket -> { is ServerFriendMessageEventPacket -> {
if (ignoreMessage) { if (ignoreMessage) return
return
}
FriendMessageEvent(session.bot, session.bot.contacts.getQQ(packet.qq), packet.message).broadcast() FriendMessageEvent(session.bot, session.bot.contacts.getQQ(packet.qq), packet.message).broadcast()
} }
is ServerGroupMessageEventPacket -> { is ServerGroupMessageEventPacket -> {
//todo message chain if (ignoreMessage) return
//GroupMessageEvent(this.bot, bot.contacts.getGroupByNumber(packet.groupNumber), bot.contacts.getQQ(packet.qq), packet.message)
}
is UnknownServerEventPacket -> { if (packet.qq == session.bot.account.qqNumber) return
//todo
GroupMessageEvent(session.bot, session.bot.contacts.getGroupByNumber(packet.groupNumber), session.bot.contacts.getQQ(packet.qq), packet.message).broadcast()
} }
is ServerSendFriendMessageResponsePacket, is ServerSendFriendMessageResponsePacket,
@ -86,6 +104,6 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
} }
fun sendGroupMessage(group: Group, message: MessageChain) { fun sendGroupMessage(group: Group, message: MessageChain) {
session.socket.sendPacket(ClientSendGroupMessagePacket(group.groupId, session.bot.account.qqNumber, session.sessionKey, message)) session.socket.sendPacket(ClientSendGroupMessagePacket(session.bot.account.qqNumber, group.groupId, session.sessionKey, message))
} }
} }

View File

@ -2,19 +2,17 @@
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.message.FaceID
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.Face import net.mamoe.mirai.message.defaults.Face
import net.mamoe.mirai.message.defaults.Image import net.mamoe.mirai.message.defaults.Image
import net.mamoe.mirai.message.defaults.MessageChain import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.message.defaults.PlainText import net.mamoe.mirai.message.defaults.PlainText
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.lazyDecode
import net.mamoe.mirai.utils.toUHexString import net.mamoe.mirai.utils.toUHexString
import java.io.ByteArrayOutputStream import net.mamoe.mirai.utils.toUInt
import java.io.DataInputStream import java.io.DataInputStream
import java.util.zip.GZIPInputStream
/** /**
* Packet id: `00 CE` or `00 17` * Packet id: `00 CE` or `00 17`
@ -59,7 +57,12 @@ abstract class ServerEventPacket(input: DataInputStream, val packetId: ByteArray
/** /**
* Unknown event * Unknown event
*/ */
class UnknownServerEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) class UnknownServerEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
override fun decode() {
super.decode()
println("UnknownServerEventPacket data: " + this.input.goto(0).readAllBytes().toUHexString())
}
}
/** /**
* Android 客户端上线 * Android 客户端上线
@ -86,8 +89,8 @@ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArr
class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
var groupNumber: Long = 0 var groupNumber: Long = 0
var qq: Long = 0 var qq: Long = 0
lateinit var message: String lateinit var senderName: String
lateinit var messageType: MessageType lateinit var message: MessageChain
enum class MessageType { enum class MessageType {
NORMAL, NORMAL,
@ -107,10 +110,29 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
override fun decode() { override fun decode() {
println(this.input.goto(0).readAllBytes().toUHexString()) println(this.input.goto(0).readAllBytes().toUHexString())
groupNumber = this.input.goto(51).readInt().toLong() groupNumber = this.input.goto(51).readInt().toLong()
qq = this.input.goto(56).readLong() qq = this.input.goto(56).readNBytes(4).toUInt().toLong()
val fontLength = this.input.goto(108).readShort()
//println(this.input.goto(110 + fontLength).readNBytesAt(2).toUHexString())//always 00 00
this.input.goto(108)
this.input.readLVByteArray()
input.skip(2)//2个0x00
message = input.readSections()
val map = input.readTLVMap(true)
if (map.containsKey(18)) {
this.senderName = lazyDecode(map.getValue(18)) {
val tlv = it.readTLVMap(true)
tlv.printTLVMap()
when {
tlv.containsKey(0x01) -> String(tlv.getValue(0x01))
tlv.containsKey(0x02) -> String(tlv.getValue(0x02))
else -> "null"
}
}
}
/*
messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) { messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) {
0x13 -> MessageType.NORMAL 0x13 -> MessageType.NORMAL
0x0E -> MessageType.XML 0x0E -> MessageType.XML
@ -126,9 +148,9 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
MiraiLogger.debug("ServerGroupMessageEventPacket id=$id") MiraiLogger.debug("ServerGroupMessageEventPacket id=$id")
MessageType.OTHER MessageType.OTHER
} }
} }*/
/*
when (messageType) { when (messageType) {
MessageType.NORMAL -> { MessageType.NORMAL -> {
val gzippedMessage = this.input.goto(110 + fontLength + 16).readNBytes(this.input.goto(110 + fontLength + 3).readShort().toInt() - 11) val gzippedMessage = this.input.goto(110 + fontLength + 16).readNBytes(this.input.goto(110 + fontLength + 3).readShort().toInt() - 11)
@ -184,10 +206,26 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
} }
}*/ }*/
} }
} }*/
} }
} }
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) { class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
var qq: Long = 0 var qq: Long = 0
lateinit var message: MessageChain lateinit var message: MessageChain
@ -195,18 +233,22 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
override fun decode() { override fun decode() {
input.goto(0) input.goto(0)
println() println("ServerFriendMessageEventPacket.input=" + input.readAllBytes().toUHexString())
println(input.readAllBytes().toUHexString())
input.goto(0) input.goto(0)
qq = input.readUIntAt(0).toLong() qq = input.readUIntAt(0).toLong()
val l1 = input.readShortAt(22) val l1 = input.readShortAt(22)
input.goto(93 + l1) input.goto(93 + l1)
input.readVarByteArray()//font input.readLVByteArray()//font
input.skip(2)//2个0x00 input.skip(2)//2个0x00
message = input.readSections() message = input.readSections()
println(message.toObjectString())
val map: Map<Int, ByteArray> = input.readTLVMap(true)
println(map[18])
//todo 后面有昵称可读
//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 val offset = unknownLength0 + fontLength//57
@ -215,50 +257,64 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
input.goto(103 + offset).readString(length.toInt()) input.goto(103 + offset).readString(length.toInt())
}))*/ }))*/
} }
}
private fun DataInputStream.readSection(): Message? { private fun DataInputStream.readSection(): Message? {
val messageType = this.readByte().toInt() val messageType = this.readByte().toInt()
val sectionLength = this.readShort().toLong()//sectionLength: short val sectionLength = this.readShort().toLong()//sectionLength: short
this.skip(1)//message和face是 0x01, image是0x06 val sectionData = this.readNBytes(sectionLength)
return when (messageType) { return when (messageType) {
0x01 -> PlainText(readVarString()) 0x01 -> PlainText.ofByteArray(sectionData)
0x02 -> { 0x02 -> Face.ofByteArray(sectionData)
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0 0x03 -> Image.ofByteArray0x03(sectionData)
//00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D 0x06 -> Image.ofByteArray0x06(sectionData)
val id1 = FaceID.ofId(readLVNumber().toInt())//可能这个是id, 也可能下面那个
this.skip(this.readByte().toLong()) 0x19 -> {//长文本
this.readLVNumber()//某id? val value = readLVByteArray()
return Face(id1) PlainText(String(value))
}
0x06 -> { // PlainText(String(GZip.uncompress( value)))
this.skip(sectionLength - 37 - 1)
val imageId = String(this.readNBytes(36))
this.skip(1)//0x41
return Image("{$imageId}.jpg")//todo 如何确定文件后缀??
}
else -> null
} }
0x14 -> {//长文本
val value = readLVByteArray()
println(value.size)
println(value.toUHexString())
this.skip(7)//几个TLV
return PlainText(String(value))
}
0x0E -> {//可能是结尾标志?
//null
null
}
else -> {
println("未知的messageType=0x${messageType.toByte().toUHexString()}")
println("后文=${this.readAllBytes().toUHexString()}")
null
}
} }
private fun DataInputStream.readSections(): MessageChain {
val chain = MessageChain()
var got: Message? = null
do {
if (got != null) {
chain.concat(got)
}
got = this.readSection()
} while (got != null)
return chain
}
} }
fun main() { private fun DataInputStream.readSections(): MessageChain {
println(String("16 20 20 39 39 31 30 20 38 38 31 43 42 20 20 20 20 20 20 31 37 36 32 65 42 39 45 32 37 32 31 43 39 36 44 37 39 41 38 32 31 36 45 30 41 44 34 30 42 35 39 35 39 31 38 36 2E 6A 70 67 66 2F 65 64 33 39 30 66 38 34 2D 34 66 38 37 2D 34 36 64 63 2D 62 33 38 35 2D 34 35 35 36 62 35 31 30 61 61 35 33 41".replace(" ", " ").hexToBytes())) val chain = MessageChain()
println(".jpg".toByteArray().size) var got: Message? = null
do {
if (got != null) {
chain.concat(got)
}
if (this.available() == 0) {
return chain
}
got = this.readSection()
} while (got != null)
return chain
} }
/* /*
牛逼 (10404 牛逼 (10404

View File

@ -11,7 +11,7 @@ import net.mamoe.mirai.network.packet.PacketNameFormatter.adjustName
import net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket import net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket import net.mamoe.mirai.network.packet.image.ServerTryGetImageIDResponsePacket
import net.mamoe.mirai.network.packet.login.* import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.task.MiraiThreadPool import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
@ -82,7 +82,9 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
println(bytes.size) println(bytes.size)
return ServerLoginResponseFailedPacket(when (bytes.size) { return ServerLoginResponseFailedPacket(when (bytes.size) {
63, 319, 135, 351 -> LoginState.WRONG_PASSWORD//这四个其中一个是被冻结 135 -> LoginState.UNKNOWN//账号已经在另一台电脑登录??
63, 319, 351 -> LoginState.WRONG_PASSWORD//63不是密码错误, 应该是登录过频繁
//135 -> LoginState.RETYPE_PASSWORD //135 -> LoginState.RETYPE_PASSWORD
279 -> LoginState.BLOCKED 279 -> LoginState.BLOCKED
263 -> LoginState.UNKNOWN_QQ_NUMBER 263 -> LoginState.UNKNOWN_QQ_NUMBER
@ -121,9 +123,9 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
"00 A7" -> ServerCanAddFriendResponsePacket(stream) "00 A7" -> ServerCanAddFriendResponsePacket(stream)
"03 88" -> ServerTryUploadGroupImageResponsePacket.Encrypted(stream) "03 88" -> ServerTryGetImageIDResponsePacket.Encrypted(stream)
else -> throw IllegalArgumentException(idHex) else -> UnknownServerPacket(stream)
} }
}.apply { this.idHex = idHex } }.apply { this.idHex = idHex }
} }
@ -197,15 +199,48 @@ fun DataInputStream.readIP(): String {
return buff return buff
} }
fun DataInputStream.readVarString(): String { fun DataInputStream.readLVString(): String {
return String(this.readVarByteArray()) return String(this.readLVByteArray())
} }
fun DataInputStream.readVarByteArray(): ByteArray { fun DataInputStream.readLVByteArray(): ByteArray {
return this.readNBytes(this.readShort().toInt()) return this.readNBytes(this.readShort().toInt())
} }
fun DataInputStream.readString(length: Int): String { 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 != 0xff) {
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)) return String(this.readNBytes(length))
} }
@ -290,8 +325,8 @@ fun DataInputStream.gotoWhere(matcher: ByteArray): DataInputStream {
if (b != matcher[i]) { if (b != matcher[i]) {
continue@loop //todo goto mark continue@loop //todo goto mark
} }
return this
} }
return this
} }
} while (true) } while (true)
} }
@ -336,4 +371,6 @@ fun DataInputStream.gotoWhere(matcher: ByteArray) {
} while (true) } while (true)
}*/ }*/
fun ByteArray.cutTail(length: Int): ByteArray = this.copyOfRange(0, this.size - length) 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)

View File

@ -1,5 +1,7 @@
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.utils.LoggerTextFormat
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
/** /**
@ -7,6 +9,10 @@ import java.io.DataInputStream
*/ */
class UnknownServerPacket(input: DataInputStream) : ServerPacket(input) { class UnknownServerPacket(input: DataInputStream) : ServerPacket(input) {
override fun decode() { override fun decode() {
println("UnknownServerPacket data: " + this.input.goto(0).readAllBytes().toUHexString())
}
override fun toString(): String {
return LoggerTextFormat.LIGHT_RED.toString() + super.toString()
} }
} }

View File

@ -38,7 +38,7 @@ class ClientSendFriendMessagePacket(
it.writeTime() it.writeTime()
it.writeRandom(4) it.writeRandom(4)
it.writeHex("00 00 00 00 09 00 86") it.writeHex("00 00 00 00 09 00 86")
it.writeHex(Protocol.friendMessageConst1)//... 85 E9 BB 91 it.writeHex(Protocol.messageConst1)//... 85 E9 BB 91
it.writeZero(2) it.writeZero(2)
it.write(message.toByteArray()) it.write(message.toByteArray())

View File

@ -3,6 +3,7 @@ package net.mamoe.mirai.network.packet.action
import net.mamoe.mirai.message.defaults.MessageChain import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.lazyEncode
import net.mamoe.mirai.utils.toUHexString import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
@ -11,8 +12,8 @@ import java.io.DataInputStream
*/ */
@PacketId("00 02") @PacketId("00 02")
class ClientSendGroupMessagePacket( class ClientSendGroupMessagePacket(
private val groupId: Long,//不是 number
private val botQQ: Long, private val botQQ: Long,
private val groupId: Long,//不是 number
private val sessionKey: ByteArray, private val sessionKey: ByteArray,
private val message: MessageChain private val message: MessageChain
) : ClientPacket() { ) : ClientPacket() {
@ -26,18 +27,19 @@ class ClientSendGroupMessagePacket(
it.writeByte(0x2A) it.writeByte(0x2A)
it.writeGroup(groupId) it.writeGroup(groupId)
it.writeShort(50 + bytes.size) it.writeLVByteArray(lazyEncode { child ->
it.writeHex("00 01 01") child.writeHex("00 01 01")
it.writeHex("00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00") child.writeHex("00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")
it.writeTime() child.writeTime()
it.writeRandom(4) child.writeRandom(4)
it.writeHex("00 00 00 00 09 00 86") child.writeHex("00 00 00 00 09 00 86")
it.writeHex(Protocol.friendMessageConst1) child.writeHex(Protocol.messageConst1)
it.writeZero(2) child.writeZero(2)
//messages //messages
it.write(bytes) child.write(bytes)
})
/*it.writeByte(0x01) /*it.writeByte(0x01)
it.writeShort(bytes.size + 3) it.writeShort(bytes.size + 3)
it.writeByte(0x01) it.writeByte(0x01)

View File

@ -11,8 +11,8 @@ import java.io.DataInputStream
/** /**
* 请求上传图片. 将发送图片的 md5, size. * 请求上传图片. 将发送图片的 md5, size.
* 服务器返回以下之一: * 服务器返回以下之一:
* - 服务器已经存有这个图片 [ServerTryUploadGroupImageFailedPacket] * - 服务器已经存有这个图片 [ServerTryGetImageIDFailedPacket]
* - 服务器未存有, 返回一个 key 用于客户端上传 [ServerTryUploadGroupImageSuccessPacket] * - 服务器未存有, 返回一个 key 用于客户端上传 [ServerTryGetImageIDSuccessPacket]
* *
* @author Him188moe * @author Him188moe
*/ */
@ -20,7 +20,7 @@ import java.io.DataInputStream
class ClientTryGetImageIDPacket( class ClientTryGetImageIDPacket(
private val botNumber: Long, private val botNumber: Long,
private val sessionKey: ByteArray, private val sessionKey: ByteArray,
private val groupNumberOrQQNumber: Long,//todo 为什么还要有qq number呢? bot不就是了么 private val groupNumberOrQQNumber: Long,
private val image: BufferedImage private val image: BufferedImage
) : ClientPacket() { ) : ClientPacket() {
override fun encode() { override fun encode() {
@ -89,18 +89,18 @@ class ClientTryGetImageIDPacket(
} }
} }
abstract class ServerTryUploadGroupImageResponsePacket(input: DataInputStream) : ServerPacket(input) { abstract class ServerTryGetImageIDResponsePacket(input: DataInputStream) : ServerPacket(input) {
class Encrypted(input: DataInputStream) : ServerPacket(input) { class Encrypted(input: DataInputStream) : ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): ServerTryUploadGroupImageResponsePacket { fun decrypt(sessionKey: ByteArray): ServerTryGetImageIDResponsePacket {
val data = this.decryptAsByteArray(sessionKey) val data = this.decryptAsByteArray(sessionKey)
println(data.size) println(data.size)
println(data.size) println(data.size)
if (data.size == 209) { if (data.size == 209) {
return ServerTryUploadGroupImageSuccessPacket(data.dataInputStream()).setId(this.idHex) return ServerTryGetImageIDSuccessPacket(data.dataInputStream()).setId(this.idHex)
} }
return ServerTryUploadGroupImageFailedPacket(data.dataInputStream()) return ServerTryGetImageIDFailedPacket(data.dataInputStream())
} }
} }
} }
@ -108,25 +108,21 @@ abstract class ServerTryUploadGroupImageResponsePacket(input: DataInputStream) :
/** /**
* 服务器未存有图片, 返回一个 key 用于客户端上传 * 服务器未存有图片, 返回一个 key 用于客户端上传
*/ */
class ServerTryUploadGroupImageSuccessPacket(input: DataInputStream) : ServerTryUploadGroupImageResponsePacket(input) { class ServerTryGetImageIDSuccessPacket(input: DataInputStream) : ServerTryGetImageIDResponsePacket(input) {
lateinit var uKey: ByteArray lateinit var uKey: ByteArray
override fun decode() { override fun decode() {
uKey = this.input.gotoWhere(ubyteArrayOf(0x42u, 0x80u, 0x01u)).readNBytes(128) this.input.gotoWhere(ubyteArrayOf(0x42u, 0x80u, 0x01u))
uKey = this.input.readNBytes(128)
} }
} }
/** /**
* 服务器已经存有这个图片 * 服务器已经存有这个图片
*/ */
class ServerTryUploadGroupImageFailedPacket(input: DataInputStream) : ServerTryUploadGroupImageResponsePacket(input) { class ServerTryGetImageIDFailedPacket(input: DataInputStream) : ServerTryGetImageIDResponsePacket(input) {
override fun decode() { override fun decode() {
} }
}
fun main() {
println(0xff)
} }

View File

@ -9,18 +9,18 @@ import java.net.URL;
* @author NaturalHG * @author NaturalHG
*/ */
public class ImageNetworkUtils { public class ImageNetworkUtils {
public static boolean postImage(String uKeyHex, int fileSize, long qqNumber, long groupCode, byte[] img) throws IOException { public static boolean postImage(String uKeyHex, int fileSize, long botNumber, long groupCode, byte[] img) throws IOException {
//http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey= 删全部空 (ukey) &filesize= 到文本 (fileSize) &range=0&uin= g_uin &groupcode= Group //http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey= 删全部空 (ukey) &filesize= 到文本 (fileSize) &range=0&uin= g_uin &groupcode= Group
String builder = "http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" + "&ukey=" + String builder = "http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" +
uKeyHex.replace(" ", "") + "&ukey=" + uKeyHex.replace(" ", "") +
"&filezise=" + fileSize + "&filezise=" + fileSize +
"&range=" + "0" + "&range=" + "0" +
"&uin=" + qqNumber + "&uin=" + botNumber +
"&groupcode=" + groupCode; "&groupcode=" + groupCode;
HttpURLConnection conn = (HttpURLConnection) new URL(builder).openConnection(); HttpURLConnection conn = (HttpURLConnection) new URL(builder).openConnection();
conn.setRequestProperty("User-agent", "QQClient"); conn.setRequestProperty("User-Agent", "QQClient");
conn.setRequestProperty("Content-length", "" + fileSize); conn.setRequestProperty("Content-Length", "" + fileSize);
conn.setRequestMethod("POST"); conn.setRequestMethod("POST");
conn.setDoOutput(true); conn.setDoOutput(true);
conn.getOutputStream().write(img); conn.getOutputStream().write(img);

View File

@ -3,15 +3,20 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.dataInputStream
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import java.io.File import java.io.File
import java.lang.reflect.Field import java.lang.reflect.Field
import java.util.* import java.util.*
import java.util.zip.CRC32 import java.util.zip.CRC32
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import javax.imageio.ImageIO import javax.imageio.ImageIO
/** /**
* @author Him188moe * @author Him188moe
* @author NaturalHG * @author NaturalHG
@ -76,6 +81,12 @@ open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream())
@JvmSynthetic @JvmSynthetic
fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().also(t).toByteArray() fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().also(t).toByteArray()
@JvmSynthetic
fun <T> lazyDecode(byteArray: ByteArray, t: (DataInputStream) -> T): T = byteArray.dataInputStream().let(t)
fun DataInputStream.skip(n: Number) {
this.skip(n.toLong())
}
fun getRandomByteArray(length: Int): ByteArray { fun getRandomByteArray(length: Int): ByteArray {
val bytes = LinkedList<Byte>() val bytes = LinkedList<Byte>()
@ -144,4 +155,15 @@ fun BufferedImage.toByteArray(formatName: String = "PNG"): ByteArray {
return lazyEncode { return lazyEncode {
ImageIO.write(this, formatName, it) ImageIO.write(this, formatName, it)
} }
}
object GZip {
fun uncompress(bytes: ByteArray): ByteArray = lazyEncode {
GZIPInputStream(bytes.inputStream()).transferTo(it)
}
fun compress(bytes: ByteArray): ByteArray = ByteArrayOutputStream().let {
GZIPOutputStream(it).write(bytes)
return it.toByteArray()
}
} }

View File

@ -58,6 +58,9 @@ public class HexComparator {
private static final String _1040400290_ = "3E 03 3F A2"; private static final String _1040400290_ = "3E 03 3F A2";
private static final String _1994701021_ = "76 E4 B8 DD"; private static final String _1994701021_ = "76 E4 B8 DD";
private static final String _jiahua_ = "B1 89 BE 09"; private static final String _jiahua_ = "B1 89 BE 09";
private static final String _Him188moe_ = UtilsKt.toUHexString("Him188moe".getBytes(), " ");
private static final String 发图片 = UtilsKt.toUHexString("发图片".getBytes(), " ");
private static final String = UtilsKt.toUHexString("发图片".getBytes(), " ");
private static final String SINGLE_PLAIN_MESSAGE_HEAD = "00 00 01 00 09 01"; private static final String SINGLE_PLAIN_MESSAGE_HEAD = "00 00 01 00 09 01";