Add Time utils; Remove klock

This commit is contained in:
Him188 2019-12-07 13:17:40 +08:00
parent cb083922d8
commit 03ca3b5fd1
13 changed files with 108 additions and 109 deletions

View File

@ -72,8 +72,6 @@ kotlin {
api(kotlinx("coroutines-core-common", coroutinesVersion))
api(kotlinx("serialization-runtime-common", serializationVersion))
api("com.soywiz.korlibs.klock:klock:$klockVersion")
api(ktor("http-cio", ktorVersion))
api(ktor("http", ktorVersion))
api(ktor("client-core-jvm", ktorVersion))

View File

@ -114,7 +114,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger, context: CoroutineCo
logger.info("Reconnected successfully")
return@launch
} else {
delay(configuration.reconnectPeriod.millisecondsLong)
delay(configuration.reconnectPeriodMillis)
}
}
}

View File

@ -2,8 +2,7 @@
package net.mamoe.mirai.contact
import com.soywiz.klock.MonthSpan
import com.soywiz.klock.TimeSpan
import net.mamoe.mirai.utils.*
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@ -26,6 +25,10 @@ interface Member : QQ, Contact {
*
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
* @return 若机器人无权限禁言这个群成员, 返回 `false`
*
* @see Int.minutesToSeconds
* @see Int.hoursToSeconds
* @see Int.daysToSeconds
*/
suspend fun mute(durationSeconds: Int): Boolean
@ -42,17 +45,6 @@ suspend inline fun Member.mute(duration: Duration): Boolean {
return this.mute(duration.inSeconds.toInt())
}
suspend inline fun Member.mute(duration: TimeSpan): Boolean {
require(duration.days <= 30) { "duration must be at most 1 month" }
require(duration.microseconds > 0) { "duration must be greater than 0 second" }
return this.mute(duration.seconds.toInt())
}
suspend inline fun Member.mute(duration: MonthSpan): Boolean {
require(duration.totalMonths == 1) { "if you pass a MonthSpan, it must be 1 month" }
return this.mute(duration.totalMonths * 30 * 24 * 3600)
}
@ExperimentalUnsignedTypes
suspend inline fun Member.mute(durationSeconds: UInt): Boolean {
require(durationSeconds.toInt() <= 30 * 24 * 3600) { "duration must be at most 1 month" }

View File

@ -2,7 +2,7 @@
package net.mamoe.mirai.contact.data
import com.soywiz.klock.Date
import io.ktor.util.date.GMTDate
/**
* 个人资料
@ -17,7 +17,7 @@ data class Profile(
val zipCode: String?,
val phone: String?,
val gender: Gender,
val birthday: Date?,
val birthday: GMTDate?,
val personalStatement: String?,// 个人说明
val school: String?,
val homepage: String?,

View File

@ -2,8 +2,6 @@
package net.mamoe.mirai.network
import com.soywiz.klock.TimeSpan
import com.soywiz.klock.seconds
import kotlinx.coroutines.*
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.*
@ -26,16 +24,14 @@ import net.mamoe.mirai.utils.assertUnreachable
import net.mamoe.mirai.utils.getGTK
import net.mamoe.mirai.utils.internal.PositiveNumbers
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
import net.mamoe.mirai.utils.secondsToMillis
import kotlin.coroutines.coroutineContext
/**
* 构造 [BotSession] 的捷径
*/
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun TIMBotNetworkHandler.BotSession(
sessionKey: SessionKey,
socket: DataPacketSocketAdapter
): BotSession = BotSession(bot, sessionKey, socket, this)
internal inline fun TIMBotNetworkHandler.BotSession(): BotSession = BotSession(bot)
/**
* 登录会话. 当登录完成后, 客户端会拿到 sessionKey.
@ -47,10 +43,7 @@ internal inline fun TIMBotNetworkHandler.BotSession(
*/
@UseExperimental(MiraiInternalAPI::class)
expect class BotSession internal constructor(
bot: Bot,
sessionKey: SessionKey,
socket: DataPacketSocketAdapter,
NetworkScope: CoroutineScope
bot: Bot
) : BotSessionBase
/**
@ -59,11 +52,12 @@ expect class BotSession internal constructor(
@MiraiInternalAPI
// cannot be internal because of `public BotSession`
abstract class BotSessionBase internal constructor(
val bot: Bot,
internal val sessionKey: SessionKey,
val socket: DataPacketSocketAdapter,
val NetworkScope: CoroutineScope
val bot: Bot
) {
internal val sessionKey: SessionKey get() = bot.sessionKey
val socket: DataPacketSocketAdapter get() = bot.network.socket
val NetworkScope: CoroutineScope get() = bot.network
/**
* Web api 使用
*/
@ -79,6 +73,29 @@ abstract class BotSessionBase internal constructor(
*/
val gtk: Int get() = _gtk
suspend inline fun Int.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0).toUInt())
suspend inline fun Long.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0))
suspend inline fun UInt.qq(): QQ = bot.getQQ(this)
suspend inline fun Int.group(): Group = bot.getGroup(this.coerceAtLeastOrFail(0).toUInt())
suspend inline fun Long.group(): Group = bot.getGroup(this.coerceAtLeastOrFail(0))
suspend inline fun UInt.group(): Group = bot.getGroup(GroupId(this))
suspend inline fun GroupId.group(): Group = bot.getGroup(this)
suspend inline fun GroupInternalId.group(): Group = bot.getGroup(this)
suspend fun Image.getLink(): ImageLink = when (this.id) {
is ImageId0x06 -> FriendImagePacket.RequestImageLink(bot.qqAccount, bot.sessionKey, id).sendAndExpect<FriendImageLink>()
is ImageId0x03 -> GroupImagePacket.RequestImageLink(bot.qqAccount, bot.sessionKey, id).sendAndExpect<GroupImageLink>().requireSuccess()
else -> assertUnreachable()
}
suspend inline fun Image.downloadAsByteArray(): ByteArray = getLink().downloadAsByteArray()
suspend inline fun Image.download(): ByteReadPacket = getLink().download()
// region internal
@Suppress("PropertyName")
internal var _sKey: String = ""
@ -115,12 +132,11 @@ abstract class BotSessionBase internal constructor(
noinline handler: suspend (P) -> R
): Deferred<R> {
val deferred: CompletableDeferred<R> = CompletableDeferred(coroutineContext[Job])
(bot.network as TIMBotNetworkHandler).addHandler(TemporaryPacketHandler(
P::class, deferred, this@BotSessionBase as BotSession, checkSequence, coroutineContext + deferred
).also {
it.toSend(this)
it.onExpect(handler)
})
(bot.network as TIMBotNetworkHandler)
.addHandler(TemporaryPacketHandler(P::class, deferred, this@BotSessionBase as BotSession, checkSequence, coroutineContext + deferred).also {
it.toSend(this)
it.onExpect(handler)
})
return deferred
}
@ -129,45 +145,19 @@ abstract class BotSessionBase internal constructor(
internal suspend inline fun <reified P : Packet, R> OutgoingPacket.sendAndExpect(
checkSequence: Boolean = true,
timeout: TimeSpan = 5.seconds,
timeoutMillis: Long = 5.secondsToMillis,
crossinline mapper: (P) -> R
): R = withTimeout(timeout.millisecondsLong) { sendAndExpectAsync<P, R>(checkSequence) { mapper(it) }.await() }
): R = withTimeout(timeoutMillis) { sendAndExpectAsync<P, R>(checkSequence) { mapper(it) }.await() }
internal suspend inline fun <reified P : Packet> OutgoingPacket.sendAndExpect(
checkSequence: Boolean = true,
timeout: TimeSpan = 5.seconds
): P = withTimeout(timeout.millisecondsLong) { sendAndExpectAsync<P, P>(checkSequence) { it }.await() }
timeoutMillist: Long = 5.secondsToMillis
): P = withTimeout(timeoutMillist) { sendAndExpectAsync<P, P>(checkSequence) { it }.await() }
internal suspend inline fun OutgoingPacket.send() =
(socket as TIMBotNetworkHandler.BotSocketAdapter).sendPacket(this)
suspend inline fun Int.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0).toUInt())
suspend inline fun Long.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0))
suspend inline fun UInt.qq(): QQ = bot.getQQ(this)
suspend inline fun Int.group(): Group = bot.getGroup(this.coerceAtLeastOrFail(0).toUInt())
suspend inline fun Long.group(): Group = bot.getGroup(this.coerceAtLeastOrFail(0))
suspend inline fun UInt.group(): Group = bot.getGroup(GroupId(this))
suspend inline fun GroupId.group(): Group = bot.getGroup(this)
suspend inline fun GroupInternalId.group(): Group = bot.getGroup(this)
suspend fun Image.getLink(): ImageLink = when (this.id) {
is ImageId0x06 -> FriendImagePacket.RequestImageLink(
bot.qqAccount,
bot.sessionKey,
id
).sendAndExpect<FriendImageLink>()
is ImageId0x03 -> GroupImagePacket.RequestImageLink(
bot.qqAccount,
bot.sessionKey,
id
).sendAndExpect<GroupImageLink>().requireSuccess()
else -> assertUnreachable()
}
suspend inline fun Image.downloadAsByteArray(): ByteArray = getLink().downloadAsByteArray()
suspend inline fun Image.download(): ByteReadPacket = getLink().download()
// endregion
}

View File

@ -20,7 +20,6 @@ import net.mamoe.mirai.network.protocol.tim.handler.TemporaryPacketHandler
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.login.*
import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.OnlineStatus
import net.mamoe.mirai.utils.currentBotConfiguration
import net.mamoe.mirai.utils.io.*
@ -197,7 +196,7 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
val expect = expectPacket<TouchPacket.TouchResponse>()
launch { processReceive() }
launch {
if (withTimeoutOrNull(currentBotConfiguration().touchTimeout.millisecondsLong) { expect.join() } == null) {
if (withTimeoutOrNull(currentBotConfiguration().touchTimeoutMillis) { expect.join() } == null) {
loginResult.complete(LoginResult.TIMEOUT)
}
}
@ -274,7 +273,7 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
bot.logger.error("Caught SendPacketInternalException: ${e.cause?.message}")
}
val configuration = currentBotConfiguration()
delay(configuration.firstReconnectDelay.millisecondsLong)
delay(configuration.firstReconnectDelayMillis)
bot.tryReinitializeNetworkHandler(configuration, e)
return@withContext
} finally {
@ -475,22 +474,22 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
BotLoginSucceedEvent(bot).broadcast()
session = BotSession(sessionKey, socket)
session = BotSession()
val configuration = currentBotConfiguration()
heartbeatJob = this@TIMBotNetworkHandler.launch {
while (socket.isOpen) {
delay(configuration.heartbeatPeriod.millisecondsLong)
delay(configuration.heartbeatPeriodMillis)
with(session) {
class HeartbeatTimeoutException : CancellationException("heartbeat timeout")
if (withTimeoutOrNull(configuration.heartbeatTimeout.millisecondsLong) {
if (withTimeoutOrNull(configuration.heartbeatTimeoutMillis) {
// FIXME: 2019/11/26 启动被挤掉线检测
HeartbeatPacket(bot.qqAccount, sessionKey).sendAndExpect<HeartbeatPacketResponse>()
} == null) {
bot.logger.warning("Heartbeat timed out")
delay(configuration.firstReconnectDelay.millisecondsLong)
delay(configuration.firstReconnectDelayMillis)
bot.tryReinitializeNetworkHandler(configuration, HeartbeatTimeoutException())
return@launch
}

View File

@ -2,7 +2,7 @@
package net.mamoe.mirai.network.protocol.tim.packet.action
import com.soywiz.klock.Date
import io.ktor.util.date.GMTDate
import kotlinx.io.core.*
import net.mamoe.mirai.contact.data.Gender
import net.mamoe.mirai.contact.data.Profile
@ -77,7 +77,7 @@ internal object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfil
else -> Gender.SECRET // 猜的
//else -> error("Cannot determine gender, bad value of 0x4E29u: ${map[0x4729u]!![0].toUHexString()}")
},
birthday = map[0x4E3Fu]?.let { Date(it.toUInt().toInt()) },
birthday = map[0x4E3Fu]?.let { GMTDate(it.toUInt().toLong()) },
personalStatement = map[0x4E33u]?.encodeToString(),
homepage = map[0x4E2Du]?.encodeToString(),
company = map[0x5DC8u]?.encodeToString(),

View File

@ -2,9 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet.event
import com.soywiz.klock.TimeSpan
import com.soywiz.klock.seconds
import com.soywiz.klock.toTimeString
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUInt
@ -21,28 +18,28 @@ import net.mamoe.mirai.qqAccount
@Suppress("unused", "MemberVisibilityCanBePrivate")
class MemberMuteEvent(
val member: Member,
override val duration: TimeSpan,
override val durationSeconds: Int,
override val operator: Member
) : MuteEvent() {
override val group: Group get() = operator.group
override fun toString(): String = "MemberMuteEvent(member=${member.id}, group=${group.id}, operator=${operator.id}, duration=${duration.toTimeString()}"
override fun toString(): String = "MemberMuteEvent(member=${member.id}, group=${group.id}, operator=${operator.id}, duration=${durationSeconds}s"
}
/**
* 机器人被禁言事件
*/
class BeingMutedEvent(
override val duration: TimeSpan,
override val durationSeconds: Int,
override val operator: Member
) : MuteEvent() {
override val group: Group get() = operator.group
override fun toString(): String = "BeingMutedEvent(group=${group.id}, operator=${operator.id}, duration=${duration.toTimeString()}"
override fun toString(): String = "BeingMutedEvent(group=${group.id}, operator=${operator.id}, duration=${durationSeconds}s"
}
sealed class MuteEvent : EventOfMute() {
abstract override val operator: Member
abstract override val group: Group
abstract val duration: TimeSpan
abstract val durationSeconds: Int
}
// endregion
@ -124,12 +121,10 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl
MemberUnmuteEvent(group.getMember(memberQQ), operator)
}
} else {
val duration = durationSeconds.seconds
if (memberQQ == bot.qqAccount) {
BeingMutedEvent(duration, operator)
BeingMutedEvent(durationSeconds, operator)
} else {
MemberMuteEvent(group.getMember(memberQQ), duration, operator)
MemberMuteEvent(group.getMember(memberQQ), durationSeconds, operator)
}
}
}

View File

@ -1,7 +1,5 @@
package net.mamoe.mirai.utils
import com.soywiz.klock.TimeSpan
import com.soywiz.klock.seconds
import kotlinx.io.core.IoBuffer
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.protocol.tim.packet.login.TouchPacket.TouchResponse
@ -28,7 +26,7 @@ class BotConfiguration : CoroutineContext.Element {
/**
* 等待 [TouchResponse] 的时间
*/
var touchTimeout: TimeSpan = 2.seconds
var touchTimeoutMillis: Long = 2.secondsToMillis
/**
* 是否使用随机的设备名.
* 使用随机可以降低被封禁的风险, 但可能导致每次登录都需要输入验证码
@ -38,20 +36,20 @@ class BotConfiguration : CoroutineContext.Element {
/**
* 心跳周期. 过长会导致被服务器断开连接.
*/
var heartbeatPeriod: TimeSpan = 60.seconds
var heartbeatPeriodMillis: Long = 60.secondsToMillis
/**
* 每次心跳时等待结果的时间.
* 一旦心跳超时, 整个网络服务将会重启 (将消耗约 1s). 除正在进行的任务 (如图片上传) 会被中断外, 事件和插件均不受影响.
*/
var heartbeatTimeout: TimeSpan = 2.seconds
var heartbeatTimeoutMillis: Long = 2.secondsToMillis
/**
* 心跳失败后的第一次重连前的等待时间.
*/
var firstReconnectDelay: TimeSpan = 5.seconds
var firstReconnectDelayMillis: Long = 5.secondsToMillis
/**
* 重连失败后, 继续尝试的每次等待时间
*/
var reconnectPeriod: TimeSpan = 60.seconds
var reconnectPeriodMillis: Long = 60.secondsToMillis
/**
* 最多尝试多少次重连
*/

View File

@ -2,13 +2,13 @@
package net.mamoe.mirai.utils
import com.soywiz.klock.DateTime
import io.ktor.client.HttpClient
import io.ktor.util.date.GMTDate
/**
* 时间戳
*/
inline val currentTime: Long get() = DateTime.nowUnixLong()
inline val currentTime: Long get() = GMTDate().timestamp
/**
* 设备名

View File

@ -0,0 +1,33 @@
package net.mamoe.mirai.utils
import kotlin.time.seconds
// 临时使用, 待 Kotlin Duration 稳定后使用 Duration.
// 内联属性, 则将来删除这些 API 将不会导致二进制不兼容.
inline val Int.secondsToMillis: Long get() = this * 1000L
inline val Int.minutesToMillis: Long get() = this * 60.secondsToMillis
inline val Int.hoursToMillis: Long get() = this * 60.minutesToMillis
inline val Int.daysToMillis: Long get() = this * 24.hoursToMillis
inline val Int.weeksToMillis: Long get() = this * 7.daysToMillis
inline val Int.monthsToMillis: Long get() = this * 30.daysToMillis
inline val Int.millisToSeconds: Long get() = (this / 1000).toLong()
inline val Int.minutesToSeconds: Long get() = (this * 60).toLong()
inline val Int.hoursToSeconds: Long get() = this * 60.minutesToSeconds
inline val Int.daysToSeconds: Long get() = this * 24.hoursToSeconds
inline val Int.weeksToSeconds: Long get() = this * 7.daysToSeconds
inline val Int.monthsToSeconds: Long get() = this * 30.daysToSeconds

View File

@ -24,11 +24,8 @@ import javax.imageio.ImageIO
@UseExperimental(MiraiInternalAPI::class)
@Suppress("unused")
actual class BotSession internal actual constructor(
bot: Bot,
sessionKey: SessionKey,
socket: DataPacketSocketAdapter,
NetworkScope: CoroutineScope
) : BotSessionBase(bot, sessionKey, socket, NetworkScope) {
bot: Bot
) : BotSessionBase(bot) {
suspend inline fun Image.downloadAsStream(): InputStream = download().inputStream()
suspend inline fun Image.downloadAsBufferedImage(): BufferedImage = withContext(IO) { downloadAsStream().use { ImageIO.read(it) } }

View File

@ -2,8 +2,6 @@
package demo.gentleman
import com.soywiz.klock.months
import com.soywiz.klock.seconds
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
@ -14,7 +12,6 @@ import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.addFriend
import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.mute
import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.event.subscribeGroupMessages
@ -70,12 +67,12 @@ suspend fun main() {
startsWith("mt2months") {
val at: At by message
at.member().mute(1.months)
at.member().mute(1)
}
startsWith("mute") {
val at: At by message
at.member().mute(30.seconds)
at.member().mute(30)
}
startsWith("unmute") {