Commit 8de1fe91 authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

parents c92e3858 b8d6e420
...@@ -25,12 +25,12 @@ data class QQDTO( ...@@ -25,12 +25,12 @@ data class QQDTO(
@Serializable @Serializable
data class MemberDTO( data class MemberDTO(
override val id: Long, override val id: Long,
val memberName: String = "", val memberName: String,
val permission: MemberPermission, val permission: MemberPermission,
val group: GroupDTO val group: GroupDTO
) : ContactDTO() { ) : ContactDTO() {
constructor(member: Member, name: String = "") : this ( constructor(member: Member) : this (
member.id, name, member.permission, GroupDTO(member.group) member.id, member.groupCard, member.permission, GroupDTO(member.group)
) )
} }
......
...@@ -31,13 +31,13 @@ data class UnKnownMessagePacketDTO(val msg: String) : MessagePacketDTO() ...@@ -31,13 +31,13 @@ data class UnKnownMessagePacketDTO(val msg: String) : MessagePacketDTO()
data class AtDTO(val target: Long, val display: String) : MessageDTO() data class AtDTO(val target: Long, val display: String) : MessageDTO()
@Serializable @Serializable
@SerialName("Face") @SerialName("Face")
data class FaceDTO(val faceID: Int) : MessageDTO() data class FaceDTO(val faceId: Int) : MessageDTO()
@Serializable @Serializable
@SerialName("Plain") @SerialName("Plain")
data class PlainDTO(val text: String) : MessageDTO() data class PlainDTO(val text: String) : MessageDTO()
@Serializable @Serializable
@SerialName("Image") @SerialName("Image")
data class ImageDTO(val path: String) : MessageDTO() data class ImageDTO(val imageId: String) : MessageDTO()
@Serializable @Serializable
@SerialName("Xml") @SerialName("Xml")
data class XmlDTO(val xml: String) : MessageDTO() data class XmlDTO(val xml: String) : MessageDTO()
...@@ -64,7 +64,7 @@ sealed class MessageDTO : DTO ...@@ -64,7 +64,7 @@ sealed class MessageDTO : DTO
*/ */
suspend fun MessagePacket<*, *>.toDTO(): MessagePacketDTO = when (this) { suspend fun MessagePacket<*, *>.toDTO(): MessagePacketDTO = when (this) {
is FriendMessage -> FriendMessagePacketDTO(QQDTO(sender)) is FriendMessage -> FriendMessagePacketDTO(QQDTO(sender))
is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender, senderName)) is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender))
else -> UnKnownMessagePacketDTO("UnKnown Message Packet") else -> UnKnownMessagePacketDTO("UnKnown Message Packet")
}.apply { messageChain = Array(message.size){ message[it].toDTO() }} }.apply { messageChain = Array(message.size){ message[it].toDTO() }}
...@@ -76,7 +76,7 @@ fun Message.toDTO() = when (this) { ...@@ -76,7 +76,7 @@ fun Message.toDTO() = when (this) {
is At -> AtDTO(target, display) is At -> AtDTO(target, display)
is Face -> FaceDTO(id.value.toInt()) is Face -> FaceDTO(id.value.toInt())
is PlainText -> PlainDTO(stringValue) is PlainText -> PlainDTO(stringValue)
is Image -> ImageDTO(this.toString()) is Image -> ImageDTO(imageId)
is XMLMessage -> XmlDTO(stringValue) is XMLMessage -> XmlDTO(stringValue)
else -> UnknownMessageDTO("未知消息类型") else -> UnknownMessageDTO("未知消息类型")
} }
...@@ -84,9 +84,9 @@ fun Message.toDTO() = when (this) { ...@@ -84,9 +84,9 @@ fun Message.toDTO() = when (this) {
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) @UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
fun MessageDTO.toMessage() = when (this) { fun MessageDTO.toMessage() = when (this) {
is AtDTO -> At(target, display) is AtDTO -> At(target, display)
is FaceDTO -> Face(FaceId(faceID.toUByte())) is FaceDTO -> Face(FaceId(faceId.toUByte()))
is PlainDTO -> PlainText(text) is PlainDTO -> PlainText(text)
is ImageDTO -> PlainText("[暂时不支持图片]") is ImageDTO -> Image(imageId)
is XmlDTO -> XMLMessage(xml) is XmlDTO -> XMLMessage(xml)
is UnknownMessageDTO -> PlainText("assert cannot reach") is UnknownMessageDTO -> PlainText("assert cannot reach")
} }
......
...@@ -4,6 +4,7 @@ import kotlinx.serialization.Serializable ...@@ -4,6 +4,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import net.mamoe.mirai.api.http.AuthedSession import net.mamoe.mirai.api.http.AuthedSession
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
@Serializable @Serializable
abstract class VerifyDTO : DTO { abstract class VerifyDTO : DTO {
...@@ -60,6 +61,22 @@ data class GroupInfoDTO( ...@@ -60,6 +61,22 @@ data class GroupInfoDTO(
) )
} }
@Serializable
data class MemberConfigDTO(
override val sessionKey: String,
val target: Long,
val memberId: Long,
val config: MemberInfoDTO
) : VerifyDTO()
@Serializable
data class MemberInfoDTO(
val name: String? = null,
val specialTitle: String? = null
) : DTO {
constructor(member: Member) : this(member.groupCard, member.specialTitle)
}
@Serializable @Serializable
open class StateCode(val code: Int, var msg: String) { open class StateCode(val code: Int, var msg: String) {
object Success : StateCode(0, "success") // 成功 object Success : StateCode(0, "success") // 成功
......
...@@ -3,17 +3,14 @@ package net.mamoe.mirai.api.http.route ...@@ -3,17 +3,14 @@ package net.mamoe.mirai.api.http.route
import io.ktor.application.Application import io.ktor.application.Application
import io.ktor.application.call import io.ktor.application.call
import io.ktor.routing.routing import io.ktor.routing.routing
import net.mamoe.mirai.api.http.dto.GroupConfigDTO import net.mamoe.mirai.api.http.dto.*
import net.mamoe.mirai.api.http.dto.GroupInfoDTO
import net.mamoe.mirai.api.http.dto.MuteDTO
import net.mamoe.mirai.api.http.dto.StateCode
fun Application.groupManageModule() { fun Application.groupManageModule() {
routing { routing {
/** /**
* 禁言 * 禁言(需要相关权限)
*/ */
miraiVerify<MuteDTO>("/muteAll") { miraiVerify<MuteDTO>("/muteAll") {
it.session.bot.getGroup(it.target).muteAll = true it.session.bot.getGroup(it.target).muteAll = true
...@@ -61,5 +58,22 @@ fun Application.groupManageModule() { ...@@ -61,5 +58,22 @@ fun Application.groupManageModule() {
call.respondStateCode(StateCode.Success) call.respondStateCode(StateCode.Success)
} }
/**
* 群员信息管理(需要相关权限)
*/
miraiGet("/memberInfo") {
val member = it.bot.getGroup(paramOrNull("target"))[paramOrNull("memberID")]
call.respondDTO(MemberInfoDTO(member))
}
miraiVerify<MemberConfigDTO>("/memberInfo") { dto ->
val member = dto.session.bot.getGroup(dto.target)[dto.memberId]
with(dto.config) {
name?.let { member.groupCard = it }
specialTitle?.let { member.specialTitle = it }
}
call.respondStateCode(StateCode.Success)
}
} }
} }
\ No newline at end of file
...@@ -12,7 +12,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI ...@@ -12,7 +12,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
*/ */
actual object QQAndroid : BotFactory { actual object QQAndroid : BotFactory {
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot { actual override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot {
return QQAndroidBot(context, BotAccount(qq, password), configuration) return QQAndroidBot(context, BotAccount(qq, password), configuration)
} }
} }
\ No newline at end of file
package net.mamoe.mirai.qqandroid package net.mamoe.mirai.qqandroid
import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotFactory import net.mamoe.mirai.BotFactory
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.Context
/** /**
* QQ for Android * QQ for Android
*/ */
expect object QQAndroid : BotFactory expect object QQAndroid : BotFactory {
\ No newline at end of file
/**
* 使用指定的 [配置][configuration] 构造 [Bot] 实例
*/
override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot
}
\ No newline at end of file
...@@ -17,6 +17,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn ...@@ -17,6 +17,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.toUHexString
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.properties.Delegates import kotlin.properties.Delegates
...@@ -50,7 +51,7 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin ...@@ -50,7 +51,7 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
} }
} }
override suspend fun uploadImage(image: ExternalImage): Image { override suspend fun uploadImage(image: ExternalImage): Image = try {
bot.network.run { bot.network.run {
val response = LongConn.OffPicUp( val response = LongConn.OffPicUp(
bot.client, Cmd0x352.TryUpImgReq( bot.client, Cmd0x352.TryUpImgReq(
...@@ -59,12 +60,11 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin ...@@ -59,12 +60,11 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
fileId = 0, fileId = 0,
fileMd5 = image.md5, fileMd5 = image.md5,
fileSize = image.inputSize.toInt(), fileSize = image.inputSize.toInt(),
fileName = image.filename, fileName = image.md5.toUHexString("") + "." + image.format,
imgOriginal = 1, imgOriginal = 1,
imgWidth = image.width, imgWidth = image.width,
imgHeight = image.height, imgHeight = image.height,
imgType = image.imageType, imgType = image.imageType
buType = 1
) )
).sendAndExpect<LongConn.OffPicUp.Response>() ).sendAndExpect<LongConn.OffPicUp.Response>()
...@@ -80,9 +80,8 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin ...@@ -80,9 +80,8 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
is LongConn.OffPicUp.Response.RequireUpload -> { is LongConn.OffPicUp.Response.RequireUpload -> {
HighwayHelper.uploadImage( HighwayHelper.uploadImage(
client = bot.client, client = bot.client,
uin = bot.uin, serverIp = response.serverIp[0].toIpV4AddressString(),
serverIp = response.serverIp[2].toIpV4AddressString(), serverPort = response.serverPort[0],
serverPort = response.serverPort[2],
imageInput = image.input, imageInput = image.input,
inputSize = image.inputSize.toInt(), inputSize = image.inputSize.toInt(),
md5 = image.md5, md5 = image.md5,
...@@ -102,6 +101,8 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin ...@@ -102,6 +101,8 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
is LongConn.OffPicUp.Response.Failed -> error(response.message) is LongConn.OffPicUp.Response.Failed -> error(response.message)
} }
} }
} finally {
image.input.close()
} }
override suspend fun queryProfile(): Profile { override suspend fun queryProfile(): Profile {
...@@ -332,6 +333,7 @@ internal class GroupImpl( ...@@ -332,6 +333,7 @@ internal class GroupImpl(
override var botPermission: MemberPermission = MemberPermission.MEMBER override var botPermission: MemberPermission = MemberPermission.MEMBER
override suspend fun quit(): Boolean { override suspend fun quit(): Boolean {
check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" }
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
} }
...@@ -363,7 +365,7 @@ internal class GroupImpl( ...@@ -363,7 +365,7 @@ internal class GroupImpl(
} }
} }
override suspend fun uploadImage(image: ExternalImage): Image { override suspend fun uploadImage(image: ExternalImage): Image = try {
bot.network.run { bot.network.run {
val response: ImgStore.GroupPicUp.Response = ImgStore.GroupPicUp( val response: ImgStore.GroupPicUp.Response = ImgStore.GroupPicUp(
bot.client, bot.client,
...@@ -401,7 +403,6 @@ internal class GroupImpl( ...@@ -401,7 +403,6 @@ internal class GroupImpl(
HighwayHelper.uploadImage( HighwayHelper.uploadImage(
client = bot.client, client = bot.client,
uin = bot.uin,
serverIp = response.uploadIpList.first().toIpV4AddressString(), serverIp = response.uploadIpList.first().toIpV4AddressString(),
serverPort = response.uploadPortList.first(), serverPort = response.uploadPortList.first(),
imageInput = image.input, imageInput = image.input,
...@@ -444,6 +445,8 @@ internal class GroupImpl( ...@@ -444,6 +445,8 @@ internal class GroupImpl(
} }
} }
} }
} finally {
image.input.close()
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
......
...@@ -110,6 +110,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -110,6 +110,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
override suspend fun init() { override suspend fun init() {
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> { this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
if (this@QQAndroidBotNetworkHandler.bot == this.bot) { if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
this.bot.logger.error("被挤下线")
close() close()
} }
} }
......
...@@ -104,8 +104,11 @@ internal open class QQAndroidClient( ...@@ -104,8 +104,11 @@ internal open class QQAndroidClient(
private val requestPacketRequestId: AtomicInt = atomic(1921334513) private val requestPacketRequestId: AtomicInt = atomic(1921334513)
internal fun nextRequestPacketRequestId(): Int = requestPacketRequestId.getAndAdd(2) internal fun nextRequestPacketRequestId(): Int = requestPacketRequestId.getAndAdd(2)
private val highwayDataTransSequenceId: AtomicInt = atomic(87017) private val highwayDataTransSequenceIdForGroup: AtomicInt = atomic(87017)
internal fun nextHighwayDataTransSequenceId(): Int = highwayDataTransSequenceId.getAndAdd(2) internal fun nextHighwayDataTransSequenceIdForGroup(): Int = highwayDataTransSequenceIdForGroup.getAndAdd(2)
private val highwayDataTransSequenceIdForFriend: AtomicInt = atomic(40717)
internal fun nextHighwayDataTransSequenceIdForFriend(): Int = highwayDataTransSequenceIdForFriend.getAndAdd(2)
val appClientVersion: Int = 0 val appClientVersion: Int = 0
......
...@@ -69,7 +69,7 @@ object Highway { ...@@ -69,7 +69,7 @@ object Highway {
sequenceId: Int, sequenceId: Int,
appId: Int = 537062845, appId: Int = 537062845,
dataFlag: Int = 4096, dataFlag: Int = 4096,
commandId: Int = 2, commandId: Int,
localId: Int = 2052, localId: Int = 2052,
uKey: ByteArray, uKey: ByteArray,
...@@ -78,6 +78,9 @@ object Highway { ...@@ -78,6 +78,9 @@ object Highway {
md5: ByteArray md5: ByteArray
): ByteReadPacket { ): ByteReadPacket {
require(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" } require(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" }
require(data !is ByteReadPacket || data.remaining.toInt() == dataSize) { "bad input. given dataSize=$dataSize, but actual readRemaining=${(data as ByteReadPacket).remaining}" }
require(data !is IoBuffer || data.readRemaining == dataSize) { "bad input. given dataSize=$dataSize, but actual readRemaining=${(data as IoBuffer).readRemaining}" }
val dataHighwayHead = CSDataHighwayHead.DataHighwayHead( val dataHighwayHead = CSDataHighwayHead.DataHighwayHead(
version = 1, version = 1,
uin = uin.toString(), uin = uin.toString(),
...@@ -91,7 +94,7 @@ object Highway { ...@@ -91,7 +94,7 @@ object Highway {
) )
val segHead = CSDataHighwayHead.SegHead( val segHead = CSDataHighwayHead.SegHead(
datalength = dataSize, datalength = dataSize,
filesize = dataSize.toLong() and 0xFFffFFff, filesize = dataSize.toLong(),
serviceticket = uKey, serviceticket = uKey,
md5 = md5, md5 = md5,
fileMd5 = md5, fileMd5 = md5,
......
...@@ -15,7 +15,6 @@ internal object HighwayHelper { ...@@ -15,7 +15,6 @@ internal object HighwayHelper {
suspend fun uploadImage( suspend fun uploadImage(
client: QQAndroidClient, client: QQAndroidClient,
uin: Long,
serverIp: String, serverIp: String,
serverPort: Int, serverPort: Int,
uKey: ByteArray, uKey: ByteArray,
...@@ -24,14 +23,20 @@ internal object HighwayHelper { ...@@ -24,14 +23,20 @@ internal object HighwayHelper {
md5: ByteArray, md5: ByteArray,
commandId: Int // group=2, friend=1 commandId: Int // group=2, friend=1
) { ) {
require(md5.size == 16) { "bad md5. Required size=16, got ${md5.size}" }
require(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" }
require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" }
val socket = PlatformSocket() val socket = PlatformSocket()
socket.connect(serverIp, serverPort) socket.connect(serverIp, serverPort)
socket.use { socket.use {
socket.send( socket.send(
Highway.RequestDataTrans( Highway.RequestDataTrans(
uin = uin, uin = client.uin,
command = "PicUp.DataUp", command = "PicUp.DataUp",
sequenceId = client.nextHighwayDataTransSequenceId(), sequenceId =
if (commandId == 2) client.nextHighwayDataTransSequenceIdForGroup()
else client.nextHighwayDataTransSequenceIdForFriend(),
uKey = uKey, uKey = uKey,
data = imageInput, data = imageInput,
dataSize = inputSize, dataSize = inputSize,
......
...@@ -91,7 +91,7 @@ internal class Cmd0x352 : ProtoBuf { ...@@ -91,7 +91,7 @@ internal class Cmd0x352 : ProtoBuf {
@SerialId(2) val msgTryupImgReq: List<TryUpImgReq>? = null,// optional @SerialId(2) val msgTryupImgReq: List<TryUpImgReq>? = null,// optional
@SerialId(3) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,// optional @SerialId(3) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,// optional
@SerialId(4) val msgDelImgReq: List<DelImgReq>? = null, @SerialId(4) val msgDelImgReq: List<DelImgReq>? = null,
@SerialId(10) val netType: Int = 0// 数据网络=5 @SerialId(10) val netType: Int = 3// 数据网络=5
) : ProtoBuf ) : ProtoBuf
@Serializable @Serializable
...@@ -117,11 +117,10 @@ internal class Cmd0x352 : ProtoBuf { ...@@ -117,11 +117,10 @@ internal class Cmd0x352 : ProtoBuf {
@SerialId(9) val innerIP: Int = 0, @SerialId(9) val innerIP: Int = 0,
@SerialId(10) val addressBook: Int = 0,//chatType == 1006为1 我觉得发0没问题 @SerialId(10) val addressBook: Int = 0,//chatType == 1006为1 我觉得发0没问题
@SerialId(11) val retry: Int = 0,//default @SerialId(11) val retry: Int = 0,//default
@SerialId(12) val buType: Int,//1或96 不确定 @SerialId(12) val buType: Int = 1,//1或96 不确定
@SerialId(13) val imgOriginal: Int,//是否为原图 @SerialId(13) val imgOriginal: Int,//是否为原图
@SerialId(14) val imgWidth: Int, @SerialId(14) val imgWidth: Int,
@SerialId(15) val imgHeight: Int, @SerialId(15) val imgHeight: Int,
@SerialId(16) val imgType: Int = 1000,
/** /**
* ImgType: * ImgType:
* JPG: 1000 * JPG: 1000
...@@ -131,7 +130,8 @@ internal class Cmd0x352 : ProtoBuf { ...@@ -131,7 +130,8 @@ internal class Cmd0x352 : ProtoBuf {
* GIG: 2000 * GIG: 2000
* APNG: 2001 * APNG: 2001
* SHARPP: 1004 * SHARPP: 1004
* */ */
@SerialId(16) val imgType: Int = 1000,
@SerialId(17) val buildVer: String = "8.2.0.1296",//版本号 @SerialId(17) val buildVer: String = "8.2.0.1296",//版本号
@SerialId(18) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,//default @SerialId(18) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,//default
@SerialId(19) val fileStoreDays: Int = 0,//default @SerialId(19) val fileStoreDays: Int = 0,//default
......
...@@ -15,7 +15,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI ...@@ -15,7 +15,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
actual object QQAndroid : BotFactory { actual object QQAndroid : BotFactory {
override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot { actual override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot {
return QQAndroidBot(context, BotAccount(qq, password), configuration) return QQAndroidBot(context, BotAccount(qq, password), configuration)
} }
......
...@@ -85,7 +85,7 @@ kotlin { ...@@ -85,7 +85,7 @@ kotlin {
api(ktor("network", ktorVersion)) api(ktor("network", ktorVersion))
//implementation("io.ktor:ktor-io:1.3.0-beta-1") //implementation("io.ktor:ktor-io:1.3.0-beta-1")
runtimeOnly(files("build/classes/kotlin/metadata/main")) // classpath is not properly set by IDE //runtimeOnly(files("build/classes/kotlin/metadata/main")) // classpath is not properly set by IDE
} }
} }
commonTest { commonTest {
...@@ -93,7 +93,7 @@ kotlin { ...@@ -93,7 +93,7 @@ kotlin {
api(kotlin("test-annotations-common")) api(kotlin("test-annotations-common"))
api(kotlin("test-common")) api(kotlin("test-common"))
runtimeOnly(files("build/classes/kotlin/metadata/test")) // classpath is not properly set by IDE //runtimeOnly(files("build/classes/kotlin/metadata/test")) // classpath is not properly set by IDE
} }
} }
......
...@@ -3,8 +3,6 @@ package net.mamoe.mirai.utils ...@@ -3,8 +3,6 @@ package net.mamoe.mirai.utils
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.CIO
import io.ktor.util.KtorExperimentalAPI import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.io.pool.useInstance import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.io.ByteArrayPool import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
...@@ -13,16 +11,9 @@ import java.io.EOFException ...@@ -13,16 +11,9 @@ import java.io.EOFException
import java.io.InputStream import java.io.InputStream
import java.net.InetAddress import java.net.InetAddress
import java.security.MessageDigest import java.security.MessageDigest
import java.util.concurrent.Executors
import java.util.zip.CRC32
import java.util.zip.Inflater import java.util.zip.Inflater
/**
* 设备名
*/
actual val deviceName: String get() = InetAddress.getLocalHost().hostName
/** /**
* Ktor HttpClient. 不同平台使用不同引擎. * Ktor HttpClient. 不同平台使用不同引擎.
*/ */
...@@ -75,16 +66,6 @@ private inline fun InputStream.readInSequence(block: (Int) -> Unit) { ...@@ -75,16 +66,6 @@ private inline fun InputStream.readInSequence(block: (Int) -> Unit) {
} }
} }
/**
* CRC32 算法
*/
actual fun crc32(key: ByteArray): Int = CRC32().apply { update(key) }.value.toInt()
/**
* hostname 解析 ipv4
*/
actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray {
this.checkOffsetAndLength(offset, length) this.checkOffsetAndLength(offset, length)
if (length == 0) return ByteArray(0) if (length == 0) return ByteArray(0)
...@@ -104,6 +85,3 @@ actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { ...@@ -104,6 +85,3 @@ actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray {
} }
} }
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {
return Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher()
}
\ No newline at end of file
...@@ -64,14 +64,14 @@ private fun calculateImageMd5ByImageId(imageId: String): ByteArray { ...@@ -64,14 +64,14 @@ private fun calculateImageMd5ByImageId(imageId: String): ByteArray {
return if (imageId.startsWith('/')) { return if (imageId.startsWith('/')) {
imageId imageId
.drop(1) .drop(1)
.replace('-', ' ') .replace("-", "")
.take(16 * 2) .take(16 * 2)
.chunkedHexToBytes() .chunkedHexToBytes()
} else { } else {
imageId imageId
.substringAfter('{') .substringAfter('{')
.substringBefore('}') .substringBefore('}')
.replace('-', ' ') .replace("-", "")
.chunkedHexToBytes() .chunkedHexToBytes()
} }
} }
......
...@@ -29,7 +29,7 @@ class ExternalImage( ...@@ -29,7 +29,7 @@ class ExternalImage(
val filename: String val filename: String
) { ) {
init { init {
check(inputSize in Int.MIN_VALUE.toLong()..Int.MAX_VALUE.toLong()) { "file is too big" } check(inputSize in 0L..Int.MAX_VALUE.toLong()) { "file is too big" }
} }
companion object { companion object {
...@@ -43,13 +43,13 @@ class ExternalImage( ...@@ -43,13 +43,13 @@ class ExternalImage(
): ExternalImage = ExternalImage(width, height, md5, format, data, data.remaining, filename) ): ExternalImage = ExternalImage(width, height, md5, format, data, data.remaining, filename)
} }
private val format: String = when (val it = imageFormat.toLowerCase()) { val format: String =
"jpeg" -> "jpg" //必须转换 when (val it = imageFormat.toLowerCase()) {
else -> it "jpeg" -> "jpg" //必须转换
} else -> it
}
/** /**
*
* ImgType: * ImgType:
* JPG: 1000 * JPG: 1000
* PNG: 1001 * PNG: 1001
......
...@@ -14,16 +14,6 @@ inline val currentTimeMillis: Long get() = GMTDate().timestamp ...@@ -14,16 +14,6 @@ inline val currentTimeMillis: Long get() = GMTDate().timestamp
inline val currentTimeSeconds: Long get() = currentTimeMillis / 1000 inline val currentTimeSeconds: Long get() = currentTimeMillis / 1000
/**
* 设备名
*/
expect val deviceName: String
/**
* CRC32 算法
*/
expect fun crc32(key: ByteArray): Int
/** /**
* 解 zip 压缩 * 解 zip 压缩
...@@ -39,11 +29,6 @@ expect fun md5(byteArray: ByteArray): ByteArray ...@@ -39,11 +29,6 @@ expect fun md5(byteArray: ByteArray): ByteArray
inline fun md5(str: String): ByteArray = md5(str.toByteArray()) inline fun md5(str: String): ByteArray = md5(str.toByteArray())
/**
* hostname 解析 ipv4
*/
expect fun solveIpAddress(hostname: String): String
/** /**
* Localhost 解析 * Localhost 解析
*/ */
...@@ -54,8 +39,6 @@ expect fun localIpAddress(): String ...@@ -54,8 +39,6 @@ expect fun localIpAddress(): String
*/ */
expect val Http: HttpClient expect val Http: HttpClient
expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher
internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) { internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) {
require(offset >= 0) { "offset shouldn't be negative: $offset" } require(offset >= 0) { "offset shouldn't be negative: $offset" }
require(length >= 0) { "length shouldn't be negative: $length" } require(length >= 0) { "length shouldn't be negative: $length" }
......
...@@ -44,7 +44,7 @@ fun BufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage { ...@@ -44,7 +44,7 @@ fun BufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage {
}) })
} }
return ExternalImage(width, height, digest.digest(), formatName, buffer, getRandomString(10) + "." + formatName) return ExternalImage(width, height, digest.digest(), formatName, buffer, getRandomString(16) + "." + formatName)
} }
suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() } suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() }
...@@ -102,8 +102,8 @@ suspend inline fun URL.suspendToExternalImage(): ExternalImage = withContext(IO) ...@@ -102,8 +102,8 @@ suspend inline fun URL.suspendToExternalImage(): ExternalImage = withContext(IO)
@Throws(IOException::class) @Throws(IOException::class)
fun InputStream.toExternalImage(): ExternalImage { fun InputStream.toExternalImage(): ExternalImage {
val file = createTempFile().apply { deleteOnExit() } val file = createTempFile().apply { deleteOnExit() }
file.outputStream().asOutput().use { file.outputStream().use {
this.asInput().copyTo(it) this.copyTo(it)
} }
this.close() this.close()
return file.toExternalImage() return file.toExternalImage()
......
...@@ -4,21 +4,13 @@ package net.mamoe.mirai.utils ...@@ -4,21 +4,13 @@ package net.mamoe.mirai.utils
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.CIO
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.io.pool.useInstance import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.io.ByteArrayPool import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.* import java.io.*
import java.net.InetAddress import java.net.InetAddress
import java.security.MessageDigest import java.security.MessageDigest
import java.util.concurrent.Executors
import java.util.zip.CRC32
import java.util.zip.Inflater import java.util.zip.Inflater
actual val deviceName: String = InetAddress.getLocalHost().hostName
actual fun crc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
actual fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray) actual fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
fun InputStream.md5(): ByteArray = this.use { fun InputStream.md5(): ByteArray = this.use {
...@@ -51,8 +43,6 @@ fun DataInput.md5(): ByteArray { ...@@ -51,8 +43,6 @@ fun DataInput.md5(): ByteArray {
return digest.digest() return digest.digest()
} }
actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
actual val Http: HttpClient get() = HttpClient(CIO) actual val Http: HttpClient get() = HttpClient(CIO)
...@@ -75,7 +65,3 @@ actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { ...@@ -75,7 +65,3 @@ actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray {
return output.toByteArray() return output.toByteArray()
} }
} }
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {
return Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher()
}
\ No newline at end of file
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