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