, bot, it.flag2, it.consumer)
}
- pendingIncomingPackets = null // release
+ val list = pendingIncomingPackets
+ pendingIncomingPackets = null // release, help gc
+ list?.clear() // help gc
BotOnlineEvent(bot).broadcast()
- Unit
+ Unit // dont remove. can help type inference
}
suspend fun doHeartBeat(): Exception? {
@@ -347,7 +372,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
// with generic type, less mistakes
- private suspend inline fun generifiedParsePacket(input: Input) {
+ private suspend fun
generifiedParsePacket(input: Input) {
KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory
, packet: P, commandName: String, sequenceId: Int ->
handlePacket(packetFactory, packet, commandName, sequenceId)
if (packet is MultiPacket<*>) {
@@ -361,7 +386,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
/**
* 处理解析完成的包.
*/
- suspend fun
handlePacket(packetFactory: PacketFactory
?, packet: P, commandName: String, sequenceId: Int) {
+ suspend fun
handlePacket(packetFactory: PacketFactory
?, packet: P, commandName: String, sequenceId: Int) {
// highest priority: pass to listeners (attached by sendAndExpect).
packetListeners.forEach { listener ->
if (listener.filter(commandName, sequenceId) && packetListeners.remove(listener)) {
@@ -370,7 +395,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
// check top-level cancelling
- if (PacketReceivedEvent(packet).broadcast().isCancelled) {
+ if (packet != null && PacketReceivedEvent(packet).broadcast().isCancelled) {
return
}
@@ -386,7 +411,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
if (packet is CancellableEvent && packet.isCancelled) return
}
- logger.info("Received: ${packet.toString().replace("\n", """\n""").replace("\r", "")}")
+ if (packet != null && (bot.logger.isEnabled || logger.isEnabled)) {
+ val logMessage = "Received: ${packet.toString().replace("\n", """\n""").replace("\r", "")}"
+
+ if (packet is Event) {
+ bot.logger.verbose(logMessage)
+ } else logger.verbose(logMessage)
+ }
packetFactory?.run {
when (this) {
@@ -480,7 +511,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
* 发送一个包, 但不期待任何返回.
*/
suspend fun OutgoingPacket.sendWithoutExpect() {
- logger.info("Send: ${this.commandName}")
+ check(bot.isActive) { "bot is dead therefore can't send any packet" }
+ check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" }
+ logger.verbose("Send: ${this.commandName}")
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(delegate)
}
@@ -495,6 +528,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
require(timeoutMillis > 0) { "timeoutMillis must > 0" }
require(retry >= 0) { "retry must >= 0" }
+ check(bot.isActive) { "bot is dead therefore can't send any packet" }
+ check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" }
+
var lastException: Exception? = null
if (retry == 0) {
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
@@ -503,7 +539,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(delegate)
}
- logger.info("Send: ${this.commandName}")
+ logger.verbose("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST")
handler.await() as E
@@ -522,7 +558,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(data, 0, length)
}
- logger.info("Send: ${this.commandName}")
+ logger.verbose("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST")
handler.await() as E
@@ -547,7 +583,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
internal inner class PacketListener( // callback
val commandName: String,
val sequenceId: Int
- ) : CompletableDeferred by CompletableDeferred(supervisor) {
+ ) : CompletableDeferred by CompletableDeferred(supervisor) {
fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId
}
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/EncryptMethod.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/EncryptMethod.kt
index 344c9dba2..80d0d6141 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/EncryptMethod.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/EncryptMethod.kt
@@ -15,12 +15,10 @@ import kotlinx.io.core.buildPacket
import kotlinx.io.core.writeFully
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.utils.cryptor.ECDH
+import net.mamoe.mirai.utils.cryptor.ECDHKeyPair
import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeShortLVByteArray
-/**
- * Encryption method to be used for packet body.
- */
@UseExperimental(ExperimentalUnsignedTypes::class)
internal interface EncryptMethod {
val id: Int
@@ -33,16 +31,6 @@ internal interface EncryptMethodSessionKey : EncryptMethod {
val currentLoginState: Int
val sessionKey: ByteArray
- /**
- * buildPacket{
- * byte 1
- * byte if (currentLoginState == 2) 3 else 2
- * fully key
- * short 258
- * short 0
- * fully encrypted
- * }
- */
override fun makeBody(client: QQAndroidClient, body: BytePacketBuilder.() -> Unit): ByteReadPacket =
buildPacket {
require(currentLoginState == 2 || currentLoginState == 3) { "currentLoginState must be either 2 or 3" }
@@ -65,29 +53,27 @@ inline class EncryptMethodSessionKeyLoginState3(override val sessionKey: ByteArr
override val currentLoginState: Int get() = 3
}
-inline class EncryptMethodECDH135(override val ecdh: ECDH) :
+internal inline class EncryptMethodECDH135(override val ecdh: ECDH) :
EncryptMethodECDH {
override val id: Int get() = 135
}
-inline class EncryptMethodECDH7(override val ecdh: ECDH) :
+internal inline class EncryptMethodECDH7(override val ecdh: ECDH) :
EncryptMethodECDH {
override val id: Int get() = 7
}
internal interface EncryptMethodECDH : EncryptMethod {
+ companion object {
+ operator fun invoke(ecdh: ECDH): EncryptMethodECDH {
+ return if (ecdh.keyPair === ECDHKeyPair.DefaultStub) {
+ EncryptMethodECDH135(ecdh)
+ } else EncryptMethodECDH7(ecdh)
+ }
+ }
+
val ecdh: ECDH
- /**
- * **Packet Structure**
- * byte 1
- * byte 1
- * byte[] [ECDH.privateKey]
- * short 258
- * short [ECDH.publicKey].size
- * byte[] [ECDH.publicKey]
- * byte[] encrypted `body()` by [ECDH.shareKey]
- */
override fun makeBody(client: QQAndroidClient, body: BytePacketBuilder.() -> Unit): ByteReadPacket =
buildPacket {
writeByte(1) // const
@@ -95,15 +81,15 @@ internal interface EncryptMethodECDH : EncryptMethod {
writeFully(client.randomKey)
writeShort(258) // const
- // writeShortLVByteArray("04 CB 36 66 98 56 1E 93 6E 80 C1 57 E0 74 CA B1 3B 0B B6 8D DE B2 82 45 48 A1 B1 8D D4 FB 61 22 AF E1 2F E4 8C 52 66 D8 D7 26 9D 76 51 A8 EB 6F E7".hexToBytes())
+ if (ecdh.keyPair === ECDHKeyPair.DefaultStub) {
+ writeShortLVByteArray(ECDHKeyPair.DefaultStub.defaultPublicKey)
+ encryptAndWrite(ECDHKeyPair.DefaultStub.defaultShareKey, body)
+ } else {
+ writeShortLVByteArray(ecdh.keyPair.publicKey.getEncoded().drop(23).take(49).toByteArray().also {
+ check(it[0].toInt() == 0x04) { "Bad publicKey generated. Expected first element=0x04, got${it[0]}" }
+ })
- writeShortLVByteArray(ecdh.keyPair.publicKey.getEncoded().drop(23).take(49).toByteArray().also {
- // it.toUHexString().debugPrint("PUBLIC KEY")
- check(it[0].toInt() == 0x04) { "Bad publicKey generated. Expected first element=0x04, got${it[0]}" }
- //check(ecdh.calculateShareKeyByPeerPublicKey(it.adjustToPublicKey()).contentEquals(ecdh.keyPair.shareKey)) { "PublicKey Validation failed" }
- })
-
- // encryptAndWrite("26 33 BA EC 86 EB 79 E6 BC E0 20 06 5E A9 56 6C".hexToBytes(), body)
- encryptAndWrite(ecdh.keyPair.initialShareKey, body)
+ encryptAndWrite(ecdh.keyPair.initialShareKey, body)
+ }
}
}
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
index e3978b082..55b6dae7b 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
@@ -33,7 +33,7 @@ import kotlin.contracts.contract
import kotlin.jvm.JvmName
-internal sealed class PacketFactory {
+internal sealed class PacketFactory {
/**
* 筛选从服务器接收到的包时的 commandName
*/
@@ -49,7 +49,7 @@ internal sealed class PacketFactory {
* @param TPacket 服务器回复包解析结果
*/
@UseExperimental(ExperimentalUnsignedTypes::class)
-internal abstract class OutgoingPacketFactory(
+internal abstract class OutgoingPacketFactory(
/**
* 命令名. 如 `wtlogin.login`, `ConfigPushSvc.PushDomain`
*/
@@ -73,7 +73,7 @@ internal abstract class OutgoingPacketFactory(
* 这个工厂可以在 [handle] 时回复一个 commandId 为 [responseCommandName] 的包, 也可以不回复.
* 必须先到 [KnownPacketFactories] 中注册工厂, 否则不能处理.
*/
-internal abstract class IncomingPacketFactory(
+internal abstract class IncomingPacketFactory(
/**
* 接收自服务器的包的 commandName
*/
@@ -97,10 +97,10 @@ internal abstract class IncomingPacketFactory(
}
@JvmName("decode0")
-private suspend inline fun OutgoingPacketFactory
.decode(bot: QQAndroidBot, packet: ByteReadPacket): P = packet.decode(bot)
+private suspend inline fun
OutgoingPacketFactory
.decode(bot: QQAndroidBot, packet: ByteReadPacket): P = packet.decode(bot)
@JvmName("decode1")
-private suspend inline fun
IncomingPacketFactory
.decode(bot: QQAndroidBot, packet: ByteReadPacket, sequenceId: Int): P =
+private suspend inline fun
IncomingPacketFactory
.decode(bot: QQAndroidBot, packet: ByteReadPacket, sequenceId: Int): P =
packet.decode(bot, sequenceId)
internal val DECRYPTER_16_ZERO = ByteArray(16)
@@ -169,7 +169,7 @@ internal object KnownPacketFactories {
// do not inline. Exceptions thrown will not be reported correctly
@UseExperimental(MiraiInternalAPI::class)
@Suppress("UNCHECKED_CAST")
- suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer) = with(rawInput) {
+ suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer) = with(rawInput) {
// login
val flag1 = readInt()
@@ -229,7 +229,7 @@ internal object KnownPacketFactories {
}
@UseExperimental(MiraiInternalAPI::class)
- internal suspend fun handleIncomingPacket(it: IncomingPacket, bot: QQAndroidBot, flag2: Int, consumer: PacketConsumer) {
+ internal suspend fun handleIncomingPacket(it: IncomingPacket, bot: QQAndroidBot, flag2: Int, consumer: PacketConsumer) {
if (it.packetFactory == null) {
bot.network.logger.debug("Received commandName: ${it.commandName}")
PacketLogger.warning { "找不到 PacketFactory" }
@@ -263,7 +263,7 @@ internal object KnownPacketFactories {
private inline fun inline(block: () -> R): R = block()
- class IncomingPacket(
+ class IncomingPacket(
val packetFactory: PacketFactory?,
val sequenceId: Int,
val data: ByteReadPacket,
@@ -337,7 +337,7 @@ internal object KnownPacketFactories {
return IncomingPacket(packetFactory, ssoSequenceId, packet, commandName)
}
- private suspend fun ByteReadPacket.parseOicqResponse(
+ private suspend fun ByteReadPacket.parseOicqResponse(
bot: QQAndroidBot,
packetFactory: OutgoingPacketFactory,
ssoSequenceId: Int,
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
index 7aad3fefa..76d8e6e33 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
@@ -16,7 +16,6 @@ import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.data.MultiPacket
import net.mamoe.mirai.data.Packet
-import net.mamoe.mirai.event.BroadcastControllable
import net.mamoe.mirai.event.events.BotJoinGroupEvent
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.event.events.MemberJoinEvent
@@ -103,23 +102,23 @@ internal class MessageSvc {
}
@UseExperimental(MiraiInternalAPI::class)
- internal class GetMsgSuccess(delegate: List) : Response(MsgSvc.SyncFlag.STOP, delegate)
+ open class GetMsgSuccess(delegate: List) : Response(MsgSvc.SyncFlag.STOP, delegate) {
+ override fun toString(): String {
+ return "MessageSvc.PbGetMsg.GetMsgSuccess(messages=List(size=${this.size}))"
+ }
+ }
/**
- * 不要直接 expect 这个 class. 它可能
+ * 不要直接 expect 这个 class. 它可能还没同步完成
*/
@MiraiInternalAPI
- open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: List) : MultiPacket(delegate),
- BroadcastControllable {
- override val shouldBroadcast: Boolean
- get() = syncFlagFromServer == MsgSvc.SyncFlag.STOP
-
+ open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: List) : MultiPacket(delegate) {
override fun toString(): String {
return "MessageSvc.PbGetMsg.Response($syncFlagFromServer=$syncFlagFromServer, messages=List(size=${this.size}))"
}
}
- object EmptyResponse : Response(MsgSvc.SyncFlag.STOP, emptyList())
+ object EmptyResponse : GetMsgSuccess(emptyList())
@UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
@@ -127,8 +126,8 @@ internal class MessageSvc {
val resp = readProtoBuf(MsgSvc.PbGetMsgResp.serializer())
if (resp.result != 0) {
- // println("!!! Result=${resp.result} !!!: " + resp.contentToString())
- return GetMsgSuccess(mutableListOf())
+ bot.network.logger.warning("MessageSvc.PushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}")
+ return EmptyResponse
}
bot.client.c2cMessageSync.syncCookie = resp.syncCookie
@@ -149,7 +148,7 @@ internal class MessageSvc {
val group = bot.getGroupByUinOrNull(msg.msgHead.fromUin)
if (msg.msgHead.authUin == bot.uin) {
if (group != null) {
- error("group is not null while bot is invited to the group")
+ return@mapNotNull null
}
// 新群
@@ -159,6 +158,7 @@ internal class MessageSvc {
}.groups.first { it.groupUin == msg.msgHead.fromUin }
+ @Suppress("DuplicatedCode")
val newGroup = GroupImpl(
bot = bot,
coroutineContext = bot.coroutineContext,
@@ -194,6 +194,7 @@ internal class MessageSvc {
override val nameCard: String get() = ""
override val permission: MemberPermission get() = MemberPermission.MEMBER
override val specialTitle: String get() = ""
+ override val muteTimestamp: Int get() = 0
override val uin: Long get() = msg.msgHead.authUin
override val nick: String get() = msg.msgHead.authNick.takeIf { it.isNotEmpty() } ?: msg.msgHead.fromNick
}).also { group.members.delegate.addLast(it) })
@@ -264,6 +265,7 @@ internal class MessageSvc {
/**
* 发送好友消息
*/
+ @Suppress("FunctionName")
fun ToFriend(
client: QQAndroidClient,
toUin: Long,
@@ -293,6 +295,7 @@ internal class MessageSvc {
/**
* 发送群消息
*/
+ @Suppress("FunctionName")
fun ToGroup(
client: QQAndroidClient,
groupCode: Long,
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
index 24710fb7c..117b1601e 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
@@ -15,9 +15,9 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes
import kotlinx.io.core.readUInt
import net.mamoe.mirai.contact.MemberPermission
+import net.mamoe.mirai.data.MultiPacket
import net.mamoe.mirai.data.NoPacket
import net.mamoe.mirai.data.Packet
-import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.qqandroid.GroupImpl
@@ -41,58 +41,44 @@ import net.mamoe.mirai.utils.io.read
import net.mamoe.mirai.utils.io.readString
import net.mamoe.mirai.utils.io.toUHexString
-internal inline class GroupMessageOrNull(val delegate: GroupMessage?) : Packet {
- override fun toString(): String {
- return delegate?.toString() ?: ""
- }
-}
-
internal class OnlinePush {
/**
* 接受群消息
*/
- internal object PbPushGroupMsg : IncomingPacketFactory("OnlinePush.PbPushGroupMsg") {
+ internal object PbPushGroupMsg : IncomingPacketFactory("OnlinePush.PbPushGroupMsg") {
@UseExperimental(ExperimentalStdlibApi::class)
- override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): GroupMessageOrNull {
+ override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): GroupMessage? {
// 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00
- if (!bot.firstLoginSucceed) return GroupMessageOrNull(null)
+ if (!bot.firstLoginSucceed) return null
val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer())
val extraInfo: ImMsgBody.ExtraInfo? = pbPushMsg.msg.msgBody.richText.elems.firstOrNull { it.extraInfo != null }?.extraInfo
if (pbPushMsg.msg.msgHead.fromUin == bot.uin) {
- return GroupMessageOrNull(null)
+ return null
}
val group = bot.getGroup(pbPushMsg.msg.msgHead.groupInfo!!.groupCode)
// println(pbPushMsg.msg.msgBody.richText.contentToString())
val flags = extraInfo?.flags ?: 0
- return GroupMessageOrNull(
- GroupMessage(
- bot = bot,
- group = group,
- senderName = pbPushMsg.msg.msgHead.groupInfo.groupCard,
- sender = group[pbPushMsg.msg.msgHead.fromUin],
- message = pbPushMsg.msg.toMessageChain(),
- permission = when {
- flags and 16 != 0 -> MemberPermission.ADMINISTRATOR
- flags and 8 != 0 -> MemberPermission.OWNER
- flags == 0 -> MemberPermission.MEMBER
- else -> {
- bot.logger.warning("判断群员权限失败")
- MemberPermission.MEMBER
- }
+ return GroupMessage(
+ bot = bot,
+ group = group,
+ senderName = pbPushMsg.msg.msgHead.groupInfo.groupCard,
+ sender = group[pbPushMsg.msg.msgHead.fromUin],
+ message = pbPushMsg.msg.toMessageChain(),
+ permission = when {
+ flags and 16 != 0 -> MemberPermission.ADMINISTRATOR
+ flags and 8 != 0 -> MemberPermission.OWNER
+ flags == 0 -> MemberPermission.MEMBER
+ else -> {
+ bot.logger.warning("判断群员权限失败")
+ MemberPermission.MEMBER
}
- )
+ }
)
}
-
- override suspend fun QQAndroidBot.handle(packet: GroupMessageOrNull, sequenceId: Int): OutgoingPacket? {
- packet.delegate?.broadcast()
- return null
- }
-
}
internal object PbPushTransMsg : IncomingPacketFactory("OnlinePush.PbPushTransMsg", "OnlinePush.RespPush") {
@@ -156,10 +142,10 @@ internal class OnlinePush {
@UseExperimental(ExperimentalStdlibApi::class)
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet {
val reqPushMsg = decodeUniPacket(OnlinePushPack.SvcReqPushMsg.serializer(), "req")
- reqPushMsg.vMsgInfos.forEach { msgInfo: MsgInfo ->
- msgInfo.vMsg!!.read {
- // TODO: 2020/2/13 可能会同时收到多个事件. 使用 map 而不要直接 return
+ @Suppress("USELESS_CAST") // 不要信任 kotlin 类型推断
+ val packets: List = reqPushMsg.vMsgInfos.mapNotNull { msgInfo: MsgInfo ->
+ msgInfo.vMsg!!.read {
when {
msgInfo.shMsgType.toInt() == 732 -> {
val group = bot.getGroup(this.readUInt().toLong())
@@ -169,7 +155,7 @@ internal class OnlinePush {
3073 -> { // mute
val operatorUin = this.readUInt().toLong()
if (operatorUin == bot.uin) {
- return NoPacket
+ return@mapNotNull null
}
val operator = group[operatorUin]
this.readUInt().toLong() // time
@@ -177,42 +163,57 @@ internal class OnlinePush {
val target = this.readUInt().toLong()
val time = this.readInt()
- return if (target == 0L) {
+ if (target == 0L) {
if (time == 0) {
- GroupMuteAllEvent(
+ return@mapNotNull GroupMuteAllEvent(
origin = group.isMuteAll.also { group._muteAll = false },
new = false,
operator = operator,
group = group
- )
+ ) as Packet
} else {
- GroupMuteAllEvent(
+ return@mapNotNull GroupMuteAllEvent(
origin = group.isMuteAll.also { group._muteAll = true },
new = true,
operator = operator,
group = group
- )
+ ) as Packet
}
} else {
- return if (target == bot.uin) {
- if (time == 0) {
- BotUnmuteEvent(operator)
- } else
- BotMuteEvent(durationSeconds = time, operator = operator)
+ if (target == bot.uin) {
+ if (group._botMuteTimestamp != time) {
+ if (time == 0) {
+ group._botMuteTimestamp = 0
+ return@mapNotNull BotUnmuteEvent(operator) as Packet
+ } else {
+ group._botMuteTimestamp = time
+ return@mapNotNull BotMuteEvent(durationSeconds = time, operator = operator) as Packet
+ }
+ } else {
+ return@mapNotNull null
+ }
} else {
val member = group[target]
- if (time == 0) {
- MemberUnmuteEvent(operator = operator, member = member)
+ member as MemberImpl
+ if (member._muteTimestamp != time) {
+ if (time == 0) {
+ member._muteTimestamp = 0
+ return@mapNotNull MemberUnmuteEvent(member, operator) as Packet
+ } else {
+ member._muteTimestamp = time
+ return@mapNotNull MemberMuteEvent(member, time, operator) as Packet
+ }
} else {
- MemberMuteEvent(operator = operator, member = member, durationSeconds = time)
+ return@mapNotNull null
}
}
}
}
- 3585 -> { // 匿名
+ 3585 -> {
+ // 匿名
val operator = group[this.readUInt().toLong()]
val switch = this.readInt() == 0
- return GroupAllowAnonymousChatEvent(
+ return@mapNotNull GroupAllowAnonymousChatEvent(
origin = group.isAnonymousChatEnabled.also { group._anonymousChat = switch },
new = switch,
operator = operator,
@@ -225,7 +226,7 @@ internal class OnlinePush {
// println(dataBytes.toUHexString())
if (dataBytes[0].toInt() != 59) {
- return GroupNameChangeEvent(
+ return@mapNotNull GroupNameChangeEvent(
origin = group.name.also { group._name = message },
new = message,
group = group,
@@ -235,7 +236,7 @@ internal class OnlinePush {
//println(message + ":" + dataBytes.toUHexString())
when (message) {
"管理员已关闭群聊坦白说" -> {
- return GroupAllowConfessTalkEvent(
+ return@mapNotNull GroupAllowConfessTalkEvent(
origin = group.isConfessTalkEnabled.also { group._confessTalk = false },
new = false,
group = group,
@@ -243,7 +244,7 @@ internal class OnlinePush {
)
}
"管理员已开启群聊坦白说" -> {
- return GroupAllowConfessTalkEvent(
+ return@mapNotNull GroupAllowConfessTalkEvent(
origin = group.isConfessTalkEnabled.also { group._confessTalk = true },
new = true,
group = group,
@@ -252,7 +253,7 @@ internal class OnlinePush {
}
else -> {
bot.network.logger.debug { "Unknown server messages $message" }
- return NoPacket
+ return@mapNotNull null
}
}
}
@@ -263,6 +264,7 @@ internal class OnlinePush {
// }
else -> {
bot.network.logger.debug { "unknown group internal type $internalType , data: " + this.readBytes().toUHexString() + " " }
+ return@mapNotNull null
}
}
}
@@ -270,18 +272,18 @@ internal class OnlinePush {
bot.network.logger.debug { "unknown shtype ${msgInfo.shMsgType.toInt()}" }
// val content = msgInfo.vMsg.loadAs(OnlinePushPack.MsgType0x210.serializer())
// println(content.contentToString())
+ return@mapNotNull null
}
else -> {
bot.network.logger.debug { "unknown shtype ${msgInfo.shMsgType.toInt()}" }
+ return@mapNotNull null
}
}
}
}
-
- return NoPacket
+ return MultiPacket(packets)
}
-
override suspend fun QQAndroidBot.handle(packet: Packet, sequenceId: Int): OutgoingPacket? {
return buildResponseUniPacket(client, sequenceId = sequenceId) {
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
index 6c7c5b26b..29ea3b873 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
@@ -47,7 +47,7 @@ internal class WtLogin {
ticket: String
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
- writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
+ writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(2) // subCommand
writeShort(4) // count of TLVs
t193(ticket)
@@ -64,7 +64,7 @@ internal class WtLogin {
captchaAnswer: String
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
- writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
+ writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(2) // subCommand
writeShort(4) // count of TLVs
t2(captchaAnswer, captchaSign, 0)
@@ -83,7 +83,7 @@ internal class WtLogin {
t402: ByteArray
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
- writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
+ writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(20) // subCommand
writeShort(4) // count of TLVs, probably ignored by server?
t8(2052)
@@ -103,7 +103,7 @@ internal class WtLogin {
client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId, unknownHex = "01 00 00 00 00 00 00 00 00 00 01 00") {
- writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
+ writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(8) // subCommand
writeShort(6) // count of TLVs, probably ignored by server?TODO
t8(2052)
@@ -131,7 +131,7 @@ internal class WtLogin {
client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
- writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
+ writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(9) // subCommand
writeShort(17) // count of TLVs, probably ignored by server?
//writeShort(LoginType.PASSWORD.value.toShort())
@@ -325,7 +325,7 @@ internal class WtLogin {
2 -> onSolveLoginCaptcha(tlvMap, bot)
160 /*-96*/ -> onUnsafeDeviceLogin(tlvMap)
204 /*-52*/ -> onSMSVerifyNeeded(tlvMap, bot)
- else -> tlvMap[0x149]?.let { analysisTlv149(it) } ?: error("unknown login result type: $type")
+ else -> tlvMap[0x149]?.let { analysisTlv149(it) } ?: error("unknown login result type: $type, TLVMap = ${tlvMap.contentToString()}")
}
}
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/Guid.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/GuidSource.kt
similarity index 95%
rename from mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/Guid.kt
rename to mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/GuidSource.kt
index ee2085cec..f2405cde6 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/Guid.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/GuidSource.kt
@@ -7,9 +7,13 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
+@file:JvmName("Utils")
+@file:JvmMultifileClass
+
package net.mamoe.mirai.qqandroid.utils
-import net.mamoe.mirai.utils.md5
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
import kotlin.jvm.JvmStatic
/**
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/Flags.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/flags.kt
similarity index 88%
rename from mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/Flags.kt
rename to mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/flags.kt
index a79cba42a..141d44bea 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/Flags.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/flags.kt
@@ -7,8 +7,14 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
+@file:JvmName("Utils")
+@file:JvmMultifileClass
+
package net.mamoe.mirai.qqandroid.utils
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
+
inline class MacOrAndroidIdChangeFlag(val value: Long = 0) {
fun macChanged(): MacOrAndroidIdChangeFlag =
MacOrAndroidIdChangeFlag(this.value or 0x1)
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/inline.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/inline.kt
index 26d1d44a0..ba27ce7d5 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/inline.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/inline.kt
@@ -7,11 +7,16 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
+@file:JvmName("Utils")
+@file:JvmMultifileClass
+
package net.mamoe.mirai.qqandroid.utils
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
/**
* Inline the block
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/type.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/type.kt
index b295ccfa7..08dfe3c07 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/type.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/type.kt
@@ -7,10 +7,16 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
+@file:JvmName("Utils")
+@file:JvmMultifileClass
+
package net.mamoe.mirai.qqandroid.utils
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
-fun Int.toIpV4AddressString(): String {
+
+internal fun Int.toIpV4AddressString(): String {
@Suppress("NAME_SHADOWING")
var var0 = this.toLong() and 0xFFFFFFFF
return buildString {
diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts
index ebecaf2f9..c543159c5 100644
--- a/mirai-core/build.gradle.kts
+++ b/mirai-core/build.gradle.kts
@@ -8,8 +8,6 @@ plugins {
id("com.jfrog.bintray") version "1.8.4-jetbrains-3"
}
-apply(from = rootProject.file("gradle/publish.gradle"))
-
val kotlinVersion: String by rootProject.ext
val atomicFuVersion: String by rootProject.ext
val coroutinesVersion: String by rootProject.ext
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/MiraiEnvironment.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/MiraiEnvironment.kt
deleted file mode 100644
index d7fce339d..000000000
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/MiraiEnvironment.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * 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
-
-actual object MiraiEnvironment {
- actual val platform: Platform get() = Platform.ANDROID
-}
\ No newline at end of file
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/event/internal/MiraiAtomicBoolean.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/event/internal/MiraiAtomicBoolean.kt
new file mode 100644
index 000000000..1b3fb785f
--- /dev/null
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/event/internal/MiraiAtomicBoolean.kt
@@ -0,0 +1,17 @@
+package net.mamoe.mirai.event.internal
+
+import java.util.concurrent.atomic.AtomicBoolean
+
+internal actual class MiraiAtomicBoolean actual constructor(initial: Boolean) {
+ private val delegate: AtomicBoolean = AtomicBoolean(initial)
+
+ actual fun compareAndSet(expect: Boolean, update: Boolean): Boolean {
+ return delegate.compareAndSet(expect, update)
+ }
+
+ actual var value: Boolean
+ get() = delegate.get()
+ set(value) {
+ delegate.set(value)
+ }
+}
\ No newline at end of file
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
index e781c9697..c245d0cde 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
@@ -68,11 +68,11 @@ actual open class BotConfiguration actual constructor() {
/**
* 重连失败后, 继续尝试的每次等待时间
*/
- actual var reconnectPeriodMillis: Long = 60.secondsToMillis
+ actual var reconnectPeriodMillis: Long = 5.secondsToMillis
/**
* 最多尝试多少次重连
*/
- actual var reconnectionRetryTimes: Int = 3
+ actual var reconnectionRetryTimes: Int = Int.MAX_VALUE
/**
* 验证码处理器
*/
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHAndroid.kt
index 98db0234f..5fe233778 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHAndroid.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHAndroid.kt
@@ -9,8 +9,10 @@
package net.mamoe.mirai.utils.cryptor
+import android.annotation.SuppressLint
import net.mamoe.mirai.utils.md5
import java.security.*
+import java.security.spec.ECGenParameterSpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.KeyAgreement
@@ -18,13 +20,13 @@ import javax.crypto.KeyAgreement
actual typealias ECDHPrivateKey = PrivateKey
actual typealias ECDHPublicKey = PublicKey
-actual class ECDHKeyPair(
+internal actual class ECDHKeyPairImpl(
private val delegate: KeyPair
-) {
- actual val privateKey: ECDHPrivateKey get() = delegate.private
- actual val publicKey: ECDHPublicKey get() = delegate.public
+) : ECDHKeyPair {
+ override val privateKey: ECDHPrivateKey get() = delegate.private
+ override val publicKey: ECDHPublicKey get() = delegate.public
- actual val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
+ override val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
}
@Suppress("FunctionName")
@@ -32,8 +34,41 @@ actual fun ECDH() = ECDH(ECDH.generateKeyPair())
actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
actual companion object {
+ @Suppress("ObjectPropertyName")
+ private var _isECDHAvailable: Boolean = false // because `runCatching` has no contract.
+ actual val isECDHAvailable: Boolean get() = _isECDHAvailable
+
+ init {
+ kotlin.runCatching {
+ @SuppressLint("PrivateApi")
+ val clazz = Class.forName(
+ "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider",
+ true,
+ ClassLoader.getSystemClassLoader()
+ )
+
+ val providerName = clazz.getDeclaredField("PROVIDER_NAME").get(null) as String
+
+ if (Security.getProvider(providerName) != null) {
+ Security.removeProvider(providerName)
+ }
+ Security.addProvider(clazz.newInstance() as Provider)
+ generateKeyPair()
+ _isECDHAvailable = true
+ }.exceptionOrNull()?.let {
+ throw IllegalStateException("cannot init BouncyCastle", it)
+ }
+ _isECDHAvailable = false
+ }
+
+
actual fun generateKeyPair(): ECDHKeyPair {
- return ECDHKeyPair(KeyPairGenerator.getInstance("ECDH").genKeyPair())
+ if (!isECDHAvailable) {
+ return ECDHKeyPair.DefaultStub
+ }
+ return ECDHKeyPairImpl(KeyPairGenerator.getInstance("ECDH")
+ .also { it.initialize(ECGenParameterSpec("secp192k1")) }
+ .genKeyPair())
}
actual fun calculateShareKey(
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
index f20543980..f6c1d2513 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
@@ -30,9 +30,16 @@ actual class PlatformSocket : Closeable {
private lateinit var socket: Socket
actual val isOpen: Boolean
- get() = socket.isConnected
+ get() =
+ if (::socket.isInitialized)
+ socket.isConnected
+ else false
- actual override fun close() = socket.close()
+ actual override fun close() {
+ if (::socket.isInitialized) {
+ socket.close()
+ }
+ }
@PublishedApi
internal lateinit var writeChannel: BufferedOutputStream
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
similarity index 100%
rename from mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsAndroid.kt
rename to mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
index a8dea8551..96fd0db38 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
@@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
-@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "FunctionName", "NOTHING_TO_INLINE")
+@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "FunctionName", "NOTHING_TO_INLINE", "UnusedImport")
package net.mamoe.mirai
@@ -35,7 +35,8 @@ import kotlin.jvm.JvmStatic
*
* 注: Bot 为全协程实现, 没有其他任务时若不使用 [join], 主线程将会退出.
*
- * @see Contact
+ * @see Contact 联系人
+ * @see kotlinx.coroutines.isActive 判断 [Bot] 是否正常运行中. (在线, 且没有被 [close])
*/
@UseExperimental(MiraiInternalAPI::class)
abstract class Bot : CoroutineScope {
@@ -195,7 +196,9 @@ abstract class Bot : CoroutineScope {
/**
* 登录, 或重新登录.
- * 重新登录时不会再次拉取联系人列表.
+ * 这个函数总是关闭一切现有网路任务, 然后重新登录并重新缓存好友列表和群列表.
+ *
+ * 一般情况下不需要重新登录. Mirai 能够自动处理掉线情况.
*
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.relogin]
*
@@ -231,24 +234,19 @@ abstract class Bot : CoroutineScope {
// endregion
/**
- * 关闭这个 [Bot], 停止一切相关活动. 所有引用都会被释放.
+ * 关闭这个 [Bot], 立即取消 [Bot] 的 [kotlinx.coroutines.SupervisorJob].
+ * 之后 [kotlinx.coroutines.isActive] 将会返回 `false`.
*
- * 注: 不可重新登录. 必须重新实例化一个 [Bot].
+ * **注意:** 不可重新登录. 必须重新实例化一个 [Bot].
*
* @param cause 原因. 为 null 时视为正常关闭, 非 null 时视为异常关闭
*
- * @see closeAndJoin
+ * @see closeAndJoin 取消并 [Bot.join], 以确保 [Bot] 相关的活动被完全关闭
*/
abstract fun close(cause: Throwable? = null)
// region extensions
- @Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("getFriend(this.toLong())"))
- fun Int.qq(): QQ = getFriend(this.toLong())
-
- @Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("getFriend(this)"))
- fun Long.qq(): QQ = getFriend(this)
-
final override fun toString(): String {
return "Bot(${uin})"
}
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
index a8686f90f..7ee7f84b3 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
@@ -14,7 +14,6 @@ package net.mamoe.mirai
import kotlinx.coroutines.*
import net.mamoe.mirai.event.Listener
import net.mamoe.mirai.event.broadcast
-import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.event.events.BotReloginEvent
import net.mamoe.mirai.event.subscribeAlways
@@ -73,11 +72,6 @@ abstract class BotImpl constructor(
}
}
- /**
- * 可阻止事件广播
- */
- abstract fun onEvent(event: BotEvent): Boolean
-
// region network
final override val network: N get() = _network
@@ -89,21 +83,22 @@ abstract class BotImpl constructor(
private val offlineListener: Listener = this.subscribeAlways { event ->
when (event) {
is BotOfflineEvent.Dropped -> {
- bot.logger.info("Connection dropped or lost by server, retrying login")
+ if (!_network.isActive) {
+ return@subscribeAlways
+ }
+ bot.logger.info("Connection dropped by server or lost, retrying login")
- var lastFailedException: Throwable? = null
- repeat(configuration.reconnectionRetryTimes) {
- try {
- network.relogin()
- logger.info("Reconnected successfully")
- return@subscribeAlways
- } catch (e: Throwable) {
- lastFailedException = e
+ tryNTimesOrException(configuration.reconnectionRetryTimes) { tryCount ->
+ if (tryCount != 0) {
delay(configuration.reconnectPeriodMillis)
}
- }
- if (lastFailedException != null) {
- throw lastFailedException!!
+ network.relogin(event.cause)
+ logger.info("Reconnected successfully")
+ BotReloginEvent(bot, event.cause).broadcast()
+ return@subscribeAlways
+ }?.let {
+ logger.info("Cannot reconnect")
+ throw it
}
}
is BotOfflineEvent.Active -> {
@@ -112,17 +107,21 @@ abstract class BotImpl constructor(
} else {
" with exception: " + event.cause.message
}
- bot.logger.info("Bot is closed manually$msg")
- close(CancellationException(event.toString()))
+ bot.logger.info { "Bot is closed manually$msg" }
+ closeAndJoin(CancellationException(event.toString()))
}
is BotOfflineEvent.Force -> {
- bot.logger.info("Connection occupied by another android device: ${event.message}")
- close(ForceOfflineException(event.toString()))
+ bot.logger.info { "Connection occupied by another android device: ${event.message}" }
+ closeAndJoin(ForceOfflineException(event.toString()))
}
}
}
- final override suspend fun login() = reinitializeNetworkHandler(null)
+ final override suspend fun login() {
+ logger.info("Logging in...")
+ reinitializeNetworkHandler(null)
+ logger.info("Login successful")
+ }
private suspend fun reinitializeNetworkHandler(
cause: Throwable?
@@ -176,15 +175,19 @@ abstract class BotImpl constructor(
@UseExperimental(MiraiInternalAPI::class)
override fun close(cause: Throwable?) {
+ if (!this.botJob.isActive) {
+ // already cancelled
+ return
+ }
kotlin.runCatching {
if (cause == null) {
+ this.botJob.cancel()
network.close()
- this.botJob.complete()
- offlineListener.complete()
+ offlineListener.cancel()
} else {
+ this.botJob.cancel(CancellationException("bot cancelled", cause))
network.close(cause)
- this.botJob.completeExceptionally(cause)
- offlineListener.completeExceptionally(cause)
+ offlineListener.cancel(CancellationException("bot cancelled", cause))
}
}
groups.delegate.clear()
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/MiraiEnvironment.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/MiraiEnvironment.kt
deleted file mode 100644
index ea33d13e1..000000000
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/MiraiEnvironment.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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
-
-/**
- * 平台相关环境属性
- */
-expect object MiraiEnvironment {
- val platform: Platform
-}
-
-/**
- * 可用平台列表
- */
-enum class Platform {
- ANDROID,
- JVM
-}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
index 80c2900bc..dcb80fe9f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
@@ -74,6 +74,16 @@ interface Contact : CoroutineScope {
* 而 [QQ] 含义为一个独立的人, 可以是好友, 也可以是陌生人.
*/
override fun equals(other: Any?): Boolean
+
+ /**
+ * @return `bot.hashCode() * 31 + id.hashCode()`
+ */
+ override fun hashCode(): Int
+
+ /**
+ * @return "QQ($id)" or "Group($id)" or "Member($id)"
+ */
+ override fun toString(): String
}
suspend inline fun Contact.sendMessage(message: Message) = sendMessage(message.toChain())
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
index a3c2a8ff2..3d618c9aa 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
@@ -12,6 +12,7 @@
package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope
+import net.mamoe.mirai.Bot
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.utils.MiraiExperimentalAPI
@@ -89,19 +90,19 @@ interface Group : Contact, CoroutineScope {
/**
* 机器人被禁言还剩余多少秒
*
- * @see BotMuteEvent
- * @see isBotMuted
+ * @see BotMuteEvent 机器人被禁言事件
+ * @see isBotMuted 判断机器人是否正在被禁言
*/
val botMuteRemaining: Int
/**
* 机器人在这个群里的权限
*
- * **MiraiExperimentalAPI**: 在未来可能会被修改
+ * @see Group.checkBotPermission 检查 [Bot] 在这个群里的权限
+ * @see Group.checkBotPermissionOperator 要求 [Bot] 在这个群里的权限为 [管理员或群主][MemberPermission.isOperator]
*
- * @see BotGroupPermissionChangeEvent
+ * @see BotGroupPermissionChangeEvent 机器人群员修改
*/
- @MiraiExperimentalAPI
val botPermission: MemberPermission
@@ -129,6 +130,7 @@ interface Group : Contact, CoroutineScope {
/**
* 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败
*/
+ @MiraiExperimentalAPI("还未支持")
suspend fun quit(): Boolean
/**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
index 66e8f2ca3..74f0a1431 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
@@ -1,7 +1,7 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * 此源代码的使用受 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
@@ -29,33 +29,50 @@ interface Member : QQ, Contact {
/**
* 成员的权限, 动态更新.
+ *
+ * @see MemberPermissionChangeEvent 权限变更事件. 由群主或机器人的操作触发.
*/
val permission: MemberPermission
/**
- * 群名片. 可能为空. 修改时将会触发事件
+ * 群名片. 可能为空.
+ *
+ * 管理员和群主都可修改任何人(包括群主)的群名片.
*
* 在修改时将会异步上传至服务器.
*
- * @see [groupCardOrNick] 获取非空群名片或昵称
+ * @see [nameCardOrNick] 获取非空群名片或昵称
*
- * @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件
+ * @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
* @throws PermissionDeniedException 无权限修改时
*/
var nameCard: String
/**
- * 群头衔
+ * 群头衔.
+ *
+ * 仅群主可以修改群头衔.
*
* 在修改时将会异步上传至服务器.
*
- * @see MemberSpecialTitleChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件
+ * @see MemberSpecialTitleChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
* @throws PermissionDeniedException 无权限修改时
*/
var specialTitle: String
/**
- * 禁言
+ * 被禁言剩余时长. 单位为秒.
+ *
+ * @see isMuted 判断改成员是否处于禁言状态
+ * @see mute 设置禁言
+ * @see unmute 取消禁言
+ */
+ val muteTimeRemaining: Int
+
+ /**
+ * 禁言.
+ *
+ * 管理员可禁言成员, 群主可禁言管理员和群员.
*
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
* @return 机器人无权限时返回 `false`
@@ -72,6 +89,8 @@ interface Member : QQ, Contact {
/**
* 解除禁言.
*
+ * 管理员可解除成员的禁言, 群主可解除管理员和群员的禁言.
+ *
* @see MemberUnmuteEvent 成员被取消禁言事件.
* @throws PermissionDeniedException 无权限修改时
*/
@@ -80,6 +99,8 @@ interface Member : QQ, Contact {
/**
* 踢出该成员.
*
+ * 管理员可踢出成员, 群主可踢出管理员和群员.
+ *
* @see MemberLeaveEvent.Kick 成员被踢出事件.
* @throws PermissionDeniedException 无权限修改时
*/
@@ -96,7 +117,14 @@ interface Member : QQ, Contact {
*
* 若 [群名片][Member.nameCard] 不为空则返回群名片, 为空则返回 [QQ.nick]
*/
-val Member.groupCardOrNick: String get() = this.nameCard.takeIf { it.isNotEmpty() } ?: this.nick
+val Member.nameCardOrNick: String get() = this.nameCard.takeIf { it.isNotEmpty() } ?: this.nick
+
+/**
+ * 判断改成员是否处于禁言状态.
+ */
+fun Member.isMuted(): Boolean {
+ return muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt()
+}
@ExperimentalTime
suspend inline fun Member.mute(duration: Duration) {
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Permission.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Permission.kt
index ac1137744..baeb5951f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Permission.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Permission.kt
@@ -9,6 +9,7 @@
package net.mamoe.mirai.contact
+import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.MiraiExperimentalAPI
@@ -68,7 +69,6 @@ inline fun Member.isAdministrator(): Boolean = this.permission.isAdministrator()
inline fun Member.isOperator(): Boolean = this.permission.isOperator()
-
/**
* 权限不足
*/
@@ -77,6 +77,11 @@ expect class PermissionDeniedException : IllegalStateException {
constructor(message: String?)
}
+/**
+ * 要求 [Bot] 在这个群里的权限为 [required], 否则抛出异常 [PermissionDeniedException]
+ *
+ * @throws PermissionDeniedException
+ */
@UseExperimental(MiraiExperimentalAPI::class)
inline fun Group.checkBotPermission(
required: MemberPermission,
@@ -89,6 +94,11 @@ inline fun Group.checkBotPermission(
}
}
+/**
+ * 要求 [Bot] 在这个群里的权限为 [管理员或群主][MemberPermission.isOperator], 否则抛出异常 [PermissionDeniedException]
+ *
+ * @throws PermissionDeniedException
+ */
@UseExperimental(MiraiExperimentalAPI::class)
inline fun Group.checkBotPermissionOperator(
lazyMessage: () -> String = {
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/EventPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/EventPacket.kt
deleted file mode 100644
index 630db03e1..000000000
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/EventPacket.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.data
-
-import net.mamoe.mirai.event.Event
-
-/**
- * 事件包. 可被监听.
- *
- * @see Event
- */
-interface EventPacket : Event, Packet
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/MemberInfo.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/MemberInfo.kt
index 3ed39ef10..37e6ba4b0 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/MemberInfo.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/MemberInfo.kt
@@ -17,4 +17,6 @@ interface MemberInfo : FriendInfo {
val permission: MemberPermission
val specialTitle: String
+
+ val muteTimestamp: Int
}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
index 5cb5489c0..8c19b90fc 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
@@ -25,6 +25,8 @@ import net.mamoe.mirai.message.data.Message
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
/**
* 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
@@ -33,7 +35,10 @@ import kotlin.contracts.contract
*/
@UseExperimental(ExperimentalContracts::class)
@MessageDsl
-inline fun CoroutineScope.subscribeMessages(crossinline listeners: MessageSubscribersBuilder>.() -> R): R {
+inline fun CoroutineScope.subscribeMessages(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ crossinline listeners: MessageSubscribersBuilder>.() -> R
+): R {
// contract 可帮助 IDE 进行类型推断. 无实际代码作用.
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
@@ -42,7 +47,7 @@ inline fun CoroutineScope.subscribeMessages(crossinline listeners: MessageSu
return MessageSubscribersBuilder { messageListener: MessageListener> ->
// subscribeAlways 即注册一个监听器. 这个监听器收到消息后就传递给 [listener]
// listener 即为 DSL 里 `contains(...) { }`, `startsWith(...) { }` 的代码块.
- subscribeAlways {
+ subscribeAlways(coroutineContext) {
messageListener.invoke(this, this.message.toString())
// this.message.toString() 即为 messageListener 中 it 接收到的值
}
@@ -56,12 +61,15 @@ inline fun CoroutineScope.subscribeMessages(crossinline listeners: MessageSu
*/
@UseExperimental(ExperimentalContracts::class)
@MessageDsl
-inline fun CoroutineScope.subscribeGroupMessages(crossinline listeners: MessageSubscribersBuilder.() -> R): R {
+inline fun CoroutineScope.subscribeGroupMessages(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ crossinline listeners: MessageSubscribersBuilder.() -> R
+): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder { listener ->
- subscribeAlways {
+ subscribeAlways(coroutineContext) {
listener(this, this.message.toString())
}
}.run(listeners)
@@ -74,12 +82,15 @@ inline fun CoroutineScope.subscribeGroupMessages(crossinline listeners: Mess
*/
@UseExperimental(ExperimentalContracts::class)
@MessageDsl
-inline fun CoroutineScope.subscribeFriendMessages(crossinline listeners: MessageSubscribersBuilder.() -> R): R {
+inline fun CoroutineScope.subscribeFriendMessages(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ crossinline listeners: MessageSubscribersBuilder.() -> R
+): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder { listener ->
- subscribeAlways {
+ subscribeAlways(coroutineContext) {
listener(this, this.message.toString())
}
}.run(listeners)
@@ -92,12 +103,15 @@ inline fun CoroutineScope.subscribeFriendMessages(crossinline listeners: Mes
*/
@UseExperimental(ExperimentalContracts::class)
@MessageDsl
-inline fun Bot.subscribeMessages(crossinline listeners: MessageSubscribersBuilder>.() -> R): R {
+inline fun Bot.subscribeMessages(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ crossinline listeners: MessageSubscribersBuilder>.() -> R
+): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder> { listener ->
- this.subscribeAlways {
+ this.subscribeAlways(coroutineContext) {
listener(this, this.message.toString())
}
}.run(listeners)
@@ -106,16 +120,21 @@ inline fun Bot.subscribeMessages(crossinline listeners: MessageSubscribersBu
/**
* 订阅来自这个 [Bot] 的所有群消息事件
*
+ * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
+ *
* @see CoroutineScope.incoming
*/
@UseExperimental(ExperimentalContracts::class)
@MessageDsl
-inline fun Bot.subscribeGroupMessages(crossinline listeners: MessageSubscribersBuilder.() -> R): R {
+inline fun Bot.subscribeGroupMessages(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ crossinline listeners: MessageSubscribersBuilder.() -> R
+): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder { listener ->
- this.subscribeAlways {
+ this.subscribeAlways(coroutineContext) {
listener(this, this.message.toString())
}
}.run(listeners)
@@ -128,12 +147,15 @@ inline fun Bot.subscribeGroupMessages(crossinline listeners: MessageSubscrib
*/
@UseExperimental(ExperimentalContracts::class)
@MessageDsl
-inline fun Bot.subscribeFriendMessages(crossinline listeners: MessageSubscribersBuilder.() -> R): R {
+inline fun Bot.subscribeFriendMessages(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ crossinline listeners: MessageSubscribersBuilder.() -> R
+): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder { listener ->
- this.subscribeAlways {
+ this.subscribeAlways(coroutineContext) {
listener(this, this.message.toString())
}
}.run(listeners)
@@ -148,9 +170,12 @@ inline fun Bot.subscribeFriendMessages(crossinline listeners: MessageSubscri
* @see subscribeMessages
* @see subscribeGroupMessages
*/
-inline fun CoroutineScope.incoming(capacity: Int = Channel.RENDEZVOUS): ReceiveChannel {
+inline fun CoroutineScope.incoming(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ capacity: Int = Channel.RENDEZVOUS
+): ReceiveChannel {
return Channel(capacity).apply {
- subscribeAlways {
+ subscribeAlways(coroutineContext) {
send(this)
}
}
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
index 3aff7e809..241656fdc 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
@@ -11,8 +11,6 @@
package net.mamoe.mirai.event
-import net.mamoe.mirai.BotImpl
-import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.internal.broadcastInternal
import net.mamoe.mirai.utils.MiraiInternalAPI
@@ -22,7 +20,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
* 若监听这个类, 监听器将会接收所有事件的广播.
*
* @see subscribeAlways
- * @see subscribeWhile
+ * @see subscribeOnce
*
* @see subscribeMessages
*
@@ -73,9 +71,6 @@ suspend fun E.broadcast(): E = apply {
if (this is BroadcastControllable && !this.shouldBroadcast) {
return@apply
}
- if (this is BotEvent && !(this.bot as BotImpl<*>).onEvent(this)) {
- return@apply
- }
this@broadcast.broadcastInternal() // inline, no extra cost
}
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
index fd6887e85..eeca3a407 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
@@ -14,10 +14,16 @@ import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import net.mamoe.mirai.Bot
+import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.internal.Handler
import net.mamoe.mirai.event.internal.subscribeInternal
import net.mamoe.mirai.utils.MiraiInternalAPI
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.jvm.JvmName
/*
* 该文件为所有的订阅事件的方法.
@@ -68,15 +74,16 @@ interface Listener : CompletableJob {
* `runBlocking` 不会结束, 也就是下一行 `foo()` 不会被执行. 直到监听时创建的 `Listener` 被停止.
*
*
- * 要创建一个全局都存在的监听, 即守护协程, 请在 [GlobalScope] 下调用本函数:
+ * 要创建一个仅在某个机器人在线时的监听, 请在 [Bot] 下调用本函数 (因为 [Bot] 也实现 [CoroutineScope]).
+ * 这种方式创建的监听会自动筛选 [Bot].
* ```kotlin
- * GlobalScope.subscribe { /* 一些处理 */ }
+ * bot1.subscribe { /* 只会处理来自 bot1 的事件 */ }
* ```
*
*
- * 要创建一个仅在某个机器人在线时的监听, 请在 [Bot] 下调用本函数 (因为 [Bot] 也实现 [CoroutineScope]):
+ * 要创建一个全局都存在的监听, 即守护协程, 请在 [GlobalScope] 下调用本函数:
* ```kotlin
- * bot.subscribe { /* 一些处理 */ }
+ * GlobalScope.subscribe { /* 会收到来自全部 Bot 的事件和与 Bot 不相关的事件 */ }
* ```
*
*
@@ -86,122 +93,137 @@ interface Listener : CompletableJob {
* 若 [this] 没有 [CoroutineExceptionHandler], 则在事件广播方的 [CoroutineExceptionHandler] 处理
* 若均找不到, 则会触发 logger warning.
* - 事件处理时抛出异常不会停止监听器.
- * - 建议在事件处理中, 即 [handler] 里处理异常, 或在 [this] 指定 [CoroutineExceptionHandler].
+ * - 建议在事件处理中 (即 [handler] 里) 处理异常,
+ * 或在 [this] 的 [CoroutineScope.coroutineContext] 中添加 [CoroutineExceptionHandler].
*
*
- * **注意:** 事件处理是 `suspend` 的, 请严格控制 JVM 阻塞方法的使用. 若致事件处理阻塞, 则会导致一些逻辑无法进行.
+ * **注意:** 事件处理是 `suspend` 的, 请规范处理 JVM 阻塞方法.
*
- * // TODO: 2020/2/13 在 bot 下监听时同时筛选对应 bot 实例
+ * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
*
- * @see subscribeMessages 监听消息 DSL
- * @see subscribeGroupMessages 监听群消息 DSL
+ * @see subscribeAlways 一直监听
+ * @see subscribeOnce 只监听一次
+ *
+ * @see subscribeMessages 监听消息 DSL
+ * @see subscribeGroupMessages 监听群消息 DSL
* @see subscribeFriendMessages 监听好友消息 DSL
*/
@UseExperimental(MiraiInternalAPI::class)
-inline fun CoroutineScope.subscribe(crossinline handler: suspend E.(E) -> ListeningStatus): Listener =
- E::class.subscribeInternal(Handler { it.handler(it); })
+inline fun CoroutineScope.subscribe(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ noinline handler: suspend E.(E) -> ListeningStatus
+): Listener =
+ E::class.subscribeInternal(Handler(coroutineContext) { it.handler(it); })
/**
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* 每当 [事件广播][Event.broadcast] 时, [listener] 都会被执行.
*
- * 仅当 [Listener.complete] 或 [Listener.cancel] 时结束.
+ * 可在任意时候通过 [Listener.complete] 来主动停止监听.
+ * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
+ *
+ * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
*
* @see subscribe 获取更多说明
*/
-@UseExperimental(MiraiInternalAPI::class)
-inline fun CoroutineScope.subscribeAlways(crossinline listener: suspend E.(E) -> Unit): Listener =
- E::class.subscribeInternal(Handler { it.listener(it); ListeningStatus.LISTENING })
+@UseExperimental(MiraiInternalAPI::class, ExperimentalContracts::class)
+inline fun CoroutineScope.subscribeAlways(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ noinline listener: suspend E.(E) -> Unit
+): Listener {
+ contract {
+ callsInPlace(listener, InvocationKind.UNKNOWN)
+ }
+ return E::class.subscribeInternal(Handler(coroutineContext) { it.listener(it); ListeningStatus.LISTENING })
+}
/**
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* 仅在第一次 [事件广播][Event.broadcast] 时, [listener] 会被执行.
*
- * 在这之前, 可通过 [Listener.complete] 来停止监听.
+ * 可在任意时候通过 [Listener.complete] 来主动停止监听.
+ * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
+ *
+ * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
*
* @see subscribe 获取更多说明
*/
@UseExperimental(MiraiInternalAPI::class)
-inline fun CoroutineScope.subscribeOnce(crossinline listener: suspend E.(E) -> Unit): Listener =
- E::class.subscribeInternal(Handler { it.listener(it); ListeningStatus.STOPPED })
+inline fun CoroutineScope.subscribeOnce(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ noinline listener: suspend E.(E) -> Unit
+): Listener =
+ E::class.subscribeInternal(Handler(coroutineContext) { it.listener(it); ListeningStatus.STOPPED })
+
+
+//
+// 以下为带筛选 Bot 的监听
+//
+
/**
- * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
- * 每当 [事件广播][Event.broadcast] 时, [listener] 都会被执行, 直到 [listener] 的返回值 [equals] 于 [valueIfStop]
+ * 在 [Bot] 的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
+ * 每当 [事件广播][Event.broadcast] 时, [handler] 都会被执行,
+ * 当 [handler] 返回 [ListeningStatus.STOPPED] 时停止监听
*
- * 可在任意时刻通过 [Listener.complete] 来停止监听.
+ * 可在任意时候通过 [Listener.complete] 来主动停止监听.
+ * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
+ *
+ * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
*
* @see subscribe 获取更多说明
*/
+@JvmName("subscribeAlwaysForBot")
@UseExperimental(MiraiInternalAPI::class)
-inline fun CoroutineScope.subscribeUntil(valueIfStop: T, crossinline listener: suspend E.(E) -> T): Listener =
- E::class.subscribeInternal(Handler { if (it.listener(it) == valueIfStop) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
+inline fun Bot.subscribe(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ noinline handler: suspend E.(E) -> ListeningStatus
+): Listener =
+ E::class.subscribeInternal(Handler(coroutineContext) { if (it.bot === this) it.handler(it) else ListeningStatus.LISTENING })
+
/**
- * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
- * 每当 [事件广播][Event.broadcast] 时, [listener] 都会被执行,
- * 如果 [listener] 的返回值 [equals] 于 [valueIfContinue], 则继续监听, 否则停止
+ * 在 [Bot] 的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
+ * 每当 [事件广播][Event.broadcast] 时, [listener] 都会被执行.
*
- * 可在任意时刻通过 [Listener.complete] 来停止监听.
+ * 可在任意时候通过 [Listener.complete] 来主动停止监听.
+ * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
+ *
+ * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
*
* @see subscribe 获取更多说明
*/
+@JvmName("subscribeAlwaysForBot1")
@UseExperimental(MiraiInternalAPI::class)
-inline fun CoroutineScope.subscribeWhile(valueIfContinue: T, crossinline listener: suspend E.(E) -> T): Listener =
- E::class.subscribeInternal(Handler { if (it.listener(it) != valueIfContinue) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
-
-// endregion
-
-// region ListenerBuilder DSL
-
-/*
-/**
- * 监听构建器. 可同时进行多种方式的监听
- *
- * ```kotlin
- * FriendMessageEvent.subscribe {
- * always{
- * it.reply("永远发生")
- * }
- *
- * untilFalse {
- * it.reply("你发送了 ${it.event}")
- * it.event eq "停止"
- * }
- * }
- * ```
- */
-@ListenersBuilderDsl
-@Suppress("MemberVisibilityCanBePrivate", "unused")
-inline class ListenerBuilder(
- @PublishedApi internal inline val handlerConsumer: CoroutineCoroutineScope.(Listener) -> Unit
-) {
- fun CoroutineCoroutineScope.handler(listener: suspend E.(E) -> ListeningStatus) {
- handlerConsumer(Handler { it.listener(it) })
- }
-
- fun CoroutineCoroutineScope.always(listener: suspend E.(E) -> Unit) = handler { listener(it); ListeningStatus.LISTENING }
-
- fun CoroutineCoroutineScope.until(until: T, listener: suspend E.(E) -> T) =
- handler { if (listener(it) == until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
-
- fun CoroutineCoroutineScope.untilFalse(listener: suspend E.(E) -> Boolean) = until(false, listener)
- fun CoroutineCoroutineScope.untilTrue(listener: suspend E.(E) -> Boolean) = until(true, listener)
- fun CoroutineCoroutineScope.untilNull(listener: suspend E.(E) -> Any?) = until(null, listener)
-
-
- fun CoroutineCoroutineScope.`while`(until: T, listener: suspend E.(E) -> T) =
- handler { if (listener(it) !== until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
-
- fun CoroutineCoroutineScope.whileFalse(listener: suspend E.(E) -> Boolean) = `while`(false, listener)
- fun CoroutineCoroutineScope.whileTrue(listener: suspend E.(E) -> Boolean) = `while`(true, listener)
- fun CoroutineCoroutineScope.whileNull(listener: suspend E.(E) -> Any?) = `while`(null, listener)
-
-
- fun CoroutineCoroutineScope.once(listener: suspend E.(E) -> Unit) = handler { listener(it); ListeningStatus.STOPPED }
+inline fun Bot.subscribeAlways(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ noinline listener: suspend E.(E) -> Unit
+): Listener {
+ return E::class.subscribeInternal(Handler(coroutineContext) { if (it.bot === this) it.listener(it); ListeningStatus.LISTENING })
}
-@DslMarker
-annotation class ListenersBuilderDsl
-*/
+/**
+ * 在 [Bot] 的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
+ * 仅在第一次 [事件广播][Event.broadcast] 时, [listener] 会被执行.
+ *
+ * 可在任意时候通过 [Listener.complete] 来主动停止监听.
+ * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
+ *
+ * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
+ *
+ * @see subscribe 获取更多说明
+ */
+@JvmName("subscribeOnceForBot2")
+@UseExperimental(MiraiInternalAPI::class)
+inline fun Bot.subscribeOnce(
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ noinline listener: suspend E.(E) -> Unit
+): Listener =
+ E::class.subscribeInternal(Handler(coroutineContext) {
+ if (it.bot === this) {
+ it.listener(it)
+ ListeningStatus.STOPPED
+ } else ListeningStatus.LISTENING
+ })
+
// endregion
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
index a855e40b2..749697d01 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
@@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
+@file:Suppress("unused")
+
package net.mamoe.mirai.event.events
import net.mamoe.mirai.Bot
@@ -57,7 +59,7 @@ sealed class BotOfflineEvent : BotEvent {
/**
* 被服务器断开或因网络问题而掉线
*/
- data class Dropped(override val bot: Bot) : BotOfflineEvent(), Packet, BotPassiveEvent
+ data class Dropped(override val bot: Bot, val cause: Throwable?) : BotOfflineEvent(), Packet, BotPassiveEvent
}
/**
@@ -194,7 +196,7 @@ data class GroupNameChangeEvent(
override val origin: String,
override val new: String,
override val group: Group,
- val isByBot: Boolean
+ val isByBot: Boolean // 无法获取 operator
) : GroupSettingChangeEvent, Packet
/**
@@ -210,6 +212,8 @@ data class GroupEntranceAnnouncementChangeEvent(
val operator: Member?
) : GroupSettingChangeEvent, Packet
+val GroupEntranceAnnouncementChangeEvent.isByBot: Boolean get() = operator != null
+
/**
* 群 "全员禁言" 功能状态改变. 此事件广播前修改就已经完成.
@@ -224,6 +228,8 @@ data class GroupMuteAllEvent(
val operator: Member?
) : GroupSettingChangeEvent, Packet
+val GroupMuteAllEvent.isByBot: Boolean get() = operator != null
+
/**
* 群 "匿名聊天" 功能状态改变. 此事件广播前修改就已经完成.
*/
@@ -237,6 +243,8 @@ data class GroupAllowAnonymousChatEvent(
val operator: Member?
) : GroupSettingChangeEvent, Packet
+val GroupAllowAnonymousChatEvent.isByBot: Boolean get() = operator != null
+
/**
* 群 "坦白说" 功能状态改变. 此事件广播前修改就已经完成.
*/
@@ -260,6 +268,8 @@ data class GroupAllowMemberInviteEvent(
val operator: Member?
) : GroupSettingChangeEvent, Packet
+val GroupAllowMemberInviteEvent.isByBot: Boolean get() = operator != null
+
// endregion
@@ -293,6 +303,8 @@ sealed class MemberLeaveEvent : GroupMemberEvent {
data class Quit(override val member: Member) : MemberLeaveEvent()
}
+val MemberLeaveEvent.Kick.isByBot: Boolean get() = operator != null
+
// endregion
// region 名片和头衔
@@ -319,6 +331,8 @@ data class MemberCardChangeEvent(
val operator: Member?
) : GroupMemberEvent
+val MemberCardChangeEvent.isByBot: Boolean get() = operator != null
+
/**
* 群头衔改动. 一定为群主操作
*/
@@ -333,9 +347,18 @@ data class MemberSpecialTitleChangeEvent(
*/
val new: String,
- override val member: Member
+ override val member: Member,
+
+ /**
+ * 操作人.
+ * 不为 null 时一定为群主. 可能与 [member] 引用相同, 此时为群员自己修改.
+ * 为 null 时则是机器人操作.
+ */
+ val operator: Member?
) : GroupMemberEvent
+val MemberSpecialTitleChangeEvent.isByBot: Boolean get() = operator != null
+
// endregion
@@ -367,6 +390,8 @@ data class MemberMuteEvent(
val operator: Member?
) : GroupMemberEvent, Packet
+val MemberMuteEvent.isByBot: Boolean get() = operator != null
+
/**
* 群成员被取消禁言事件. 被禁言的成员都不可能是机器人本人
*/
@@ -378,6 +403,8 @@ data class MemberUnmuteEvent(
val operator: Member?
) : GroupMemberEvent, Packet
+val MemberUnmuteEvent.isByBot: Boolean get() = operator != null
+
// endregion
// endregion
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
index e439a0974..c09b5f303 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
@@ -9,7 +9,6 @@
package net.mamoe.mirai.event.internal
-import kotlinx.atomicfu.atomic
import kotlinx.coroutines.*
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.EventDisabled
@@ -32,8 +31,12 @@ fun , E : Event> KClass.subscribeInternal(listener: L): L
@PublishedApi
@Suppress("FunctionName")
-internal fun CoroutineScope.Handler(handler: suspend (E) -> ListeningStatus): Handler {
- return Handler(coroutineContext[Job], coroutineContext, handler)
+internal fun CoroutineScope.Handler(
+ coroutineContext: CoroutineContext,
+ handler: suspend (E) -> ListeningStatus
+): Handler {
+ val context = this.newCoroutineContext(coroutineContext)
+ return Handler(context[Job], context, handler)
}
private inline fun inline(block: () -> Unit) = block()
@@ -77,7 +80,32 @@ internal class Handler
*/
internal fun KClass.listeners(): EventListeners = EventListenerManager.get(this)
-internal class EventListeners : LockFreeLinkedList>()
+internal class EventListeners(clazz: KClass) : LockFreeLinkedList>() {
+ @Suppress("UNCHECKED_CAST")
+ val supertypes: Set> by lazy {
+ val supertypes = mutableSetOf>()
+
+ fun addSupertypes(clazz: KClass) {
+ clazz.supertypes.forEach {
+ val classifier = it.classifier as? KClass
+ if (classifier != null) {
+ supertypes.add(classifier)
+ addSupertypes(classifier)
+ }
+ }
+ }
+ addSupertypes(clazz)
+
+ supertypes
+ }
+}
+
+internal expect class MiraiAtomicBoolean(initial: Boolean) {
+
+ fun compareAndSet(expect: Boolean, update: Boolean): Boolean
+
+ var value: Boolean
+}
/**
* 管理每个事件 class 的 [EventListeners].
@@ -88,16 +116,8 @@ internal object EventListenerManager {
private val registries = LockFreeLinkedList>()
- private val lock = atomic(false)
-
- private fun setLockValue(value: Boolean) {
- lock.value = value
- }
-
- @Suppress("BooleanLiteralArgument")
- private fun trySetLockTrue(): Boolean {
- return lock.compareAndSet(false, true)
- }
+ // 不要用 atomicfu. 在 publish 后会出现 VerifyError
+ private val lock: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
@Suppress("UNCHECKED_CAST", "BooleanLiteralArgument")
internal tailrec fun get(clazz: KClass): EventListeners {
@@ -106,11 +126,11 @@ internal object EventListenerManager {
return it.listeners as EventListeners
}
}
- if (trySetLockTrue()) {
- val registry = Registry(clazz, EventListeners())
+ if (lock.compareAndSet(false, true)) {
+ val registry = Registry(clazz as KClass, EventListeners(clazz))
registries.addLast(registry)
- setLockValue(false)
- return registry.listeners as EventListeners
+ lock.value = false
+ return registry.listeners
}
return get(clazz)
}
@@ -123,19 +143,10 @@ internal suspend inline fun Event.broadcastInternal() {
EventLogger.info { "Event broadcast: $this" }
- callAndRemoveIfRequired(this::class.listeners())
-
- var supertypes = this::class.supertypes
- while (true) {
- val superSubscribableType = supertypes.firstOrNull {
- it.classifier as? KClass != null
- }
-
- superSubscribableType?.let {
- callAndRemoveIfRequired((it.classifier as KClass).listeners())
- }
-
- supertypes = (superSubscribableType?.classifier as? KClass<*>)?.supertypes ?: return
+ val listeners = this::class.listeners()
+ callAndRemoveIfRequired(listeners)
+ listeners.supertypes.forEach {
+ callAndRemoveIfRequired(it.listeners())
}
}
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
index 14c42998e..8ead2fe6a 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
@@ -13,7 +13,6 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.BroadcastControllable
import net.mamoe.mirai.message.data.MessageChain
-import net.mamoe.mirai.utils.MiraiInternalAPI
class FriendMessage(
bot: Bot,
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
index 0232fcecf..efd272542 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
@@ -14,7 +14,6 @@ import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.Event
-import net.mamoe.mirai.message.data.At
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.utils.getValue
@@ -38,7 +37,6 @@ class GroupMessage(
override val subject: Group get() = group
- inline fun At.member(): Member = group[this.target]
inline fun Long.member(): Member = group[this]
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
index 4aeb9c04c..a42a778fc 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
@@ -19,7 +19,7 @@ import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
-import net.mamoe.mirai.data.EventPacket
+import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.*
@@ -37,7 +37,7 @@ expect abstract class MessagePacket(bot: Bot)
*/ // Tips: 在 IntelliJ 中 (左侧边栏) 打开 `Structure`, 可查看类结构
@Suppress("NOTHING_TO_INLINE")
@MiraiInternalAPI
-abstract class MessagePacketBase(_bot: Bot) : EventPacket, BotEvent {
+abstract class MessagePacketBase(_bot: Bot) : Packet, BotEvent {
/**
* 接受到这条消息的
*/
@@ -115,6 +115,8 @@ abstract class MessagePacketBase(_bot: Bot) :
*/
inline fun QQ.at(): At = At(this as? Member ?: error("`QQ.at` can only be used in GroupMessage"))
+ inline fun At.member(): Member = (this@MessagePacketBase as? GroupMessage)?.group?.get(this.target) ?: error("`At.member` can only be used in GroupMessage")
+
// endregion
// region 下载图片
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt
index a0e103fd0..949b7e6ae 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt
@@ -15,7 +15,7 @@
package net.mamoe.mirai.message.data
import net.mamoe.mirai.contact.Member
-import net.mamoe.mirai.contact.groupCardOrNick
+import net.mamoe.mirai.contact.nameCardOrNick
import net.mamoe.mirai.utils.MiraiInternalAPI
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
@@ -28,7 +28,7 @@ import kotlin.jvm.JvmName
*/
class At @MiraiInternalAPI constructor(val target: Long, val display: String) : Message {
@UseExperimental(MiraiInternalAPI::class)
- constructor(member: Member) : this(member.id, "@${member.groupCardOrNick}")
+ constructor(member: Member) : this(member.id, "@${member.nameCardOrNick}")
override fun toString(): String = display
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt
index 53a35641e..69ab4596c 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt
@@ -14,309 +14,161 @@ package net.mamoe.mirai.message.data
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
-import kotlin.jvm.JvmStatic
/**
* QQ 自带表情
*/
-inline class Face(val id: FaceId) : Message {
- override fun toString(): String = "[face${id.value}]"
+class Face(val id: Int) : Message {
+ override fun toString(): String = "[mirai:face$id]"
- companion object Key : Message.Key
+ /**
+ * @author LamGC
+ */
+ @Suppress("SpellCheckingInspection", "unused")
+ companion object IdList : Message.Key {
+ const val unknown: Int = 0xff
+ const val jingya: Int = 0
+ const val piezui: Int = 1
+ const val se: Int = 2
+ const val fadai: Int = 3
+ const val deyi: Int = 4
+ const val liulei: Int = 5
+ const val haixiu: Int = 6
+ const val bizui: Int = 7
+ const val shui: Int = 8
+ const val daku: Int = 9
+ const val ganga: Int = 10
+ const val fanu: Int = 11
+ const val tiaopi: Int = 12
+ const val ciya: Int = 13
+ const val weixiao: Int = 14
+ const val nanguo: Int = 15
+ const val ku: Int = 16
+ const val zhuakuang: Int = 18
+ const val tu: Int = 19
+ const val touxiao: Int = 20
+ const val keai: Int = 21
+ const val baiyan: Int = 22
+ const val aoman: Int = 23
+ const val ji_e: Int = 24
+ const val kun: Int = 25
+ const val jingkong: Int = 26
+ const val liuhan: Int = 27
+ const val hanxiao: Int = 28
+ const val dabing: Int = 29
+ const val fendou: Int = 30
+ const val zhouma: Int = 31
+ const val yiwen: Int = 32
+ const val yun: Int = 34
+ const val zhemo: Int = 35
+ const val shuai: Int = 36
+ const val kulou: Int = 37
+ const val qiaoda: Int = 38
+ const val zaijian: Int = 39
+ const val fadou: Int = 41
+ const val aiqing: Int = 42
+ const val tiaotiao: Int = 43
+ const val zhutou: Int = 46
+ const val yongbao: Int = 49
+ const val dan_gao: Int = 53
+ const val shandian: Int = 54
+ const val zhadan: Int = 55
+ const val dao: Int = 56
+ const val zuqiu: Int = 57
+ const val bianbian: Int = 59
+ const val kafei: Int = 60
+ const val fan: Int = 61
+ const val meigui: Int = 63
+ const val diaoxie: Int = 64
+ const val aixin: Int = 66
+ const val xinsui: Int = 67
+ const val liwu: Int = 69
+ const val taiyang: Int = 74
+ const val yueliang: Int = 75
+ const val qiang: Int = 76
+ const val ruo: Int = 77
+ const val woshou: Int = 78
+ const val shengli: Int = 79
+ const val feiwen: Int = 85
+ const val naohuo: Int = 86
+ const val xigua: Int = 89
+ const val lenghan: Int = 96
+ const val cahan: Int = 97
+ const val koubi: Int = 98
+ const val guzhang: Int = 99
+ const val qiudale: Int = 100
+ const val huaixiao: Int = 101
+ const val zuohengheng: Int = 102
+ const val youhengheng: Int = 103
+ const val haqian: Int = 104
+ const val bishi: Int = 105
+ const val weiqu: Int = 106
+ const val kuaikule: Int = 107
+ const val yinxian: Int = 108
+ const val qinqin: Int = 109
+ const val xia: Int = 110
+ const val kelian: Int = 111
+ const val caidao: Int = 112
+ const val pijiu: Int = 113
+ const val lanqiu: Int = 114
+ const val pingpang: Int = 115
+ const val shiai: Int = 116
+ const val piaochong: Int = 117
+ const val baoquan: Int = 118
+ const val gouyin: Int = 119
+ const val quantou: Int = 120
+ const val chajin: Int = 121
+ const val aini: Int = 122
+ const val bu: Int = 123
+ const val hao: Int = 124
+ const val zhuanquan: Int = 125
+ const val ketou: Int = 126
+ const val huitou: Int = 127
+ const val tiaosheng: Int = 128
+ const val huishou: Int = 129
+ const val jidong: Int = 130
+ const val jiewu: Int = 131
+ const val xianwen: Int = 132
+ const val zuotaiji: Int = 133
+ const val youtaiji: Int = 134
+ const val shuangxi: Int = 136
+ const val bianpao: Int = 137
+ const val denglong: Int = 138
+ const val facai: Int = 139
+ const val K_ge: Int = 140
+ const val gouwu: Int = 141
+ const val youjian: Int = 142
+ const val shuai_qi: Int = 143
+ const val hecai: Int = 144
+ const val qidao: Int = 145
+ const val baojin: Int = 146
+ const val bangbangtang: Int = 147
+ const val he_nai: Int = 148
+ const val xiamian: Int = 149
+ const val xiangjiao: Int = 150
+ const val feiji: Int = 151
+ const val kaiche: Int = 152
+ const val gaotiezuochetou: Int = 153
+ const val chexiang: Int = 154
+ const val gaotieyouchetou: Int = 155
+ const val duoyun: Int = 156
+ const val xiayu: Int = 157
+ const val chaopiao: Int = 158
+ const val xiongmao: Int = 159
+ const val dengpao: Int = 160
+ const val fengche: Int = 161
+ const val naozhong: Int = 162
+ const val dasan: Int = 163
+ const val caiqiu: Int = 164
+ const val zuanjie: Int = 165
+ const val shafa: Int = 166
+ const val zhijin: Int = 167
+ const val yao: Int = 168
+ const val shouqiang: Int = 169
+ const val qingwa: Int = 170
+ }
override fun eq(other: Message): Boolean {
return other is Face && other.id == this.id
}
-}
-
-/**
- * @author LamGC
- */
-@Suppress("SpellCheckingInspection", "unused")
-@UseExperimental(ExperimentalUnsignedTypes::class)
-inline class FaceId constructor(inline val value: UByte) {
- companion object {
- @JvmStatic
- val unknown: FaceId = FaceId(0xffu)
- @JvmStatic
- val jingya: FaceId = FaceId(0u)
- @JvmStatic
- val piezui: FaceId = FaceId(1u)
- @JvmStatic
- val se: FaceId = FaceId(2u)
- @JvmStatic
- val fadai: FaceId = FaceId(3u)
- @JvmStatic
- val deyi: FaceId = FaceId(4u)
- @JvmStatic
- val liulei: FaceId = FaceId(5u)
- @JvmStatic
- val haixiu: FaceId = FaceId(6u)
- @JvmStatic
- val bizui: FaceId = FaceId(7u)
- @JvmStatic
- val shui: FaceId = FaceId(8u)
- @JvmStatic
- val daku: FaceId = FaceId(9u)
- @JvmStatic
- val ganga: FaceId = FaceId(10u)
- @JvmStatic
- val fanu: FaceId = FaceId(11u)
- @JvmStatic
- val tiaopi: FaceId = FaceId(12u)
- @JvmStatic
- val ciya: FaceId = FaceId(13u)
- @JvmStatic
- val weixiao: FaceId = FaceId(14u)
- @JvmStatic
- val nanguo: FaceId = FaceId(15u)
- @JvmStatic
- val ku: FaceId = FaceId(16u)
- @JvmStatic
- val zhuakuang: FaceId = FaceId(18u)
- @JvmStatic
- val tu: FaceId = FaceId(19u)
- @JvmStatic
- val touxiao: FaceId = FaceId(20u)
- @JvmStatic
- val keai: FaceId = FaceId(21u)
- @JvmStatic
- val baiyan: FaceId = FaceId(22u)
- @JvmStatic
- val aoman: FaceId = FaceId(23u)
- @JvmStatic
- val ji_e: FaceId = FaceId(24u)
- @JvmStatic
- val kun: FaceId = FaceId(25u)
- @JvmStatic
- val jingkong: FaceId = FaceId(26u)
- @JvmStatic
- val liuhan: FaceId = FaceId(27u)
- @JvmStatic
- val hanxiao: FaceId = FaceId(28u)
- @JvmStatic
- val dabing: FaceId = FaceId(29u)
- @JvmStatic
- val fendou: FaceId = FaceId(30u)
- @JvmStatic
- val zhouma: FaceId = FaceId(31u)
- @JvmStatic
- val yiwen: FaceId = FaceId(32u)
- @JvmStatic
- val yun: FaceId = FaceId(34u)
- @JvmStatic
- val zhemo: FaceId = FaceId(35u)
- @JvmStatic
- val shuai: FaceId = FaceId(36u)
- @JvmStatic
- val kulou: FaceId = FaceId(37u)
- @JvmStatic
- val qiaoda: FaceId = FaceId(38u)
- @JvmStatic
- val zaijian: FaceId = FaceId(39u)
- @JvmStatic
- val fadou: FaceId = FaceId(41u)
- @JvmStatic
- val aiqing: FaceId = FaceId(42u)
- @JvmStatic
- val tiaotiao: FaceId = FaceId(43u)
- @JvmStatic
- val zhutou: FaceId = FaceId(46u)
- @JvmStatic
- val yongbao: FaceId = FaceId(49u)
- @JvmStatic
- val dan_gao: FaceId = FaceId(53u)
- @JvmStatic
- val shandian: FaceId = FaceId(54u)
- @JvmStatic
- val zhadan: FaceId = FaceId(55u)
- @JvmStatic
- val dao: FaceId = FaceId(56u)
- @JvmStatic
- val zuqiu: FaceId = FaceId(57u)
- @JvmStatic
- val bianbian: FaceId = FaceId(59u)
- @JvmStatic
- val kafei: FaceId = FaceId(60u)
- @JvmStatic
- val fan: FaceId = FaceId(61u)
- @JvmStatic
- val meigui: FaceId = FaceId(63u)
- @JvmStatic
- val diaoxie: FaceId = FaceId(64u)
- @JvmStatic
- val aixin: FaceId = FaceId(66u)
- @JvmStatic
- val xinsui: FaceId = FaceId(67u)
- @JvmStatic
- val liwu: FaceId = FaceId(69u)
- @JvmStatic
- val taiyang: FaceId = FaceId(74u)
- @JvmStatic
- val yueliang: FaceId = FaceId(75u)
- @JvmStatic
- val qiang: FaceId = FaceId(76u)
- @JvmStatic
- val ruo: FaceId = FaceId(77u)
- @JvmStatic
- val woshou: FaceId = FaceId(78u)
- @JvmStatic
- val shengli: FaceId = FaceId(79u)
- @JvmStatic
- val feiwen: FaceId = FaceId(85u)
- @JvmStatic
- val naohuo: FaceId = FaceId(86u)
- @JvmStatic
- val xigua: FaceId = FaceId(89u)
- @JvmStatic
- val lenghan: FaceId = FaceId(96u)
- @JvmStatic
- val cahan: FaceId = FaceId(97u)
- @JvmStatic
- val koubi: FaceId = FaceId(98u)
- @JvmStatic
- val guzhang: FaceId = FaceId(99u)
- @JvmStatic
- val qiudale: FaceId = FaceId(100u)
- @JvmStatic
- val huaixiao: FaceId = FaceId(101u)
- @JvmStatic
- val zuohengheng: FaceId = FaceId(102u)
- @JvmStatic
- val youhengheng: FaceId = FaceId(103u)
- @JvmStatic
- val haqian: FaceId = FaceId(104u)
- @JvmStatic
- val bishi: FaceId = FaceId(105u)
- @JvmStatic
- val weiqu: FaceId = FaceId(106u)
- @JvmStatic
- val kuaikule: FaceId = FaceId(107u)
- @JvmStatic
- val yinxian: FaceId = FaceId(108u)
- @JvmStatic
- val qinqin: FaceId = FaceId(109u)
- @JvmStatic
- val xia: FaceId = FaceId(110u)
- @JvmStatic
- val kelian: FaceId = FaceId(111u)
- @JvmStatic
- val caidao: FaceId = FaceId(112u)
- @JvmStatic
- val pijiu: FaceId = FaceId(113u)
- @JvmStatic
- val lanqiu: FaceId = FaceId(114u)
- @JvmStatic
- val pingpang: FaceId = FaceId(115u)
- @JvmStatic
- val shiai: FaceId = FaceId(116u)
- @JvmStatic
- val piaochong: FaceId = FaceId(117u)
- @JvmStatic
- val baoquan: FaceId = FaceId(118u)
- @JvmStatic
- val gouyin: FaceId = FaceId(119u)
- @JvmStatic
- val quantou: FaceId = FaceId(120u)
- @JvmStatic
- val chajin: FaceId = FaceId(121u)
- @JvmStatic
- val aini: FaceId = FaceId(122u)
- @JvmStatic
- val bu: FaceId = FaceId(123u)
- @JvmStatic
- val hao: FaceId = FaceId(124u)
- @JvmStatic
- val zhuanquan: FaceId = FaceId(125u)
- @JvmStatic
- val ketou: FaceId = FaceId(126u)
- @JvmStatic
- val huitou: FaceId = FaceId(127u)
- @JvmStatic
- val tiaosheng: FaceId = FaceId(128u)
- @JvmStatic
- val huishou: FaceId = FaceId(129u)
- @JvmStatic
- val jidong: FaceId = FaceId(130u)
- @JvmStatic
- val jiewu: FaceId = FaceId(131u)
- @JvmStatic
- val xianwen: FaceId = FaceId(132u)
- @JvmStatic
- val zuotaiji: FaceId = FaceId(133u)
- @JvmStatic
- val youtaiji: FaceId = FaceId(134u)
- @JvmStatic
- val shuangxi: FaceId = FaceId(136u)
- @JvmStatic
- val bianpao: FaceId = FaceId(137u)
- @JvmStatic
- val denglong: FaceId = FaceId(138u)
- @JvmStatic
- val facai: FaceId = FaceId(139u)
- @JvmStatic
- val K_ge: FaceId = FaceId(140u)
- @JvmStatic
- val gouwu: FaceId = FaceId(141u)
- @JvmStatic
- val youjian: FaceId = FaceId(142u)
- @JvmStatic
- val shuai_qi: FaceId = FaceId(143u)
- @JvmStatic
- val hecai: FaceId = FaceId(144u)
- @JvmStatic
- val qidao: FaceId = FaceId(145u)
- @JvmStatic
- val baojin: FaceId = FaceId(146u)
- @JvmStatic
- val bangbangtang: FaceId = FaceId(147u)
- @JvmStatic
- val he_nai: FaceId = FaceId(148u)
- @JvmStatic
- val xiamian: FaceId = FaceId(149u)
- @JvmStatic
- val xiangjiao: FaceId = FaceId(150u)
- @JvmStatic
- val feiji: FaceId = FaceId(151u)
- @JvmStatic
- val kaiche: FaceId = FaceId(152u)
- @JvmStatic
- val gaotiezuochetou: FaceId = FaceId(153u)
- @JvmStatic
- val chexiang: FaceId = FaceId(154u)
- @JvmStatic
- val gaotieyouchetou: FaceId = FaceId(155u)
- @JvmStatic
- val duoyun: FaceId = FaceId(156u)
- @JvmStatic
- val xiayu: FaceId = FaceId(157u)
- @JvmStatic
- val chaopiao: FaceId = FaceId(158u)
- @JvmStatic
- val xiongmao: FaceId = FaceId(159u)
- @JvmStatic
- val dengpao: FaceId = FaceId(160u)
- @JvmStatic
- val fengche: FaceId = FaceId(161u)
- @JvmStatic
- val naozhong: FaceId = FaceId(162u)
- @JvmStatic
- val dasan: FaceId = FaceId(163u)
- @JvmStatic
- val caiqiu: FaceId = FaceId(164u)
- @JvmStatic
- val zuanjie: FaceId = FaceId(165u)
- @JvmStatic
- val shafa: FaceId = FaceId(166u)
- @JvmStatic
- val zhijin: FaceId = FaceId(167u)
- @JvmStatic
- val yao: FaceId = FaceId(168u)
- @JvmStatic
- val shouqiang: FaceId = FaceId(169u)
- @JvmStatic
- val qingwa: FaceId = FaceId(170u)
- }
-
- override fun toString(): String = "$FaceId($value)"
-}
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt
index 509dffe4a..f4e6dd98f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt
@@ -47,7 +47,7 @@ sealed class Image : Message {
abstract val imageId: String
final override fun toString(): String {
- return "[image::$imageId]"
+ return "[mirai:$imageId]"
}
final override fun eq(other: Message): Boolean {
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
index 4234275ce..7614f5d4c 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
@@ -57,7 +57,7 @@ interface Message {
*/
interface Key
- infix fun eq(other: Message): Boolean = this == other
+ infix fun eq(other: Message): Boolean = this.toString() == other.toString()
/**
* 将 [toString] 与 [other] 比较
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
index e9412ab1f..af0781be1 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
@@ -38,6 +38,7 @@ import kotlin.reflect.KProperty
interface MessageChain : Message, MutableList {
// region Message override
override operator fun contains(sub: String): Boolean
+
override fun followedBy(tail: Message): MessageChain
// endregion
@@ -67,6 +68,36 @@ interface MessageChain : Message, MutableList {
}
}
+/**
+ * 遍历每一个有内容的消息, 即 [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage]
+ */
+inline fun MessageChain.foreachContent(block: (Message) -> Unit) {
+ this.forEachIndexed { index: Int, message: Message ->
+ if (message is At) {
+ if (index == 0 || this[index - 1] !is QuoteReply) {
+ block(message)
+ }
+ } else if (message.hasContent()) {
+ block(message)
+ }
+ }
+}
+
+/**
+ * 判断这个 [Message] 是否含有内容, 即是否为 [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage]
+ */
+fun Message.hasContent(): Boolean {
+ return when (this) {
+ is At,
+ is AtAll,
+ is PlainText,
+ is Image,
+ is Face,
+ is XMLMessage -> true
+ else -> false
+ }
+}
+
/**
* 提供一个类型的值. 若不存在则会抛出异常 [NoSuchElementException]
*/
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
index 3653cfabb..e5b831b17 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
@@ -31,6 +31,11 @@ interface MessageSource : Message {
*/
val messageUid: Long
+ /**
+ * 发送时间, 单位为秒
+ */
+ val time: Long
+
/**
* 发送人号码
*/
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt
index 66e4a544e..b2f36d33b 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt
@@ -68,7 +68,7 @@ abstract class BotNetworkHandler : CoroutineScope {
*/
@Suppress("SpellCheckingInspection")
@MiraiInternalAPI
- abstract suspend fun relogin()
+ abstract suspend fun relogin(cause: Throwable? = null)
/**
* 初始化获取好友列表等值.
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
index 068e67d4a..f912a8e0f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
@@ -70,6 +70,13 @@ interface MiraiLogger {
*/
val identity: String?
+ /**
+ * 获取 [MiraiLogger] 是否已开启
+ *
+ * 除 [MiraiLoggerWithSwitch] 可控制开关外, 其他的所有 [MiraiLogger] 均一直开启.
+ */
+ val isEnabled: Boolean
+
/**
* 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用.
* [follower] 的存在可以让一次日志被多个日志记录器记录.
@@ -151,43 +158,43 @@ interface MiraiLogger {
inline fun MiraiLogger.verbose(lazyMessage: () -> String) {
- if (this is MiraiLoggerWithSwitch && switch) verbose(lazyMessage())
+ if (isEnabled) verbose(lazyMessage())
}
inline fun MiraiLogger.verbose(lazyMessage: () -> String, e: Throwable?) {
- if (this is MiraiLoggerWithSwitch && switch) verbose(lazyMessage(), e)
+ if (isEnabled) verbose(lazyMessage(), e)
}
inline fun MiraiLogger.debug(lazyMessage: () -> String?) {
- if (this is MiraiLoggerWithSwitch && switch) debug(lazyMessage())
+ if (isEnabled) debug(lazyMessage())
}
inline fun MiraiLogger.debug(lazyMessage: () -> String?, e: Throwable?) {
- if (this is MiraiLoggerWithSwitch && switch) debug(lazyMessage(), e)
+ if (isEnabled) debug(lazyMessage(), e)
}
inline fun MiraiLogger.info(lazyMessage: () -> String?) {
- if (this is MiraiLoggerWithSwitch && switch) info(lazyMessage())
+ if (isEnabled) info(lazyMessage())
}
inline fun MiraiLogger.info(lazyMessage: () -> String?, e: Throwable?) {
- if (this is MiraiLoggerWithSwitch && switch) info(lazyMessage(), e)
+ if (isEnabled) info(lazyMessage(), e)
}
inline fun MiraiLogger.warning(lazyMessage: () -> String?) {
- if (this is MiraiLoggerWithSwitch && switch) warning(lazyMessage())
+ if (isEnabled) warning(lazyMessage())
}
inline fun MiraiLogger.warning(lazyMessage: () -> String?, e: Throwable?) {
- if (this is MiraiLoggerWithSwitch && switch) warning(lazyMessage(), e)
+ if (isEnabled) warning(lazyMessage(), e)
}
inline fun MiraiLogger.error(lazyMessage: () -> String?) {
- if (this is MiraiLoggerWithSwitch && switch) error(lazyMessage())
+ if (isEnabled) error(lazyMessage())
}
inline fun MiraiLogger.error(lazyMessage: () -> String?, e: Throwable?) {
- if (this is MiraiLoggerWithSwitch && switch) error(lazyMessage(), e)
+ if (isEnabled) error(lazyMessage(), e)
}
/**
@@ -268,7 +275,7 @@ class MiraiLoggerWithSwitch internal constructor(private val delegate: MiraiLogg
@PublishedApi
internal var switch: Boolean = default
- val isEnabled: Boolean get() = switch
+ override val isEnabled: Boolean get() = switch
fun enable() {
switch = true
@@ -278,16 +285,16 @@ class MiraiLoggerWithSwitch internal constructor(private val delegate: MiraiLogg
switch = false
}
- override fun verbose0(message: String?) = if (switch) delegate.verbose(message) else Unit
- override fun verbose0(message: String?, e: Throwable?) = if (switch) delegate.verbose(message, e) else Unit
- override fun debug0(message: String?) = if (switch) delegate.debug(message) else Unit
- override fun debug0(message: String?, e: Throwable?) = if (switch) delegate.debug(message, e) else Unit
- override fun info0(message: String?) = if (switch) delegate.info(message) else Unit
- override fun info0(message: String?, e: Throwable?) = if (switch) delegate.info(message, e) else Unit
- override fun warning0(message: String?) = if (switch) delegate.warning(message) else Unit
- override fun warning0(message: String?, e: Throwable?) = if (switch) delegate.warning(message, e) else Unit
- override fun error0(message: String?) = if (switch) delegate.error(message) else Unit
- override fun error0(message: String?, e: Throwable?) = if (switch) delegate.error(message, e) else Unit
+ override fun verbose0(message: String?) = delegate.verbose(message)
+ override fun verbose0(message: String?, e: Throwable?) = delegate.verbose(message, e)
+ override fun debug0(message: String?) = delegate.debug(message)
+ override fun debug0(message: String?, e: Throwable?) = delegate.debug(message, e)
+ override fun info0(message: String?) = delegate.info(message)
+ override fun info0(message: String?, e: Throwable?) = delegate.info(message, e)
+ override fun warning0(message: String?) = delegate.warning(message)
+ override fun warning0(message: String?, e: Throwable?) = delegate.warning(message, e)
+ override fun error0(message: String?) = delegate.error(message)
+ override fun error0(message: String?, e: Throwable?) = delegate.error(message, e)
}
/**
@@ -298,54 +305,65 @@ class MiraiLoggerWithSwitch internal constructor(private val delegate: MiraiLogg
* 在定义 logger 变量时, 请一直使用 [MiraiLogger] 或者 [MiraiLoggerWithSwitch].
*/
abstract class MiraiLoggerPlatformBase : MiraiLogger {
+ override val isEnabled: Boolean get() = true
final override var follower: MiraiLogger? = null
final override fun verbose(message: String?) {
+ if (!isEnabled) return
follower?.verbose(message)
verbose0(message)
}
final override fun verbose(message: String?, e: Throwable?) {
+ if (!isEnabled) return
follower?.verbose(message, e)
verbose0(message, e)
}
final override fun debug(message: String?) {
+ if (!isEnabled) return
follower?.debug(message)
debug0(message)
}
final override fun debug(message: String?, e: Throwable?) {
+ if (!isEnabled) return
follower?.debug(message, e)
debug0(message, e)
}
final override fun info(message: String?) {
+ if (!isEnabled) return
follower?.info(message)
info0(message)
}
final override fun info(message: String?, e: Throwable?) {
+ if (!isEnabled) return
follower?.info(message, e)
info0(message, e)
}
final override fun warning(message: String?) {
+ if (!isEnabled) return
follower?.warning(message)
warning0(message)
}
final override fun warning(message: String?, e: Throwable?) {
+ if (!isEnabled) return
follower?.warning(message, e)
warning0(message, e)
}
final override fun error(message: String?) {
+ if (!isEnabled) return
follower?.error(message)
error0(message)
}
final override fun error(message: String?, e: Throwable?) {
+ if (!isEnabled) return
follower?.error(message, e)
error0(message, e)
}
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/annotataions.kt
similarity index 100%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/annotataions.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt
index 36ac8186e..0b93f83b5 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt
@@ -19,7 +19,9 @@ expect interface ECDHPublicKey {
fun getEncoded(): ByteArray
}
-expect class ECDHKeyPair {
+internal expect class ECDHKeyPairImpl : ECDHKeyPair
+
+interface ECDHKeyPair {
val privateKey: ECDHPrivateKey
val publicKey: ECDHPublicKey
@@ -27,6 +29,15 @@ expect class ECDHKeyPair {
* 私匙和固定公匙([initialPublicKey]) 计算得到的 shareKey
*/
val initialShareKey: ByteArray
+
+ object DefaultStub : ECDHKeyPair {
+ val defaultPublicKey = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes()
+ val defaultShareKey = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes()
+
+ override val privateKey: Nothing get() = error("stub!")
+ override val publicKey: Nothing get() = error("stub!")
+ override val initialShareKey: ByteArray get() = defaultShareKey
+ }
}
/**
@@ -41,6 +52,8 @@ expect class ECDH(keyPair: ECDHKeyPair) {
fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray
companion object {
+ val isECDHAvailable: Boolean
+
/**
* 由完整的 publicKey ByteArray 得到 [ECDHPublicKey]
*/
@@ -60,14 +73,11 @@ expect class ECDH(keyPair: ECDHKeyPair) {
override fun toString(): String
}
-/**
- *
- */
@Suppress("FunctionName")
expect fun ECDH(): ECDH
-val initialPublicKey =
- ECDH.constructPublicKey("3046301006072A8648CE3D020106052B8104001F03320004928D8850673088B343264E0C6BACB8496D697799F37211DEB25BB73906CB089FEA9639B4E0260498B51A992D50813DA8".chunkedHexToBytes())
+val initialPublicKey
+ get() = ECDH.constructPublicKey("3046301006072A8648CE3D020106052B8104001F03320004928D8850673088B343264E0C6BACB8496D697799F37211DEB25BB73906CB089FEA9639B4E0260498B51A992D50813DA8".chunkedHexToBytes())
private val commonHeadFor02 = "302E301006072A8648CE3D020106052B8104001F031A00".chunkedHexToBytes()
private val commonHeadForNot02 = "3046301006072A8648CE3D020106052B8104001F033200".chunkedHexToBytes()
private const val constantHead = "3046301006072A8648CE3D020106052B8104001F03320004"
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/protoBuf.kt
similarity index 99%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/protoBuf.kt
index 1096a3d6d..61dc928be 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/protoBuf.kt
@@ -17,7 +17,6 @@ import kotlinx.io.core.readUInt
import kotlinx.io.core.readULong
import net.mamoe.mirai.utils.MiraiDebugAPI
import net.mamoe.mirai.utils.MiraiExperimentalAPI
-import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.*
import kotlin.jvm.JvmStatic
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/byteArrays.kt
similarity index 100%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/byteArrays.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/conversion.kt
similarity index 97%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/conversion.kt
index 75a93a88e..5e5f6973e 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/conversion.kt
@@ -21,6 +21,16 @@ import kotlin.random.nextInt
* 这些函数为内部函数, 可能会改变
*/
+/**
+ * 255 -> 00 FF
+ */
+fun Short.toByteArray(): ByteArray = with(toInt()) {
+ byteArrayOf(
+ (shr(8) and 0xFF).toByte(),
+ (shr(0) and 0xFF).toByte()
+ )
+}
+
/**
* 255 -> 00 00 00 FF
*/
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/debugging.kt
similarity index 100%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/debugging.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt
similarity index 100%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
similarity index 100%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/maps.kt
similarity index 100%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/maps.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/numbers.kt
similarity index 100%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/numbers.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/platform.kt
similarity index 100%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/platform.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/time.kt
similarity index 98%
rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/time.kt
index b8cfd9ff8..81eba7d3b 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/time.kt
@@ -14,7 +14,6 @@ package net.mamoe.mirai.utils
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
-import kotlin.time.seconds
// 临时使用, 待 Kotlin Duration 稳定后使用 Duration.
// 内联属性, 则将来删除这些 API 将不会导致二进制不兼容.
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/tryNTimes.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/tryNTimes.kt
index 417d8536a..c6b6884d0 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/tryNTimes.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/tryNTimes.kt
@@ -15,17 +15,16 @@ expect fun Throwable.addSuppressed(e: Throwable)
@MiraiInternalAPI
@Suppress("DuplicatedCode")
-inline fun tryNTimes(repeat: Int, block: () -> R): R {
+inline fun tryNTimes(repeat: Int, block: (Int) -> R): R {
var lastException: Throwable? = null
repeat(repeat) {
try {
- return block()
+ return block(it)
} catch (e: Throwable) {
if (lastException == null) {
lastException = e
- }
- lastException!!.addSuppressed(e)
+ } else lastException!!.addSuppressed(e)
}
}
@@ -34,17 +33,16 @@ inline fun tryNTimes(repeat: Int, block: () -> R): R {
@MiraiInternalAPI
@Suppress("DuplicatedCode")
-inline fun tryNTimesOrNull(repeat: Int, block: () -> R): R? {
+inline fun tryNTimesOrNull(repeat: Int, block: (Int) -> R): R? {
var lastException: Throwable? = null
repeat(repeat) {
try {
- return block()
+ return block(it)
} catch (e: Throwable) {
if (lastException == null) {
lastException = e
- }
- lastException!!.addSuppressed(e)
+ } else lastException!!.addSuppressed(e)
}
}
@@ -53,18 +51,17 @@ inline fun tryNTimesOrNull(repeat: Int, block: () -> R): R? {
@MiraiInternalAPI
@Suppress("DuplicatedCode")
-inline fun tryNTimesOrException(repeat: Int, block: () -> R): Throwable? {
+inline fun tryNTimesOrException(repeat: Int, block: (Int) -> R): Throwable? {
var lastException: Throwable? = null
repeat(repeat) {
try {
- block()
+ block(it)
return null
} catch (e: Throwable) {
if (lastException == null) {
lastException = e
- }
- lastException!!.addSuppressed(e)
+ } else lastException!!.addSuppressed(e)
}
}
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotFactoryJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotFactoryJvm.kt
index e1fff684c..ea27fb1eb 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotFactoryJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotFactoryJvm.kt
@@ -29,13 +29,13 @@ internal val factory: BotFactory = run {
"""
No BotFactory found. Please ensure that you've added dependency of protocol modules.
Available modules:
- - net.mamoe:mirai-core-timpc
+ - net.mamoe:mirai-core-timpc (stays at 0.12.0)
- net.mamoe:mirai-core-qqandroid (recommended)
You should have at lease one protocol module installed.
-------------------------------------------------------
找不到 BotFactory. 请确保你依赖了至少一个协议模块.
可用的协议模块:
- - net.mamoe:mirai-core-timpc
+ - net.mamoe:mirai-core-timpc (0.12.0 后停止更新)
- net.mamoe:mirai-core-qqandroid (推荐)
请添加上述任一模块的依赖(与 mirai-core 版本相同)
""".trimIndent()
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/MiraiEnvironmentJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/MiraiEnvironmentJvm.kt
deleted file mode 100644
index ceebf145b..000000000
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/MiraiEnvironmentJvm.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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
- */
-
-@file:Suppress("MayBeConstant", "unused")
-
-package net.mamoe.mirai
-
-actual object MiraiEnvironment {
- @JvmStatic
- actual val platform: Platform
- get() = Platform.JVM
-}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventInternalJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventInternalJvm.kt
index ccc2a718f..ddae0cdd2 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventInternalJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventInternalJvm.kt
@@ -16,15 +16,16 @@ import net.mamoe.mirai.event.ListeningStatus
import net.mamoe.mirai.utils.MiraiInternalAPI
import java.util.function.Consumer
import java.util.function.Function
+import kotlin.coroutines.EmptyCoroutineContext
@MiraiInternalAPI
@Suppress("FunctionName")
fun Class._subscribeEventForJaptOnly(scope: CoroutineScope, onEvent: Function): Listener {
- return this.kotlin.subscribeInternal(scope.Handler { onEvent.apply(it) })
+ return this.kotlin.subscribeInternal(scope.Handler(EmptyCoroutineContext) { onEvent.apply(it) })
}
@MiraiInternalAPI
@Suppress("FunctionName")
fun Class._subscribeEventForJaptOnly(scope: CoroutineScope, onEvent: Consumer): Listener {
- return this.kotlin.subscribeInternal(scope.Handler { onEvent.accept(it); ListeningStatus.LISTENING; })
+ return this.kotlin.subscribeInternal(scope.Handler(EmptyCoroutineContext) { onEvent.accept(it); ListeningStatus.LISTENING; })
}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/MiraiAtomicBoolean.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/MiraiAtomicBoolean.kt
new file mode 100644
index 000000000..0b468cf04
--- /dev/null
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/MiraiAtomicBoolean.kt
@@ -0,0 +1,18 @@
+package net.mamoe.mirai.event.internal
+
+import java.util.concurrent.atomic.AtomicBoolean
+
+
+internal actual class MiraiAtomicBoolean actual constructor(initial: Boolean) {
+ private val delegate: AtomicBoolean = AtomicBoolean(initial)
+
+ actual fun compareAndSet(expect: Boolean, update: Boolean): Boolean {
+ return delegate.compareAndSet(expect, update)
+ }
+
+ actual var value: Boolean
+ get() = delegate.get()
+ set(value) {
+ delegate.set(value)
+ }
+}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
index fe4402025..5f9115b07 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
@@ -105,8 +105,8 @@ class DefaultLoginSolver(
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? = loginSolverLock.withLock {
val logger = getLogger(bot)
logger.info("需要进行账户安全认证")
- logger.info("该账户有[设备锁]/[不常用登陆地点]/[不常用设备登陆]的问题")
- logger.info("完成以下账号认证即可成功登陆|理论本认证在mirai每个账户中最多出现1次")
+ logger.info("该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题")
+ logger.info("完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次")
logger.info("请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符")
logger.info("这步操作将在后续的版本中优化")
logger.info(url)
@@ -221,11 +221,11 @@ actual open class BotConfiguration actual constructor() {
/**
* 重连失败后, 继续尝试的每次等待时间
*/
- actual var reconnectPeriodMillis: Long = 60.secondsToMillis
+ actual var reconnectPeriodMillis: Long = 5.secondsToMillis
/**
* 最多尝试多少次重连
*/
- actual var reconnectionRetryTimes: Int = 3
+ actual var reconnectionRetryTimes: Int = Int.MAX_VALUE
/**
* 验证码处理器
*/
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHJvm.kt
index 0d6e93829..a0e236b71 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHJvm.kt
@@ -9,7 +9,6 @@
package net.mamoe.mirai.utils.cryptor
-import net.mamoe.mirai.utils.io.chunkedHexToBytes
import net.mamoe.mirai.utils.md5
import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.*
@@ -21,20 +20,13 @@ import javax.crypto.KeyAgreement
actual typealias ECDHPrivateKey = PrivateKey
actual typealias ECDHPublicKey = PublicKey
-actual class ECDHKeyPair(
- private val delegate: KeyPair?
-) {
- actual val privateKey: ECDHPrivateKey get() = delegate?.private ?: error("ECDH is not available")
- actual val publicKey: ECDHPublicKey get() = delegate?.public ?: defaultPublicKey
+internal actual class ECDHKeyPairImpl(
+ private val delegate: KeyPair
+) : ECDHKeyPair {
+ override val privateKey: ECDHPrivateKey get() = delegate.private
+ override val publicKey: ECDHPublicKey get() = delegate.public
- actual val initialShareKey: ByteArray = if (delegate == null) {
- defaultShareKey
- } else ECDH.calculateShareKey(privateKey, initialPublicKey)
-
- companion object {
- internal val defaultPublicKey = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes().adjustToPublicKey()
- internal val defaultShareKey = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes()
- }
+ override val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
}
@Suppress("FunctionName")
@@ -42,33 +34,34 @@ actual fun ECDH() = ECDH(ECDH.generateKeyPair())
actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
actual companion object {
- private var isECDHAvailable = true
+ @Suppress("ObjectPropertyName")
+ private val _isECDHAvailable: Boolean = kotlin.runCatching {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) != null) {
+ Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
+ }
+ Security.addProvider(BouncyCastleProvider())
+ generateKeyPair() // try if it is working
+ }.isSuccess
- init {
- isECDHAvailable = kotlin.runCatching {
- Security.addProvider(BouncyCastleProvider())
- generateKeyPair() // try if it is working
- }.isSuccess
- }
+ actual val isECDHAvailable: Boolean get() = _isECDHAvailable
actual fun generateKeyPair(): ECDHKeyPair {
- return if (!isECDHAvailable) {
- ECDHKeyPair(null)
- } else ECDHKeyPair(KeyPairGenerator.getInstance("EC", "BC").apply { initialize(ECGenParameterSpec("secp192k1")) }.genKeyPair())
+ if (!isECDHAvailable) {
+ return ECDHKeyPair.DefaultStub
+ }
+ return ECDHKeyPairImpl(KeyPairGenerator.getInstance("ECDH")
+ .also { it.initialize(ECGenParameterSpec("secp192k1")) }
+ .genKeyPair())
}
actual fun calculateShareKey(
privateKey: ECDHPrivateKey,
publicKey: ECDHPublicKey
): ByteArray {
- return if (!isECDHAvailable) {
- ECDHKeyPair.defaultShareKey
- } else {
- val instance = KeyAgreement.getInstance("ECDH", "BC")
- instance.init(privateKey)
- instance.doPhase(publicKey, true)
- md5(instance.generateSecret())
- }
+ val instance = KeyAgreement.getInstance("ECDH", "BC")
+ instance.init(privateKey)
+ instance.doPhase(publicKey, true)
+ return md5(instance.generateSecret())
}
actual fun constructPublicKey(key: ByteArray): ECDHPublicKey {
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
index 07f22e1e3..f6c1d2513 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
@@ -30,7 +30,10 @@ actual class PlatformSocket : Closeable {
private lateinit var socket: Socket
actual val isOpen: Boolean
- get() = socket.isConnected
+ get() =
+ if (::socket.isInitialized)
+ socket.isConnected
+ else false
actual override fun close() {
if (::socket.isInitialized) {
diff --git a/mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt b/mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt
index 413a510ce..1ffc86c6a 100644
--- a/mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt
+++ b/mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt
@@ -63,9 +63,6 @@ internal class LockFreeLinkedListTest {
val addJob = async { list.concurrentDo(2, 30000) { addLast(1) } }
//delay(1) // let addJob fly
- if (addJob.isCompleted) {
- println("Number of elements are not enough")
- }
val foreachJob = async {
list.concurrentDo(1, 10000) {
forEach { it + it }
diff --git a/mirai-japt/README.md b/mirai-japt/README.md
index 234ef52b6..73586ab92 100644
--- a/mirai-japt/README.md
+++ b/mirai-japt/README.md
@@ -32,7 +32,7 @@ Mirai Java Apt
net.mamoe
- mirai-core-qqandroid
+ mirai-core-qqandroid-jvm
CORE_VERSION
@@ -51,7 +51,7 @@ repositories {
}
dependencies {
- implementation("net.mamoe:mirai-core-qqandroid:CORE_VERSION")
+ implementation("net.mamoe:mirai-core-qqandroid-jvm:CORE_VERSION")
implementation("net.mamoe:mirai-japt:JAPT_VERSION")
}
```
diff --git a/mirai-japt/build.gradle.kts b/mirai-japt/build.gradle.kts
index 3bd7d20a5..423789c2e 100644
--- a/mirai-japt/build.gradle.kts
+++ b/mirai-japt/build.gradle.kts
@@ -59,7 +59,7 @@ fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$v
fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version"
dependencies {
- api(project(":mirai-core"))
+ implementation(project(":mirai-core"))
runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) // classpath is not added correctly by IDE
api(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-javafx", version = "1.3.2")
diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java
index fb55493a6..6b2e17197 100644
--- a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java
+++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java
@@ -184,5 +184,13 @@ public interface BlockingBot {
/**
* 关闭这个 [Bot], 停止一切相关活动. 不可重新登录.
*/
- void dispose(@Nullable Throwable throwable);
+ void close(@Nullable Throwable throwable);
+
+ /**
+ * @deprecated 使用 {@link #close(Throwable)}
+ */
+ @Deprecated
+ default void dispose(@Nullable Throwable throwable) {
+ close(throwable);
+ }
}
diff --git a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingBotImpl.kt b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingBotImpl.kt
index 0fbc212b4..d589fed7c 100644
--- a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingBotImpl.kt
+++ b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingBotImpl.kt
@@ -65,5 +65,5 @@ internal class BlockingBotImpl(private val bot: Bot) : BlockingBot {
override fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult = runBlocking { bot.addFriend(id, message, remark) }
override fun approveFriendAddRequest(id: Long, remark: String?) = runBlocking { bot.approveFriendAddRequest(id, remark) }
- override fun dispose(throwable: Throwable?) = bot.close(throwable)
+ override fun close(throwable: Throwable?) = bot.close(throwable)
}
\ No newline at end of file