Commit e60a6bcf authored by Him188's avatar Him188

Revert "Use `ktor.io` than `kotlinx.io`"

parent 6b54abe7
......@@ -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)
\ No newline at end of file
) : 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)
}
}
\ No newline at end of file
......@@ -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()
}
}
\ No newline at end of file
}
@Suppress("DEPRECATION")
@UseExperimental(MiraiInternalAPI::class)
internal expect fun io.ktor.utils.io.ByteReadChannel.toKotlinByteReadChannel(): ByteReadChannel
\ No newline at end of file
......@@ -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
}
......
......@@ -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
......
......@@ -16,10 +16,10 @@ 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.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
......@@ -30,6 +30,7 @@ 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,9 +11,9 @@
package net.mamoe.mirai.qqandroid.network.highway
import io.ktor.utils.io.ByteReadChannel
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
......
......@@ -9,13 +9,10 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.*
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.data.MemberInfo
......@@ -281,60 +278,6 @@ internal class MessageSvc {
}
}
internal class MessageSourceFromSendFriend(
val messageRandom: Int,
override val time: Long,
override val qqId: Long,
override val groupId: Long,
val sequenceId: Int
) : MessageSource {
@UseExperimental(ExperimentalCoroutinesApi::class)
override val id: Long
get() = sequenceId.toLong().shl(32) or
messageRandom.toLong().and(0xFFFFFFFF)
override suspend fun ensureSequenceIdAvailable() {
// nothing to do
}
override fun toString(): String {
return ""
}
}
internal class MessageSourceFromSendGroup(
val messageRandom: Int,
override val time: Long,
override val qqId: Long,
override val groupId: Long// ,
// override val sourceMessage: MessageChain
) : MessageSource {
private lateinit var sequenceIdDeferred: Deferred<Int>
@UseExperimental(ExperimentalCoroutinesApi::class)
override val id: Long
get() = sequenceIdDeferred.getCompleted().toLong().shl(32) or
messageRandom.toLong().and(0xFFFFFFFF)
@UseExperimental(MiraiExperimentalAPI::class)
fun startWaitingSequenceId(contact: Contact) {
sequenceIdDeferred =
contact.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(timeoutMillis = 3000) {
if (it.messageRandom == this@MessageSourceFromSendGroup.messageRandom) {
it.sequenceId
} else null
}
}
override suspend fun ensureSequenceIdAvailable() {
sequenceIdDeferred.join()
}
override fun toString(): String {
return ""
}
}
inline fun ToFriend(
client: QQAndroidClient,
toUin: Long,
......
......@@ -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 ->
......
......@@ -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 ->
......@@ -288,7 +288,7 @@ internal class WtLogin {
}
class Picture(
val data: IoBuffer,
val data: ByteArray,
val sign: ByteArray
) : Captcha() {
override fun toString(): String = "LoginPacketResponse.Captcha.Picture"
......@@ -394,11 +394,8 @@ internal class WtLogin {
imageData.discardExact(2)//image Length
val sign = imageData.readBytes(signInfoLength.toInt())
val buffer = IoBuffer.Pool.borrow()
imageData.readAvailable(buffer)
return LoginPacketResponse.Captcha.Picture(
data = buffer,
data = imageData.readBytes(),
sign = sign
)
// } else error("UNKNOWN CAPTCHA QUESTION: ${question.toUHexString()}, tlvMap=" + tlvMap.contentToString())
......
......@@ -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)
\ No newline at end of file
) : 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)
}
}
\ No newline at end of file
......@@ -107,6 +107,7 @@ kotlin {
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))
}
......@@ -131,9 +132,7 @@ kotlin {
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("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
......
......@@ -31,7 +31,7 @@ import java.net.URL
/**
* 读取 [BufferedImage] 的属性, 然后构造 [ExternalImage]
* 读取 [Bitmap] 的属性, 然后构造 [ExternalImage]
*/
@Throws(IOException::class)
fun Bitmap.toExternalImage(formatName: String = "gif"): Nothing {
......@@ -123,6 +123,7 @@ fun Input.toExternalImage(): ExternalImage {
*/
suspend inline fun Input.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() }
/*
/**
* 保存为临时文件然后调用 [File.toExternalImage].
*/
......@@ -134,4 +135,4 @@ suspend fun ByteReadChannel.toExternalImage(): ExternalImage {
}
return file.suspendToExternalImage()
}
\ No newline at end of file
}*/
\ No newline at end of file
......@@ -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.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,7 +11,7 @@
package net.mamoe.mirai.utils
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.io.InputStream
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input
......
......@@ -14,8 +14,9 @@
package net.mamoe.mirai.utils
import io.ktor.utils.io.ByteReadChannel
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
......@@ -120,6 +121,7 @@ suspend fun ByteReadChannel.copyAndClose(dst: Output) {
* 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
*/
suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) {
@Suppress("DuplicatedCode")
try {
@UseExperimental(MiraiInternalAPI::class)
ByteArrayPool.useInstance { buffer ->
......@@ -130,7 +132,26 @@ suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) {
}
} finally {
@Suppress("DuplicatedCode")
dst.close()
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)
}
}
......
......@@ -9,11 +9,11 @@
package net.mamoe.mirai.utils.io
import io.ktor.utils.io.ByteReadChannel
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
......
......@@ -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,9 +11,9 @@
package net.mamoe.mirai.message
import io.ktor.utils.io.ByteWriteChannel
import kotlinx.coroutines.io.ByteWriteChannel
import kotlinx.io.core.Input
import io.ktor.utils.io.core.Output
import kotlinx.io.core.Output
import kotlinx.io.core.use
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ
......
......@@ -37,28 +37,32 @@ import java.net.URL
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun <C : Contact> BufferedImage.sendTo(contact: C): MessageReceipt<C> = 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 <C : Contact> URL.sendAsImageTo(contact: C): MessageReceipt<C> = 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 <C : Contact> Input.sendAsImageTo(contact: C): MessageReceipt<C> = 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 <C : Contact> InputStream.sendAsImageTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
suspend fun <C : Contact> InputStream.sendAsImageTo(contact: C): MessageReceipt<C> =
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
/**
* 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
......@@ -79,28 +83,32 @@ suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun BufferedImage.upload(contact: Contact): OfflineImage = 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): OfflineImage = 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): OfflineImage = 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): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
suspend fun InputStream.uploadAsImage(contact: Contact): OfflineImage =
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
/**
* 在 [Dispatchers.IO] 中将文件作为图片上传后构造 [Image]
......@@ -121,7 +129,8 @@ suspend fun File.uploadAsImage(contact: Contact): OfflineImage {
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend inline fun <C : Contact> C.sendImage(bufferedImage: BufferedImage): MessageReceipt<C> = bufferedImage.sendTo(this)
suspend inline fun <C : Contact> C.sendImage(bufferedImage: BufferedImage): MessageReceipt<C> =
bufferedImage.sendTo(this)
/**
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
......@@ -142,7 +151,8 @@ suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt<
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend inline fun <C : Contact> C.sendImage(imageStream: InputStream): MessageReceipt<C> = imageStream.sendAsImageTo(this)
suspend inline fun <C : Contact> C.sendImage(imageStream: InputStream): MessageReceipt<C> =
imageStream.sendAsImageTo(this)
/**
* 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
......
......@@ -11,7 +11,7 @@
package net.mamoe.mirai.utils
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
import kotlinx.io.core.Input
......
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