Commit e60a6bcf authored by Him188's avatar Him188

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

parent 6b54abe7
...@@ -9,10 +9,20 @@ ...@@ -9,10 +9,20 @@
package net.mamoe.mirai.qqandroid 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.BotAccount
import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.Context import net.mamoe.mirai.utils.Context
import net.mamoe.mirai.utils.MiraiInternalAPI 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) @UseExperimental(MiraiInternalAPI::class)
internal actual class QQAndroidBot internal actual class QQAndroidBot
...@@ -20,4 +30,167 @@ actual constructor( ...@@ -20,4 +30,167 @@ actual constructor(
context: Context, context: Context,
account: BotAccount, account: BotAccount,
configuration: BotConfiguration configuration: BotConfiguration
) : QQAndroidBotBase(context, account, configuration) ) : QQAndroidBotBase(context, account, configuration)
\ No newline at end of file
@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 ...@@ -11,8 +11,8 @@ package net.mamoe.mirai.qqandroid
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse import io.ktor.client.statement.HttpResponse
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.BotAccount import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.BotImpl import net.mamoe.mirai.BotImpl
import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.LowLevelAPI
...@@ -222,6 +222,10 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -222,6 +222,10 @@ internal abstract class QQAndroidBotBase constructor(
} }
override suspend fun openChannel(image: Image): ByteReadChannel { 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 @@ ...@@ -9,12 +9,15 @@
package net.mamoe.mirai.qqandroid.network package net.mamoe.mirai.qqandroid.network
import io.ktor.utils.io.core.*
import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock 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.MultiPacket
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.* import net.mamoe.mirai.event.*
...@@ -67,14 +70,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -67,14 +70,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
_packetReceiverJob?.join() _packetReceiverJob?.join()
return this.launch(CoroutineName("Incoming Packet Receiver")) { return this.launch(CoroutineName("Incoming Packet Receiver")) {
while (channel.isOpen) { while (channel.isOpen && isActive) {
val rawInput = try { val rawInput = try {
channel.read() channel.read()
} catch (e: CancellationException) { } catch (e: CancellationException) {
return@launch return@launch
} catch (e: Throwable) { } catch (e: Throwable) {
if (this@QQAndroidBotNetworkHandler.isActive) { if (this@QQAndroidBotNetworkHandler.isActive) {
BotOfflineEvent.Dropped(bot, e).broadcast() bot.launch { BotOfflineEvent.Dropped(bot, e).broadcast() }
} }
return@launch return@launch
} }
...@@ -141,10 +144,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -141,10 +144,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
continue@mainloop continue@mainloop
} }
is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> { is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> {
var ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url) val ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url).orEmpty()
if (ticket == null) {
ticket = ""
}
response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect() response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
continue@mainloop continue@mainloop
} }
......
...@@ -159,8 +159,8 @@ internal open class QQAndroidClient( ...@@ -159,8 +159,8 @@ internal open class QQAndroidClient(
val uin: Long get() = _uin val uin: Long get() = _uin
@UseExperimental(RawAccountIdUse::class) @UseExperimental(RawAccountIdUse::class)
@Suppress("PropertyName") @Suppress("PropertyName", "DEPRECATION_ERROR")
internal var _uin: Long = bot.uin internal var _uin: Long = bot.account.id
var t530: ByteArray? = null var t530: ByteArray? = null
var t528: ByteArray? = null var t528: ByteArray? = null
......
...@@ -16,10 +16,10 @@ import io.ktor.http.HttpStatusCode ...@@ -16,10 +16,10 @@ import io.ktor.http.HttpStatusCode
import io.ktor.http.URLProtocol import io.ktor.http.URLProtocol
import io.ktor.http.content.OutgoingContent import io.ktor.http.content.OutgoingContent
import io.ktor.http.userAgent import io.ktor.http.userAgent
import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.ByteWriteChannel
import io.ktor.utils.io.copyAndClose
import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.io.InputStream import kotlinx.io.InputStream
import kotlinx.io.core.Input import kotlinx.io.core.Input
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
...@@ -30,6 +30,7 @@ import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf ...@@ -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.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import net.mamoe.mirai.utils.MiraiInternalAPI 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.ByteArrayPool
import net.mamoe.mirai.utils.io.PlatformSocket import net.mamoe.mirai.utils.io.PlatformSocket
import net.mamoe.mirai.utils.io.withUse import net.mamoe.mirai.utils.io.withUse
...@@ -67,7 +68,7 @@ internal suspend fun HttpClient.postImage( ...@@ -67,7 +68,7 @@ internal suspend fun HttpClient.postImage(
override val contentType: ContentType = ContentType.Image.Any override val contentType: ContentType = ContentType.Image.Any
override val contentLength: Long = inputSize 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 -> ByteArrayPool.useInstance { buffer: ByteArray ->
when (imageInput) { when (imageInput) {
is Input -> { is Input -> {
......
...@@ -11,9 +11,9 @@ ...@@ -11,9 +11,9 @@
package net.mamoe.mirai.qqandroid.network.highway package net.mamoe.mirai.qqandroid.network.highway
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.io.InputStream import kotlinx.io.InputStream
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input import kotlinx.io.core.Input
......
...@@ -9,13 +9,10 @@ ...@@ -9,13 +9,10 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive 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.FlowPreview
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.data.MemberInfo
...@@ -281,60 +278,6 @@ internal class MessageSvc { ...@@ -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( inline fun ToFriend(
client: QQAndroidClient, client: QQAndroidClient,
toUin: Long, toUin: Long,
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive 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.contact.MemberPermission
import net.mamoe.mirai.data.MultiPacketBySequence import net.mamoe.mirai.data.MultiPacketBySequence
import net.mamoe.mirai.data.NoPacket import net.mamoe.mirai.data.NoPacket
...@@ -341,7 +341,7 @@ internal class OnlinePush { ...@@ -341,7 +341,7 @@ internal class OnlinePush {
} }
} }
0x11 -> { 0x11 -> {
discard(1) discardExact(1)
val proto = readProtoBuf(TroopTips0x857.NotifyMsgBody.serializer()) val proto = readProtoBuf(TroopTips0x857.NotifyMsgBody.serializer())
proto.optMsgRecall?.let { recallReminder -> proto.optMsgRecall?.let { recallReminder ->
......
...@@ -127,7 +127,7 @@ internal class WtLogin { ...@@ -127,7 +127,7 @@ internal class WtLogin {
private const val appId = 16L private const val appId = 16L
private const val subAppId = 537062845L private const val subAppId = 537062845L
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
operator fun invoke( operator fun invoke(
client: QQAndroidClient client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
...@@ -288,7 +288,7 @@ internal class WtLogin { ...@@ -288,7 +288,7 @@ internal class WtLogin {
} }
class Picture( class Picture(
val data: IoBuffer, val data: ByteArray,
val sign: ByteArray val sign: ByteArray
) : Captcha() { ) : Captcha() {
override fun toString(): String = "LoginPacketResponse.Captcha.Picture" override fun toString(): String = "LoginPacketResponse.Captcha.Picture"
...@@ -394,11 +394,8 @@ internal class WtLogin { ...@@ -394,11 +394,8 @@ internal class WtLogin {
imageData.discardExact(2)//image Length imageData.discardExact(2)//image Length
val sign = imageData.readBytes(signInfoLength.toInt()) val sign = imageData.readBytes(signInfoLength.toInt())
val buffer = IoBuffer.Pool.borrow()
imageData.readAvailable(buffer)
return LoginPacketResponse.Captcha.Picture( return LoginPacketResponse.Captcha.Picture(
data = buffer, data = imageData.readBytes(),
sign = sign sign = sign
) )
// } else error("UNKNOWN CAPTCHA QUESTION: ${question.toUHexString()}, tlvMap=" + tlvMap.contentToString()) // } else error("UNKNOWN CAPTCHA QUESTION: ${question.toUHexString()}, tlvMap=" + tlvMap.contentToString())
......
...@@ -9,19 +9,189 @@ ...@@ -9,19 +9,189 @@
package net.mamoe.mirai.qqandroid 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.BotAccount
import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.Context import net.mamoe.mirai.utils.Context
import net.mamoe.mirai.utils.ContextImpl import net.mamoe.mirai.utils.ContextImpl
import net.mamoe.mirai.utils.MiraiInternalAPI 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) @UseExperimental(MiraiInternalAPI::class)
@Suppress("FunctionName") @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) @UseExperimental(MiraiInternalAPI::class)
internal actual class QQAndroidBot actual constructor( internal actual class QQAndroidBot actual constructor(
context: Context, context: Context,
account: BotAccount, account: BotAccount,
configuration: BotConfiguration configuration: BotConfiguration
) : QQAndroidBotBase(context, account, configuration) ) : QQAndroidBotBase(context, account, configuration)
\ No newline at end of file
@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 { ...@@ -107,6 +107,7 @@ kotlin {
api(kotlinx("io-jvm", kotlinXIoVersion)) api(kotlinx("io-jvm", kotlinXIoVersion))
api(kotlinx("serialization-runtime", serializationVersion)) api(kotlinx("serialization-runtime", serializationVersion))
api(kotlinx("coroutines-android", coroutinesVersion)) api(kotlinx("coroutines-android", coroutinesVersion))
api(kotlinx("coroutines-io-jvm", coroutinesIoVersion))
api(ktor("client-android", ktorVersion)) api(ktor("client-android", ktorVersion))
} }
...@@ -131,9 +132,7 @@ kotlin { ...@@ -131,9 +132,7 @@ kotlin {
api(ktor("client-core-jvm", ktorVersion)) api(ktor("client-core-jvm", ktorVersion))
api(kotlinx("io-jvm", kotlinXIoVersion)) api(kotlinx("io-jvm", kotlinXIoVersion))
api(kotlinx("serialization-runtime", serializationVersion)) api(kotlinx("serialization-runtime", serializationVersion))
api(kotlinx("coroutines-io", coroutinesIoVersion))
api(kotlinx("coroutines-io-jvm", coroutinesIoVersion)) api(kotlinx("coroutines-io-jvm", coroutinesIoVersion))
api(kotlinx("io-jvm", coroutinesIoVersion))
api("org.bouncycastle:bcprov-jdk15on:1.64") api("org.bouncycastle:bcprov-jdk15on:1.64")
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
package net.mamoe.mirai package net.mamoe.mirai
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.MessageReceipt
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import android.graphics.Bitmap import android.graphics.Bitmap
import io.ktor.utils.io.core.Input
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.io.core.Input
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.JavaHappyAPI import net.mamoe.mirai.JavaHappyAPI
import net.mamoe.mirai.event.events.* import net.mamoe.mirai.event.events.*
......
...@@ -12,9 +12,9 @@ ...@@ -12,9 +12,9 @@
package net.mamoe.mirai.message package net.mamoe.mirai.message
import android.graphics.Bitmap import android.graphics.Bitmap
import io.ktor.utils.io.core.Input
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.io.core.Input
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.utils.OverFileSizeMaxException import net.mamoe.mirai.utils.OverFileSizeMaxException
...@@ -36,28 +36,31 @@ import java.net.URL ...@@ -36,28 +36,31 @@ import java.net.URL
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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) withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
/** /**
...@@ -65,9 +68,9 @@ suspend fun InputStream.sendAsImageTo(contact: Contact) = ...@@ -65,9 +68,9 @@ suspend fun InputStream.sendAsImageTo(contact: Contact) =
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend fun File.sendAsImageTo(contact: Contact) { suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
require(this.exists() && this.canRead()) require(this.exists() && this.canRead())
withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) return withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
} }
// endregion // endregion
...@@ -124,35 +127,36 @@ suspend fun File.uploadAsImage(contact: Contact): Image { ...@@ -124,35 +127,36 @@ suspend fun File.uploadAsImage(contact: Contact): Image {
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 中将文件作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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 // endregion
......
...@@ -31,7 +31,7 @@ import java.net.URL ...@@ -31,7 +31,7 @@ import java.net.URL
/** /**
* 读取 [BufferedImage] 的属性, 然后构造 [ExternalImage] * 读取 [Bitmap] 的属性, 然后构造 [ExternalImage]
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun Bitmap.toExternalImage(formatName: String = "gif"): Nothing { fun Bitmap.toExternalImage(formatName: String = "gif"): Nothing {
...@@ -123,6 +123,7 @@ fun Input.toExternalImage(): ExternalImage { ...@@ -123,6 +123,7 @@ fun Input.toExternalImage(): ExternalImage {
*/ */
suspend inline fun Input.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() } suspend inline fun Input.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() }
/*
/** /**
* 保存为临时文件然后调用 [File.toExternalImage]. * 保存为临时文件然后调用 [File.toExternalImage].
*/ */
...@@ -134,4 +135,4 @@ suspend fun ByteReadChannel.toExternalImage(): ExternalImage { ...@@ -134,4 +135,4 @@ suspend fun ByteReadChannel.toExternalImage(): ExternalImage {
} }
return file.suspendToExternalImage() return file.suspendToExternalImage()
} }*/
\ No newline at end of file \ No newline at end of file
...@@ -11,10 +11,10 @@ ...@@ -11,10 +11,10 @@
package net.mamoe.mirai package net.mamoe.mirai
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult import net.mamoe.mirai.data.AddFriendResult
...@@ -89,7 +89,7 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor { ...@@ -89,7 +89,7 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
// region contacts // region contacts
/** /**
* [_lowLevelNewQQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewQQ] 实例 * [QQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewQQ] 实例
*/ */
abstract val selfQQ: QQ abstract val selfQQ: QQ
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
package net.mamoe.mirai.message 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.Bot
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.io.InputStream import kotlinx.io.InputStream
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input import kotlinx.io.core.Input
......
...@@ -14,8 +14,9 @@ ...@@ -14,8 +14,9 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.io.ByteReadChannel
import io.ktor.utils.io.readAvailable import kotlinx.coroutines.io.ByteWriteChannel
import kotlinx.coroutines.io.readAvailable
import kotlinx.io.OutputStream import kotlinx.io.OutputStream
import kotlinx.io.core.Output import kotlinx.io.core.Output
import kotlinx.io.pool.useInstance import kotlinx.io.pool.useInstance
...@@ -120,6 +121,7 @@ suspend fun ByteReadChannel.copyAndClose(dst: Output) { ...@@ -120,6 +121,7 @@ suspend fun ByteReadChannel.copyAndClose(dst: Output) {
* 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst] * 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
*/ */
suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) { suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) {
@Suppress("DuplicatedCode")
try { try {
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
ByteArrayPool.useInstance { buffer -> ByteArrayPool.useInstance { buffer ->
...@@ -130,7 +132,26 @@ suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) { ...@@ -130,7 +132,26 @@ suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) {
} }
} finally { } finally {
@Suppress("DuplicatedCode") @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 @@ ...@@ -9,11 +9,11 @@
package net.mamoe.mirai.utils.io package net.mamoe.mirai.utils.io
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.io.InputStream import kotlinx.io.InputStream
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input import kotlinx.io.core.Input
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
package net.mamoe.mirai package net.mamoe.mirai
import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult import net.mamoe.mirai.data.AddFriendResult
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import io.ktor.utils.io.core.Input
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.io.core.Input
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.JavaHappyAPI import net.mamoe.mirai.JavaHappyAPI
import net.mamoe.mirai.event.events.* import net.mamoe.mirai.event.events.*
......
...@@ -11,9 +11,9 @@ ...@@ -11,9 +11,9 @@
package net.mamoe.mirai.message package net.mamoe.mirai.message
import io.ktor.utils.io.ByteWriteChannel import kotlinx.coroutines.io.ByteWriteChannel
import kotlinx.io.core.Input import kotlinx.io.core.Input
import io.ktor.utils.io.core.Output import kotlinx.io.core.Output
import kotlinx.io.core.use import kotlinx.io.core.use
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
......
...@@ -37,28 +37,32 @@ import java.net.URL ...@@ -37,28 +37,32 @@ import java.net.URL
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 中将文件作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
...@@ -79,28 +83,32 @@ suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> { ...@@ -79,28 +83,32 @@ suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传后构造 [Image]
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传后构造 [Image]
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传后构造 [Image]
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] * 在 [Dispatchers.IO] 中将文件作为图片上传后构造 [Image]
...@@ -121,7 +129,8 @@ suspend fun File.uploadAsImage(contact: Contact): OfflineImage { ...@@ -121,7 +129,8 @@ suspend fun File.uploadAsImage(contact: Contact): OfflineImage {
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 到临时文件并将其作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
...@@ -142,7 +151,8 @@ suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt< ...@@ -142,7 +151,8 @@ suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt<
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @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] 中将文件作为图片发送到指定联系人 * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.io.core.Input 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