mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-06 13:53:51 +08:00
Image uploading is now available
This commit is contained in:
parent
0e5f6d4787
commit
d6201a14ba
@ -59,7 +59,7 @@ subscribe<FriendMessageEvent>{
|
||||
![JsssF](.github/J%5DCE%29IK4BU08%28EO~UVLJ%7B%5BF.png)
|
||||
![](.github/68f8fec9.png)
|
||||
|
||||
发送图片已经完成,但我们还在开发上传图片至服务器。
|
||||
上传发送图片已经完成, 您可以在 Demo 中找到发送方式.
|
||||
机器人可以转发图片消息.详情查看 [Image.kt](mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt#L81)
|
||||
|
||||
## 现已支持
|
||||
@ -70,6 +70,7 @@ subscribe<FriendMessageEvent>{
|
||||
- 成员权限, 昵称(10/18)
|
||||
- 好友在线状态改变(10/14)
|
||||
- Android客户端上线/下线(10/18)
|
||||
- 上传并发送图片(10/21)
|
||||
|
||||
## 使用方法
|
||||
### 要求
|
||||
|
@ -20,7 +20,8 @@ class ServerFriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacke
|
||||
override fun decode() = with(input) {
|
||||
qq = readUInt()
|
||||
discardExact(8)
|
||||
status = OnlineStatus.ofId(readUByte())
|
||||
val id = readUByte()
|
||||
status = OnlineStatus.ofId(id) ?: error("Unknown online status id $id")
|
||||
}
|
||||
|
||||
//在线 XX XX XX XX 01 00 00 00 00 00 00 00 0A 15 E3 10 00 01 2E 01 00 00 00 00 00 00 00 00 00 00 00 13 08 02 C2 76 E4 B8 DD 00 00 00 00 00 00 00 00 00 00 00
|
||||
|
@ -18,7 +18,7 @@ suspend fun QQ.uploadImage(image: BufferedImage): ImageId = with(bot.network.ses
|
||||
//SubmitImageFilenamePacket(account, account, "sdiovaoidsa.png", sessionKey).sendAndExpect<ServerSubmitImageFilenameResponsePacket>().join()
|
||||
DebugLogger.logPurple("正在上传好友图片, md5=${image.md5.toUHexString()}")
|
||||
return FriendImageIdRequestPacket(account, sessionKey, account, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> {
|
||||
if (it.uKey != null) {
|
||||
if (it.uKey != null)
|
||||
require(httpPostFriendImage(
|
||||
uKeyHex = it.uKey!!.toUHexString(""),
|
||||
botNumber = bot.qqAccount,
|
||||
@ -27,7 +27,6 @@ suspend fun QQ.uploadImage(image: BufferedImage): ImageId = with(bot.network.ses
|
||||
qq = account
|
||||
))
|
||||
it.imageId!!
|
||||
} else TODO("分析服务器已有图片时的 imageId")
|
||||
}.await()
|
||||
}
|
||||
|
||||
@ -255,8 +254,8 @@ class FriendImageIdRequestPacket(
|
||||
@PacketId(0x0352u)
|
||||
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
|
||||
class Response(input: ByteReadPacket) : ServerSessionPacket(input) {
|
||||
var uKey: ByteArray? = null
|
||||
var imageId: ImageId? = null
|
||||
var uKey: ByteArray? = null//最终可能为null
|
||||
var imageId: ImageId? = null//最终不会为null
|
||||
|
||||
override fun decode() = with(input) {
|
||||
//00 00 00 08 00 00
|
||||
@ -279,13 +278,17 @@ class FriendImageIdRequestPacket(
|
||||
discardExact(1)//52, id
|
||||
imageId = ImageId(readString(readUnsignedVarInt().toInt()))//37
|
||||
|
||||
DebugLogger.logPurple("获得 uKey(${uKey!!.size})=${uKey!!.toUHexString()}")
|
||||
DebugLogger.logPurple("获得 imageId(${imageId!!.value.length})=${imageId}")
|
||||
//DebugLogger.logPurple("获得 uKey(${uKey!!.size})=${uKey!!.toUHexString()}")
|
||||
//DebugLogger.logPurple("获得 imageId(${imageId!!.value.length})=${imageId}")
|
||||
} else {
|
||||
//服务器已经有这个图片了
|
||||
DebugLogger.logPurple("服务器已有好友图片 ")
|
||||
println("获取图片 repsonse 后文=" + readRemainingBytes().toUHexString())
|
||||
TODO("分析后文获取 imageId")
|
||||
//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
|
||||
|
||||
discardExact(60)
|
||||
|
||||
discardExact(1)//52, id
|
||||
imageId = ImageId(readString(readUnsignedVarInt().toInt()))//37
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,11 @@ suspend fun Group.uploadImage(
|
||||
.sendAndExpect<GroupImageIdRequestPacket.Response, Unit> {
|
||||
if (it.uKey != null) {
|
||||
httpPostGroupImage(
|
||||
bot = bot.qqAccount,
|
||||
groupNumber = groupId,
|
||||
imageData = image.data,
|
||||
fileSize = image.fileSize,
|
||||
uKeyHex = it.uKey!!.toUHexString()
|
||||
uKeyHex = it.uKey!!.toUHexString("")
|
||||
)
|
||||
}
|
||||
}.await()
|
||||
@ -145,8 +147,8 @@ class GroupImageIdRequestPacket(
|
||||
}
|
||||
writeTV(0x38_01u)
|
||||
writeTV(0x48_01u)
|
||||
writeTUVarint(0x50u, image.imageWidth.toUInt())
|
||||
writeTUVarint(0x58u, image.imageHeight.toUInt())
|
||||
writeTUVarint(0x50u, image.width.toUInt())
|
||||
writeTUVarint(0x58u, image.height.toUInt())
|
||||
writeTV(0x60_02u)
|
||||
writeTByteArray(0x6Au, value0x6A)
|
||||
}
|
||||
@ -159,6 +161,7 @@ class GroupImageIdRequestPacket(
|
||||
// this.debugColorizedPrintThis(compareTo = "00 00 00 07 00 00 00 5D 08 01 12 03 98 01 01 10 01 1A 59 08 FB D2 D8 94 02 10 A2 FF 8C F0 03 18 00 22 10 1D D2 2B 9B BC F2 10 83 DC 99 D2 2E 20 39 CC 0E 28 8A 03 32 1A 5B 00 40 00 33 00 48 00 5F 00 58 00 46 00 51 00 45 00 51 00 40 00 24 00 4F 00 38 01 48 01 50 EF 01 58 C2 01 60 02 6A 05 32 36 39 33 33 70 00 78 03 80 01 00")
|
||||
}*/
|
||||
|
||||
|
||||
writeQQ(bot)
|
||||
writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
|
||||
|
||||
@ -193,9 +196,13 @@ class GroupImageIdRequestPacket(
|
||||
override fun decode(): Unit = with(input) {
|
||||
discardExact(6)//00 00 00 05 00 00
|
||||
|
||||
//if (readUByte() != UByte.MIN_VALUE) {
|
||||
//服务器还没有
|
||||
discardExact(remaining - 128 - 14)
|
||||
val length = remaining - 128 - 14
|
||||
if (length < 0) {
|
||||
//服务器已经有这个图片了
|
||||
return@with
|
||||
}
|
||||
|
||||
discardExact(length)
|
||||
uKey = readBytes(128)
|
||||
//} else {
|
||||
// println("服务器已经有了这个图片")
|
||||
|
@ -7,10 +7,7 @@ import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readUInt
|
||||
import net.mamoe.mirai.message.MessageChain
|
||||
import net.mamoe.mirai.message.internal.readMessageChain
|
||||
import net.mamoe.mirai.utils.printStringFromHex
|
||||
import net.mamoe.mirai.utils.read
|
||||
import net.mamoe.mirai.utils.readLVByteArray
|
||||
import net.mamoe.mirai.utils.readTLVMap
|
||||
import net.mamoe.mirai.utils.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
||||
@ -45,7 +42,7 @@ class ServerGroupMessageEventPacket(input: ByteReadPacket, eventIdentity: EventP
|
||||
val map = readTLVMap(true)
|
||||
//map.printTLVMap("父map")
|
||||
if (map.containsKey(18)) {
|
||||
senderName = map.getValue(18).read {
|
||||
map.getValue(18).read {
|
||||
val tlv = readTLVMap(true)
|
||||
//tlv.printTLVMap("子map")
|
||||
////群主的18: 05 00 04 00 00 00 03 08 00 04 00 00 00 04 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08
|
||||
@ -66,10 +63,13 @@ class ServerGroupMessageEventPacket(input: ByteReadPacket, eventIdentity: EventP
|
||||
}
|
||||
0x01u -> SenderPermission.MEMBER
|
||||
|
||||
else -> error("Could not determine member permission, unknown tlv(key=0x03,value=$value0x03)")
|
||||
else -> {
|
||||
tlv.printTLVMap("Child TLV map")
|
||||
error("Could not determine member permission, unknown TLV(key=0x03,value=$value0x03;)")
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
senderName = when {
|
||||
tlv.containsKey(0x01) -> kotlinx.io.core.String(tlv.getValue(0x01))//这个人的qq昵称
|
||||
tlv.containsKey(0x02) -> kotlinx.io.core.String(tlv.getValue(0x02))//这个人的群名片
|
||||
else -> "null"
|
||||
|
@ -23,6 +23,6 @@ enum class OnlineStatus(
|
||||
|
||||
|
||||
companion object {
|
||||
fun ofId(id: UByte): OnlineStatus = values().first { it.id == id }
|
||||
fun ofId(id: UByte): OnlineStatus? = values().firstOrNull { it.id == id }
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ expect suspend fun httpPostFriendImage(
|
||||
* 上传群图片
|
||||
*/
|
||||
expect suspend fun httpPostGroupImage(
|
||||
bot: UInt,
|
||||
groupNumber: UInt,
|
||||
uKeyHex: String,
|
||||
fileSize: Long,
|
||||
imageData: ByteReadPacket
|
||||
|
@ -41,7 +41,7 @@ private object IgnoreIdList : List<String> by listOf(
|
||||
)
|
||||
|
||||
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 { it.name in IgnoreIdList || /*"delegate" in it.name||*/ "$" in it.name }
|
||||
.joinToString(", ", "{", "}") {
|
||||
it.isAccessible = true
|
||||
it.name + "=" + it.get(this).let { value ->
|
||||
|
@ -5,11 +5,10 @@ package net.mamoe.mirai.utils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.streams.writePacket
|
||||
import kotlinx.io.core.readBytes
|
||||
import org.jsoup.Connection
|
||||
import java.net.HttpURLConnection
|
||||
import org.jsoup.Jsoup
|
||||
import java.net.InetAddress
|
||||
import java.net.URL
|
||||
import java.security.MessageDigest
|
||||
import java.util.zip.CRC32
|
||||
|
||||
@ -37,83 +36,46 @@ actual suspend fun httpPostFriendImage(
|
||||
botNumber: UInt,
|
||||
qq: UInt,
|
||||
imageData: ByteReadPacket
|
||||
): Boolean {/*Jsoup
|
||||
//htdata2.qq.com
|
||||
|
||||
// 101.227.143.109/cgi-bin/httpconn
|
||||
// ?htcmd=0x6ff0070
|
||||
// &ver=5603
|
||||
// &ukey=3B121C959B85035F12497519221FB9E09740E477D3A440D28253E96C95BD72EA1D11B25189894F0256F3E0F3D553FB92925A8834F85583C78D0D9639A3F35C730783D45C065FF9E9E74765183A11492D50750C6BB5DCAD9F171285B68F6A11061CDDA740AD2DCD28D5B2DB2D6440143FA53F1B6F14584DB49E926FDDC4F49907
|
||||
// &filesize=137791
|
||||
// &range=0
|
||||
// &uin=1040400290
|
||||
.connect("http://101.227.143.109/cgi-bin/httpconn" +
|
||||
): Boolean = Jsoup.connect("http://htdata2.qq.com/cgi-bin/httpconn" +
|
||||
"?htcmd=0x6ff0070" +
|
||||
"&ver=5603" +
|
||||
"&ukey=" + uKeyHex.replace(" ", "") +
|
||||
"&filezise=" + fileSize +
|
||||
"&range=" + "0" +
|
||||
"&uin=" + botNumber.toLong())
|
||||
//.userAgent("QQClient")
|
||||
.header("Content-Length", fileSize.toString())
|
||||
.requestBody(String(imageData, Charset.forName("ASCII")))
|
||||
.method(Connection.Method.POST)
|
||||
.ignoreContentType(true)
|
||||
.let {
|
||||
withContext(Dispatchers.IO) {
|
||||
it.execute()
|
||||
}
|
||||
};*/
|
||||
|
||||
val conn = URL("http://htdata2.qq.com/cgi-bin/httpconn" +
|
||||
"?htcmd=0x6ff0070" +
|
||||
"&ver=5603" +
|
||||
"&ukey=" + uKeyHex.replace(" ", "") +
|
||||
"&filezise=" + fileSize +
|
||||
"&range=" + "0" +
|
||||
"&uin=" + botNumber.toLong()).openConnection() as HttpURLConnection
|
||||
conn.setRequestProperty("User-Agent", "QQClient")
|
||||
conn.setRequestProperty("Content-Length", imageData.toString())
|
||||
conn.setRequestProperty("Connection", "Keep-Alive")
|
||||
conn.requestMethod = "POST"
|
||||
conn.doOutput = true
|
||||
conn.doInput = true
|
||||
withContext(Dispatchers.IO) {
|
||||
conn.connect()
|
||||
}
|
||||
|
||||
conn.outputStream.writePacket(imageData)
|
||||
|
||||
println(conn.responseMessage)
|
||||
println(conn.responseCode)
|
||||
return conn.responseCode == 200
|
||||
}
|
||||
"&ukey=${uKeyHex}" +
|
||||
"&filezise=${imageData.remaining}" +
|
||||
"&range=0" +
|
||||
"&uin=$botNumber")
|
||||
.postImage(imageData)
|
||||
|
||||
/**
|
||||
* 上传群图片
|
||||
*/
|
||||
actual suspend fun httpPostGroupImage(uKeyHex: String, fileSize: Long, imageData: ByteReadPacket): Boolean {
|
||||
val conn = URL("http://htdata2.qq.com/cgi-bin/httpconn" +
|
||||
actual suspend fun httpPostGroupImage(
|
||||
bot: UInt,
|
||||
groupNumber: UInt,
|
||||
uKeyHex: String,
|
||||
fileSize: Long,
|
||||
imageData: ByteReadPacket
|
||||
): Boolean = Jsoup.connect("http://htdata2.qq.com/cgi-bin/httpconn" +
|
||||
"?htcmd=0x6ff0071" +
|
||||
"&term=pc" +
|
||||
"&ver=5603" +
|
||||
"&ukey=" + uKeyHex.replace(" ", "")).openConnection() as HttpURLConnection
|
||||
conn.setRequestProperty("Content-Length", imageData.remaining.toString())
|
||||
conn.setRequestProperty("Connection", "Keep-Alive")
|
||||
conn.requestMethod = "POST"
|
||||
conn.doOutput = true
|
||||
conn.doInput = true
|
||||
withContext(Dispatchers.IO) {
|
||||
conn.connect()
|
||||
}
|
||||
"&filesize=${imageData.remaining}" +
|
||||
"&uin=$bot" +
|
||||
"&groupcode=$groupNumber" +
|
||||
"&range=0" +
|
||||
"&ukey=" + uKeyHex)
|
||||
.postImage(imageData)
|
||||
|
||||
val stream = conn.outputStream
|
||||
stream.writePacket(imageData)
|
||||
|
||||
println(conn.responseMessage)
|
||||
println(conn.responseCode)
|
||||
return conn.responseCode == 200
|
||||
}
|
||||
private suspend fun Connection.postImage(image: ByteReadPacket): Boolean = this
|
||||
.userAgent("QQClient")
|
||||
.header("Content-Length", image.remaining.toString())
|
||||
.requestBody(String(image.readBytes(), Charsets.ISO_8859_1))
|
||||
.method(Connection.Method.POST)
|
||||
.postDataCharset("ISO_8859_1")
|
||||
.header("Content-type", "image/png")
|
||||
.ignoreContentType(true)
|
||||
.suspendExecute()
|
||||
.statusCode() == 200
|
||||
|
||||
private suspend fun Connection.suspendExecute(): Connection.Response = withContext(Dispatchers.IO) {
|
||||
execute()
|
||||
|
@ -57,6 +57,12 @@ suspend fun main() {
|
||||
}
|
||||
}
|
||||
|
||||
subscribeAlways<GroupMessageEvent> {
|
||||
if (it.message eq "复读" && it.group.groupId == 580266363u) {
|
||||
it.reply(it.message)
|
||||
}
|
||||
}
|
||||
|
||||
//提供泛型以监听事件
|
||||
subscribeAlways<FriendMessageEvent> {
|
||||
//获取第一个纯文本消息, 获取不到会抛出 NoSuchElementException
|
||||
@ -71,7 +77,7 @@ suspend fun main() {
|
||||
|
||||
"复读" in it.message -> it.sender.sendMessage(it.message)
|
||||
|
||||
"发群" in it.message -> Group(bot, 580266363u).sendMessage("h")
|
||||
"发群消息" in it.message -> Group(bot, 580266363u).sendMessage(it.message.toString().substringAfter("发群消息"))
|
||||
|
||||
"直接发送包" in it.message -> {
|
||||
val d = ("01 " + 1994701021u.toByteArray().toUHexString() + " 3E 03 3F A2 00 00 02 BB 00 0A 00 01 00 01 00 5E 4F 53 52 6F 6F 74 3A 43 3A 5C 55 73 65 72 73 5C 48 69 6D 31 38 5C 44 6F 63 75 6D 65 6E 74 73 5C 54 65 6E 63 65 6E 74 20 46 69 6C 65 73 5C 31 30 34 30 34 30 30 32 39 30 5C 49 6D 61 67 65 5C 43 32 43 5C 7B 47 47 42 7E 49 31 5A 4D 43 28 25 49 4D 5A 5F 47 55 51 36 35 5D 51 2E 6A 70 67 00 00 04 7D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 35 02")
|
||||
@ -79,16 +85,16 @@ suspend fun main() {
|
||||
it.bot.network.socket.sendPacket(ClientRawPacket(0x01_BDu, it.bot.qqAccount, "00 00 00 01 2E 01 00 00 69 35".hexToBytes(), it.bot.network.session.sessionKey, d))
|
||||
}
|
||||
|
||||
"上传好友图片" in it.message -> withTimeoutOrNull(3000) {
|
||||
"上传好友图片" in it.message -> withTimeoutOrNull(5000) {
|
||||
val id = QQ(bot, 1040400290u)
|
||||
.uploadImage(withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\色图.jpg").readBytes().inputStream()) }.toMiraiImage("png"))
|
||||
.uploadImage(withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\${it.message.toString().substringAfter("上传好友图片")}").readBytes().inputStream()) }.toMiraiImage("png"))
|
||||
it.reply(id.value)
|
||||
delay(1000)
|
||||
it.reply(Image(id))
|
||||
}
|
||||
|
||||
"上传群图片" in it.message -> withTimeoutOrNull(3000) {
|
||||
val image = withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\色图.jpg").readBytes().inputStream()) }.toMiraiImage("png")
|
||||
"上传群图片" in it.message -> withTimeoutOrNull(5000) {
|
||||
val image = withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\${it.message.toString().substringAfter("上传群图片")}").readBytes().inputStream()) }.toMiraiImage("png")
|
||||
Group(bot, 580266363u).uploadImage(image)
|
||||
it.reply(image.groupImageId.value)
|
||||
delay(1000)
|
||||
@ -96,7 +102,7 @@ suspend fun main() {
|
||||
}
|
||||
|
||||
"发群图片" in it.message -> {
|
||||
Group(bot, 580266363u).sendMessage(Image(ImageId(it.message.toString().substringAfter("发图片"))))
|
||||
Group(bot, 580266363u).sendMessage(Image(ImageId(it.message.toString().substringAfter("发群图片"))))
|
||||
}
|
||||
|
||||
"发好友图片" in it.message -> {
|
||||
|
Loading…
Reference in New Issue
Block a user