Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
This commit is contained in:
Him188 2020-05-09 15:38:20 +08:00
commit 1c1a37a103
14 changed files with 121 additions and 24 deletions

View File

@ -7,7 +7,7 @@ assignees: ''
--- ---
## 问题
<!--在这里简略描述你遇到的问题--> <!--在这里简略描述你遇到的问题-->
@ -17,14 +17,18 @@ assignees: ''
<!--如果有控制台报错,请尽量在这里附加全面的日志. (不建议截图)--> <!--如果有控制台报错,请尽量在这里附加全面的日志. (不建议截图)-->
``` ```
## 如何复现 ### 如何复现
<!--在这里简略说明如何让这个问题再次发生--> <!--在这里简略说明如何让这个问题再次发生-->
<!--可使用 1. 2. 3. 的列表格式,或其他任意恰当的格式--> <!--可使用 1. 2. 3. 的列表格式,或其他任意恰当的格式-->
### 版本
mirai: `` <!--在``中填写你正在使用的版本号如0.40.0-->
<!--如有必要,你可以在下文继续添加其他信息--> <!--如有必要,你可以在下文继续添加其他信息-->

View File

@ -18,6 +18,7 @@ buildscript {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.Kotlin.stdlib}") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.Kotlin.stdlib}")
classpath("org.jetbrains.kotlin:kotlin-serialization:${Versions.Kotlin.stdlib}") classpath("org.jetbrains.kotlin:kotlin-serialization:${Versions.Kotlin.stdlib}")
classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${Versions.Kotlin.atomicFU}") classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${Versions.Kotlin.atomicFU}")
classpath("org.jetbrains.kotlinx:binary-compatibility-validator:${Versions.Kotlin.binaryValidator}")
} }
} }
@ -26,6 +27,10 @@ plugins {
// id("com.jfrog.bintray") version Versions.Publishing.bintray apply false // id("com.jfrog.bintray") version Versions.Publishing.bintray apply false
} }
// https://github.com/kotlin/binary-compatibility-validator
apply(plugin = "binary-compatibility-validator")
project.ext.set("isAndroidSDKAvailable", false) project.ext.set("isAndroidSDKAvailable", false)
// until // until
@ -139,17 +144,20 @@ subprojects {
} }
fun Project.findLatestFile(): Map.Entry<String, File>? { fun Project.findLatestFile(): Map.Entry<String, File> {
return File(projectDir, "build/libs").walk() return File(projectDir, "build/libs").walk()
.filter { it.isFile } .filter { it.isFile }
.onEach { println("all files=$it") } .onEach { println("all files=$it") }
.filter { it.name.matches(Regex("""${project.name}-([0-9]|\.)*\.jar""")) } .filter { it.name.matches(Regex("""${project.name}-[0-9][0-9]*(\.[0-9]*)*.*\.jar""")) }
.onEach { println("matched file: ${it.name}") } .onEach { println("matched file: ${it.name}") }
.associateBy { it.nameWithoutExtension.substringAfterLast('-') } .associateBy { it.nameWithoutExtension.substringAfterLast('-') }
.onEach { println("versions: $it") } .onEach { println("versions: $it") }
.maxBy { .maxBy { (version, file) ->
it.key.split('.').foldRightIndexed(0) { index: Int, s: String, acc: Int -> version.split('.').let {
acc + 100.0.pow(2 - index).toInt() * (s.toIntOrNull() ?: 0) if (it.size == 2) it + "0"
else it
}.reversed().foldIndexed(0) { index: Int, acc: Int, s: String ->
acc + 100.0.pow(index).toInt() * (s.toIntOrNull() ?: 0)
} }
} } ?: error("cannot find any file to upload")
} }

View File

@ -18,6 +18,7 @@ object Versions {
const val atomicFU = "0.14.2" const val atomicFU = "0.14.2"
const val serialization = "0.20.0" const val serialization = "0.20.0"
const val ktor = "1.3.2" const val ktor = "1.3.2"
const val binaryValidator = "0.2.3"
const val io = "0.1.16" const val io = "0.1.16"
const val coroutinesIo = "0.1.16" const val coroutinesIo = "0.1.16"

View File

@ -0,0 +1,13 @@
apiValidation {
ignoredPackages += [
"net.mamoe.mirai.event.internal",
"net.mamoe.mirai.utils.internal"
]
ignoredPackages += [
"net.mamoe.mirai.qqandroid.contact",
"net.mamoe.mirai.qqandroid.message",
"net.mamoe.mirai.qqandroid.network",
"net.mamoe.mirai.qqandroid.utils"
]
}

View File

@ -504,8 +504,8 @@ internal class MsgSvc : ProtoBuf {
@Serializable @Serializable
internal class SecretFileHead( internal class SecretFileHead(
@ProtoId(1) @JvmField val secretFileMsg: SubMsgType0xc1.MsgBody? = null, @ProtoId(1) @JvmField val secretFileMsg: SubMsgType0xc1.MsgBody? = null
@ProtoId(2) @JvmField val secretFileStatus: SubMsgType0x1a.MsgBody? = null // @ProtoId(2) @JvmField val secretFileStatus: SubMsgType0x1a.MsgBody? = null
) )
@Serializable @Serializable
@ -815,6 +815,7 @@ internal class SubMsgType0xc1 {
) : ProtoBuf ) : ProtoBuf
} }
/*
@Serializable @Serializable
internal class SubMsgType0x1a { internal class SubMsgType0x1a {
@Serializable @Serializable
@ -830,4 +831,4 @@ internal class SubMsgType0x1a {
@ProtoId(9) @JvmField val fromUin: Long = 0L, @ProtoId(9) @JvmField val fromUin: Long = 0L,
@ProtoId(10) @JvmField val toUin: Long = 0L @ProtoId(10) @JvmField val toUin: Long = 0L
) : ProtoBuf ) : ProtoBuf
} }*/

View File

@ -515,6 +515,7 @@ internal class Submsgtype0x129 {
} }
/*
internal class Submsgtype0x1a { internal class Submsgtype0x1a {
internal class SubMsgType0x1a : ProtoBuf { internal class SubMsgType0x1a : ProtoBuf {
@Serializable @Serializable
@ -531,7 +532,7 @@ internal class Submsgtype0x1a {
@ProtoId(10) @JvmField val toUin: Long = 0L @ProtoId(10) @JvmField val toUin: Long = 0L
) : ProtoBuf ) : ProtoBuf
} }
} }*/
internal class Submsgtype0x26 { internal class Submsgtype0x26 {
@ -2730,6 +2731,7 @@ internal class Submsgtype0xbe {
} }
/*
internal class Submsgtype0xc1 { internal class Submsgtype0xc1 {
internal class Submsgtype0xc1 : ProtoBuf { internal class Submsgtype0xc1 : ProtoBuf {
@Serializable @Serializable
@ -2740,7 +2742,7 @@ internal class Submsgtype0xc1 {
) : ProtoBuf ) : ProtoBuf
} }
} }
*/
internal class Submsgtype0xc3 { internal class Submsgtype0xc3 {
internal class Submsgtype0xc3 : ProtoBuf { internal class Submsgtype0xc3 : ProtoBuf {

View File

@ -52,7 +52,7 @@ internal class NewContact {
struct.msgSeq, struct.msgSeq,
msgAdditional, msgAdditional,
struct.reqUin, struct.reqUin,
Group.calculateGroupUinByGroupCode(groupCode), groupCode,
reqUinNick reqUinNick
) )
} }
@ -143,7 +143,7 @@ internal class NewContact {
bot, bot,
struct.msgSeq, struct.msgSeq,
actionUin, actionUin,
Group.calculateGroupUinByGroupCode(groupCode), groupCode,
groupName, groupName,
actionUinNick actionUinNick
) )
@ -155,7 +155,7 @@ internal class NewContact {
struct.msgSeq, struct.msgSeq,
msgAdditional, msgAdditional,
struct.reqUin, struct.reqUin,
Group.calculateGroupUinByGroupCode(groupCode), groupCode,
groupName, groupName,
reqUinNick reqUinNick
) )
@ -185,7 +185,7 @@ internal class NewContact {
true -> 11 // accept true -> 11 // accept
false -> 12 // reject false -> 12 // reject
}, },
groupCode = Group.calculateGroupCodeByGroupUin(event.groupId), groupCode = event.groupId,
msg = "", msg = "",
remark = "", remark = "",
blacklist = blackList blacklist = blackList

View File

@ -13,6 +13,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import kotlinx.io.core.* import kotlinx.io.core.*
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.mamoe.mirai.Bot
import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.nameCardOrNick import net.mamoe.mirai.contact.nameCardOrNick
@ -401,13 +402,15 @@ internal class OnlinePush {
when { when {
pkg.authorUin == bot.id && operator.id == bot.id -> null pkg.authorUin == bot.id && operator.id == bot.id -> null
else -> { else -> {
MessageRecallEvent.GroupRecall(bot, MessageRecallEvent.GroupRecall(
bot,
pkg.authorUin, pkg.authorUin,
pkg.seq, pkg.seq,
pkg.msgRandom, pkg.msgRandom,
pkg.time, pkg.time,
operator, operator,
group) group
)
} }
} }
} }
@ -583,6 +586,10 @@ internal class OnlinePush {
} ?: emptySequence() } ?: emptySequence()
} }
fun Submsgtype0x27.SubMsgType0x27.ModCustomFace.transform(bot: QQAndroidBot): Sequence<Packet> =
sequenceOf(BotFaceChangedEvent(Bot.getInstance(uin)))
return@lambda528 vProtobuf.loadAs(Submsgtype0x27.SubMsgType0x27.MsgBody.serializer()).msgModInfos.asSequence() return@lambda528 vProtobuf.loadAs(Submsgtype0x27.SubMsgType0x27.MsgBody.serializer()).msgModInfos.asSequence()
.flatMap { .flatMap {
when { when {
@ -590,6 +597,7 @@ internal class OnlinePush {
it.msgDelFriend != null -> it.msgDelFriend.transform(bot) it.msgDelFriend != null -> it.msgDelFriend.transform(bot)
it.msgModGroupProfile != null -> it.msgModGroupProfile.transform(bot) it.msgModGroupProfile != null -> it.msgModGroupProfile.transform(bot)
it.msgModGroupMemberProfile != null -> it.msgModGroupMemberProfile.transform(bot) it.msgModGroupMemberProfile != null -> it.msgModGroupMemberProfile.transform(bot)
it.msgModCustomFace != null -> it.msgModCustomFace.transform(bot)
else -> { else -> {
bot.network.logger.debug { bot.network.logger.debug {
"Transformers528 0x27L: new data: ${it._miraiContentToString()}" "Transformers528 0x27L: new data: ${it._miraiContentToString()}"

View File

@ -24,6 +24,7 @@ import net.mamoe.mirai.network.closeAndJoin
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.internal.retryCatching import net.mamoe.mirai.utils.internal.retryCatching
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.time.Duration
import kotlin.time.ExperimentalTime import kotlin.time.ExperimentalTime
import kotlin.time.measureTime import kotlin.time.measureTime
@ -138,7 +139,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
reconnect() reconnect()
} }
logger.info { "Reconnected successfully in ${time.inMilliseconds} ms" } logger.info { "Reconnected successfully in ${time.asHumanReadable}" }
} }
is BotOfflineEvent.Active -> { is BotOfflineEvent.Active -> {
val msg = if (event.cause == null) { val msg = if (event.cause == null) {

View File

@ -287,7 +287,7 @@ open class MessageSubscribersBuilder<M : MessageEvent, out Ret, R : RR, RR>(
@MessageDsl @MessageDsl
fun atAll(): ListeningFilter = content { message.firstIsInstanceOrNull<AtAll>() != null } fun atAll(): ListeningFilter = content { message.firstIsInstanceOrNull<AtAll>() != null }
/** [消息内容][Message.contentToString]包含 [AtAll] */ /** [消息内容][Message.contentToString]包含目标为 [target] 的 [At] */
@MessageDsl @MessageDsl
fun at(target: Long): ListeningFilter = content { message.firstIsInstanceOrNull<At>()?.target == target } fun at(target: Long): ListeningFilter = content { message.firstIsInstanceOrNull<At>()?.target == target }
@ -447,4 +447,4 @@ open class MessageSubscribersBuilder<M : MessageEvent, out Ret, R : RR, RR>(
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPE) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPE)
@DslMarker @DslMarker
annotation class MessageDsl annotation class MessageDsl

View File

@ -88,6 +88,13 @@ data class BotReloginEvent(
val cause: Throwable? val cause: Throwable?
) : BotEvent, BotActiveEvent, AbstractEvent() ) : BotEvent, BotActiveEvent, AbstractEvent()
/**
* [Bot] 头像被修改通过其他客户端修改了Bot的头像
*/
data class BotFaceChangedEvent(
override val bot: Bot
) : BotEvent, Packet, AbstractEvent()
// endregion // endregion
// region 消息 // region 消息

View File

@ -15,6 +15,10 @@ package net.mamoe.mirai.utils
import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
import kotlin.math.floor
import kotlin.time.Duration
import kotlin.time.DurationUnit
import kotlin.time.ExperimentalTime
/** /**
* 时间戳 * 时间戳
@ -76,4 +80,21 @@ inline val Int.weeksToSeconds: Long
@get:JvmSynthetic @get:JvmSynthetic
inline val Int.monthsToSeconds: Long inline val Int.monthsToSeconds: Long
get() = this * 30.daysToSeconds get() = this * 30.daysToSeconds
@ExperimentalTime
val Duration.asHumanReadable: String
get() {
val builder = StringBuilder()
val days = toInt(DurationUnit.DAYS)
val hours = toInt(DurationUnit.HOURS) % 24
val minutes = toInt(DurationUnit.MINUTES) % 60
val s = floor(toDouble(DurationUnit.SECONDS) % 60 * 1000) / 1000
with(builder) {
if (days != 0) append("${days}d ")
if (hours != 0) append("${hours}h ")
if (minutes != 0) append("${minutes}min ")
append("${s}s")
}
return builder.toString()
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.utils
import kotlin.test.Test
import kotlin.test.assertTrue
import kotlin.time.DurationUnit
import kotlin.time.ExperimentalTime
import kotlin.time.toDuration
internal class TimeTest {
@ExperimentalTime
@Test
fun testTimeHumanReadable() {
val time0 = 1.toDuration(DurationUnit.DAYS) +
20.toDuration(DurationUnit.HOURS) +
15.toDuration(DurationUnit.MINUTES) +
2057.toDuration(DurationUnit.MILLISECONDS)
println(time0.asHumanReadable)
assertTrue { time0.asHumanReadable == "1d 20h 15min 2.057s" }
val time1 = 1.toDuration(DurationUnit.DAYS) + 59.toDuration(DurationUnit.MINUTES)
println(time1.asHumanReadable)
assertTrue { time1.asHumanReadable == "1d 59min 0.0s" }
}
}

View File

@ -24,8 +24,8 @@ internal actual class DeferredReusableInput actual constructor(
is InputStream -> strategy.newImageCache(input) is InputStream -> strategy.newImageCache(input)
is ByteArray -> strategy.newImageCache(input) is ByteArray -> strategy.newImageCache(input)
is Input -> strategy.newImageCache(input) is Input -> strategy.newImageCache(input)
is BufferedImage -> strategy.newImageCache(input, extraArg as String)
is URL -> strategy.newImageCache(input) is URL -> strategy.newImageCache(input)
is BufferedImage -> strategy.newImageCache(input, extraArg as String)
else -> error("Internal error: unsupported DeferredReusableInput.input: ${input::class.qualifiedName}") else -> error("Internal error: unsupported DeferredReusableInput.input: ${input::class.qualifiedName}")
}.input }.input
} }