Commit d06197d3 authored by Him188's avatar Him188

Image uploading

parent 3474c87f
......@@ -6,8 +6,8 @@ import kotlinx.coroutines.cancelChildren
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocketAdapter
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.LoginHandler
import net.mamoe.mirai.network.protocol.tim.handler.*
import net.mamoe.mirai.network.protocol.tim.packet.ClientHeartbeatPacket
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import net.mamoe.mirai.network.protocol.tim.packet.HeartbeatPacket
import net.mamoe.mirai.network.protocol.tim.packet.Packet
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRefreshmentRequestPacket
......@@ -39,7 +39,7 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> {
*
* [BotNetworkHandler] 的协程包含:
* - UDP 包接收: [PlatformDatagramChannel.read]
* - 心跳 Job [ClientHeartbeatPacket]
* - 心跳 Job [HeartbeatPacket]
* - SKey 刷新 [ClientSKeyRefreshmentRequestPacket]
* - 所有数据包处理和发送
*
......
......@@ -48,29 +48,6 @@ class BotSession(
var gtk: Int = 0
private set
/**
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket].
*
* 实现方法:
* ```kotlin
* session.sendAndExpect<ServerPacketXXX> {
* toSend { ClientPacketXXX(...) }
* onExpect {//it: ServerPacketXXX
*
* }
* }
* ```
*
* @param P 期待的包
* @param handlerTemporary 处理器.
*/
//@JvmSynthetic
suspend inline fun <reified P : ServerPacket, R> sendAndExpect(handlerTemporary: TemporaryPacketHandler<P, R>.() -> Unit): CompletableDeferred<R> {
val deferred: CompletableDeferred<R> = coroutineContext[Job].takeIf { it != null }?.let { CompletableDeferred<R>(it) } ?: CompletableDeferred()
this.bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this).also(handlerTemporary))
return deferred
}
/**
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket].
* 发送成功后, 该方法会等待收到 [ServerPacket] 直到超时.
......@@ -78,8 +55,10 @@ class BotSession(
*
* 实现方法:
* ```kotlin
* ClientPacketXXX(...).sendAndExpect<ServerPacketXXX> {
* //it: ServerPacketXXX
* with(session){
* ClientPacketXXX(...).sendAndExpect<ServerPacketXXX> {
* //it: ServerPacketXXX
* }
* }
* ```
*
......@@ -95,6 +74,8 @@ class BotSession(
return deferred
}
suspend inline fun <reified P : ServerPacket> ClientPacket.sendAndExpect(): CompletableDeferred<Unit> = sendAndExpect<P, Unit> {}
suspend inline fun ClientPacket.send() = socket.sendPacket(this)
}
......
......@@ -16,7 +16,10 @@ import net.mamoe.mirai.event.subscribe
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.handler.*
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import net.mamoe.mirai.network.protocol.tim.packet.HeartbeatPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.UnknownServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.*
import net.mamoe.mirai.network.session
......@@ -408,7 +411,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
class HeartbeatTimeoutException : CancellationException("heartbeat timeout")
if (withTimeoutOrNull(configuration.heartbeatTimeout.millisecondsLong) {
ClientHeartbeatPacket(bot.qqAccount, sessionKey).sendAndExpect<ServerHeartbeatResponsePacket, Unit> {}
HeartbeatPacket(bot.qqAccount, sessionKey).sendAndExpect<HeartbeatPacket.Response>().join()
} == null) {
bot.logPurple("Heartbeat timed out")
bot.reinitializeNetworkHandler(configuration, HeartbeatTimeoutException())
......@@ -438,11 +441,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
is ServerSessionKeyResponsePacket.Encrypted -> socket.distributePacket(packet.decrypt(this.sessionResponseDecryptionKey))
is ServerTouchResponsePacket.Encrypted -> socket.distributePacket(packet.decrypt())
is ServerHeartbeatResponsePacket -> {
}
is UnknownServerPacket.Encrypted -> socket.distributePacket(packet.decrypt(sessionKey))
else -> {
......
......@@ -8,7 +8,10 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.isOpen
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.ClientAccountInfoRequestPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerAccountInfoResponsePacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerSessionPacket
import net.mamoe.mirai.network.protocol.tim.packet.action.AddFriendResult
import net.mamoe.mirai.network.protocol.tim.packet.action.ClientAddFriendPacket
import net.mamoe.mirai.network.protocol.tim.packet.action.ClientCanAddFriendPacket
......@@ -42,20 +45,6 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
it.onPacketReceived(packet)
}
}
is ServerTryGetImageIDSuccessPacket -> {
// ImageNetworkUtils.postImage(packet.uKey.toUHexString(), )
}
is ServerTryGetImageIDFailedPacket -> {
}
is ServerSubmitImageFilenameResponsePacket -> {
}
is ServerTryGetImageIDResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerSubmitImageFilenameResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerAccountInfoResponsePacket -> {
......@@ -82,6 +71,7 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
is ServerEventPacket.Raw.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerEventPacket.Raw -> session.socket.distributePacket(packet.distribute())
is ServerSessionPacket.Encrypted<*> -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
else -> {
}
......
......@@ -26,25 +26,21 @@ class TemporaryPacketHandler<P : ServerPacket, R>(
) {
private lateinit var toSend: ClientPacket
private lateinit var expect: suspend (P) -> R
private lateinit var handler: suspend (P) -> R
lateinit var session: BotSession//无需覆盖
fun toSend(packet: () -> ClientPacket) {
this.toSend = packet()
}
fun toSend(packet: ClientPacket) {
this.toSend = packet
}
fun onExpect(handler: suspend (P) -> R) {
this.expect = handler
this.handler = handler
}
suspend fun send(session: BotSession) {
require(::handler.isInitialized) { "handler is not initialized" }
this.session = session
session.socket.sendPacket(toSend)
}
......@@ -54,7 +50,7 @@ class TemporaryPacketHandler<P : ServerPacket, R>(
@Suppress("UNCHECKED_CAST")
val ret = try {
expect(packet as P)
handler(packet as P)
} catch (e: Exception) {
deferred.completeExceptionally(e)
return true
......
......@@ -10,7 +10,7 @@ import net.mamoe.mirai.utils.writeHex
import net.mamoe.mirai.utils.writeQQ
@PacketId(0x00_58u)
class ClientHeartbeatPacket(
class HeartbeatPacket(
private val bot: UInt,
private val sessionKey: ByteArray
) : ClientPacket() {
......@@ -21,7 +21,7 @@ class ClientHeartbeatPacket(
writeHex("00 01 00 01")
}
}
}
@PacketId(0x00_58u)
class ServerHeartbeatResponsePacket(input: ByteReadPacket) : ServerPacket(input)
\ No newline at end of file
@PacketId(0x00_58u)
class Response(input: ByteReadPacket) : ServerSessionPacket(input)
}
\ No newline at end of file
package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.ByteReadPacket
/**
* 登录完成之后的所有 packet.
* 它们都使用 sessionKey 解密.
* 它们都必须有一个公开的仅有一个 [ByteReadPacket] 参数的构造器.
*
* 注意: 需要为指定 ID, 通过 [PacketId].
*/
abstract class ServerSessionPacket(input: ByteReadPacket) : ServerPacket(input) {
/**
* 加密过的 [ServerSessionPacket]. 将会在处理时解密为对应的 [ServerSessionPacket]
*/
class Encrypted<P : ServerSessionPacket>(input: ByteReadPacket, val constructor: (ByteReadPacket) -> P) : ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): P = constructor(decryptBy(sessionKey)).applySequence(sequenceId)
}
companion object {
@Suppress("FunctionName")
inline fun <reified P : ServerSessionPacket> Encrypted(input: ByteReadPacket): Encrypted<P> = Encrypted(input) { P::class.constructors.first().call(it) }
}
}
......@@ -2,30 +2,40 @@
package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.writeUByte
import kotlinx.io.core.*
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.session
import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.*
fun main() {
"1A".hexToBytes().read {
println(readUnsignedVarInt())
}
suspend fun Group.uploadImage(
image: PlatformImage
) = with(bot.network.session) {
GroupImageIdRequestPacket(bot.qqAccount, groupId, image, sessionKey)
.sendAndExpect<GroupImageIdRequestPacket.Response, Unit> {
if (it.uKey != null) {
httpPostGroupImage(
imageData = image.fileData,
fileSize = image.fileSize,
uKeyHex = it.uKey!!.toUHexString()
)
}
}.await()
}
/**
* 获取 Image Id 和上传用的一个 uKey
*/
@PacketId(0x0388u)
class ClientGroupImageIdRequestPacket(
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
class GroupImageIdRequestPacket(
private val bot: UInt,
private val group: UInt,
private val groupId: UInt,
private val image: PlatformImage,
private val imageData: ByteArray,
private val sessionKey: ByteArray
) : ClientPacket() {
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
override fun encode(builder: BytePacketBuilder) = with(builder) {
//未知图片A
// 00 00 00 07 00 00 00
......@@ -110,12 +120,12 @@ class ClientGroupImageIdRequestPacket(
writeHex("01 12 03 98 01 01 10 01 1A")
writeUVarintLVPacket(lengthOffset = { it + 1 }) {
writeUVarInt(group)
writeUVarInt(groupId)
writeUVarInt(bot)
writeTV(0x1800u)
writeTLV(0x22u, md5(imageData))
writeTUVarint(0x28u, imageData.size.toUInt())
writeTLV(0x22u, image.md5)
writeTUVarint(0x28u, image.fileSize.toUInt())
writeUVarintLVPacket(tag = 0x32u) {
writeTV(0x31_00u)
writeTV(0x35_00u)
......@@ -149,5 +159,38 @@ class ClientGroupImageIdRequestPacket(
companion object {
private val value0x6A: UByteArray = ubyteArrayOf(32u, 36u, 39u, 33u, 33u)
}
@PacketId(0x0388u)
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
class Response(input: ByteReadPacket) : ServerSessionPacket(input) {
var uKey: ByteArray? = null
override fun decode(): Unit = with(input) {
discardExact(6)//00 00 00 05 00 00
if (readUByte() != UByte.MIN_VALUE) {
//服务器还没有
discardExact(remaining - 128)
uKey = readBytes(128)
}
// 已经有了的一张图片
// 00 3B 12 03 98 01 01
// 08 AB A7 89 D8 02 //群ID
// 10 01 1A 31 08 00 10 00 20 01 2A 1B 0A 10 7A A4 B3 AA 8C 3C 0F 45 2D 9B 7F 30 2A 0A CE AA 10 04 18 F3 06 20 41 28 34 30 DF CF A2 93 02 38 50 48 D0 A9 E5 C8 0B
// 服务器还没有的一张图片
// 02 4E 12 03 98 01 02
// 08 AB A7 89 D8 02 //群ID
// 10 02 22 C3 04 08 F8 9D D0 F5 09 12 10 2F CA 6B E7 B7 95 B7 27 06 35 27 54 0E 43 B4 30 18 00 48 BD EE 92 8D 05 48 BD EE 92 E5 01 48 BB CA 80 A3 02 48 BA F6 D7 5C 48 EF BC 90 F5 0A 50 50 50 50 50 50 50 50 50 50 5A 0D 67 63 68 61 74 2E 71 70 69 63 2E 63 6E 62 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 31 39 38 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 6A 77 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 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 72 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 37 32 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 78 00
// [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() {
("12 03 98 01 01").hexToBytes().read {
println(readUnsignedVarInt())
}
}
\ No newline at end of file
package net.mamoe.mirai.utils
import kotlin.jvm.JvmOverloads
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.message.ImageId
data class PlatformImage(
val width: Int,
val height: Int,
val md5: ByteArray,
val format: String,
val fileData: ByteReadPacket
) {
val fileSize: Long = fileData.remaining
expect class PlatformImage
val id: ImageId by lazy { ImageId("{${md5[0..4]}-${md5[0..2]}-${md5[0..2]}-${md5[0..2]}-${md5[0..6]}}.$format") }
@JvmOverloads
expect fun PlatformImage.toByteArray(formatName: String = "JPG"): ByteArray
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PlatformImage) return false
if (width != other.width) return false
if (height != other.height) return false
if (!md5.contentEquals(other.md5)) return false
if (format != other.format) return false
if (fileData != other.fileData) return false
if (fileSize != other.fileSize) return false
return true
}
override fun hashCode(): Int {
var result = width
result = 31 * result + height
result = 31 * result + md5.contentHashCode()
result = 31 * result + format.hashCode()
result = 31 * result + fileData.hashCode()
result = 31 * result + fileSize.hashCode()
return result
}
}
private operator fun ByteArray.get(range: IntRange): String = buildString {
range.forEach {
append(this@get[it].toUHexString())
}
}
expect val PlatformImage.imageWidth: Int
......
......@@ -3,6 +3,7 @@
package net.mamoe.mirai.utils
import com.soywiz.klock.DateTime
import kotlinx.io.core.ByteReadPacket
/**
* 时间戳
......@@ -38,14 +39,23 @@ expect fun solveIpAddress(hostname: String): String
expect fun localIpAddress(): String
/**
* 上传图片
* 上传好友图片
*/
expect suspend fun httpPostFriendImage(
uKeyHex: String,
fileSize: Int,
fileSize: Long,
botNumber: UInt,
qq: UInt,
imageData: ByteArray
imageData: ByteReadPacket
): Boolean
/**
* 上传群图片
*/
expect suspend fun httpPostGroupImage(
uKeyHex: String,
fileSize: Long,
imageData: ByteReadPacket
): Boolean
fun main() {
......
package net.mamoe.mirai.utils
import kotlin.properties.Delegates
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
fun <T : Any> Delegates.notNullBy(initializer: () -> T): ReadWriteProperty<Any?, T> = NotNullVarWithDefault(lazy(initializer = initializer))
class NotNullVarWithDefault<T : Any>(
private val initializer: Lazy<T>
) : ReadWriteProperty<Any?, T> {
private var value: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: initializer.value
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
......@@ -70,18 +70,20 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
}
0x08_28u -> ServerSessionKeyResponsePacket.Encrypted(this)
0x00_EC_u -> ServerLoginSuccessPacket(this)
0x00_1D_u -> ServerSKeyResponsePacket.Encrypted(this)
0x00_5C_u -> ServerAccountInfoResponsePacket.Encrypted(this)
0x00_58_u -> ServerHeartbeatResponsePacket(this)
0x00_BA_u -> ServerCaptchaPacket.Encrypted(this)
0x00_CE_u, 0x00_17_u -> ServerEventPacket.Raw.Encrypted(this, id, sequenceId)
0x00_81_u -> ServerFriendOnlineStatusChangedPacket.Encrypted(this)
0x00_CD_u -> ServerSendFriendMessageResponsePacket(this)
0x00_02_u -> ServerSendGroupMessageResponsePacket(this)
0x00_A7_u -> ServerCanAddFriendResponsePacket(this)
0x03_52_u -> ServerTryGetImageIDResponsePacket.Encrypted(this)
0x01_BDu -> ServerSubmitImageFilenameResponsePacket.Encrypted(this)
0x00_ECu -> ServerLoginSuccessPacket(this)
0x00_1Du -> ServerSKeyResponsePacket.Encrypted(this)
0x00_5Cu -> ServerAccountInfoResponsePacket.Encrypted(this)
0x00_BAu -> ServerCaptchaPacket.Encrypted(this)
0x00_CEu, 0x00_17u -> ServerEventPacket.Raw.Encrypted(this, id, sequenceId)
0x00_81u -> ServerFriendOnlineStatusChangedPacket.Encrypted(this)
0x00_CDu -> ServerSendFriendMessageResponsePacket(this)
0x00_02u -> ServerSendGroupMessageResponsePacket(this)
0x00_A7u -> ServerCanAddFriendResponsePacket(this)
0x00_58u -> ServerSessionPacket.Encrypted<HeartbeatPacket.Response>(this)
0x03_88u -> ServerSessionPacket.Encrypted<GroupImageIdRequestPacket.Response>(this)
0x03_52u -> ServerSessionPacket.Encrypted<FriendImageIdRequestPacket.Response>(this)
0x01_BDu -> ServerSessionPacket.Encrypted<SubmitImageFilenamePacket.Response>(this)
else -> UnknownServerPacket.Encrypted(this, id, sequenceId)
}.applySequence(sequenceId)
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.utils
import kotlinx.io.core.buildPacket
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.OutputStream
import java.security.MessageDigest
import javax.imageio.ImageIO
actual typealias PlatformImage = BufferedImage
fun BufferedImage.toPlatformImage(formatName: String = "PNG"): PlatformImage {
val digest = MessageDigest.getInstance("md5")
digest.reset()
val buffer = buildPacket {
ImageIO.write(this@toPlatformImage, formatName, object : OutputStream() {
override fun write(b: Int) {
b.toByte().let {
this@buildPacket.writeByte(it)
digest.update(it)
}
}
})
}
@JvmOverloads
actual fun BufferedImage.toByteArray(formatName: String): ByteArray = ByteArrayOutputStream().use { ImageIO.write(this, "PNG", it); it.toByteArray() }
return PlatformImage(this.width, this.height, digest.digest(), formatName, buffer)
}
actual val PlatformImage.imageWidth: Int get() = this.width
......
......@@ -2,6 +2,11 @@
package net.mamoe.mirai.utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.io.core.ByteReadPacket
import org.jsoup.Connection
import java.io.OutputStream
import java.net.HttpURLConnection
import java.net.InetAddress
import java.net.URL
......@@ -28,10 +33,10 @@ fun main() {
actual suspend fun httpPostFriendImage(
uKeyHex: String,
fileSize: Int,
fileSize: Long,
botNumber: UInt,
qq: UInt,
imageData: ByteArray
imageData: ByteReadPacket
): Boolean {/*Jsoup
//htdata2.qq.com
......@@ -60,7 +65,7 @@ actual suspend fun httpPostFriendImage(
}
};*/
val conn = URL("http://101.227.143.109/cgi-bin/httpconn" +
val conn = URL("http://htdata2.qq.com/cgi-bin/httpconn" +
"?htcmd=0x6ff0070" +
"&ver=5603" +
"&ukey=" + uKeyHex.replace(" ", "") +
......@@ -69,18 +74,55 @@ actual suspend fun httpPostFriendImage(
"&uin=" + botNumber.toLong()).openConnection() as HttpURLConnection
conn.setRequestProperty("User-Agent", "QQClient")
conn.setRequestProperty("Content-Length", "" + fileSize)
conn.setRequestProperty("connection", "Keep-Alive")
conn.setRequestProperty("Content-type", "image/png")
conn.setRequestProperty("Connection", "Keep-Alive")
conn.requestMethod = "POST"
conn.doOutput = true
conn.doInput = true
conn.connect()
withContext(Dispatchers.IO) {
conn.connect()
}
val buffered = conn.outputStream.buffered()
buffered.write(imageData)
buffered.flush()
conn.outputStream.writePacket(imageData)
println(conn.responseMessage)
println(conn.responseCode)
return conn.responseCode == 200
}
/**
* 上传群图片
*/
actual suspend fun httpPostGroupImage(uKeyHex: String, fileSize: Long, imageData: ByteReadPacket): Boolean {
val conn = URL("http://htdata2.qq.com/cgi-bin/httpconn" +
"?htcmd=0x6ff0071" +
"&term=pc" +
"&ver=5603" +
"&ukey=" + uKeyHex.replace(" ", "")).openConnection() as HttpURLConnection
conn.setRequestProperty("Content-Length", fileSize.toString())
conn.setRequestProperty("Connection", "Keep-Alive")
conn.requestMethod = "POST"
conn.doOutput = true
conn.doInput = true
withContext(Dispatchers.IO) {
conn.connect()
}
val stream = conn.outputStream
stream.writePacket(imageData)
println(conn.responseMessage)
println(conn.responseCode)
return conn.responseCode == 200
}
private suspend fun Connection.suspendExecute(): Connection.Response = withContext(Dispatchers.IO) {
execute()
}
private fun OutputStream.writePacket(packet: ByteReadPacket) {
val byteArray = ByteArray(1)
repeat(packet.remaining.toInt()) {
packet.readAvailable(byteArray)
this.write(byteArray)
}
}
\ No newline at end of file
......@@ -2,7 +2,9 @@
package demo1
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group
......@@ -78,12 +80,21 @@ suspend fun main() {
}
"上传好友图片" in it.message -> withTimeoutOrNull(3000) {
val id = QQ(bot, 1040400290u).uploadImage(ImageIO.read(File("C:\\Users\\Him18\\Desktop\\lemon.png").readBytes().inputStream()))
val id = QQ(bot, 1040400290u)
.uploadImage(withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\lemon.png").readBytes().inputStream()) }.toPlatformImage("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\\lemon.png").readBytes().inputStream()) }.toPlatformImage("PNG")
Group(bot, 580266363u).uploadImage(image)
it.reply(image.id.value)
delay(1000)
it.reply(Image(image.id))
}
/*it.event eq "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, Group(session.bot, 580266363)).of()
})*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment