mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-01 07:58:22 +08:00
Improve image uploading
This commit is contained in:
parent
a2f35f5d2d
commit
28859056a1
@ -50,7 +50,7 @@ subscribeAlways<FriendMessageEvent>{
|
||||
- 成员权限, 昵称(10/18)
|
||||
- 好友在线状态改变(10/14)
|
||||
- Android客户端上线/下线(10/18)
|
||||
- 上传并发送图片(10/21)
|
||||
- 上传并发送好友/群图片(10/26)
|
||||
|
||||
## 使用方法
|
||||
### 要求
|
||||
|
@ -147,7 +147,7 @@ object MiraiServer {
|
||||
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) {
|
||||
if (state == LoginState.REQUIRE_UPLOAD) {
|
||||
Bot.instances.add(bot);
|
||||
getLogger().logGreen(" Login Succeed");
|
||||
} else {
|
||||
|
@ -3,8 +3,10 @@
|
||||
package net.mamoe.mirai
|
||||
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.network.BotSession
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
|
||||
import net.mamoe.mirai.network.session
|
||||
import net.mamoe.mirai.utils.BotNetworkConfiguration
|
||||
|
||||
/*
|
||||
@ -22,6 +24,8 @@ suspend fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.get
|
||||
val Bot.groups: ContactList<Group> get() = this.contacts.groups
|
||||
val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
|
||||
|
||||
inline fun <T> Bot.withSession(block: BotSession.() -> T): T = with(this.network.session) { block() }
|
||||
|
||||
|
||||
//NetworkHandler
|
||||
suspend fun Bot.sendPacket(packet: OutgoingPacket) = this.network.sendPacket(packet)
|
||||
|
@ -4,7 +4,7 @@ package net.mamoe.mirai.message
|
||||
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.FriendImageIdRequestPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
|
||||
/**
|
||||
|
@ -97,7 +97,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
||||
override suspend fun close(cause: Throwable?) {
|
||||
super.close(cause)
|
||||
|
||||
this.heartbeatJob?.cancel(CancellationException("handler closed"))
|
||||
this.heartbeatJob?.cancelChildren(CancellationException("handler closed"))
|
||||
this.heartbeatJob?.join()//等待 cancel 完成
|
||||
this.heartbeatJob = null
|
||||
|
||||
|
@ -20,7 +20,7 @@ object TIMProtocol {
|
||||
).forEach { list.add(solveIpAddress(it)) }
|
||||
|
||||
list.toList()
|
||||
}()//不使用lazy是为了在启动时就加载.
|
||||
}()//不使用lazy, 在初始化时就加载.
|
||||
|
||||
const val head = "02"
|
||||
const val ver = "37 13"
|
||||
|
@ -1,16 +1,23 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS", "unused")
|
||||
|
||||
package net.mamoe.mirai.network.protocol.tim.packet
|
||||
package net.mamoe.mirai.network.protocol.tim.packet.action
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.message.ImageId
|
||||
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
|
||||
import net.mamoe.mirai.network.qqAccount
|
||||
import net.mamoe.mirai.network.session
|
||||
import net.mamoe.mirai.qqAccount
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.httpPostFriendImage
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
import net.mamoe.mirai.utils.readUnsignedVarInt
|
||||
import net.mamoe.mirai.utils.writeUVarInt
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
@ -18,14 +25,16 @@ import net.mamoe.mirai.utils.io.*
|
||||
suspend fun QQ.uploadImage(image: ExternalImage): ImageId = with(bot.network.session) {
|
||||
//SubmitImageFilenamePacket(account, account, "sdiovaoidsa.png", sessionKey).sendAndExpect<ServerSubmitImageFilenameResponsePacket>().join()
|
||||
DebugLogger.logPurple("正在上传好友图片, md5=${image.md5.toUHexString()}")
|
||||
return FriendImageIdRequestPacket(this.qqAccount, sessionKey, this.qqAccount, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> {
|
||||
return FriendImageIdRequestPacket(this.qqAccount, sessionKey, id, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> {
|
||||
if (it.uKey != null)
|
||||
require(httpPostFriendImage(
|
||||
botAccount = bot.qqAccount,
|
||||
require(
|
||||
httpPostFriendImage(
|
||||
botAccount = bot.qqAccount,
|
||||
uKeyHex = it.uKey!!.toUHexString(""),
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize
|
||||
))
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize
|
||||
)
|
||||
)
|
||||
it.imageId!!
|
||||
}.await()
|
||||
}
|
||||
@ -43,12 +52,12 @@ suspend fun QQ.uploadImage(image: ExternalImage): ImageId = with(bot.network.ses
|
||||
* 似乎没有必要. 服务器的返回永远都是 01 00 00 00 02 00 00
|
||||
*/
|
||||
@PacketId(0X01_BDu)
|
||||
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173")
|
||||
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
|
||||
class SubmitImageFilenamePacket(
|
||||
private val bot: UInt,
|
||||
private val target: UInt,
|
||||
private val filename: String,
|
||||
private val sessionKey: ByteArray
|
||||
private val bot: UInt,
|
||||
private val target: UInt,
|
||||
private val filename: String,
|
||||
private val sessionKey: ByteArray
|
||||
) : OutgoingPacket() {
|
||||
override fun encode(builder: BytePacketBuilder) = with(builder) {
|
||||
writeQQ(bot)
|
||||
@ -99,7 +108,7 @@ class SubmitImageFilenamePacket(
|
||||
* - 服务器未存有, 返回一个 key 用于客户端上传
|
||||
*/
|
||||
@PacketId(0x03_52u)
|
||||
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
|
||||
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
|
||||
class FriendImageIdRequestPacket(
|
||||
private val botNumber: UInt,
|
||||
private val sessionKey: ByteArray,
|
||||
@ -187,13 +196,10 @@ class FriendImageIdRequestPacket(
|
||||
* 70 [80 14]
|
||||
* 78 [A0 0B]//84
|
||||
*/
|
||||
|
||||
writeZero(3)
|
||||
writeUShort(0x07_00u)
|
||||
writeZero(1)
|
||||
writeHex("00 00 00 07 00 00 00")
|
||||
|
||||
//proto
|
||||
val packet = buildPacket {
|
||||
writeUVarintLVPacket(lengthOffset = { it - 7 }) {
|
||||
writeUByte(0x08u)
|
||||
writeUShort(0x01_12u)
|
||||
writeUShort(0x03_98u)
|
||||
@ -201,62 +207,85 @@ class FriendImageIdRequestPacket(
|
||||
writeUShort(0x08_01u)
|
||||
|
||||
|
||||
writeUShort(0x12_47u)//?似乎会变
|
||||
writeUVarintLVPacket(tag = 0x12u, lengthOffset = { it + 1 }) {
|
||||
writeUByte(0x08u)
|
||||
writeUVarInt(botNumber)
|
||||
|
||||
writeUByte(0x08u)
|
||||
writeUVarInt(botNumber)
|
||||
writeUByte(0x10u)
|
||||
writeUVarInt(target)
|
||||
|
||||
writeUByte(0x10u)
|
||||
writeUVarInt(target)
|
||||
writeUShort(0x18_00u)
|
||||
|
||||
writeUShort(0x18_00u)
|
||||
writeUByte(0x22u)
|
||||
writeUByte(0x10u)
|
||||
writeFully(image.md5)
|
||||
|
||||
writeUByte(0x22u)
|
||||
writeUByte(0x10u)
|
||||
writeFully(image.md5)
|
||||
|
||||
writeUByte(0x28u)
|
||||
writeUVarInt(image.inputSize.toUInt())
|
||||
writeUByte(0x28u)
|
||||
writeUVarInt(image.inputSize.toUInt())
|
||||
|
||||
|
||||
writeUByte(0x32u)
|
||||
//长度应为1A
|
||||
writeUVarintLVPacket {
|
||||
writeUShort(0x28_00u)
|
||||
writeUShort(0x46_00u)
|
||||
writeUShort(0x51_00u)
|
||||
writeUShort(0x56_00u)
|
||||
writeUShort(0x4B_00u)
|
||||
writeUShort(0x41_00u)
|
||||
writeUShort(0x49_00u)
|
||||
writeUShort(0x25_00u)
|
||||
writeUShort(0x4B_00u)
|
||||
writeUShort(0x24_00u)
|
||||
writeUShort(0x55_00u)
|
||||
writeUShort(0x30_00u)
|
||||
writeUShort(0x24_00u)
|
||||
writeUByte(0x32u)
|
||||
//长度应为1A
|
||||
writeUVarintLVPacket {
|
||||
writeUShort(0x28_00u)
|
||||
writeUShort(0x46_00u)
|
||||
writeUShort(0x51_00u)
|
||||
writeUShort(0x56_00u)
|
||||
writeUShort(0x4B_00u)
|
||||
writeUShort(0x41_00u)
|
||||
writeUShort(0x49_00u)
|
||||
writeUShort(0x25_00u)
|
||||
writeUShort(0x4B_00u)
|
||||
writeUShort(0x24_00u)
|
||||
writeUShort(0x55_00u)
|
||||
writeUShort(0x30_00u)
|
||||
writeUShort(0x24_00u)
|
||||
}
|
||||
|
||||
writeUShort(0x38_01u)
|
||||
writeUShort(0x48_00u)
|
||||
|
||||
writeUByte(0x70u)
|
||||
writeUVarInt(image.width.toUInt())
|
||||
writeUByte(0x78u)
|
||||
writeUVarInt(image.height.toUInt())
|
||||
}
|
||||
|
||||
writeUShort(0x38_01u)
|
||||
writeUShort(0x48_00u)
|
||||
|
||||
writeUByte(0x70u)
|
||||
writeUVarInt(image.width.toUInt())
|
||||
writeUByte(0x78u)
|
||||
writeUVarInt(image.height.toUInt())
|
||||
}
|
||||
writeShort((packet.remaining - 7).toShort())//why?
|
||||
writePacket(packet)
|
||||
|
||||
|
||||
//println(this.build().readBytes().toUHexString())
|
||||
}
|
||||
}
|
||||
|
||||
@PacketId(0x0352u)
|
||||
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
|
||||
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
|
||||
class Response(input: ByteReadPacket) : ResponsePacket(input) {
|
||||
var uKey: ByteArray? = null//最终可能为null
|
||||
var imageId: ImageId? = null//最终不会为null
|
||||
/**
|
||||
* 访问 HTTP API 时需要使用的一个 key. 128 位
|
||||
*/
|
||||
var uKey: ByteArray? = null
|
||||
|
||||
/**
|
||||
* 发送消息时使用的 id
|
||||
*/
|
||||
var imageId: ImageId? = null
|
||||
|
||||
lateinit var state: State
|
||||
|
||||
enum class State {
|
||||
/**
|
||||
* 需要上传. 此时 [uKey], [imageId] 均不为 `null`
|
||||
*/
|
||||
REQUIRE_UPLOAD,
|
||||
/**
|
||||
* 服务器已有这个图片. 此时 [uKey] 为 `null`, [imageId] 不为 `null`
|
||||
*/
|
||||
ALREADY_EXISTS,
|
||||
/**
|
||||
* 图片过大. 此时 [uKey], [imageId] 均为 `null`
|
||||
*/
|
||||
OVER_FILE_SIZE_MAX,
|
||||
}
|
||||
|
||||
override fun decode() = with(input) {
|
||||
//00 00 00 08 00 00
|
||||
@ -278,41 +307,27 @@ class FriendImageIdRequestPacket(
|
||||
|
||||
discardExact(1)//52, id
|
||||
imageId = ImageId(readString(readUnsignedVarInt().toInt()))//37
|
||||
state = State.REQUIRE_UPLOAD
|
||||
|
||||
//DebugLogger.logPurple("获得 uKey(${uKey!!.size})=${uKey!!.toUHexString()}")
|
||||
//DebugLogger.logPurple("获得 imageId(${imageId!!.value.length})=${imageId}")
|
||||
} else {
|
||||
//服务器已经有这个图片了
|
||||
//DebugLogger.logPurple("服务器已有好友图片 ")
|
||||
//89 12 06 98 01 01 A0 01 00 08 01 12 82 01 08 00 10 AB A7 89 D8 02 18 00 28 01 32 20 0A 10 5A 39 37 10 EA D5 B5 57 A8 04 14 70 CE 90 67 14 10 67 18 8A 94 17 20 ED 03 28 97 04 30 0A 52 25 2F 39 38 31 65 61 31 64 65 2D 62 32 31 33 2D 34 31 61 39 2D 38 38 37 65 2D 32 38 37 39 39 66 31 39 36 37 35 65 5A 25 2F 39 38 31 65 61 31 64 65 2D 62 32 31 33 2D 34 31 61 39 2D 38 38 37 65 2D 32 38 37 39 39 66 31 39 36 37 35 65 60 00 68 80 80 08 20 01
|
||||
// 89
|
||||
// 12 06 98 01 01 A0 01 00 08 01 12 82 01 08 00 10 AB A7 89 D8 02 18 00 28 01 32 20 0A 10 5A 39 37 10 EA D5 B5 57 A8 04 14 70 CE 90 67 14 10 67 18 8A 94 17 20 ED 03 28 97 04 30 0A 52 25 2F 39 38 31 65 61 31 64 65 2D 62 32 31 33 2D 34 31 61 39 2D 38 38 37 65 2D 32 38 37 39 39 66 31 39 36 37 35 65 5A 25 2F 39 38 31 65 61 31 64 65 2D 62 32 31 33 2D 34 31 61 39 2D 38 38 37 65 2D 32 38 37 39 39 66 31 39 36 37 35 65 60 00 68 80 80 08 20 01
|
||||
|
||||
discardExact(60)
|
||||
|
||||
discardExact(1)//52, id
|
||||
imageId = ImageId(readString(readUnsignedVarInt().toInt()))//37
|
||||
//83 12 06 98 01 01 A0 01 00 08 01 12 7D 08 00 10 9B A4 DC 92 06 18 00 28 01 32 1B 0A 10 8E C4 9D 72 26 AE 20 C0 5D A2 B6 78 4D 12 B7 3A 10 00 18 86 1F 20 30 28 30 52 25 2F 30 31 62
|
||||
val toDiscard = readUByte().toInt() - 37
|
||||
if (toDiscard < 0) {
|
||||
state = State.OVER_FILE_SIZE_MAX
|
||||
} else {
|
||||
discardExact(toDiscard)
|
||||
imageId = ImageId(readString(37))
|
||||
state = State.ALREADY_EXISTS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
//GlobalSysTemp:II%E]PA}OVFK]61EGGF$356.jpg
|
||||
//实际文件名为 II%E]PA}OVFK]61EGGF$356.jpg
|
||||
|
||||
println(SubmitImageFilenamePacket(
|
||||
1994701021u,
|
||||
1040400290u,
|
||||
"testfilename.png",
|
||||
"99 82 67 D4 62 20 CA 5D 81 F8 6F 83 EE 8A F7 68".hexToBytes()
|
||||
|
||||
).packet.readBytes().toUHexString())
|
||||
|
||||
println("01ee6426-5ff1-4cf0-8278-e8634d2909e".toByteArray().toUHexString())
|
||||
|
||||
"5A 25 2F 36 61 38 35 32 66 64 65 2D 38 32 38 35 2D 34 33 35 31 2D 61 65 65 38 2D 35 34 65 37 35 65 65 32 65 61 37 63 60 00 68 80 80 08 20 01"
|
||||
.printStringFromHex()
|
||||
|
||||
"25 2F ".hexToBytes().read {
|
||||
println(readUnsignedVarInt())
|
||||
}
|
||||
}
|
@ -1,41 +1,62 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
|
||||
package net.mamoe.mirai.network.protocol.tim.packet
|
||||
package net.mamoe.mirai.network.protocol.tim.packet.action
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.GroupId
|
||||
import net.mamoe.mirai.contact.GroupInternalId
|
||||
import net.mamoe.mirai.network.session
|
||||
import net.mamoe.mirai.contact.withSession
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
|
||||
import net.mamoe.mirai.qqAccount
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import net.mamoe.mirai.utils.httpPostGroupImage
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
import net.mamoe.mirai.utils.readUnsignedVarInt
|
||||
|
||||
|
||||
/**
|
||||
* 图片文件过大
|
||||
*/
|
||||
class OverFileSizeMaxException : IllegalStateException()
|
||||
|
||||
/**
|
||||
* 上传群图片
|
||||
* 挂起直到上传完成或失败
|
||||
* 失败后抛出 [OverFileSizeMaxException]
|
||||
*/
|
||||
suspend fun Group.uploadImage(
|
||||
image: ExternalImage
|
||||
) = with(bot.network.session) {
|
||||
) = withSession {
|
||||
GroupImageIdRequestPacket(bot.qqAccount, internalId, image, sessionKey)
|
||||
.sendAndExpect<GroupImageIdRequestPacket.Response, Unit> {
|
||||
if (it.uKey != null) {
|
||||
httpPostGroupImage(
|
||||
botAccount = bot.qqAccount,
|
||||
groupInternalId = internalId,
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize,
|
||||
uKeyHex = it.uKey!!.toUHexString("")
|
||||
)
|
||||
when (it.state) {
|
||||
GroupImageIdRequestPacket.Response.State.REQUIRE_UPLOAD -> {
|
||||
httpPostGroupImage(
|
||||
botAccount = bot.qqAccount,
|
||||
groupId = GroupId(id),
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize,
|
||||
uKeyHex = it.uKey!!.toUHexString("")
|
||||
)
|
||||
}
|
||||
|
||||
GroupImageIdRequestPacket.Response.State.ALREADY_EXISTS -> {
|
||||
|
||||
}
|
||||
|
||||
GroupImageIdRequestPacket.Response.State.OVER_FILE_SIZE_MAX -> throw OverFileSizeMaxException()
|
||||
}
|
||||
}.await()
|
||||
}.join()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Image Id 和上传用的一个 uKey
|
||||
*/
|
||||
@PacketId(0x0388u)
|
||||
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
|
||||
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
|
||||
class GroupImageIdRequestPacket(
|
||||
private val bot: UInt,
|
||||
private val groupInternalId: GroupInternalId,
|
||||
@ -51,8 +72,8 @@ class GroupImageIdRequestPacket(
|
||||
|
||||
//小图B
|
||||
// 00 00 00 07 00 00 00
|
||||
// 5B 08 =后文长度-6
|
||||
// 01 12 03 98 01 01 10 01 1A
|
||||
// 5B =后文长度-7
|
||||
// 08 01 12 03 98 01 01 10 01 1A
|
||||
// 57长度
|
||||
// 08 FB D2 D8 94 02
|
||||
// 10 A2 FF 8C F0 03
|
||||
@ -83,6 +104,28 @@ class GroupImageIdRequestPacket(
|
||||
// 78 03
|
||||
// 80 01 00
|
||||
|
||||
//450*298
|
||||
//00 00 00 07 00 00 00
|
||||
// 5D=后文-7 varint
|
||||
// 08 01 12 03 98 01 01 10 01 1A
|
||||
// 59 =后文长度 varint
|
||||
// 08 A0 89 F7 B6 03
|
||||
// 10 A2 FF 8C F0 03
|
||||
// 18 00
|
||||
// 22 10 01 FC 9D 6B E9 B2 D9 CD AC 25 66 73 F9 AF 6A 67
|
||||
// 28 [C9 10] varint size
|
||||
// 32 1A
|
||||
// 58 00 51 00 56 00 51 00 58 00 47 00 55 00 47 00 38 00 57 00 5F 00 4A 00 43 00
|
||||
// 38 01 48 01
|
||||
// 50 [C2 03]
|
||||
// 58 [AA 02]
|
||||
// 60 02
|
||||
// 6A 05 32 36 39 33 33
|
||||
// 70 00
|
||||
// 78 03
|
||||
// 80 01
|
||||
// 00
|
||||
|
||||
//大图C
|
||||
// 00 00 00 07 00 00 00
|
||||
// 5E 08 =后文长度-6
|
||||
@ -144,11 +187,11 @@ class GroupImageIdRequestPacket(
|
||||
encryptAndWrite(sessionKey) {
|
||||
writeHex("00 00 00 07 00 00 00")
|
||||
|
||||
writeUVarintLVPacket(lengthOffset = { it - 6 }) {
|
||||
writeUVarintLVPacket(lengthOffset = { it - 7 }) {
|
||||
writeByte(0x08)
|
||||
writeHex("01 12 03 98 01 01 10 01 1A")
|
||||
|
||||
writeUVarintLVPacket(lengthOffset = { it + 1 }) {
|
||||
writeUVarintLVPacket(lengthOffset = { it }) {
|
||||
writeTUVarint(0x08u, groupInternalId.value)
|
||||
writeTUVarint(0x10u, bot)
|
||||
writeTV(0x1800u)
|
||||
@ -207,31 +250,6 @@ class GroupImageIdRequestPacket(
|
||||
}.readBytes().toUHexString())
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//以下仅支持中等大小图片
|
||||
/*
|
||||
writeQQ(bot)
|
||||
writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
|
||||
|
||||
encryptAndWrite(sessionKey) {
|
||||
writeHex("00 00 00 07 00 00 00 5E 08 01 12 03 98 01 01 10 01 1A")
|
||||
writeHex("5A 08")
|
||||
writeUVarInt(groupId)
|
||||
writeUByte(0x10u)
|
||||
writeUVarInt(bot)
|
||||
writeHex("18 00 22 10")
|
||||
writeFully(image.md5)
|
||||
writeUByte(0x28u)
|
||||
writeUVarInt(image.fileSize.toUInt())
|
||||
writeHex("32 1A 37 00 4D 00 32 00 25 00 4C 00 31 00 56 00 32 00 7B 00 39 00 30 00 29 00 52 00")
|
||||
writeHex("38 01 48 01 50")
|
||||
writeUVarInt(image.width.toUInt())
|
||||
writeUByte(0x58u)
|
||||
writeUVarInt(image.height.toUInt())
|
||||
writeHex("60 04 6A 05 32 36 36 35 36 70 00 78 03 80 01 00")
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -239,21 +257,51 @@ class GroupImageIdRequestPacket(
|
||||
}
|
||||
|
||||
@PacketId(0x0388u)
|
||||
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
|
||||
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
|
||||
class Response(input: ByteReadPacket) : ResponsePacket(input) {
|
||||
lateinit var state: State
|
||||
|
||||
/**
|
||||
* 访问 HTTP API 时需要使用的一个 key. 128 位
|
||||
*/
|
||||
var uKey: ByteArray? = null
|
||||
|
||||
enum class State {
|
||||
/**
|
||||
* 需要上传. 此时 [uKey] 不为 `null`
|
||||
*/
|
||||
REQUIRE_UPLOAD,
|
||||
/**
|
||||
* 服务器已有这个图片. 此时 [uKey] 为 `null`
|
||||
*/
|
||||
ALREADY_EXISTS,
|
||||
/**
|
||||
* 图片过大. 此时 [uKey] 为 `null`
|
||||
*/
|
||||
OVER_FILE_SIZE_MAX,
|
||||
}
|
||||
|
||||
override fun decode(): Unit = with(input) {
|
||||
discardExact(6)//00 00 00 05 00 00
|
||||
|
||||
val length = remaining - 128 - 14
|
||||
if (length < 0) {
|
||||
//服务器已经有这个图片了
|
||||
state = if (readUShort().toUInt() == 0x0025u) {
|
||||
State.OVER_FILE_SIZE_MAX
|
||||
} else {
|
||||
State.ALREADY_EXISTS
|
||||
}
|
||||
|
||||
//图片过大 00 25 12 03 98 01 01 08 9B A4 DC 92 06 10 01 1A 1B 08 00 10 C5 01 1A 12 6F 76 65 72 20 66 69 6C 65 20 73 69 7A 65 20 6D 61 78 20 00
|
||||
//图片过大 00 25 12 03 98 01 01 08 9B A4 DC 92 06 10 01 1A 1B 08 00 10 C5 01 1A 12 6F 76 65 72 20 66 69 6C 65 20 73 69 7A 65 20 6D 61 78 20 00
|
||||
//图片已有 00 3F 12 03 98 01 01 08 9B A4 DC 92 06 10 01 1A 35 08 00 10 00 20 01 2A 1F 0A 10 24 66 B9 6B E8 58 FE C0 12 BD 1E EC CB 74 A8 8E 10 04 18 83 E2 AF 01 20 80 3C 28 E0 21 30 EF 9A 88 B9 0B 38 50 48 90 D7 DA B0 08
|
||||
//debugPrint("后文")
|
||||
return@with
|
||||
}
|
||||
|
||||
discardExact(length)
|
||||
uKey = readBytes(128)
|
||||
state = State.REQUIRE_UPLOAD
|
||||
//} else {
|
||||
// println("服务器已经有了这个图片")
|
||||
//println("后文 = ${readRemainingBytes().toUHexString()}")
|
||||
@ -272,12 +320,4 @@ class GroupImageIdRequestPacket(
|
||||
// [80 01] 04 9A 01 79 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 33 39 36 37 39 34 39 34 32 37 2F 33 39 36 37 39 34 39 34 32 37 2D 32 36 36 32 36 30 30 34 34 30 2D 32 46 43 41 36 42 45 37 42 37 39 35 42 37 32 37 30 36 33 35 32 37 35 34 30 45 34 33 42 34 33 30 2F 34 30 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 A0 01 00
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
("A2 FF 8C F0 03").hexToBytes().read {
|
||||
println(readUnsignedVarInt())
|
||||
}
|
||||
|
||||
println(0x40)
|
||||
}
|
@ -20,7 +20,7 @@ data class EventPacketIdentity(
|
||||
val to: UInt,//对于好友消息, 这个是bot
|
||||
internal val uniqueId: IoBuffer//8
|
||||
) {
|
||||
override fun toString(): String = "(from=$from, to=$to)"
|
||||
override fun toString(): String = "($from->$to)"
|
||||
}
|
||||
|
||||
fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) = with(identity) {
|
||||
|
@ -18,17 +18,26 @@ class ExternalImage(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val md5: ByteArray,
|
||||
val format: String,
|
||||
imageFormat: String,
|
||||
val input: Input,
|
||||
val inputSize: Long
|
||||
) {
|
||||
private val format: String
|
||||
|
||||
init {
|
||||
if (imageFormat == "JPEG" || imageFormat == "jpeg") {//必须转换
|
||||
this.format = "jpg"
|
||||
} else {
|
||||
this.format = imageFormat
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于发送消息的 [ImageId]
|
||||
*/
|
||||
val groupImageId: ImageId by lazy { ImageId("{${md5[0..3]}-${md5[4..5]}-${md5[6..7]}-${md5[8..9]}-${md5[10..15]}}.$format") }
|
||||
|
||||
override fun toString(): String = "[ExternalImage(${width}x${height} $format)]"
|
||||
override fun toString(): String = "[ExternalImage(${width}x$height $format)]"
|
||||
}
|
||||
|
||||
private operator fun ByteArray.get(range: IntRange): String = buildString {
|
||||
|
@ -10,7 +10,7 @@ import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.http.userAgent
|
||||
import kotlinx.io.core.Input
|
||||
import net.mamoe.mirai.contact.GroupInternalId
|
||||
import net.mamoe.mirai.contact.GroupId
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
@ -59,52 +59,95 @@ suspend fun httpPostFriendImage(
|
||||
uKeyHex: String,
|
||||
imageInput: Input,
|
||||
inputSize: Long
|
||||
): Boolean = (httpClient.postImage(imageInput, inputSize, uKeyHex) {
|
||||
url {
|
||||
parameters["htcmd"] = "0x6ff0070"
|
||||
parameters["uin"] = botAccount.toLong().toString()
|
||||
}
|
||||
): Boolean = (httpClient.postImage(
|
||||
htcmd = "0x6ff0070",
|
||||
uin = botAccount,
|
||||
groupcode = null,
|
||||
imageInput = imageInput,
|
||||
inputSize = inputSize,
|
||||
uKeyHex = uKeyHex
|
||||
) as HttpStatusCode).value.also { println(it) } == 200
|
||||
|
||||
} as HttpStatusCode).value.also { println(it) } == 200
|
||||
/*
|
||||
httpPostFriendImageOld(uKeyHex, botAccount, imageInput.readBytes().toReadPacket())
|
||||
|
||||
expect suspend fun httpPostFriendImageOld(
|
||||
uKeyHex: String,
|
||||
botNumber: UInt,
|
||||
imageData: ByteReadPacket
|
||||
): Boolean
|
||||
*/
|
||||
/**
|
||||
* 上传群图片
|
||||
*/
|
||||
@Suppress("DuplicatedCode")
|
||||
suspend fun httpPostGroupImage(
|
||||
botAccount: UInt,
|
||||
groupInternalId: GroupInternalId,
|
||||
groupId: GroupId,
|
||||
uKeyHex: String,
|
||||
imageInput: Input,
|
||||
inputSize: Long
|
||||
): Boolean = (httpClient.postImage(imageInput, inputSize, uKeyHex) {
|
||||
url {
|
||||
parameters["htcmd"] = "0x6ff0071"
|
||||
parameters["uin"] = botAccount.toLong().toString()
|
||||
parameters["groupcode"] = groupInternalId.value.toLong().toString()
|
||||
}
|
||||
} as HttpStatusCode).value.also { println(it) } == 200
|
||||
): Boolean = (httpClient.postImage(
|
||||
htcmd = "0x6ff0071",
|
||||
uin = botAccount,
|
||||
groupcode = groupId,
|
||||
imageInput = imageInput,
|
||||
inputSize = inputSize,
|
||||
uKeyHex = uKeyHex
|
||||
) as HttpStatusCode).value.also { println(it) } == 200
|
||||
/* = (httpClient.post {
|
||||
url {
|
||||
protocol = URLProtocol.HTTP
|
||||
host = "htdata2.qq.com"
|
||||
path("cgi-bin/httpconn")
|
||||
|
||||
parameters["htcmd"] = "0x6ff0071"
|
||||
parameters["ver"] = "5603"
|
||||
parameters["term"] = "pc"
|
||||
parameters["ukey"] = uKeyHex
|
||||
parameters["filesize"] = inputSize.toString()
|
||||
parameters["range"] = 0.toString()
|
||||
parameters["uin"] = botAccount.toLong().toString()
|
||||
parameters["groupcode"] = groupId.value.toLong().toString()
|
||||
// userAgent("QQClient")
|
||||
}
|
||||
|
||||
println(url.buildString())
|
||||
body = ByteArrayContent(imageInput.readBytes())
|
||||
//configureBody(inputSize, imageInput)
|
||||
} as HttpStatusCode).value.also { println(it) } == 200*/
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
private suspend inline fun <reified T> HttpClient.postImage(
|
||||
htcmd: String,
|
||||
uin: UInt,
|
||||
groupcode: GroupId?,
|
||||
imageInput: Input,
|
||||
inputSize: Long,
|
||||
uKeyHex: String,
|
||||
block: HttpRequestBuilder.() -> Unit = {}
|
||||
uKeyHex: String
|
||||
): T = post {
|
||||
url {
|
||||
protocol = URLProtocol.HTTP
|
||||
host = "htdata2.qq.com"
|
||||
path("cgi-bin/httpconn")
|
||||
|
||||
parameters["htcmd"] = htcmd
|
||||
parameters["uin"] = uin.toLong().toString()
|
||||
|
||||
if (groupcode != null) {
|
||||
parameters["groupcode"] = groupcode.value.toLong().toString()
|
||||
}
|
||||
|
||||
parameters["term"] = "pc"
|
||||
parameters["ver"] = "5603"
|
||||
parameters["filezise"] = inputSize.toString()
|
||||
parameters["filesize"] = inputSize.toString()
|
||||
parameters["range"] = 0.toString()
|
||||
parameters["ukey"] = uKeyHex
|
||||
|
||||
userAgent("QQClient")
|
||||
}
|
||||
block()
|
||||
|
||||
println(url.buildString())
|
||||
configureBody(inputSize, imageInput)
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import kotlinx.io.charsets.Charset
|
||||
import kotlinx.io.charsets.Charsets
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.String
|
||||
import kotlinx.io.core.use
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
@JvmOverloads
|
||||
@ -34,6 +35,6 @@ fun UByteArray.toUHexString(separator: String = " "): String = this.joinToString
|
||||
|
||||
fun ByteArray.toReadPacket() = ByteReadPacket(this)
|
||||
|
||||
fun <R> ByteArray.read(t: ByteReadPacket.() -> R): R = this.toReadPacket().run(t)
|
||||
fun <R> ByteArray.read(t: ByteReadPacket.() -> R): R = this.toReadPacket().use(t)
|
||||
|
||||
fun ByteArray.cutTail(length: Int): ByteArray = this.copyOfRange(0, this.size - length)
|
@ -5,9 +5,7 @@ package net.mamoe.mirai.utils.io
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.*
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.CanAddFriendPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.SendFriendMessagePacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.SendGroupMessagePacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.*
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.login.*
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
|
@ -6,6 +6,8 @@ import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.IoBuffer
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.jvm.kotlinProperty
|
||||
|
||||
internal object PacketNameFormatter {
|
||||
@JvmStatic
|
||||
@ -22,7 +24,7 @@ internal object PacketNameFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
private object IgnoreIdList : List<String> by listOf(
|
||||
private object IgnoreIdListEquals : List<String> by listOf(
|
||||
"idHex",
|
||||
"id",
|
||||
"packetId",
|
||||
@ -32,18 +34,32 @@ private object IgnoreIdList : List<String> by listOf(
|
||||
"idByteArray",
|
||||
"encoded",
|
||||
"packet",
|
||||
"Companion",
|
||||
"EMPTY_ID_HEX",
|
||||
"input",
|
||||
"sequenceId",
|
||||
"output",
|
||||
"this\$0",
|
||||
"\$\$delegatedProperties",
|
||||
"bot",
|
||||
"UninitializedByteReadPacket",
|
||||
"sessionKey"
|
||||
)
|
||||
|
||||
private object IgnoreIdListInclude : List<String> by listOf(
|
||||
"Companion",
|
||||
"EMPTY_ID_HEX",
|
||||
"input",
|
||||
"output",
|
||||
"this\$",
|
||||
"\$\$delegatedProperties",
|
||||
"UninitializedByteReadPacket",
|
||||
"\$FU",
|
||||
"RefVolatile"
|
||||
)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjustName(this::class.simpleName + "(${this.idHexString})") + this::class.java.allDeclaredFields
|
||||
.filterNot { it.name in IgnoreIdList /*|| "delegate" in it.name|| "$" in it.name */ }
|
||||
.filterNot { field ->
|
||||
IgnoreIdListEquals.any { field.name.replace("\$delegate", "") == it } || IgnoreIdListInclude.any { it in field.name }
|
||||
}
|
||||
.joinToString(", ", "{", "}") {
|
||||
it.isAccessible = true
|
||||
it.name.replace("\$delegate", "") + "=" + it.get(this).let { value ->
|
||||
@ -55,6 +71,7 @@ internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjust
|
||||
//is ByteReadPacket -> value.copy().readBytes().toUHexString()
|
||||
is IoBuffer -> "[IoBuffer(${value.readRemaining})]"
|
||||
is Lazy<*> -> "[Lazy]"
|
||||
is ReadWriteProperty<*, *> -> (value as ReadWriteProperty<Packet, *>).getValue(this, it.kotlinProperty!!)
|
||||
else -> value.toString()
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,18 @@
|
||||
|
||||
import net.mamoe.mirai.contact.groupId
|
||||
import net.mamoe.mirai.contact.toInternalId
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.GroupImageIdRequestPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.GroupImageIdRequestPacket
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import net.mamoe.mirai.utils.io.readRemainingBytes
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
import net.mamoe.mirai.utils.toMiraiImage
|
||||
import net.mamoe.mirai.utils.toExternalImage
|
||||
import java.io.File
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
val sessionKey: ByteArray = "F1 ED F2 BC 55 17 7B FE CC CC F3 08 D1 8D A7 0E".hexToBytes()
|
||||
|
||||
fun main() = println({
|
||||
val image = ImageIO.read(File("C:\\Users\\Him18\\Desktop\\test2.gif").readBytes().inputStream()).toMiraiImage("png")
|
||||
val image = ImageIO.read(File("C:\\Users\\Him18\\Desktop\\test2.gif").readBytes().inputStream()).toExternalImage("png")
|
||||
|
||||
// File("C:\\Users\\Him18\\Desktop\\test2.jpg").writeBytes(image.fileData.readBytes())
|
||||
GroupImageIdRequestPacket(
|
||||
|
9
mirai-core/src/jvmTest/kotlin/TestImageFile.kt
Normal file
9
mirai-core/src/jvmTest/kotlin/TestImageFile.kt
Normal file
@ -0,0 +1,9 @@
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
|
||||
|
||||
val file = File("C:\\Users\\Him18\\Desktop\\lemon.png")
|
||||
println(file.inputStream().readAllBytes().size)
|
||||
println(file.length())
|
||||
}
|
@ -1,16 +1,36 @@
|
||||
plugins {
|
||||
id 'application'
|
||||
id 'org.openjfx.javafxplugin' version '0.0.8'
|
||||
}
|
||||
|
||||
apply plugin: "kotlin"
|
||||
apply plugin: "java"
|
||||
|
||||
javafx {
|
||||
version = "11"
|
||||
modules = [ 'javafx.controls' ]
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':mirai-core')
|
||||
compile files('./lib/jpcap.jar')
|
||||
implementation files('./lib/jpcap.jar')
|
||||
|
||||
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
|
||||
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io', version: kotlinxio_version
|
||||
|
||||
api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
|
||||
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-javafx
|
||||
compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-javafx', version: '1.3.2'
|
||||
|
||||
implementation 'org.pcap4j:pcap4j-distribution:1.8.2'
|
||||
|
||||
implementation 'no.tornado:tornadofx:1.7.17'
|
||||
|
||||
}
|
||||
|
||||
mainClassName = 'Application'
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
@file:Suppress("ObjectPropertyName", "unused", "NonAsciiCharacters", "MayBeConstant")
|
||||
|
||||
import net.mamoe.mirai.utils.io.printCompareHex
|
||||
import java.util.*
|
||||
|
||||
|
||||
fun main() {
|
||||
// println(HexComparator.printColorize("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 scanner = Scanner(System.`in`)
|
||||
while (true) {
|
||||
println("Hex1: ")
|
||||
val hex1 = scanner.nextLine()
|
||||
val hex1 = readLine()!!
|
||||
println("Hex2: ")
|
||||
val hex2 = scanner.nextLine()
|
||||
val hex2 = readLine()!!
|
||||
println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n")
|
||||
println(printCompareHex(hex1, hex2))
|
||||
println()
|
||||
|
@ -3,9 +3,7 @@
|
||||
import Main.localIp
|
||||
import Main.qq
|
||||
import Main.sessionKey
|
||||
import jpcap.JpcapCaptor
|
||||
import jpcap.packet.IPPacket
|
||||
import jpcap.packet.UDPPacket
|
||||
import com.sun.jna.Platform
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.io.core.readUInt
|
||||
@ -20,6 +18,12 @@ import net.mamoe.mirai.utils.decryptBy
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import org.pcap4j.core.BpfProgram.BpfCompileMode
|
||||
import org.pcap4j.core.PacketListener
|
||||
import org.pcap4j.core.PcapNetworkInterface
|
||||
import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode
|
||||
import org.pcap4j.core.Pcaps
|
||||
|
||||
|
||||
/**
|
||||
* 抓包分析器.
|
||||
@ -30,11 +34,31 @@ import net.mamoe.mirai.utils.toUHexString
|
||||
object Main {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
val devices = JpcapCaptor.getDeviceList()
|
||||
val jpcap: JpcapCaptor?
|
||||
val caplen = 4096
|
||||
val promiscCheck = true
|
||||
jpcap = JpcapCaptor.openDevice(devices[0], caplen, promiscCheck, 50)
|
||||
val nif: PcapNetworkInterface = Pcaps.findAllDevs()[0]
|
||||
println(nif.name + "(" + nif.description + ")")
|
||||
|
||||
val handle = nif.openLive(65536, PromiscuousMode.PROMISCUOUS, 3000)
|
||||
|
||||
handle.setFilter("src $localIp && udp port 8000", BpfCompileMode.OPTIMIZE)
|
||||
|
||||
val listener = PacketListener {
|
||||
println(it.rawData.toUHexString())
|
||||
println()
|
||||
}
|
||||
|
||||
handle.loop(Int.MAX_VALUE, listener)
|
||||
|
||||
val ps = handle.stats
|
||||
println("ps_recv: " + ps.numPacketsReceived)
|
||||
println("ps_drop: " + ps.numPacketsDropped)
|
||||
println("ps_ifdrop: " + ps.numPacketsDroppedByIf)
|
||||
if (Platform.isWindows()) {
|
||||
println("bs_capt: " + ps.numPacketsCaptured)
|
||||
}
|
||||
|
||||
handle.close()
|
||||
|
||||
/*
|
||||
while (true) {
|
||||
assert(jpcap != null)
|
||||
val pk = jpcap!!.packet ?: continue
|
||||
@ -65,6 +89,8 @@ object Main {
|
||||
//pk.dst_ip
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,9 +105,9 @@ object Main {
|
||||
* 6. 运行到 `mov eax,dword ptr ss:[ebp+10]`
|
||||
* 7. 查看内存, 从 `eax` 开始的 16 bytes 便是 `sessionKey`
|
||||
*/
|
||||
val sessionKey: ByteArray = "1D 1E 71 68 B9 41 FD 5B F3 5A 3F 71 87 B5 86 CB".hexToBytes()
|
||||
val sessionKey: ByteArray = "0D D7 C8 06 C6 C1 40 FE A8 3B CF 81 EE DF 69 83".hexToBytes()
|
||||
const val qq: UInt = 1040400290u
|
||||
const val localIp = "192.168.3."
|
||||
const val localIp = "192.168.3.10"
|
||||
|
||||
fun dataReceived(data: ByteArray) {
|
||||
//println("raw = " + data.toUHexString())
|
||||
@ -159,12 +185,14 @@ object Main {
|
||||
return@read
|
||||
}
|
||||
|
||||
println("fixVer2=" + when (val flag = readByte().toInt()) {
|
||||
2 -> byteArrayOf(2) + readBytes(TIMProtocol.fixVer2.hexToBytes().size - 1)
|
||||
4 -> byteArrayOf(4) + readBytes(TIMProtocol.fixVer2.hexToBytes().size - 1 + 8)//8个0
|
||||
0 -> byteArrayOf(0) + readBytes(2)
|
||||
else -> error("unknown fixVer2 flag=$flag. Remaining =${readBytes().toUHexString()}")
|
||||
}.toUHexString())
|
||||
println(
|
||||
"fixVer2=" + when (val flag = readByte().toInt()) {
|
||||
2 -> byteArrayOf(2) + readBytes(TIMProtocol.fixVer2.hexToBytes().size - 1)
|
||||
4 -> byteArrayOf(4) + readBytes(TIMProtocol.fixVer2.hexToBytes().size - 1 + 8)//8个0
|
||||
0 -> byteArrayOf(0) + readBytes(2)
|
||||
else -> error("unknown fixVer2 flag=$flag. Remaining =${readBytes().toUHexString()}")
|
||||
}.toUHexString()
|
||||
)
|
||||
|
||||
//39 27 DC E2 04 00 00 00 00 00 00 00 1E 0E 89 00 00 01 05 0F 05 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 3E 03 3F A2 00 00 00 00 00 00 00 00 00 00 00
|
||||
|
||||
@ -191,8 +219,8 @@ object Main {
|
||||
try {
|
||||
messageData.read {
|
||||
discardExact(
|
||||
4 + 4 + 12 + 2 + 4 + 4 + 16 + 2 + 2 + 4 + 2 + 16 + 4 + 4 + 7 + 15 + 2
|
||||
+ 1
|
||||
4 + 4 + 12 + 2 + 4 + 4 + 16 + 2 + 2 + 4 + 2 + 16 + 4 + 4 + 7 + 15 + 2
|
||||
+ 1
|
||||
)
|
||||
val chain = readMessageChain()
|
||||
println(chain)
|
||||
|
@ -17,8 +17,8 @@ import net.mamoe.mirai.message.ImageId
|
||||
import net.mamoe.mirai.message.PlainText
|
||||
import net.mamoe.mirai.message.firstOrNull
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingRawPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.uploadImage
|
||||
import net.mamoe.mirai.network.session
|
||||
import net.mamoe.mirai.qqAccount
|
||||
import net.mamoe.mirai.utils.*
|
||||
@ -99,25 +99,27 @@ suspend fun main() {
|
||||
}
|
||||
|
||||
"上传好友图片" in it.message -> withTimeoutOrNull(5000) {
|
||||
val filename = it.message.toString().substringAfter("上传好友图片")
|
||||
val id = 1040400290u.qq()
|
||||
.uploadImage(File("C:\\Users\\Him18\\Desktop\\${it.message.toString().substringAfter("上传好友图片")}").toMiraiImage())
|
||||
.uploadImage(File("C:\\Users\\Him18\\Desktop\\$filename").toExternalImage())
|
||||
it.reply(id.value)
|
||||
delay(1000)
|
||||
delay(100)
|
||||
it.reply(Image(id))
|
||||
}
|
||||
|
||||
"上传群图片" in it.message -> withTimeoutOrNull(5000) {
|
||||
val filename = it.message.toString().substringAfter("上传群图片")
|
||||
val image = File(
|
||||
"C:\\Users\\Him18\\Desktop\\${it.message.toString().substringAfter("上传群图片")}"
|
||||
).toMiraiImage()
|
||||
580266363u.group().uploadImage(image)
|
||||
"C:\\Users\\Him18\\Desktop\\$filename"
|
||||
).toExternalImage()
|
||||
920503456u.group().uploadImage(image)
|
||||
it.reply(image.groupImageId.value)
|
||||
delay(1000)
|
||||
580266363u.group().sendMessage(Image(image.groupImageId))
|
||||
delay(100)
|
||||
920503456u.group().sendMessage(Image(image.groupImageId))
|
||||
}
|
||||
|
||||
"发群图片" in it.message -> {
|
||||
580266363u.group().sendMessage(Image(ImageId(it.message.toString().substringAfter("发群图片"))))
|
||||
920503456u.group().sendMessage(Image(ImageId(it.message.toString().substringAfter("发群图片"))))
|
||||
}
|
||||
|
||||
"发好友图片" in it.message -> {
|
||||
|
Loading…
Reference in New Issue
Block a user