mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-25 06:50:09 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
1b1be6944c
CHANGELOG.mdbuild.gradle.ktsgradle.properties
mirai-core-qqandroid
build.gradle.kts
src
androidMain/kotlin/net/mamoe/mirai/qqandroid
commonMain/kotlin/net/mamoe/mirai/qqandroid
ContactImpl.ktQQAndroidBot.kt
io/serialization
message
network
commonTest/kotlin/test
jvmMain/kotlin/net/mamoe/mirai/qqandroid
jvmTest/kotlin
mirai-core
build.gradle.kts
src
androidMain/kotlin/net/mamoe/mirai
commonMain/kotlin/net.mamoe.mirai
jvmMain/kotlin/net/mamoe/mirai
jvmTest/kotlin/mirai/test
@ -2,6 +2,10 @@
|
||||
|
||||
开发版本. 频繁更新, 不保证高稳定性
|
||||
|
||||
## `0.26.0` 2020/3/7
|
||||
- 使用 `kotlinx.io` 而不是 `ktor.io`
|
||||
- 修复 #111, #108, #116, #112
|
||||
|
||||
## `0.25.0` 2020/3/6
|
||||
- 适配 8.2.7 版本(2020 年 3 月)协议
|
||||
- 全面的 `Image` 类型: Online/Offline Image, Friend/Group Image
|
||||
|
@ -4,12 +4,11 @@ import java.util.*
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { setUrl("https://mirrors.huaweicloud.com/repository/maven") }
|
||||
maven(url = "https://mirrors.huaweicloud.com/repository/maven")
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
// mavenCentral()
|
||||
google()
|
||||
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
|
||||
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-dev") }
|
||||
// maven (url="https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@ -41,11 +40,10 @@ allprojects {
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { setUrl("https://mirrors.huaweicloud.com/repository/maven") }
|
||||
maven(url = "https://mirrors.huaweicloud.com/repository/maven")
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
// mavenCentral()
|
||||
google()
|
||||
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
|
||||
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-dev") }
|
||||
// maven (url="https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
# style guide
|
||||
kotlin.code.style=official
|
||||
# config
|
||||
miraiVersion=0.25.0
|
||||
miraiVersion=0.26.0
|
||||
kotlin.incremental.multiplatform=true
|
||||
kotlin.parallel.tasks.in.project=true
|
||||
# kotlin
|
||||
|
@ -11,6 +11,7 @@ plugins {
|
||||
val kotlinVersion: String by rootProject.ext
|
||||
val atomicFuVersion: String by rootProject.ext
|
||||
val coroutinesVersion: String by rootProject.ext
|
||||
val kotlinXIoVersion: String by rootProject.ext
|
||||
val coroutinesIoVersion: String by rootProject.ext
|
||||
|
||||
|
||||
@ -66,6 +67,7 @@ kotlin {
|
||||
api(kotlin("stdlib", kotlinVersion))
|
||||
|
||||
api("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
|
||||
api(kotlinx("io", kotlinXIoVersion))
|
||||
api(kotlinx("coroutines-io", coroutinesIoVersion))
|
||||
api(kotlinx("coroutines-core", coroutinesVersion))
|
||||
}
|
||||
|
@ -9,10 +9,20 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.consumeEachBufferRange
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import kotlinx.coroutines.io.*
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.Context
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import net.mamoe.mirai.utils.io.toReadPacket
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
internal actual class QQAndroidBot
|
||||
@ -20,4 +30,167 @@ actual constructor(
|
||||
context: Context,
|
||||
account: BotAccount,
|
||||
configuration: BotConfiguration
|
||||
) : QQAndroidBotBase(context, account, configuration)
|
||||
) : QQAndroidBotBase(context, account, configuration)
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@Suppress("DEPRECATION")
|
||||
internal actual fun ByteReadChannel.toKotlinByteReadChannel(): kotlinx.coroutines.io.ByteReadChannel {
|
||||
return object : kotlinx.coroutines.io.ByteReadChannel {
|
||||
override val availableForRead: Int
|
||||
get() = this@toKotlinByteReadChannel.availableForRead
|
||||
override val isClosedForRead: Boolean
|
||||
get() = this@toKotlinByteReadChannel.isClosedForRead
|
||||
override val isClosedForWrite: Boolean
|
||||
get() = this@toKotlinByteReadChannel.isClosedForWrite
|
||||
|
||||
@Suppress("DEPRECATION_ERROR", "OverridingDeprecatedMember")
|
||||
override var readByteOrder: ByteOrder
|
||||
get() = when (this@toKotlinByteReadChannel.readByteOrder) {
|
||||
io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN -> ByteOrder.BIG_ENDIAN
|
||||
io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN -> ByteOrder.LITTLE_ENDIAN
|
||||
}
|
||||
set(value) {
|
||||
this@toKotlinByteReadChannel.readByteOrder = when (value) {
|
||||
ByteOrder.BIG_ENDIAN -> io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN
|
||||
ByteOrder.LITTLE_ENDIAN -> io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION_ERROR", "DEPRECATION", "OverridingDeprecatedMember")
|
||||
override val totalBytesRead: Long
|
||||
get() = this@toKotlinByteReadChannel.totalBytesRead
|
||||
|
||||
override fun cancel(cause: Throwable?): Boolean = this@toKotlinByteReadChannel.cancel(cause)
|
||||
override suspend fun consumeEachBufferRange(visitor: ConsumeEachBufferVisitor) =
|
||||
this@toKotlinByteReadChannel.consumeEachBufferRange(visitor)
|
||||
|
||||
override suspend fun discard(max: Long): Long = this@toKotlinByteReadChannel.discard(max)
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
|
||||
@ExperimentalIoApi
|
||||
override fun <R> lookAhead(visitor: LookAheadSession.() -> R): R {
|
||||
return this@toKotlinByteReadChannel.lookAhead l@{
|
||||
visitor(object : LookAheadSession {
|
||||
override fun consumed(n: Int) {
|
||||
return this@l.consumed(n)
|
||||
}
|
||||
|
||||
override fun request(skip: Int, atLeast: Int): ByteBuffer? {
|
||||
return this@l.request(skip, atLeast)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
|
||||
@ExperimentalIoApi
|
||||
override suspend fun <R> lookAheadSuspend(visitor: suspend LookAheadSuspendSession.() -> R): R =
|
||||
this@toKotlinByteReadChannel.lookAheadSuspend l@{
|
||||
visitor(object : LookAheadSuspendSession {
|
||||
override suspend fun awaitAtLeast(n: Int): Boolean {
|
||||
return this@l.awaitAtLeast(n)
|
||||
}
|
||||
|
||||
override fun consumed(n: Int) {
|
||||
return this@l.consumed(n)
|
||||
}
|
||||
|
||||
override fun request(skip: Int, atLeast: Int): ByteBuffer? {
|
||||
return this@l.request(skip, atLeast)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun read(min: Int, consumer: (ByteBuffer) -> Unit) =
|
||||
this@toKotlinByteReadChannel.read(min, consumer)
|
||||
|
||||
override suspend fun readAvailable(dst: ByteBuffer): Int = this@toKotlinByteReadChannel.readAvailable(dst)
|
||||
override suspend fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int =
|
||||
this@toKotlinByteReadChannel.readAvailable(dst, offset, length)
|
||||
|
||||
override suspend fun readAvailable(dst: IoBuffer): Int {
|
||||
ByteArrayPool.useInstance {
|
||||
val read = this@toKotlinByteReadChannel.readAvailable(it, 0, it.size)
|
||||
dst.writeFully(it, 0, read)
|
||||
return read
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readBoolean(): Boolean = this@toKotlinByteReadChannel.readBoolean()
|
||||
override suspend fun readByte(): Byte = this@toKotlinByteReadChannel.readByte()
|
||||
override suspend fun readDouble(): Double = this@toKotlinByteReadChannel.readDouble()
|
||||
override suspend fun readFloat(): Float = this@toKotlinByteReadChannel.readFloat()
|
||||
override suspend fun readFully(dst: ByteBuffer): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override suspend fun readFully(dst: ByteArray, offset: Int, length: Int) =
|
||||
this@toKotlinByteReadChannel.readFully(dst, offset, length)
|
||||
|
||||
override suspend fun readFully(dst: IoBuffer, n: Int) {
|
||||
ByteArrayPool.useInstance {
|
||||
dst.writeFully(it, 0, this.readAvailable(it, 0, it.size))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readInt(): Int = this@toKotlinByteReadChannel.readInt()
|
||||
override suspend fun readLong(): Long = this@toKotlinByteReadChannel.readLong()
|
||||
override suspend fun readPacket(size: Int, headerSizeHint: Int): ByteReadPacket {
|
||||
return this@toKotlinByteReadChannel.readPacket(size, headerSizeHint).readBytes().toReadPacket()
|
||||
}
|
||||
|
||||
override suspend fun readRemaining(limit: Long, headerSizeHint: Int): ByteReadPacket {
|
||||
return this@toKotlinByteReadChannel.readRemaining(limit, headerSizeHint).readBytes().toReadPacket()
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalIoApi::class)
|
||||
@ExperimentalIoApi
|
||||
override fun readSession(consumer: ReadSession.() -> Unit) {
|
||||
@Suppress("DEPRECATION")
|
||||
this@toKotlinByteReadChannel.readSession lambda@{
|
||||
consumer(object : ReadSession {
|
||||
override val availableForRead: Int
|
||||
get() = this@lambda.availableForRead
|
||||
|
||||
override fun discard(n: Int): Int = this@lambda.discard(n)
|
||||
|
||||
override fun request(atLeast: Int): IoBuffer? {
|
||||
val ioBuffer: io.ktor.utils.io.core.IoBuffer = this@lambda.request(atLeast) ?: return null
|
||||
val buffer = IoBuffer.Pool.borrow()
|
||||
val bytes = (ioBuffer as Input).readBytes()
|
||||
buffer.writeFully(bytes)
|
||||
return buffer
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readShort(): Short = this@toKotlinByteReadChannel.readShort()
|
||||
|
||||
@Suppress("EXPERIMENTAL_OVERRIDE", "EXPERIMENTAL_API_USAGE")
|
||||
@ExperimentalIoApi
|
||||
override suspend fun readSuspendableSession(consumer: suspend SuspendableReadSession.() -> Unit) =
|
||||
this@toKotlinByteReadChannel.readSuspendableSession l@{
|
||||
consumer(object : SuspendableReadSession {
|
||||
override val availableForRead: Int
|
||||
get() = this@l.availableForRead
|
||||
|
||||
override suspend fun await(atLeast: Int): Boolean = this@l.await(atLeast)
|
||||
override fun discard(n: Int): Int = this@l.discard(n)
|
||||
override fun request(atLeast: Int): IoBuffer? {
|
||||
@Suppress("DuplicatedCode") val ioBuffer: io.ktor.utils.io.core.IoBuffer =
|
||||
this@l.request(atLeast) ?: return null
|
||||
val buffer = IoBuffer.Pool.borrow()
|
||||
val bytes = (ioBuffer as Input).readBytes()
|
||||
buffer.writeFully(bytes)
|
||||
return buffer
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun readUTF8Line(limit: Int): String? = this@toKotlinByteReadChannel.readUTF8Line(limit)
|
||||
override suspend fun <A : Appendable> readUTF8LineTo(out: A, limit: Int): Boolean =
|
||||
this@toKotlinByteReadChannel.readUTF8LineTo(out, limit)
|
||||
}
|
||||
}
|
@ -9,9 +9,9 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid
|
||||
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.io.core.Closeable
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.*
|
||||
|
@ -11,8 +11,8 @@ package net.mamoe.mirai.qqandroid
|
||||
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.BotImpl
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
@ -222,6 +222,10 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
}
|
||||
|
||||
override suspend fun openChannel(image: Image): ByteReadChannel {
|
||||
return Http.get<HttpResponse>(queryImageUrl(image)).content
|
||||
return Http.get<HttpResponse>(queryImageUrl(image)).content.toKotlinByteReadChannel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
internal expect fun io.ktor.utils.io.ByteReadChannel.toKotlinByteReadChannel(): ByteReadChannel
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization
|
||||
|
||||
import io.ktor.utils.io.charsets.Charset
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.charsets.Charset
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.internal.*
|
||||
import kotlinx.serialization.modules.EmptyModule
|
||||
|
@ -12,10 +12,7 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization
|
||||
|
||||
import io.ktor.utils.io.core.BytePacketBuilder
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import io.ktor.utils.io.core.writeFully
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.SerialDescriptor
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
|
@ -9,7 +9,10 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.message
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.io.core.readUInt
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.message.data.*
|
||||
|
@ -9,12 +9,15 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.atomicfu.AtomicRef
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.use
|
||||
import net.mamoe.mirai.data.MultiPacket
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.*
|
||||
@ -67,14 +70,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
_packetReceiverJob?.join()
|
||||
|
||||
return this.launch(CoroutineName("Incoming Packet Receiver")) {
|
||||
while (channel.isOpen) {
|
||||
while (channel.isOpen && isActive) {
|
||||
val rawInput = try {
|
||||
channel.read()
|
||||
} catch (e: CancellationException) {
|
||||
return@launch
|
||||
} catch (e: Throwable) {
|
||||
if (this@QQAndroidBotNetworkHandler.isActive) {
|
||||
BotOfflineEvent.Dropped(bot, e).broadcast()
|
||||
bot.launch { BotOfflineEvent.Dropped(bot, e).broadcast() }
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
@ -141,10 +144,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
continue@mainloop
|
||||
}
|
||||
is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> {
|
||||
var ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url)
|
||||
if (ticket == null) {
|
||||
ticket = ""
|
||||
}
|
||||
val ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url).orEmpty()
|
||||
response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
|
||||
continue@mainloop
|
||||
}
|
||||
@ -338,6 +338,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
suspend fun doHeartBeat(): Exception? {
|
||||
val lastException: Exception?
|
||||
try {
|
||||
kotlin.runCatching {
|
||||
Heartbeat.Alive(bot.client)
|
||||
.sendAndExpect<Heartbeat.Alive.Response>(
|
||||
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
||||
retry = 2
|
||||
)
|
||||
return null
|
||||
}
|
||||
Heartbeat.Alive(bot.client)
|
||||
.sendAndExpect<Heartbeat.Alive.Response>(
|
||||
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.atomicfu.AtomicInt
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.RawAccountIdUse
|
||||
import net.mamoe.mirai.data.OnlineStatus
|
||||
@ -159,8 +159,8 @@ internal open class QQAndroidClient(
|
||||
val uin: Long get() = _uin
|
||||
|
||||
@UseExperimental(RawAccountIdUse::class)
|
||||
@Suppress("PropertyName")
|
||||
internal var _uin: Long = bot.uin
|
||||
@Suppress("PropertyName", "DEPRECATION_ERROR")
|
||||
internal var _uin: Long = bot.account.id
|
||||
|
||||
var t530: ByteArray? = null
|
||||
var t528: ByteArray? = null
|
||||
@ -315,7 +315,7 @@ internal class Pt4Token(data: ByteArray, creationTime: Long, expireTime: Long) :
|
||||
internal typealias PSKeyMap = MutableMap<String, PSKey>
|
||||
internal typealias Pt4TokenMap = MutableMap<String, Pt4Token>
|
||||
|
||||
internal inline fun Input.readUShortLVString(): String = io.ktor.utils.io.core.String(this.readUShortLVByteArray())
|
||||
internal inline fun Input.readUShortLVString(): String = kotlinx.io.core.String(this.readUShortLVByteArray())
|
||||
|
||||
internal inline fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt())
|
||||
|
||||
|
@ -16,20 +16,21 @@ import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.http.content.OutgoingContent
|
||||
import io.ktor.http.userAgent
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.copyAndClose
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.readAvailable
|
||||
import io.ktor.utils.io.core.readInt
|
||||
import io.ktor.utils.io.core.use
|
||||
import io.ktor.utils.io.pool.useInstance
|
||||
import io.ktor.utils.io.ByteWriteChannel
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.io.InputStream
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readAvailable
|
||||
import kotlinx.io.core.use
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.copyAndClose
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import net.mamoe.mirai.utils.io.PlatformSocket
|
||||
import net.mamoe.mirai.utils.io.withUse
|
||||
@ -67,7 +68,7 @@ internal suspend fun HttpClient.postImage(
|
||||
override val contentType: ContentType = ContentType.Image.Any
|
||||
override val contentLength: Long = inputSize
|
||||
|
||||
override suspend fun writeTo(channel: io.ktor.utils.io.ByteWriteChannel) {
|
||||
override suspend fun writeTo(channel: ByteWriteChannel) {
|
||||
ByteArrayPool.useInstance { buffer: ByteArray ->
|
||||
when (imageInput) {
|
||||
is Input -> {
|
||||
|
@ -11,11 +11,14 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.highway
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.io.InputStream
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.writeFully
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
|
||||
|
@ -9,7 +9,10 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.core.BytePacketBuilder
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.writeFully
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.utils.cryptor.ECDH
|
||||
import net.mamoe.mirai.utils.cryptor.ECDHKeyPair
|
||||
|
@ -10,7 +10,10 @@
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet
|
||||
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.core.BytePacketBuilder
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.writeFully
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.encryptAndWrite
|
||||
@ -43,7 +46,6 @@ internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
|
||||
body: BytePacketBuilder.(sequenceId: Int) -> Unit
|
||||
): OutgoingPacket {
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
return OutgoingPacket(name, commandName, sequenceId, buildPacket {
|
||||
writeIntLVPacket(lengthOffset = { it + 4 }) {
|
||||
writeInt(0x0B)
|
||||
@ -52,7 +54,7 @@ internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
|
||||
writeByte(0)
|
||||
client.uin.toString().let {
|
||||
writeInt(it.length + 4)
|
||||
writeText(it)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
encryptAndWrite(key) {
|
||||
writeUniPacket(commandName, client.outgoingPacketSessionId, extraData) {
|
||||
@ -75,7 +77,6 @@ internal inline fun IncomingPacketFactory<*>.buildResponseUniPacket(
|
||||
sequenceId: Int = client.nextSsoSequenceId(),
|
||||
body: BytePacketBuilder.(sequenceId: Int) -> Unit
|
||||
): OutgoingPacket {
|
||||
@Suppress("DuplicatedCode")
|
||||
return OutgoingPacket(name, commandName, sequenceId, buildPacket {
|
||||
writeIntLVPacket(lengthOffset = { it + 4 }) {
|
||||
writeInt(0x0B)
|
||||
@ -84,7 +85,7 @@ internal inline fun IncomingPacketFactory<*>.buildResponseUniPacket(
|
||||
writeByte(0)
|
||||
client.uin.toString().let {
|
||||
writeInt(it.length + 4)
|
||||
writeText(it)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
encryptAndWrite(key) {
|
||||
writeUniPacket(commandName, client.outgoingPacketSessionId, extraData) {
|
||||
@ -105,7 +106,7 @@ internal inline fun BytePacketBuilder.writeUniPacket(
|
||||
writeIntLVPacket(lengthOffset = { it + 4 }) {
|
||||
commandName.let {
|
||||
writeInt(it.length + 4)
|
||||
writeText(it)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
|
||||
writeInt(4 + 4)
|
||||
@ -152,7 +153,7 @@ internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket(
|
||||
|
||||
client.uin.toString().let {
|
||||
writeInt(it.length + 4)
|
||||
writeText(it)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
|
||||
if (key === NO_ENCRYPT) {
|
||||
@ -207,7 +208,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
|
||||
}
|
||||
commandName.let {
|
||||
writeInt(it.length + 4)
|
||||
writeText(it)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
|
||||
writeInt(4 + 4)
|
||||
@ -215,7 +216,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
|
||||
|
||||
client.device.imei.let {
|
||||
writeInt(it.length + 4)
|
||||
writeText(it)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
|
||||
writeInt(4)
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import io.ktor.utils.io.pool.useInstance
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.Event
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
|
@ -9,7 +9,10 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.core.BytePacketBuilder
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.toByteArray
|
||||
import kotlinx.io.core.writeFully
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.LoginType
|
||||
import net.mamoe.mirai.qqandroid.utils.NetworkType
|
||||
import net.mamoe.mirai.utils.currentTimeMillis
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
||||
|
@ -9,7 +9,10 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.io.core.toByteArray
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.data.Packet
|
||||
@ -375,7 +378,7 @@ internal class TroopManagement {
|
||||
OutgoingPacketFactory<EditGroupNametag.Response>("friendlist.ModifyGroupCardReq") {
|
||||
object Response : Packet
|
||||
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): EditGroupNametag.Response {
|
||||
return Response
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
||||
|
@ -9,9 +9,10 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.discardExact
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.data.MultiPacketBySequence
|
||||
import net.mamoe.mirai.data.NoPacket
|
||||
@ -341,7 +341,7 @@ internal class OnlinePush {
|
||||
}
|
||||
}
|
||||
0x11 -> {
|
||||
discard(1)
|
||||
discardExact(1)
|
||||
val proto = readProtoBuf(TroopTips0x857.NotifyMsgBody.serializer())
|
||||
proto.optMsgRecall?.let { recallReminder ->
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.list
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.login
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.JceCharset
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.login
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.*
|
||||
|
@ -11,7 +11,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.login
|
||||
|
||||
|
||||
import io.ktor.util.InternalAPI
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.network.*
|
||||
@ -127,7 +127,7 @@ internal class WtLogin {
|
||||
private const val appId = 16L
|
||||
private const val subAppId = 537062845L
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
||||
operator fun invoke(
|
||||
client: QQAndroidClient
|
||||
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
|
||||
|
@ -11,11 +11,11 @@
|
||||
|
||||
package test
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.readAvailable
|
||||
import io.ktor.utils.io.core.use
|
||||
import io.ktor.utils.io.pool.useInstance
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.readAvailable
|
||||
import kotlinx.io.core.use
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLoggerWithSwitch
|
||||
|
@ -9,19 +9,189 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.consumeEachBufferRange
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import kotlinx.coroutines.io.*
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.Context
|
||||
import net.mamoe.mirai.utils.ContextImpl
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import net.mamoe.mirai.utils.io.toReadPacket
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@Suppress("FunctionName")
|
||||
internal fun QQAndroidBot(account: BotAccount, configuration: BotConfiguration): QQAndroidBot = QQAndroidBot(ContextImpl(), account, configuration)
|
||||
internal fun QQAndroidBot(account: BotAccount, configuration: BotConfiguration): QQAndroidBot =
|
||||
QQAndroidBot(ContextImpl(), account, configuration)
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
internal actual class QQAndroidBot actual constructor(
|
||||
context: Context,
|
||||
account: BotAccount,
|
||||
configuration: BotConfiguration
|
||||
) : QQAndroidBotBase(context, account, configuration)
|
||||
) : QQAndroidBotBase(context, account, configuration)
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@Suppress("DEPRECATION")
|
||||
internal actual fun ByteReadChannel.toKotlinByteReadChannel(): kotlinx.coroutines.io.ByteReadChannel {
|
||||
return object : kotlinx.coroutines.io.ByteReadChannel {
|
||||
override val availableForRead: Int
|
||||
get() = this@toKotlinByteReadChannel.availableForRead
|
||||
override val isClosedForRead: Boolean
|
||||
get() = this@toKotlinByteReadChannel.isClosedForRead
|
||||
override val isClosedForWrite: Boolean
|
||||
get() = this@toKotlinByteReadChannel.isClosedForWrite
|
||||
|
||||
@Suppress("DEPRECATION_ERROR", "OverridingDeprecatedMember")
|
||||
override var readByteOrder: ByteOrder
|
||||
get() = when (this@toKotlinByteReadChannel.readByteOrder) {
|
||||
io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN -> ByteOrder.BIG_ENDIAN
|
||||
io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN -> ByteOrder.LITTLE_ENDIAN
|
||||
}
|
||||
set(value) {
|
||||
this@toKotlinByteReadChannel.readByteOrder = when (value) {
|
||||
ByteOrder.BIG_ENDIAN -> io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN
|
||||
ByteOrder.LITTLE_ENDIAN -> io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION_ERROR", "DEPRECATION", "OverridingDeprecatedMember")
|
||||
override val totalBytesRead: Long
|
||||
get() = this@toKotlinByteReadChannel.totalBytesRead
|
||||
|
||||
override fun cancel(cause: Throwable?): Boolean = this@toKotlinByteReadChannel.cancel(cause)
|
||||
override suspend fun consumeEachBufferRange(visitor: ConsumeEachBufferVisitor) = this@toKotlinByteReadChannel.consumeEachBufferRange(visitor)
|
||||
override suspend fun discard(max: Long): Long = this@toKotlinByteReadChannel.discard(max)
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
|
||||
@ExperimentalIoApi
|
||||
override fun <R> lookAhead(visitor: LookAheadSession.() -> R): R {
|
||||
return this@toKotlinByteReadChannel.lookAhead l@{
|
||||
visitor(object : LookAheadSession{
|
||||
override fun consumed(n: Int) {
|
||||
return this@l.consumed(n)
|
||||
}
|
||||
override fun request(skip: Int, atLeast: Int): ByteBuffer? {
|
||||
return this@l.request(skip, atLeast)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
|
||||
@ExperimentalIoApi
|
||||
override suspend fun <R> lookAheadSuspend(visitor: suspend LookAheadSuspendSession.() -> R): R =
|
||||
this@toKotlinByteReadChannel.lookAheadSuspend l@{
|
||||
visitor(object : LookAheadSuspendSession {
|
||||
override suspend fun awaitAtLeast(n: Int): Boolean {
|
||||
return this@l.awaitAtLeast(n)
|
||||
}
|
||||
|
||||
override fun consumed(n: Int) {
|
||||
return this@l.consumed(n)
|
||||
}
|
||||
|
||||
override fun request(skip: Int, atLeast: Int): ByteBuffer? {
|
||||
return this@l.request(skip, atLeast)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun read(min: Int, consumer: (ByteBuffer) -> Unit) =
|
||||
this@toKotlinByteReadChannel.read(min, consumer)
|
||||
|
||||
override suspend fun readAvailable(dst: ByteBuffer): Int = this@toKotlinByteReadChannel.readAvailable(dst)
|
||||
override suspend fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int =
|
||||
this@toKotlinByteReadChannel.readAvailable(dst, offset, length)
|
||||
|
||||
override suspend fun readAvailable(dst: IoBuffer): Int {
|
||||
ByteArrayPool.useInstance {
|
||||
val read = this@toKotlinByteReadChannel.readAvailable(it, 0, it.size)
|
||||
dst.writeFully(it, 0, read)
|
||||
return read
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readBoolean(): Boolean = this@toKotlinByteReadChannel.readBoolean()
|
||||
override suspend fun readByte(): Byte = this@toKotlinByteReadChannel.readByte()
|
||||
override suspend fun readDouble(): Double = this@toKotlinByteReadChannel.readDouble()
|
||||
override suspend fun readFloat(): Float = this@toKotlinByteReadChannel.readFloat()
|
||||
override suspend fun readFully(dst: ByteBuffer): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override suspend fun readFully(dst: ByteArray, offset: Int, length: Int) =
|
||||
this@toKotlinByteReadChannel.readFully(dst, offset, length)
|
||||
|
||||
override suspend fun readFully(dst: IoBuffer, n: Int) {
|
||||
ByteArrayPool.useInstance {
|
||||
dst.writeFully(it, 0, this.readAvailable(it, 0, it.size))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readInt(): Int = this@toKotlinByteReadChannel.readInt()
|
||||
override suspend fun readLong(): Long = this@toKotlinByteReadChannel.readLong()
|
||||
override suspend fun readPacket(size: Int, headerSizeHint: Int): ByteReadPacket {
|
||||
return this@toKotlinByteReadChannel.readPacket(size, headerSizeHint).readBytes().toReadPacket()
|
||||
}
|
||||
|
||||
override suspend fun readRemaining(limit: Long, headerSizeHint: Int): ByteReadPacket {
|
||||
return this@toKotlinByteReadChannel.readRemaining(limit, headerSizeHint).readBytes().toReadPacket()
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalIoApi::class)
|
||||
@ExperimentalIoApi
|
||||
override fun readSession(consumer: ReadSession.() -> Unit) {
|
||||
@Suppress("DEPRECATION")
|
||||
this@toKotlinByteReadChannel.readSession lambda@{
|
||||
consumer(object : ReadSession {
|
||||
override val availableForRead: Int
|
||||
get() = this@lambda.availableForRead
|
||||
|
||||
override fun discard(n: Int): Int = this@lambda.discard(n)
|
||||
|
||||
override fun request(atLeast: Int): IoBuffer? {
|
||||
val ioBuffer: io.ktor.utils.io.core.IoBuffer = this@lambda.request(atLeast) ?: return null
|
||||
val buffer = IoBuffer.Pool.borrow()
|
||||
val bytes = (ioBuffer as Input).readBytes()
|
||||
buffer.writeFully(bytes)
|
||||
return buffer
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readShort(): Short = this@toKotlinByteReadChannel.readShort()
|
||||
|
||||
@Suppress("EXPERIMENTAL_OVERRIDE", "EXPERIMENTAL_API_USAGE")
|
||||
@ExperimentalIoApi
|
||||
override suspend fun readSuspendableSession(consumer: suspend SuspendableReadSession.() -> Unit) =
|
||||
this@toKotlinByteReadChannel.readSuspendableSession l@{
|
||||
consumer(object : SuspendableReadSession {
|
||||
override val availableForRead: Int
|
||||
get() = this@l.availableForRead
|
||||
|
||||
override suspend fun await(atLeast: Int): Boolean = this@l.await(atLeast)
|
||||
override fun discard(n: Int): Int = this@l.discard(n)
|
||||
override fun request(atLeast: Int): IoBuffer? {
|
||||
@Suppress("DuplicatedCode") val ioBuffer: io.ktor.utils.io.core.IoBuffer =
|
||||
this@l.request(atLeast) ?: return null
|
||||
val buffer = IoBuffer.Pool.borrow()
|
||||
val bytes = (ioBuffer as Input).readBytes()
|
||||
buffer.writeFully(bytes)
|
||||
return buffer
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun readUTF8Line(limit: Int): String? = this@toKotlinByteReadChannel.readUTF8Line(limit)
|
||||
override suspend fun <A : Appendable> readUTF8LineTo(out: A, limit: Int): Boolean =
|
||||
this@toKotlinByteReadChannel.readUTF8LineTo(out, limit)
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ package net.mamoe.mirai.qqandroid.io.serialization
|
||||
|
||||
/*
|
||||
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.JceOutput
|
||||
|
@ -28,7 +28,7 @@ fun main() {
|
||||
println(
|
||||
File(
|
||||
"""
|
||||
E:\Projects\QQAndroidFF\app\src\main\java\tencent\im\oidb\cmd0x857
|
||||
E:\Projects\QQAndroidFF\app\src\main\java\tencent\im\msgrevoke
|
||||
""".trimIndent()
|
||||
)
|
||||
.generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n")
|
||||
|
@ -11,6 +11,7 @@ plugins {
|
||||
val kotlinVersion: String by rootProject.ext
|
||||
val atomicFuVersion: String by rootProject.ext
|
||||
val coroutinesVersion: String by rootProject.ext
|
||||
val kotlinXIoVersion: String by rootProject.ext
|
||||
val coroutinesIoVersion: String by rootProject.ext
|
||||
|
||||
|
||||
@ -51,48 +52,37 @@ kotlin {
|
||||
)
|
||||
}
|
||||
|
||||
jvm("jvm") {
|
||||
}
|
||||
jvm()
|
||||
|
||||
sourceSets {
|
||||
all {
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
|
||||
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
|
||||
}
|
||||
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(kotlin("stdlib", kotlinVersion))
|
||||
api(kotlin("serialization", kotlinVersion))
|
||||
|
||||
api("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
|
||||
api(kotlinx("coroutines-io", coroutinesIoVersion))
|
||||
api(kotlinx("coroutines-core", coroutinesVersion))
|
||||
}
|
||||
}
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(kotlin("reflect", kotlinVersion))
|
||||
api(kotlin("serialization", kotlinVersion))
|
||||
|
||||
api(kotlinx("coroutines-core-common", coroutinesVersion))
|
||||
api(kotlinx("serialization-runtime-common", serializationVersion))
|
||||
api(kotlinx("io", kotlinXIoVersion))
|
||||
api(kotlinx("coroutines-io", coroutinesIoVersion))
|
||||
api(kotlinx("coroutines-core", coroutinesVersion))
|
||||
|
||||
api("org.jetbrains.kotlinx:atomicfu-common:$atomicFuVersion")
|
||||
|
||||
api(ktor("http-cio", ktorVersion))
|
||||
api(ktor("http", ktorVersion))
|
||||
api(ktor("client-core-jvm", ktorVersion))
|
||||
api(ktor("client-cio", ktorVersion))
|
||||
api(ktor("client-core", ktorVersion))
|
||||
api(ktor("network", ktorVersion))
|
||||
//implementation("io.ktor:ktor-io:1.3.0-beta-1")
|
||||
|
||||
//runtimeOnly(files("build/classes/kotlin/metadata/main")) // classpath is not properly set by IDE
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
implementation(kotlin("test-common"))
|
||||
|
||||
//runtimeOnly(files("build/classes/kotlin/metadata/test")) // classpath is not properly set by IDE
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,8 +91,10 @@ kotlin {
|
||||
dependencies {
|
||||
api(kotlin("reflect", kotlinVersion))
|
||||
|
||||
api(kotlinx("io-jvm", kotlinXIoVersion))
|
||||
api(kotlinx("serialization-runtime", serializationVersion))
|
||||
api(kotlinx("coroutines-android", coroutinesVersion))
|
||||
api(kotlinx("coroutines-io-jvm", coroutinesIoVersion))
|
||||
|
||||
api(ktor("client-android", ktorVersion))
|
||||
}
|
||||
@ -125,10 +117,10 @@ kotlin {
|
||||
api(kotlin("reflect", kotlinVersion))
|
||||
|
||||
api(ktor("client-core-jvm", ktorVersion))
|
||||
api(kotlinx("io-jvm", kotlinXIoVersion))
|
||||
api(kotlinx("serialization-runtime", serializationVersion))
|
||||
api(kotlinx("coroutines-io", coroutinesIoVersion))
|
||||
api(kotlinx("coroutines-io-jvm", coroutinesIoVersion))
|
||||
api(kotlinx("io-jvm", coroutinesIoVersion))
|
||||
api(kotlinx("coroutines-core", coroutinesVersion))
|
||||
|
||||
api("org.bouncycastle:bcprov-jdk15on:1.64")
|
||||
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
package net.mamoe.mirai
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
|
@ -10,8 +10,8 @@
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import io.ktor.utils.io.core.Input
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.io.core.Input
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.JavaHappyAPI
|
||||
import net.mamoe.mirai.event.events.*
|
||||
|
@ -12,9 +12,9 @@
|
||||
package net.mamoe.mirai.message
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import io.ktor.utils.io.core.Input
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.Input
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
@ -36,28 +36,31 @@ import java.net.URL
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun Bitmap.sendTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
suspend fun <C : Contact> Bitmap.sendTo(contact: C): MessageReceipt<C> =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun URL.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
suspend fun <C : Contact> URL.sendAsImageTo(contact: C): MessageReceipt<C> =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun Input.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
suspend fun <C : Contact> Input.sendAsImageTo(contact: C): MessageReceipt<C> =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun InputStream.sendAsImageTo(contact: Contact) =
|
||||
suspend fun <C : Contact> InputStream.sendAsImageTo(contact: C): MessageReceipt<C> =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
|
||||
/**
|
||||
@ -65,9 +68,9 @@ suspend fun InputStream.sendAsImageTo(contact: Contact) =
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun File.sendAsImageTo(contact: Contact) {
|
||||
suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
|
||||
require(this.exists() && this.canRead())
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
return withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -124,35 +127,36 @@ suspend fun File.uploadAsImage(contact: Contact): Image {
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(bufferedImage: Bitmap) = bufferedImage.sendTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(bufferedImage: Bitmap): MessageReceipt<C> = bufferedImage.sendTo(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(imageUrl: URL) = imageUrl.sendAsImageTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(imageUrl: URL): MessageReceipt<C> = imageUrl.sendAsImageTo(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(imageInput: Input) = imageInput.sendAsImageTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt<C> = imageInput.sendAsImageTo(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(imageStream: InputStream) = imageStream.sendAsImageTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(imageStream: InputStream): MessageReceipt<C> =
|
||||
imageStream.sendAsImageTo(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(file: File) = file.sendAsImageTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(file: File): MessageReceipt<C> = file.sendAsImageTo(this)
|
||||
|
||||
// endregion
|
||||
|
||||
|
@ -13,13 +13,14 @@ package net.mamoe.mirai.utils
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.copyTo
|
||||
import io.ktor.utils.io.errors.IOException
|
||||
import io.ktor.utils.io.streams.asOutput
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.IoBuffer
|
||||
import kotlinx.io.core.copyTo
|
||||
import kotlinx.io.errors.IOException
|
||||
import kotlinx.io.streams.asInput
|
||||
import kotlinx.io.streams.asOutput
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.net.URL
|
||||
@ -30,7 +31,7 @@ import java.net.URL
|
||||
|
||||
|
||||
/**
|
||||
* 读取 [BufferedImage] 的属性, 然后构造 [ExternalImage]
|
||||
* 读取 [Bitmap] 的属性, 然后构造 [ExternalImage]
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun Bitmap.toExternalImage(formatName: String = "gif"): Nothing {
|
||||
@ -52,7 +53,8 @@ fun File.toExternalImage(): ExternalImage {
|
||||
height = input.height,
|
||||
md5 = this.inputStream().use { it.md5() },
|
||||
imageFormat = this.nameWithoutExtension,
|
||||
input = this.inputStream(),
|
||||
input = this.inputStream().asInput(IoBuffer.Pool),
|
||||
inputSize = this.length(),
|
||||
filename = this.name
|
||||
)
|
||||
}
|
||||
@ -68,8 +70,8 @@ suspend inline fun File.suspendToExternalImage(): ExternalImage = withContext(IO
|
||||
@Throws(IOException::class)
|
||||
fun URL.toExternalImage(): ExternalImage {
|
||||
val file = createTempFile().apply { deleteOnExit() }
|
||||
file.outputStream().use { output ->
|
||||
openStream().use { input ->
|
||||
file.outputStream().asOutput().use { output ->
|
||||
openStream().asInput().use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
output.flush()
|
||||
@ -121,6 +123,7 @@ fun Input.toExternalImage(): ExternalImage {
|
||||
*/
|
||||
suspend inline fun Input.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() }
|
||||
|
||||
/*
|
||||
/**
|
||||
* 保存为临时文件然后调用 [File.toExternalImage].
|
||||
*/
|
||||
@ -132,4 +135,4 @@ suspend fun ByteReadChannel.toExternalImage(): ExternalImage {
|
||||
}
|
||||
|
||||
return file.suspendToExternalImage()
|
||||
}
|
||||
}*/
|
@ -13,7 +13,7 @@ import android.annotation.SuppressLint
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Build
|
||||
import android.telephony.TelephonyManager
|
||||
import io.ktor.utils.io.core.toByteArray
|
||||
import kotlinx.io.core.toByteArray
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UnstableDefault
|
||||
|
@ -6,7 +6,7 @@ private var isAddSuppressedSupported: Boolean = true
|
||||
|
||||
@PublishedApi
|
||||
internal actual fun Throwable.addSuppressedMirai(e: Throwable) {
|
||||
if (this === e) {
|
||||
if (this == e) {
|
||||
return
|
||||
}
|
||||
if (!isAddSuppressedSupported) {
|
||||
|
@ -9,12 +9,12 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import io.ktor.utils.io.nio.readPacketAtMost
|
||||
import io.ktor.utils.io.nio.writePacket
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Closeable
|
||||
import kotlinx.io.nio.readPacketAtMost
|
||||
import kotlinx.io.nio.writePacket
|
||||
import java.net.InetSocketAddress
|
||||
import java.nio.channels.DatagramChannel
|
||||
import java.nio.channels.ReadableByteChannel
|
||||
|
@ -9,13 +9,13 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import io.ktor.utils.io.core.ExperimentalIoApi
|
||||
import io.ktor.utils.io.streams.readPacketAtMost
|
||||
import io.ktor.utils.io.streams.writePacket
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Closeable
|
||||
import kotlinx.io.core.ExperimentalIoApi
|
||||
import kotlinx.io.streams.readPacketAtMost
|
||||
import kotlinx.io.streams.writePacket
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
|
@ -12,7 +12,7 @@ package net.mamoe.mirai.utils
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import io.ktor.utils.io.pool.useInstance
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.DataInput
|
||||
|
@ -11,10 +11,10 @@
|
||||
|
||||
package net.mamoe.mirai
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.coroutines.launch
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
@ -89,7 +89,7 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
|
||||
// region contacts
|
||||
|
||||
/**
|
||||
* [_lowLevelNewQQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewQQ] 实例
|
||||
* [QQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewQQ] 实例
|
||||
*/
|
||||
abstract val selfQQ: QQ
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
package net.mamoe.mirai
|
||||
|
||||
import io.ktor.utils.io.core.toByteArray
|
||||
import kotlinx.io.core.toByteArray
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.md5
|
||||
|
@ -10,8 +10,8 @@
|
||||
package net.mamoe.mirai.data
|
||||
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.readBytes
|
||||
import net.mamoe.mirai.utils.Http
|
||||
|
||||
interface ImageLink {
|
||||
|
@ -21,7 +21,6 @@ import net.mamoe.mirai.event.events.ImageUploadEvent.Failed
|
||||
import net.mamoe.mirai.event.events.ImageUploadEvent.Succeed
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
package net.mamoe.mirai.message
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Group
|
||||
|
@ -11,15 +11,16 @@
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Input
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.io.InputStream
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Input
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.OfflineImage
|
||||
import net.mamoe.mirai.message.data.sendTo
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
|
||||
@ -159,7 +160,7 @@ suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> =
|
||||
*
|
||||
* @see contact 图片上传对象. 由于好友图片与群图片不通用, 上传时必须提供目标联系人
|
||||
*/
|
||||
suspend fun ExternalImage.upload(contact: Contact): Image = when (contact) {
|
||||
suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact) {
|
||||
is Group -> contact.uploadImage(this)
|
||||
is QQ -> contact.uploadImage(this)
|
||||
else -> error("unreachable")
|
||||
|
@ -14,12 +14,12 @@
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.core.Output
|
||||
import io.ktor.utils.io.core.writeFully
|
||||
import io.ktor.utils.io.pool.useInstance
|
||||
import io.ktor.utils.io.readAvailable
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.coroutines.io.ByteWriteChannel
|
||||
import kotlinx.coroutines.io.readAvailable
|
||||
import kotlinx.io.OutputStream
|
||||
import kotlinx.io.core.Output
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
@ -52,6 +52,19 @@ suspend fun ByteReadChannel.copyTo(dst: Output) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst]
|
||||
*/
|
||||
suspend fun ByteReadChannel.copyTo(dst: ByteWriteChannel) {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
ByteArrayPool.useInstance { buffer ->
|
||||
var size: Int
|
||||
while (this.readAvailable(buffer).also { size = it } > 0) {
|
||||
dst.writeFully(buffer, 0, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* // 垃圾 kotlin, Unresolved reference: ByteWriteChannel
|
||||
/**
|
||||
@ -104,6 +117,44 @@ suspend fun ByteReadChannel.copyAndClose(dst: Output) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
|
||||
*/
|
||||
suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) {
|
||||
@Suppress("DuplicatedCode")
|
||||
try {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
ByteArrayPool.useInstance { buffer ->
|
||||
var size: Int
|
||||
while (this.readAvailable(buffer).also { size = it } > 0) {
|
||||
dst.writeFully(buffer, 0, size)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@Suppress("DuplicatedCode")
|
||||
dst.close(null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
|
||||
*/
|
||||
suspend fun ByteReadChannel.copyAndClose(dst: io.ktor.utils.io.ByteWriteChannel) {
|
||||
@Suppress("DuplicatedCode")
|
||||
try {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
ByteArrayPool.useInstance { buffer ->
|
||||
var size: Int
|
||||
while (this.readAvailable(buffer).also { size = it } > 0) {
|
||||
dst.writeFully(buffer, 0, size)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@Suppress("DuplicatedCode")
|
||||
dst.close(null)
|
||||
}
|
||||
}
|
||||
|
||||
/*// 垃圾 kotlin, Unresolved reference: ByteWriteChannel
|
||||
/**
|
||||
* 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
|
||||
|
@ -9,9 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.utils.cryptor
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.readFully
|
||||
import io.ktor.utils.io.pool.useInstance
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import net.mamoe.mirai.utils.io.toByteArray
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Closeable
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
|
||||
/**
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Closeable
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
|
||||
/**
|
||||
|
@ -13,11 +13,11 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.charsets.Charset
|
||||
import io.ktor.utils.io.charsets.Charsets
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.String
|
||||
import io.ktor.utils.io.core.use
|
||||
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 net.mamoe.mirai.utils.checkOffsetAndLength
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
|
@ -9,17 +9,15 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.isNotEmpty
|
||||
import io.ktor.utils.io.core.readAvailable
|
||||
import io.ktor.utils.io.pool.useInstance
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.io.InputStream
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import kotlinx.io.core.IoBuffer
|
||||
import kotlinx.io.pool.ObjectPool
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextInt
|
||||
|
||||
@ -200,3 +202,13 @@ fun ByteArray.toInt(): Int =
|
||||
(this[0].toInt().and(255) shl 24) + (this[1].toInt().and(255) shl 16) + (this[2].toInt().and(255) shl 8) + (this[3].toInt().and(
|
||||
255
|
||||
) shl 0)
|
||||
|
||||
/**
|
||||
* 从 [IoBuffer.Pool] [borrow][ObjectPool.borrow] 一个 [IoBuffer] 然后将 [this] 写入.
|
||||
* 注意回收 ([ObjectPool.recycle])
|
||||
*/
|
||||
fun ByteArray.toIoBuffer(
|
||||
offset: Int = 0,
|
||||
length: Int = this.size - offset,
|
||||
pool: ObjectPool<IoBuffer> = IoBuffer.Pool
|
||||
): IoBuffer = pool.borrow().let { it.writeFully(this, offset, length); it }
|
@ -13,7 +13,7 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.coerceAtMostOrFail
|
||||
import net.mamoe.mirai.utils.cryptor.TEA
|
||||
|
@ -12,7 +12,7 @@
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.utils.io.core.toByteArray
|
||||
import kotlinx.io.core.toByteArray
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
package net.mamoe.mirai
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import io.ktor.utils.io.core.Input
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.io.core.Input
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.JavaHappyAPI
|
||||
import net.mamoe.mirai.event.events.*
|
||||
|
@ -11,8 +11,10 @@
|
||||
|
||||
package net.mamoe.mirai.message
|
||||
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.use
|
||||
import kotlinx.coroutines.io.ByteWriteChannel
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.Output
|
||||
import kotlinx.io.core.use
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
@ -41,17 +43,15 @@ actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual con
|
||||
// endregion
|
||||
|
||||
// region 发送图片
|
||||
suspend inline fun sendImage(image: BufferedImage) = subject.sendImage(image)
|
||||
|
||||
suspend inline fun sendImage(image: URL) = subject.sendImage(image)
|
||||
suspend inline fun sendImage(image: Input) = subject.sendImage(image)
|
||||
suspend inline fun sendImage(image: InputStream) = subject.sendImage(image)
|
||||
suspend inline fun sendImage(image: File) = subject.sendImage(image)
|
||||
suspend inline fun sendImage(image: BufferedImage): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||
suspend inline fun sendImage(image: URL): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||
suspend inline fun sendImage(image: Input): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||
suspend inline fun sendImage(image: InputStream): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||
suspend inline fun sendImage(image: File): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||
// endregion
|
||||
|
||||
// region 上传图片 (扩展)
|
||||
suspend inline fun BufferedImage.upload(): Image = upload(subject)
|
||||
|
||||
suspend inline fun URL.uploadAsImage(): Image = uploadAsImage(subject)
|
||||
suspend inline fun Input.uploadAsImage(): Image = uploadAsImage(subject)
|
||||
suspend inline fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
|
||||
@ -59,12 +59,11 @@ actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual con
|
||||
// endregion 上传图片 (扩展)
|
||||
|
||||
// region 发送图片 (扩展)
|
||||
suspend inline fun BufferedImage.send() = sendTo(subject)
|
||||
|
||||
suspend inline fun URL.sendAsImage() = sendAsImageTo(subject)
|
||||
suspend inline fun Input.sendAsImage() = sendAsImageTo(subject)
|
||||
suspend inline fun InputStream.sendAsImage() = sendAsImageTo(subject)
|
||||
suspend inline fun File.sendAsImage() = sendAsImageTo(subject)
|
||||
suspend inline fun BufferedImage.send(): MessageReceipt<TSubject> = sendTo(subject)
|
||||
suspend inline fun URL.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
||||
suspend inline fun Input.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
||||
suspend inline fun InputStream.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
||||
suspend inline fun File.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
||||
// endregion 发送图片 (扩展)
|
||||
|
||||
// region 下载图片 (扩展)
|
||||
@ -80,6 +79,26 @@ actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual con
|
||||
*/
|
||||
suspend inline fun Image.downloadAndClose(output: OutputStream) = channel().copyAndClose(output)
|
||||
|
||||
/**
|
||||
* 下载图片到 [output] 但不关闭这个 [output]
|
||||
*/
|
||||
suspend inline fun Image.downloadTo(output: Output) = channel().copyTo(output)
|
||||
|
||||
/**
|
||||
* 下载图片到 [output] 并关闭这个 [output]
|
||||
*/
|
||||
suspend inline fun Image.downloadAndClose(output: Output) = channel().copyAndClose(output)
|
||||
|
||||
/**
|
||||
* 下载图片到 [output] 但不关闭这个 [output]
|
||||
*/
|
||||
suspend inline fun Image.downloadTo(output: ByteWriteChannel) = channel().copyTo(output)
|
||||
|
||||
/**
|
||||
* 下载图片到 [output] 并关闭这个 [output]
|
||||
*/
|
||||
suspend inline fun Image.downloadAndClose(output: ByteWriteChannel) = channel().copyAndClose(output)
|
||||
|
||||
/*
|
||||
suspend inline fun Image.downloadAsStream(): InputStream = channel().asInputStream()
|
||||
suspend inline fun Image.downloadAsExternalImage(): ExternalImage = withContext(Dispatchers.IO) { downloadAsStream().toExternalImage() }
|
||||
|
@ -59,15 +59,7 @@ actual open class MessageReceipt<C : Contact> actual constructor(
|
||||
actual suspend fun recall() {
|
||||
@Suppress("BooleanLiteralArgument")
|
||||
if (_isRecalled.compareAndSet(false, true)) {
|
||||
when (val contact = target) {
|
||||
is Group -> {
|
||||
contact.bot.recall(source)
|
||||
}
|
||||
is QQ -> {
|
||||
TODO()
|
||||
}
|
||||
else -> error("Unknown contact type")
|
||||
}
|
||||
target.bot.recall(source)
|
||||
} else error("message is already or planned to be recalled")
|
||||
}
|
||||
|
||||
|
@ -11,11 +11,12 @@
|
||||
|
||||
package net.mamoe.mirai.message
|
||||
|
||||
import io.ktor.utils.io.core.Input
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.Input
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.OfflineImage
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
import net.mamoe.mirai.utils.sendTo
|
||||
import net.mamoe.mirai.utils.toExternalImage
|
||||
@ -36,37 +37,41 @@ import java.net.URL
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun BufferedImage.sendTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
suspend fun <C : Contact> BufferedImage.sendTo(contact: C): MessageReceipt<C> =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun URL.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
suspend fun <C : Contact> URL.sendAsImageTo(contact: C): MessageReceipt<C> =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun Input.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
suspend fun <C : Contact> Input.sendAsImageTo(contact: C): MessageReceipt<C> =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun InputStream.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
suspend fun <C : Contact> InputStream.sendAsImageTo(contact: C): MessageReceipt<C> =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun File.sendAsImageTo(contact: Contact) {
|
||||
suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
|
||||
require(this.exists() && this.canRead())
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
return withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -78,35 +83,39 @@ suspend fun File.sendAsImageTo(contact: Contact) {
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun BufferedImage.upload(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
suspend fun BufferedImage.upload(contact: Contact): OfflineImage =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传后构造 [Image]
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun URL.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
suspend fun URL.uploadAsImage(contact: Contact): OfflineImage =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传后构造 [Image]
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun Input.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
suspend fun Input.uploadAsImage(contact: Contact): OfflineImage =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传后构造 [Image]
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun InputStream.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
suspend fun InputStream.uploadAsImage(contact: Contact): OfflineImage =
|
||||
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中将文件作为图片上传后构造 [Image]
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend fun File.uploadAsImage(contact: Contact): Image {
|
||||
suspend fun File.uploadAsImage(contact: Contact): OfflineImage {
|
||||
require(this.exists() && this.canRead())
|
||||
return withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
|
||||
}
|
||||
@ -120,35 +129,37 @@ suspend fun File.uploadAsImage(contact: Contact): Image {
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(bufferedImage: BufferedImage) = bufferedImage.sendTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(bufferedImage: BufferedImage): MessageReceipt<C> =
|
||||
bufferedImage.sendTo(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(imageUrl: URL) = imageUrl.sendAsImageTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(imageUrl: URL): MessageReceipt<C> = imageUrl.sendAsImageTo(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(imageInput: Input) = imageInput.sendAsImageTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt<C> = imageInput.sendAsImageTo(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(imageStream: InputStream) = imageStream.sendAsImageTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(imageStream: InputStream): MessageReceipt<C> =
|
||||
imageStream.sendAsImageTo(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.sendImage(file: File) = file.sendAsImageTo(this)
|
||||
suspend inline fun <C : Contact> C.sendImage(file: File): MessageReceipt<C> = file.sendAsImageTo(this)
|
||||
|
||||
// endregion
|
||||
|
||||
@ -159,34 +170,34 @@ suspend inline fun Contact.sendImage(file: File) = file.sendAsImageTo(this)
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.uploadImage(bufferedImage: BufferedImage): Image = bufferedImage.upload(this)
|
||||
suspend inline fun Contact.uploadImage(bufferedImage: BufferedImage): OfflineImage = bufferedImage.upload(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传, 但不发送
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.uploadImage(imageUrl: URL): Image = imageUrl.uploadAsImage(this)
|
||||
suspend inline fun Contact.uploadImage(imageUrl: URL): OfflineImage = imageUrl.uploadAsImage(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传, 但不发送
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.uploadImage(imageInput: Input): Image = imageInput.uploadAsImage(this)
|
||||
suspend inline fun Contact.uploadImage(imageInput: Input): OfflineImage = imageInput.uploadAsImage(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传, 但不发送
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.uploadImage(imageStream: InputStream): Image = imageStream.uploadAsImage(this)
|
||||
suspend inline fun Contact.uploadImage(imageStream: InputStream): OfflineImage = imageStream.uploadAsImage(this)
|
||||
|
||||
/**
|
||||
* 在 [Dispatchers.IO] 中将文件作为图片上传, 但不发送
|
||||
* @throws OverFileSizeMaxException
|
||||
*/
|
||||
@Throws(OverFileSizeMaxException::class)
|
||||
suspend inline fun Contact.uploadImage(file: File): Image = file.uploadAsImage(this)
|
||||
suspend inline fun Contact.uploadImage(file: File): OfflineImage = file.uploadAsImage(this)
|
||||
|
||||
// endregion
|
@ -9,7 +9,6 @@
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.utils.io.core.use
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
@ -21,6 +20,7 @@ import kotlinx.coroutines.io.writeFully
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.use
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
import java.awt.Image
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.buildPacket
|
||||
import io.ktor.utils.io.core.copyTo
|
||||
import io.ktor.utils.io.errors.IOException
|
||||
import io.ktor.utils.io.streams.asOutput
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.copyTo
|
||||
import kotlinx.io.errors.IOException
|
||||
import kotlinx.io.streams.asOutput
|
||||
import net.mamoe.mirai.utils.io.getRandomString
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.File
|
||||
|
@ -13,7 +13,7 @@ package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.utils.io.pool.useInstance
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import java.io.*
|
||||
import java.net.InetAddress
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.utils.io.core.toByteArray
|
||||
import kotlinx.io.core.toByteArray
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UnstableDefault
|
||||
|
@ -4,7 +4,7 @@ private var isAddSuppressedSupported: Boolean = true
|
||||
|
||||
@PublishedApi
|
||||
internal actual fun Throwable.addSuppressedMirai(e: Throwable) {
|
||||
if (this === e) {
|
||||
if (this == e) {
|
||||
return
|
||||
}
|
||||
if (!isAddSuppressedSupported) {
|
||||
|
@ -9,13 +9,13 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import io.ktor.utils.io.core.ExperimentalIoApi
|
||||
import io.ktor.utils.io.streams.readPacketAtMost
|
||||
import io.ktor.utils.io.streams.writePacket
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Closeable
|
||||
import kotlinx.io.core.ExperimentalIoApi
|
||||
import kotlinx.io.streams.readPacketAtMost
|
||||
import kotlinx.io.streams.writePacket
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
|
@ -9,12 +9,12 @@
|
||||
|
||||
package net.mamoe.mirai.utils.io
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import io.ktor.utils.io.nio.readPacketAtMost
|
||||
import io.ktor.utils.io.nio.writePacket
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Closeable
|
||||
import kotlinx.io.nio.readPacketAtMost
|
||||
import kotlinx.io.nio.writePacket
|
||||
import java.net.InetSocketAddress
|
||||
import java.nio.channels.DatagramChannel
|
||||
import java.nio.channels.ReadableByteChannel
|
||||
|
24
mirai-core/src/jvmTest/kotlin/mirai/test/KReflectionTest.kt
Normal file
24
mirai-core/src/jvmTest/kotlin/mirai/test/KReflectionTest.kt
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package mirai.test
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
||||
class A {
|
||||
|
||||
val valProp: Any = Any()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
A::class.members.filterIsInstance<KProperty<*>>().forEach {
|
||||
println(it.getter.call(A()))
|
||||
}
|
||||
}
|
16
mirai-core/src/jvmTest/kotlin/mirai/test/UnixColorText.kt
Normal file
16
mirai-core/src/jvmTest/kotlin/mirai/test/UnixColorText.kt
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package mirai.test
|
||||
|
||||
fun main() {
|
||||
repeat(100) {
|
||||
println("\u001b[1;${it}m" + it)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package mirai.test.testCaptchaPacket
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.cryptor.TEA.decrypt
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
|
||||
|
||||
@MiraiInternalAPI
|
||||
fun main() {
|
||||
val key = "65 F7 F3 14 E3 94 10 1F DD 95 84 A3 F5 9F AD 94".hexToBytes()
|
||||
val data =
|
||||
decrypt(
|
||||
"8D 4F 6A 70 F8 4A DE 43 AF 75 D1 3F 3A 3F F2 E0 A8 16 1A 46 13 CD B0 51 45 00 29 52 57 75 6D 4A 4C D9 B7 98 8C B0 96 EC 57 4E 67 FB 8D C5 F1 BF 72 38 40 42 19 54 C2 28 F4 72 C8 AE 24 EB 66 B5 D0 45 0B 72 44 81 E2 F6 2B EE C3 85 93 BA CB B7 72 F4 1A 30 F9 5B 3D B0 79 3E F4 0B F2 1A A7 49 60 3B 37 02 60 0C 5D D5 76 76 47 4F B5 B3 F5 CA 58 6C FC D2 41 3E 24 D1 FB 0A 18 53 D8 E5 A5 85 A8 BC 51 54 3B 66 5B 21 C6 7B AF C9 62 F0 AA 9C CF 2E 84 0F CC 15 5B 35 93 49 5C E4 28 49 A7 8A D3 30 A9 6E 36 4E 7A 49 28 69 4D C3 25 39 6E 45 6E 40 F2 86 1E F4 4F 00 A6 9D E6 9B 84 19 69 C1 31 6A 17 BA F0 0D 8A 22 09 86 24 92 F7 22 C3 47 7F F2 BF 94 8A 8A B5 29".hexToBytes(),
|
||||
key
|
||||
)
|
||||
println(data.toUHexString())
|
||||
|
||||
//00 02 00 00 08 04 01 E0 00 00 04 56 00 00 00 01 00 00 15 E3 01 00 38 58 CE A0 12 81 31 5C 5E 36 23 5B E4 0E 05 A6 47 BF 7C 1A 7A 35 37 59 90 17 50 66 0C 07 03 77 E4 48 DB 28 0A CF C3 A9 B7 C0 95 D3 9D 00 AA A5 EB FB D6 85 8D 10 61 5A D0 01 03 00 19 02 CA 53 7E F0 7B 32 82 EC 9F DE CF 51 8B A4 93 26 76 EC 42 1C 02 00 74 58 14 00 05 00 00 00 00 00 04 6C 73 64 61 00 40 CE 99 84 E8 F1 59 31 B0 3F 6C 4D 44 09 E4 82 77 96 67 03 A7 3A EA 8F 36 B9 20 79 7E C9 0F 75 3C 2A C3 E1 E5 C6 00 B3 5E 91 5B 47 63 EF AF 30 C0 48 2F 58 23 96 CF 65 2F 4C 75 95 A6 CA 5A 2C 5C 00 10 E1 50 C9 F4 F6 F4 2F D1 7F E9 8C AB B6 1C 38 7B
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user