From ff8e6e8b80593eab867bb5175891f2bc1bba6832 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 16:29:39 +0800 Subject: [PATCH 01/52] Add sequenceId to MessageSource --- .../mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt | 2 ++ .../kotlin/net.mamoe.mirai/message/data/MessageSource.kt | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt index fe4aab43f..e0d351c50 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt @@ -22,6 +22,7 @@ internal inline class MessageSourceFromServer( val delegate: ImMsgBody.SourceMsg ) : MessageSource { override val time: Long get() = delegate.time.toLong() and 0xFFFFFFFF + override val sequenceId: Int get() = delegate.origSeqs?.firstOrNull() ?: error("cannot find sequenceId from ImMsgBody.SourceMsg") override val messageUid: Long get() = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!! override val sourceMessage: MessageChain get() = delegate.toMessageChain() override val senderId: Long get() = delegate.senderUin @@ -34,6 +35,7 @@ internal inline class MessageSourceFromMsg( val delegate: MsgComm.Msg ) : MessageSource { override val time: Long get() = delegate.msgHead.msgTime.toLong() and 0xFFFFFFFF + override val sequenceId: Int get() = delegate.msgHead.msgSeq override val messageUid: Long get() = delegate.msgBody.richText.attr!!.random.toLong() override val sourceMessage: MessageChain get() = delegate.toMessageChain() override val senderId: Long get() = delegate.msgHead.fromUin 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 e5b831b17..6804a18fc 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 @@ -26,6 +26,11 @@ import kotlin.jvm.JvmName interface MessageSource : Message { companion object Key : Message.Key + /** + * 序列号 + */ + val sequenceId: Int + /** * 实际上是个随机数, 但服务器确实是用它当做 uid */ From bd379c3acc6f43d1c1b9678b93212ee13c52cb33 Mon Sep 17 00:00:00 2001 From: jasonczc Date: Fri, 21 Feb 2020 17:36:51 +0800 Subject: [PATCH 02/52] Update docs --- docs/guide_getting_started.md | 4 +- docs/guide_subscribe_events.md | 114 +++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 docs/guide_subscribe_events.md diff --git a/docs/guide_getting_started.md b/docs/guide_getting_started.md index 3cd88b1db..f705ccefd 100644 --- a/docs/guide_getting_started.md +++ b/docs/guide_getting_started.md @@ -69,7 +69,7 @@ JDK要求6以上 ### 4 Try Bot - 在src/main文件夹下新建文件夹,命名为```kotlin``` -- 在```kotlin```下新建包(在```kotlin```文件夹上右键-```New```-```Packages```) 包名为```net.mamoe.mirai.simpleloader``` +- 在```kotlin```下新建包(在```kotlin```文件夹上右键-```New```-```Package```) 包名为```net.mamoe.mirai.simpleloader``` - 在包下新建kotlin文件```MyLoader.kt``` @@ -103,9 +103,9 @@ suspend fun main() { - 本例的功能中,在任意群内任意成员发送包含“舔”字或“刘老板”字样的消息,MiraiBot会回复“刘老板太强了” - 至此,简单的入门已经结束,下面可根据不同的需求参阅wiki进行功能的添加。 +下面,可以尝试对不同事件进行监听[Mirai Guide - Subscribe Events](/docs/guide_subscribe_events.md) ### 此外,还可以使用Maven作为包管理工具 本项目推荐使用gradle,因此不提供详细入门指导 diff --git a/docs/guide_subscribe_events.md b/docs/guide_subscribe_events.md new file mode 100644 index 000000000..de0035001 --- /dev/null +++ b/docs/guide_subscribe_events.md @@ -0,0 +1,114 @@ +# Mirai Guide - Subscribe Event + +由于Mirai项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020-02-21```,对应版本```0.17.0``` + +本页面采用Kotlin作为开发语言,**若你希望使用 Java 开发**, 请参阅: [mirai-japt](mirai-japt/README.md) + +本页面是[Mirai Guide - Getting Started](/docs/guide_getting_started.md)的后续Guide + +## 消息事件-Message Event + +首先我们来回顾上一个Guide的源码 + +```kotlin +suspend fun main() { + val qqId = 10000L//Bot的QQ号,需为Long类型,在结尾处添加大写L + val password = "your_password"//Bot的密码 + val miraiBot = Bot(qqId, password).alsoLogin()//新建Bot并登录 + miraiBot.subscribeMessages { + "你好" reply "你好!" + case("at me") { + reply(sender.at() + " 给爷爬 ") + } + + (contains("舔") or contains("刘老板")) { + "刘老板太强了".reply() + } + } + miraiBot.join() // 等待 Bot 离线, 避免主线程退出 +} +``` + +在本例中,```miraiBot```是一个Bot对象,让其登录,然后对```Message Event```进行了监听。 + +对于``````Message Event``````,```Mirai```提供了较其他Event更强大的[MessageSubscribersBuilder](https://github.com/mamoe/mirai/wiki/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt#L140),本例也采用了[MessageSubscribersBuilder](https://github.com/mamoe/mirai/wiki/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt#L140)。其他具体使用方法可以参考[Wiki:Message Event](https://github.com/mamoe/mirai/wiki/Development-Guide---Kotlin#Message-Event)部分。 + + + +## 事件-Event + +上一节中提到的```Message Event```仅仅是众多```Event```的这一种,其他```Event```有群员加入群,离开群,私聊等等... + +具体doc暂不提供,现可翻阅源码[**BotEvents.kt**](https://github.com/mamoe/mirai/blob/master/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt),查看注释。当前事件仍在扩充中,可能有一定不足。 + +下面我们开始示例对一些事件进行监听。 + + + +## 尝试监听事件-Try Subscribe Events + +### 监听加群事件 + +在代码中的```miraiBot.join()```前添加 + +```kotlin +miraiBot.subscribeAlways { + it.group.sendMessage("欢迎 ${it.member.nameCardOrNick} 加入本群!") +} +``` + +本段语句监听了加入群的事件。 + +### 监听禁言事件 + +在代码中添加 + +```kotlin +miraiBot.subscribeAlways (){ + it.group.sendMessage("恭喜老哥 ${it.member.nameCardOrNick} 喜提禁言套餐一份") +} +``` + +在被禁言后,Bot将发送恭喜语句。 + +### 添加后的可执行代码 + +至此,当前的代码为 + +```kotlin +package net.mamoe.mirai.simpleloader + +import kotlinx.coroutines.* +import net.mamoe.mirai.Bot +import net.mamoe.mirai.alsoLogin +import net.mamoe.mirai.event.subscribeMessages +import net.mamoe.mirai.contact.nameCardOrNick +import net.mamoe.mirai.contact.sendMessage +import net.mamoe.mirai.event.events.MemberJoinEvent +import net.mamoe.mirai.event.events.MemberMuteEvent +import net.mamoe.mirai.event.subscribeAlways + +suspend fun main() { + val qqId = 10000L//Bot的QQ号,需为Long类型,在结尾处添加大写L + val password = "your_password"//Bot的密码 + val miraiBot = Bot(qqId, password).alsoLogin()//新建Bot并登录 + miraiBot.subscribeMessages { + "你好" reply "你好!" + case("at me") { + reply(sender.at() + " 给爷爬 ") + } + + (contains("舔") or contains("刘老板")) { + "刘老板太强了".reply() + } + } + miraiBot.subscribeAlways { + it.group.sendMessage("欢迎 ${it.member.nameCardOrNick} 加入本群!") + } + miraiBot.subscribeAlways (){ + it.group.sendMessage("恭喜老哥 ${it.member.nameCardOrNick} 喜提禁言套餐一份") + } + miraiBot.join() // 等待 Bot 离线, 避免主线程退出 +} +``` + From da72d3bbb64702c61e581e7fe6ef4a049efd3231 Mon Sep 17 00:00:00 2001 From: jasonczc Date: Fri, 21 Feb 2020 17:38:24 +0800 Subject: [PATCH 03/52] format docs --- docs/guide_subscribe_events.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_subscribe_events.md b/docs/guide_subscribe_events.md index de0035001..36c062890 100644 --- a/docs/guide_subscribe_events.md +++ b/docs/guide_subscribe_events.md @@ -1,4 +1,4 @@ -# Mirai Guide - Subscribe Event +# Mirai Guide - Subscribe Events 由于Mirai项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020-02-21```,对应版本```0.17.0``` @@ -45,7 +45,7 @@ suspend fun main() { -## 尝试监听事件-Try Subscribe Events +## 尝试监听事件-Try Subscribing Events ### 监听加群事件 From 587fab23e12857f43d292de4962a9d2e398bbf23 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 18:21:58 +0800 Subject: [PATCH 04/52] Support recall, add MessageReceipt --- .../net/mamoe/mirai/qqandroid/ContactImpl.kt | 50 ++++++- .../protocol/data/proto/MsgRevokeUserDef.kt | 38 +++++ .../network/protocol/packet/PacketFactory.kt | 4 +- .../protocol/packet/chat/PbMessageSvc.kt | 91 ++++++++++++ .../packet/chat/receive/MessageSvc.kt | 65 +++++++- .../packet/chat/receive/OnlinePush.kt | 12 +- .../androidPacketTests/clientToServer.kt | 2 + .../kotlin/test/ProtoBufDataClassGenerator.kt | 2 +- .../commonMain/kotlin/net.mamoe.mirai/Bot.kt | 4 +- .../kotlin/net.mamoe.mirai/contact/Contact.kt | 12 +- .../kotlin/net.mamoe.mirai/contact/Group.kt | 90 ++++++++++- .../kotlin/net.mamoe.mirai/contact/QQ.kt | 19 +++ .../kotlin/net.mamoe.mirai/data/GroupInfo.kt | 11 ++ .../event/{Subscribable.kt => Event.kt} | 0 .../kotlin/net.mamoe.mirai/event/linear.kt | 11 ++ ...ageSubscribers.kt => subscribeMessages.kt} | 0 .../event/{Subscribers.kt => subscriber.kt} | 0 .../net.mamoe.mirai/message/GroupMessage.kt | 10 +- .../net.mamoe.mirai/message/MessagePacket.kt | 27 +--- .../net.mamoe.mirai/message/MessageReceipt.kt | 108 ++++++++++++++ .../message/data/MessageChain.kt | 140 +++--------------- .../message/data/MessageSource.kt | 2 +- .../net.mamoe.mirai/message/data/PlainText.kt | 13 +- 23 files changed, 530 insertions(+), 181 deletions(-) create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgRevokeUserDef.kt create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt rename mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/{Subscribable.kt => Event.kt} (100%) create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt rename mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/{MessageSubscribers.kt => subscribeMessages.kt} (100%) rename mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/{Subscribers.kt => subscriber.kt} (100%) create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index 29a17b2c9..cda9fcc33 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -16,14 +16,13 @@ import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.* import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent -import net.mamoe.mirai.message.data.CustomFaceFromFile -import net.mamoe.mirai.message.data.Image -import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.message.data.NotOnlineImageFromFile +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.* import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper import net.mamoe.mirai.qqandroid.network.highway.postImage import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352 +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn @@ -66,7 +65,7 @@ internal class QQImpl( override val nick: String get() = friendInfo.nick - override suspend fun sendMessage(message: MessageChain) { + override suspend fun sendMessage(message: MessageChain): MessageReceipt { val event = FriendMessageSendEvent(this, message).broadcast() if (event.isCancelled) { throw EventCancelledException("cancelled by FriendMessageSendEvent") @@ -80,6 +79,7 @@ internal class QQImpl( ).sendAndExpect() is MessageSvc.PbSendMsg.Response.SUCCESS ) { "send message failed" } } + return MessageReceipt(message, this) } override suspend fun uploadImage(image: ExternalImage): Image = try { @@ -325,6 +325,24 @@ internal class GroupImpl( override lateinit var owner: Member + @UseExperimental(MiraiExperimentalAPI::class) + override val botAsMember: Member by lazy { + Member(object : MemberInfo { + override val nameCard: String + get() = bot.nick // TODO: 2020/2/21 机器人在群内的昵称获取 + override val permission: MemberPermission + get() = botPermission + override val specialTitle: String + get() = "" // TODO: 2020/2/21 获取机器人在群里的头衔 + override val muteTimestamp: Int + get() = botMuteRemaining + override val uin: Long + get() = bot.uin + override val nick: String + get() = bot.nick + }) + } + @UseExperimental(MiraiExperimentalAPI::class) override lateinit var botPermission: MemberPermission @@ -340,6 +358,9 @@ internal class GroupImpl( override val members: ContactList = ContactList(members.mapNotNull { if (it.uin == bot.uin) { botPermission = it.permission + if (it.permission == MemberPermission.OWNER) { + owner = botAsMember + } null } else Member(it).also { member -> if (member.permission == MemberPermission.OWNER) { @@ -475,6 +496,18 @@ internal class GroupImpl( TODO("not implemented") } + override suspend fun recall(source: MessageSource) { + if (source.senderId != bot.uin) { + checkBotPermissionOperator() + } + + bot.network.run { + val response = PbMessageSvc.PbMsgWithDraw.Group(bot.client, this@GroupImpl.id, source.sequenceId, source.messageUid.toInt()) + .sendAndExpect() + check(response is PbMessageSvc.PbMsgWithDraw.Response.Success) { "Failed to recall message #${source.sequenceId}: $response" } + } + } + @UseExperimental(MiraiExperimentalAPI::class) override fun Member(memberInfo: MemberInfo): Member { return MemberImpl( @@ -498,7 +531,7 @@ internal class GroupImpl( return members.delegate.filteringGetOrNull { it.id == id } } - override suspend fun sendMessage(message: MessageChain) { + override suspend fun sendMessage(message: MessageChain): MessageReceipt { check(!isBotMuted) { "bot is muted. Remaining seconds=$botMuteRemaining" } val event = GroupMessageSendEvent(this, message).broadcast() if (event.isCancelled) { @@ -514,6 +547,11 @@ internal class GroupImpl( response is MessageSvc.PbSendMsg.Response.SUCCESS ) { "send message failed: $response" } } + + ((message.last() as MessageSource) as MessageSvc.PbSendMsg.MessageSourceFromSend) + .startWaitingSequenceId(this) + + return MessageReceipt(message, this) } override suspend fun uploadImage(image: ExternalImage): Image = try { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgRevokeUserDef.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgRevokeUserDef.kt new file mode 100644 index 000000000..86d34da88 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgRevokeUserDef.kt @@ -0,0 +1,38 @@ +/* + * 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.qqandroid.network.protocol.data.proto + +import kotlinx.serialization.SerialId +import kotlinx.serialization.Serializable +import net.mamoe.mirai.qqandroid.io.ProtoBuf + +class MsgRevokeUserDef : ProtoBuf { + @Serializable + class MsgInfoUserDef( + @SerialId(1) val longMessageFlag: Int = 0, + @SerialId(2) val longMsgInfo: List? = null, + @SerialId(3) val fileUuid: List = listOf() + ) : ProtoBuf { + @Serializable + class MsgInfoDef( + @SerialId(1) val msgSeq: Int = 0, + @SerialId(2) val longMsgId: Int = 0, + @SerialId(3) val longMsgNum: Int = 0, + @SerialId(4) val longMsgIndex: Int = 0 + ) : ProtoBuf + } + + @Serializable + class UinTypeUserDef( + @SerialId(1) val fromUinType: Int = 0, + @SerialId(2) val fromGroupCode: Long = 0L, + @SerialId(3) val fileUuid: List = listOf() + ) : ProtoBuf +} \ 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 55b6dae7b..2c20312dc 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 @@ -14,6 +14,7 @@ import kotlinx.io.pool.useInstance import net.mamoe.mirai.data.Packet import net.mamoe.mirai.event.Event import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn @@ -138,7 +139,8 @@ internal object KnownPacketFactories { TroopManagement.GetGroupInfo, TroopManagement.EditGroupNametag, TroopManagement.Kick, - Heartbeat.Alive + Heartbeat.Alive, + PbMessageSvc.PbMsgWithDraw ) object IncomingFactories : List> by mutableListOf( diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt new file mode 100644 index 000000000..eab98c991 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt @@ -0,0 +1,91 @@ +/* + * 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.qqandroid.network.protocol.packet.chat + +import kotlinx.io.core.ByteReadPacket +import net.mamoe.mirai.data.Packet +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf +import net.mamoe.mirai.qqandroid.io.serialization.toByteArray +import net.mamoe.mirai.qqandroid.io.serialization.writeProtoBuf +import net.mamoe.mirai.qqandroid.network.QQAndroidClient +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgRevokeUserDef +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory +import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket + +internal class PbMessageSvc { + object PbMsgWithDraw : OutgoingPacketFactory( + "PbMessageSvc.PbMsgWithDraw" + ) { + sealed class Response : Packet { + object Success : Response() { + override fun toString(): String { + return "PbMessageSvc.PbMsgWithDraw.Response.Success" + } + } + + data class Failed( + val result: Int, + val errorMessage: String + ) : Response() + } + + // 12 1A 08 01 10 00 18 E7 C1 AD B8 02 22 0A 08 BF BA 03 10 BF 81 CB B7 03 2A 02 08 00 + fun Group( + client: QQAndroidClient, + groupCode: Long, + messageSequenceId: Int, // 56639 + messageRandom: Int, // 921878719 + messageType: Int = 0 + ): OutgoingPacket = buildOutgoingUniPacket(client) { + writeProtoBuf( + MsgSvc.PbMsgWithDrawReq.serializer(), + MsgSvc.PbMsgWithDrawReq( + groupWithDraw = listOf( + MsgSvc.PbGroupMsgWithDrawReq( + subCmd = 1, + groupType = 0, // 普通群 + groupCode = groupCode, + msgList = listOf( + MsgSvc.PbGroupMsgWithDrawReq.MessageInfo( + msgSeq = messageSequenceId, + msgRandom = messageRandom, + msgType = messageType + ) + ), + userdef = MsgRevokeUserDef.MsgInfoUserDef( + longMessageFlag = 0 + ).toByteArray(MsgRevokeUserDef.MsgInfoUserDef.serializer()) + ) + ) + ) + ) + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + val resp = readProtoBuf(MsgSvc.PbMsgWithDrawResp.serializer()) + resp.groupWithDraw?.firstOrNull()?.let { + if (it.result != 0) { + return Response.Failed(it.result, it.errmsg) + } + return Response.Success + } + resp.c2cWithDraw?.firstOrNull()?.let { + if (it.result != 0) { + return Response.Failed(it.result, it.errmsg) + } + return Response.Success + } + return Response.Failed(-1, "No response") + } + } +} \ No newline at end of file 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 76d8e6e33..1c0ea0752 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 @@ -9,18 +9,25 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact +import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Group 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.ListeningStatus import net.mamoe.mirai.event.events.BotJoinGroupEvent import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.MemberJoinEvent +import net.mamoe.mirai.event.subscribe import net.mamoe.mirai.message.FriendMessage import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.message.data.addOrRemove import net.mamoe.mirai.qqandroid.GroupImpl import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket @@ -262,6 +269,36 @@ internal class MessageSvc { } } + internal class MessageSourceFromSend( + override val messageUid: Long, + override val time: Long, + override val senderId: Long, + override val groupId: Long, + override val sourceMessage: MessageChain + ) : MessageSource { + lateinit var sequenceIdDeferred: CompletableDeferred + + fun startWaitingSequenceId(contact: Contact) { + sequenceIdDeferred = CompletableDeferred() + contact.subscribe { event -> + if (event.messageRandom == messageUid.toInt()) { + sequenceIdDeferred.complete(event.sequenceId) + return@subscribe ListeningStatus.STOPPED + } + + return@subscribe ListeningStatus.LISTENING + } + } + + @UseExperimental(ExperimentalCoroutinesApi::class) + override val sequenceId: Int + get() = sequenceIdDeferred.getCompleted() + + override fun toString(): String { + return "" + } + } + /** * 发送好友消息 */ @@ -271,9 +308,17 @@ internal class MessageSvc { toUin: Long, message: MessageChain ): OutgoingPacket = buildOutgoingUniPacket(client) { - ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) + val source = MessageSourceFromSend( + messageUid = Random.nextInt().absoluteValue.toLong() and 0xffffffff, + senderId = client.uin, + time = currentTimeSeconds + client.timeDifference, + groupId = 0, + sourceMessage = message + ) + message.addOrRemove(source) + ///return@buildOutgoingUniPacket writeProtoBuf( MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( @@ -285,8 +330,8 @@ internal class MessageSvc { ) ), msgSeq = client.atomicNextMessageSequenceId(), - msgRand = Random.nextInt().absoluteValue, - syncCookie = SyncCookie(time = currentTimeSeconds).toByteArray(SyncCookie.serializer()) + msgRand = source.messageUid.toInt(), + syncCookie = SyncCookie(time = source.time).toByteArray(SyncCookie.serializer()) // msgVia = 1 ) ) @@ -302,11 +347,19 @@ internal class MessageSvc { message: MessageChain ): OutgoingPacket = buildOutgoingUniPacket(client) { + val source = MessageSourceFromSend( + messageUid = Random.nextInt().absoluteValue.toLong() and 0xffffffff, + senderId = client.uin, + time = currentTimeSeconds + client.timeDifference, + groupId = groupCode, + sourceMessage = message + ) + message.addOrRemove(source) + ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) // DebugLogger.debug("sending group message: " + message.toRichTextElems().contentToString()) - val seq = client.atomicNextMessageSequenceId() ///return@buildOutgoingUniPacket writeProtoBuf( MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( @@ -317,8 +370,8 @@ internal class MessageSvc { elems = message.toRichTextElems() ) ), - msgSeq = seq, - msgRand = Random.nextInt().absoluteValue, + msgSeq = client.atomicNextMessageSequenceId(), + msgRand = source.messageUid.toInt(), syncCookie = EMPTY_BYTE_ARRAY, msgVia = 1 ) 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 3811853bf..0f92e0cca 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 @@ -19,6 +19,7 @@ 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.Event import net.mamoe.mirai.event.events.* import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.qqandroid.GroupImpl @@ -46,9 +47,14 @@ internal class OnlinePush { /** * 接受群消息 */ - internal object PbPushGroupMsg : IncomingPacketFactory("OnlinePush.PbPushGroupMsg") { + internal object PbPushGroupMsg : IncomingPacketFactory("OnlinePush.PbPushGroupMsg") { + internal class SendGroupMessageReceipt( + val messageRandom: Int, + val sequenceId: Int + ) : Packet, Event + @UseExperimental(ExperimentalStdlibApi::class) - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): GroupMessage? { + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? { // 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 null val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer()) @@ -56,7 +62,7 @@ internal class OnlinePush { val extraInfo: ImMsgBody.ExtraInfo? = pbPushMsg.msg.msgBody.richText.elems.firstOrNull { it.extraInfo != null }?.extraInfo if (pbPushMsg.msg.msgHead.fromUin == bot.uin) { - return null + return SendGroupMessageReceipt(pbPushMsg.msg.msgBody.richText.attr!!.random, pbPushMsg.msg.msgHead.msgSeq) } val group = bot.getGroup(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt index 01eae0859..fcc294793 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt @@ -107,6 +107,8 @@ fun main() { * 顶层方法. TCP 切掉头后直接来这里 */ fun ByteReadPacket.decodeMultiClientToServerPackets() { + DebugLogger.enable() + PacketLogger.enable() println("=======================处理客户端到服务器=======================") var count = 0 while (remaining != 0L) { diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt index d7bc7319d..17eacf023 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt @@ -25,7 +25,7 @@ fun main() { println( File( """ - E:\Projects\QQAndroidFF\app\src\main\java\tencent\im\statsvc\getonline + E:\Projects\QQAndroidFF\app\src\main\java\tencent\im\msgrevoke """.trimIndent() ) .generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n") 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 96fd0db38..11b5b9460 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt @@ -84,7 +84,7 @@ abstract class Bot : CoroutineScope { */ @MiraiExperimentalAPI("还未支持") val nick: String - get() = TODO("bot 昵称获取") + get() = ""// TODO("bot 昵称获取") /** * 日志记录器 @@ -175,8 +175,6 @@ abstract class Bot : CoroutineScope { */ abstract suspend fun queryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Sequence - // TODO 目前还不能构造群对象. 这将在以后支持 - // endregion // region network 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 dcb80fe9f..68224bdd0 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 @@ -18,6 +18,7 @@ import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.ImageUploadEvent import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent +import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.WeakRefProperty @@ -40,19 +41,24 @@ interface Contact : CoroutineScope { * * 对于 [QQ], `uin` 与 `id` 是相同的意思. * 对于 [Group], `groupCode` 与 `id` 是相同的意思. + * + * @see QQ.id + * @see Group.id */ val id: Long /** - * 向这个对象发送消息. + * 向这个对象发送消息. 发送成功后 [message] 中会添加 [MessageSource], 此后可以 [引用回复][MessageReceipt.quote](仅群聊)或 [撤回][MessageReceipt.recall] 这条消息. * * @see FriendMessageSendEvent 发送好友信息事件, cancellable * @see GroupMessageSendEvent 发送群消息事件. cancellable * * @throws EventCancelledException 当发送消息事件被取消 * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * + * @return 消息回执. 可 [引用回复][MessageReceipt.quote](仅群聊)或 [撤回][MessageReceipt.recall] 这条消息. */ - suspend fun sendMessage(message: MessageChain) + suspend fun sendMessage(message: MessageChain): MessageReceipt /** * 上传一个图片以备发送. @@ -88,4 +94,4 @@ interface Contact : CoroutineScope { suspend inline fun Contact.sendMessage(message: Message) = sendMessage(message.toChain()) -suspend inline fun Contact.sendMessage(plain: String) = sendMessage(plain.singleChain()) \ No newline at end of file +suspend inline fun Contact.sendMessage(plain: String) = sendMessage(plain.toMessage()) \ No newline at end of file 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 3d618c9aa..689611954 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 @@ -11,11 +11,22 @@ package net.mamoe.mirai.contact +import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch import net.mamoe.mirai.Bot import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.event.events.* +import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent +import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.message.data.quote import net.mamoe.mirai.utils.MiraiExperimentalAPI +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext import kotlin.jvm.JvmName /** @@ -83,10 +94,18 @@ interface Group : Contact, CoroutineScope { override val id: Long /** - * 群主 + * 群主. + * + * @return 若机器人是群主, 返回 [botAsMember]. 否则返回相应的成员 */ val owner: Member + /** + * [Bot] 在群内的 [Member] 实例 + */ + @MiraiExperimentalAPI + val botAsMember: Member + /** * 机器人被禁言还剩余多少秒 * @@ -133,6 +152,17 @@ interface Group : Contact, CoroutineScope { @MiraiExperimentalAPI("还未支持") suspend fun quit(): Boolean + /** + * 撤回这条消息. + * + * [Bot] 撤回自己的消息不需要权限. + * [Bot] 撤回群员的消息需要管理员权限. + * + * @throws PermissionDeniedException 当 [Bot] 无权限操作时 + * @see Group.recall (扩展函数) 接受参数 [MessageChain] + */ + suspend fun recall(source: MessageSource) + /** * 构造一个 [Member]. * 非特殊情况请不要使用这个函数. 优先使用 [get]. @@ -142,6 +172,19 @@ interface Group : Contact, CoroutineScope { @JvmName("newMember") fun Member(memberInfo: MemberInfo): Member + /** + * 向这个对象发送消息. 发送成功后 [message] 中会添加 [MessageSource], 此后可以 [引用回复][quote] 或 [撤回][recall] 这条消息. + * + * @see FriendMessageSendEvent 发送好友信息事件, cancellable + * @see GroupMessageSendEvent 发送群消息事件. cancellable + * + * @throws EventCancelledException 当发送消息事件被取消 + * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * + * @return 消息回执. 可进行撤回 ([MessageReceipt.recall]) + */ + override suspend fun sendMessage(message: MessageChain): MessageReceipt + companion object { /** @@ -184,7 +227,52 @@ interface Group : Contact, CoroutineScope { fun toFullString(): String = "Group(id=${this.id}, name=$name, owner=${owner.id}, members=${members.idContentString})" } +/** + * 撤回这条消息. + * + * [Bot] 撤回自己的消息不需要权限. + * [Bot] 撤回群员的消息需要管理员权限. + * + * @throws PermissionDeniedException 当 [Bot] 无权限操作时 + * @see Group.recall + */ +suspend inline fun Group.recall(message: MessageChain) = this.recall(message[MessageSource]) + +/** + * 在一段时间后撤回这条消息. + * + * @param delay 延迟的时间, 单位为毫秒 + * @param coroutineContext 额外的 [CoroutineContext] + * @see recall + */ +fun Group.recallIn( + message: MessageSource, + delay: Long, + coroutineContext: CoroutineContext = EmptyCoroutineContext +): Job = this.launch(coroutineContext + CoroutineName("MessageRecall")) { + kotlinx.coroutines.delay(delay) + recall(message) +} + +/** + * 在一段时间后撤回这条消息. + * + * @param delay 延迟的时间, 单位为毫秒 + * @param coroutineContext 额外的 [CoroutineContext] + * @see recall + */ +fun Group.recallIn( + message: MessageChain, + delay: Long, + coroutineContext: CoroutineContext = EmptyCoroutineContext +): Job = this.launch(coroutineContext + CoroutineName("MessageRecall")) { + kotlinx.coroutines.delay(delay) + recall(message) +} + /** * 返回机器人是否正在被禁言 + * + * @see Group.botMuteRemaining 剩余禁言时间 */ val Group.isBotMuted: Boolean get() = this.botMuteRemaining != 0 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt index 986c00072..9dcd5aa03 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt @@ -16,6 +16,12 @@ import net.mamoe.mirai.Bot import net.mamoe.mirai.data.FriendNameRemark import net.mamoe.mirai.data.PreviousNameList import net.mamoe.mirai.data.Profile +import net.mamoe.mirai.event.events.EventCancelledException +import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent +import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.MiraiExperimentalAPI /** @@ -68,4 +74,17 @@ interface QQ : Contact, CoroutineScope { */ @MiraiExperimentalAPI("还未支持") suspend fun queryRemark(): FriendNameRemark + + /** + * 向这个对象发送消息. 发送成功后 [message] 中会添加 [MessageSource], 此后可以 [撤回][recall] 这条消息. + * + * @see FriendMessageSendEvent 发送好友信息事件, cancellable + * @see GroupMessageSendEvent 发送群消息事件. cancellable + * + * @throws EventCancelledException 当发送消息事件被取消 + * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * + * @return 消息回执. 可进行撤回 ([MessageReceipt.recall]) + */ + override suspend fun sendMessage(message: MessageChain): MessageReceipt } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/GroupInfo.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/GroupInfo.kt index 0a594a4b0..9dcb261b5 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/GroupInfo.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/GroupInfo.kt @@ -62,4 +62,15 @@ interface GroupInfo { * 机器人被禁言还剩时间, 秒. */ val botMuteRemaining: Int + + /* + /** + * 机器人的头衔 + */ + val botSpecialTitle: String + + /** + * 机器人的昵称 + */ + val botNameCard: String*/ } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt similarity index 100% rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt new file mode 100644 index 000000000..115c95c3e --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt @@ -0,0 +1,11 @@ +/* + * 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.event + diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt similarity index 100% rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt similarity index 100% rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt 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 efd272542..6f2b62a97 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 @@ -10,12 +10,11 @@ package net.mamoe.mirai.message import net.mamoe.mirai.Bot -import net.mamoe.mirai.contact.Group -import net.mamoe.mirai.contact.Member -import net.mamoe.mirai.contact.MemberPermission +import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.Event import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.getValue import net.mamoe.mirai.utils.unsafeWeakRef import kotlin.jvm.JvmName @@ -60,6 +59,11 @@ class GroupMessage( @JvmName("reply2") suspend inline fun MessageChain.quoteReply() = quoteReply(this) + suspend inline fun MessageChain.recall() = group.recall(this) + suspend inline fun MessageSource.recall() = group.recall(this) + inline fun MessageSource.recallIn(delay: Long) = group.recallIn(this, delay) + inline fun MessageChain.recallIn(delay: Long) = group.recallIn(this, delay) + override fun toString(): String = "GroupMessage(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)" } \ No newline at end of file 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 a42a778fc..f11d244b6 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 @@ -35,7 +35,7 @@ expect abstract class MessagePacket(bot: Bot) /** * 仅内部使用, 请使用 [MessagePacket] */ // Tips: 在 IntelliJ 中 (左侧边栏) 打开 `Structure`, 可查看类结构 -@Suppress("NOTHING_TO_INLINE") +@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") @MiraiInternalAPI abstract class MessagePacketBase(_bot: Bot) : Packet, BotEvent { /** @@ -73,20 +73,19 @@ abstract class MessagePacketBase(_bot: Bot) : * 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息 * 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息 */ - suspend inline fun reply(message: MessageChain) = subject.sendMessage(message) + suspend inline fun reply(message: MessageChain): MessageReceipt = subject.sendMessage(message) as MessageReceipt - suspend inline fun reply(message: Message) = subject.sendMessage(message.toChain()) - suspend inline fun reply(plain: String) = subject.sendMessage(plain.singleChain()) + suspend inline fun reply(message: Message): MessageReceipt = subject.sendMessage(message.toChain()) as MessageReceipt + suspend inline fun reply(plain: String): MessageReceipt = subject.sendMessage(plain.toMessage().toChain()) as MessageReceipt @JvmName("reply1") - suspend inline fun String.reply() = reply(this) + suspend inline fun String.reply(): MessageReceipt = reply(this) @JvmName("reply1") - suspend inline fun Message.reply() = reply(this) + suspend inline fun Message.reply(): MessageReceipt = reply(this) @JvmName("reply1") - suspend inline fun MessageChain.reply() = reply(this) - + suspend inline fun MessageChain.reply(): MessageReceipt = reply(this) // endregion // region @@ -135,16 +134,4 @@ abstract class MessagePacketBase(_bot: Bot) : */ suspend inline fun Image.download(): ByteReadPacket = bot.run { download() } // endregion - - @Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("bot.getFriend(this.target)")) - fun At.qq(): QQ = bot.getFriend(this.target) - - @Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("bot.getFriend(this.toLong())")) - fun Int.qq(): QQ = bot.getFriend(this.coerceAtLeastOrFail(0).toLong()) - - @Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("bot.getFriend(this)")) - fun Long.qq(): QQ = bot.getFriend(this.coerceAtLeastOrFail(0)) - - @Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("bot.getGroup(this)")) - fun Long.group(): Group = bot.getGroup(this) } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt new file mode 100644 index 000000000..d84aed165 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt @@ -0,0 +1,108 @@ +/* + * 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.message + +import kotlinx.atomicfu.atomic +import kotlinx.coroutines.Job +import net.mamoe.mirai.Bot +import net.mamoe.mirai.contact.* +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.quote +import net.mamoe.mirai.utils.MiraiExperimentalAPI +import net.mamoe.mirai.utils.getValue +import net.mamoe.mirai.utils.unsafeWeakRef + +/** + * 发送消息后得到的回执. 可用于撤回. + * + * 此对象持有 [Contact] 的弱引用, [Bot] 离线后将会释放引用, 届时 [target] 将无法访问. + * + * @see Group.sendMessage 发送群消息, 返回回执(此对象) + * @see QQ.sendMessage 发送群消息, 返回回执(此对象) + */ +open class MessageReceipt( + val originalMessage: MessageChain, + target: C +) { + init { + require(target is Group || target is QQ) { "target must be either Group or QQ" } + } + + /** + * 发送目标, 为 [Group] 或 [QQ] + */ + val target: C by target.unsafeWeakRef() + + private val _isRecalled = atomic(false) + + /** + * 判断消息是否已被撤回. + */ + val isRecalled: Boolean get() = _isRecalled.value + + /** + * 撤回这条消息. [recall] 或 [recallIn] 只能被调用一次. + * + * @see Group.recall + * @throws IllegalStateException 当此消息已经被撤回或正计划撤回时 + */ + @UseExperimental(MiraiExperimentalAPI::class) + suspend fun recall() { + @Suppress("BooleanLiteralArgument") + if (_isRecalled.compareAndSet(false, true)) { + when (val contact = target) { + is Group -> { + contact.recall(originalMessage) + } + is QQ -> { + TODO() + } + else -> error("Unknown contact type") + } + } else error("message is already or planned to be recalled") + } + + /** + * 撤回这条消息. [recall] 或 [recallIn] 只能被调用一次. + * + * @param delay 延迟时间, 单位为毫秒 + * + * @throws IllegalStateException 当此消息已经被撤回或正计划撤回时 + */ + @UseExperimental(MiraiExperimentalAPI::class) + fun recallIn(delay: Long): Job { + @Suppress("BooleanLiteralArgument") + if (_isRecalled.compareAndSet(false, true)) { + when (val contact = target) { + is Group -> { + return contact.recallIn(originalMessage, delay) + } + is QQ -> { + TODO() + } + else -> error("Unknown contact type") + } + } else error("message is already or planned to be recalled") + } + + /** + * 引用这条消息. 仅群消息能被引用 + * + * @see MessageChain.quote 引用一条消息 + * + * @throws IllegalStateException 当此消息不是群消息时 + */ + @UseExperimental(MiraiExperimentalAPI::class) + open fun quote(): MessageChain { + val target = target + check(target is Group) { "quote is only available for GroupMessage" } + return this.originalMessage.quote(target.botAsMember) + } +} \ No newline at end of file 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 af0781be1..e917fc625 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 @@ -13,9 +13,6 @@ package net.mamoe.mirai.message.data import net.mamoe.mirai.message.data.NullMessageChain.toString -import net.mamoe.mirai.utils.MiraiExperimentalAPI -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract import kotlin.js.JsName import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName @@ -68,6 +65,15 @@ interface MessageChain : Message, MutableList { } } +/** + * 先删除同类型的消息, 再添加 [message] + */ +fun MessageChain.addOrRemove(message: Message) { + val clazz = message::class + this.removeAll { clazz.isInstance(it) } + this.add(message) +} + /** * 遍历每一个有内容的消息, 即 [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage] */ @@ -132,6 +138,16 @@ fun MessageChain(vararg messages: Message): MessageChain = if (messages.isEmpty()) EmptyMessageChain() else MessageChainImpl(messages.toMutableList()) +/** + * 构造 [MessageChain] 的快速途径 (无 [Array] 创建) + * 若仅提供一个参数, 请考虑使用 [Message.toChain] 以优化性能 + */ +@JvmName("newChain") +@JsName("newChain") +@Suppress("FunctionName") +fun MessageChain(message: Message): MessageChain = + MessageChainImpl(mutableListOf(message)) + /** * 构造 [MessageChain] */ @@ -141,30 +157,6 @@ fun MessageChain(vararg messages: Message): MessageChain = fun MessageChain(messages: Iterable): MessageChain = MessageChainImpl(messages.toMutableList()) -/** - * 构造单元素的不可修改的 [MessageChain]. 内部类实现为 [SingleMessageChain] - * - * 参数 [delegate] 不能为 [MessageChain] 的实例, 否则将会抛出异常. - * 使用 [Message.toChain] 将帮助提前处理这个问题. - * - * @param delegate 所构造的单元素 [MessageChain] 代表的 [Message] - * @throws IllegalArgumentException 当 [delegate] 为 [MessageChain] 的实例时 - * - * @see Message.toChain receiver 模式 - */ -@JvmName("newSingleMessageChain") -@JsName("newChain") -@MiraiExperimentalAPI -@UseExperimental(ExperimentalContracts::class) -@Suppress("FunctionName") -fun SingleMessageChain(delegate: Message): MessageChain { - contract { - returns() implies (delegate !is MessageChain) - } - require(delegate !is MessageChain) { "delegate for SingleMessageChain should not be any instance of MessageChain" } - return SingleMessageChainImpl(delegate) -} - /** * 得到包含 [this] 的 [MessageChain]. @@ -387,97 +379,3 @@ internal inline class MessageChainImpl constructor( // endregion } -/** - * 单个成员的不可修改的 [MessageChain]. - * - * 在连接时将会把它当做一个普通 [Message] 看待, 但它不能被 [plusAssign] - */ -@PublishedApi -internal inline class SingleMessageChainImpl( - private val delegate: Message -) : Message, MutableList, - MessageChain { - - // region Message override - override operator fun contains(sub: String): Boolean = delegate.contains(sub) - - override fun followedBy(tail: Message): MessageChain { - require(tail !is SingleOnly) { "SingleOnly Message cannot follow another message" } - return if (tail is MessageChain) tail.apply { followedBy(delegate) } - else MessageChain(delegate, tail) - } - - override fun plusAssign(message: Message) = - throw UnsupportedOperationException("SingleMessageChainImpl cannot be plusAssigned") - - override fun toString(): String = delegate.toString() - // endregion - - // region MutableList override - override fun containsAll(elements: Collection): Boolean = elements.all { it == delegate } - - override operator fun get(index: Int): Message = if (index == 0) delegate else throw NoSuchElementException() - override fun indexOf(element: Message): Int = if (delegate == element) 0 else -1 - override fun isEmpty(): Boolean = false - override fun lastIndexOf(element: Message): Int = if (delegate == element) 0 else -1 - override fun add(element: Message): Boolean = throw UnsupportedOperationException() - override fun add(index: Int, element: Message) = throw UnsupportedOperationException() - override fun addAll(index: Int, elements: Collection): Boolean = throw UnsupportedOperationException() - override fun addAll(elements: Collection): Boolean = throw UnsupportedOperationException() - override fun clear() = throw UnsupportedOperationException() - override fun listIterator(): MutableListIterator = object : MutableListIterator { - private var hasNext = true - override fun hasPrevious(): Boolean = !hasNext - override fun nextIndex(): Int = if (hasNext) 0 else -1 - override fun previous(): Message = - if (hasPrevious()) { - hasNext = true - delegate - } else throw NoSuchElementException() - - override fun previousIndex(): Int = if (!hasNext) 0 else -1 - override fun add(element: Message) = throw UnsupportedOperationException() - override fun hasNext(): Boolean = hasNext - override fun next(): Message = - if (hasNext) { - hasNext = false - delegate - } else throw NoSuchElementException() - - override fun remove() = throw UnsupportedOperationException() - override fun set(element: Message) = throw UnsupportedOperationException() - } - - override fun listIterator(index: Int): MutableListIterator = - if (index == 0) listIterator() else throw UnsupportedOperationException() - - override fun remove(element: Message): Boolean = throw UnsupportedOperationException() - override fun removeAll(elements: Collection): Boolean = throw UnsupportedOperationException() - override fun removeAt(index: Int): Message = throw UnsupportedOperationException() - override fun retainAll(elements: Collection): Boolean = throw UnsupportedOperationException() - override fun set(index: Int, element: Message): Message = throw UnsupportedOperationException() - override fun subList(fromIndex: Int, toIndex: Int): MutableList { - return if (fromIndex == 0) when (toIndex) { - 1 -> mutableListOf(this) - 0 -> mutableListOf() - else -> throw UnsupportedOperationException() - } - else throw UnsupportedOperationException() - } - - override fun iterator(): MutableIterator = object : MutableIterator { - private var hasNext = true - override fun hasNext(): Boolean = hasNext - override fun next(): Message = - if (hasNext) { - hasNext = false - delegate - } else throw NoSuchElementException() - - override fun remove() = throw UnsupportedOperationException() - } - - override operator fun contains(element: Message): Boolean = element == delegate - override val size: Int get() = 1 - // endregion -} \ No newline at end of file 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 6804a18fc..e5962e5bf 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 @@ -47,7 +47,7 @@ interface MessageSource : Message { val senderId: Long /** - * 群号码 + * 群号码, 为 0 时则来自好友消息 */ val groupId: Long diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt index c11c1e395..e7dbe30ee 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt @@ -38,15 +38,4 @@ inline class PlainText(val stringValue: String) : Message { * 构造 [PlainText] */ @Suppress("NOTHING_TO_INLINE") -inline fun String.toMessage(): PlainText = PlainText(this) - -/** - * 得到包含作为 [PlainText] 的 [this] 的 [MessageChain]. - * - * @return 唯一成员且不可修改的 [SingleMessageChainImpl] - * - * @see SingleMessageChain - * @see SingleMessageChainImpl - */ -@Suppress("NOTHING_TO_INLINE") -inline fun String.singleChain(): MessageChain = SingleMessageChainImpl(this.toMessage()) \ No newline at end of file +inline fun String.toMessage(): PlainText = PlainText(this) \ No newline at end of file From ca9440d3e31868e101fd38628cce093cfdc110f7 Mon Sep 17 00:00:00 2001 From: ryoii Date: Fri, 21 Feb 2020 18:47:03 +0800 Subject: [PATCH 05/52] graphic command input --- .../controller/MiraiGraphicalUIController.kt | 2 ++ .../mirai/console/graphical/view/PrimaryView.kt | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt index 939cd424d..422952d22 100644 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt @@ -28,6 +28,8 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI { MiraiConsole.CommandListener.commandChannel.send("/login $qq $psd") } + suspend fun sendCommand(command: String) = MiraiConsole.CommandListener.commandChannel.send(command) + override fun pushLog(identity: Long, message: String) = Platform.runLater { when (identity) { 0L -> mainLog.add(message) diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt index 1a81c3721..6ccca3816 100644 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt @@ -5,6 +5,8 @@ import javafx.collections.ObservableList import javafx.scene.control.Tab import javafx.scene.control.TabPane import javafx.scene.image.Image +import javafx.scene.input.KeyCode +import kotlinx.coroutines.runBlocking import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController import net.mamoe.mirai.console.graphical.model.BotModel import net.mamoe.mirai.console.graphical.util.jfxListView @@ -52,6 +54,17 @@ class PrimaryView : View() { } } } + + // command input + textfield { + setOnKeyPressed { + if (it.code == KeyCode.ENTER) { + runAsync { + runBlocking { controller.sendCommand(text) } + }.ui { text = "" } + } + } + } } center = jfxTabPane { From 97da6fce604a60b16823f69027c9eb9b84e09dc7 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 19:04:29 +0800 Subject: [PATCH 06/52] Ensure `sequenceId` available --- .../kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt | 2 ++ .../mirai/qqandroid/message/MessageSourceFromMsg.kt | 9 +++++++++ .../network/protocol/packet/chat/receive/MessageSvc.kt | 4 ++++ .../kotlin/net.mamoe.mirai/message/MessageReceipt.kt | 5 ----- .../kotlin/net.mamoe.mirai/message/data/MessageSource.kt | 9 ++++++++- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index cda9fcc33..159082173 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -501,6 +501,8 @@ internal class GroupImpl( checkBotPermissionOperator() } + source.ensureSequenceIdAvailable() + bot.network.run { val response = PbMessageSvc.PbMsgWithDraw.Group(bot.client, this@GroupImpl.id, source.sequenceId, source.messageUid.toInt()) .sendAndExpect() diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt index e0d351c50..f966bc71b 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt @@ -23,6 +23,11 @@ internal inline class MessageSourceFromServer( ) : MessageSource { override val time: Long get() = delegate.time.toLong() and 0xFFFFFFFF override val sequenceId: Int get() = delegate.origSeqs?.firstOrNull() ?: error("cannot find sequenceId from ImMsgBody.SourceMsg") + + override suspend fun ensureSequenceIdAvailable() { + // nothing to do + } + override val messageUid: Long get() = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!! override val sourceMessage: MessageChain get() = delegate.toMessageChain() override val senderId: Long get() = delegate.senderUin @@ -36,6 +41,10 @@ internal inline class MessageSourceFromMsg( ) : MessageSource { override val time: Long get() = delegate.msgHead.msgTime.toLong() and 0xFFFFFFFF override val sequenceId: Int get() = delegate.msgHead.msgSeq + override suspend fun ensureSequenceIdAvailable() { + // nothing to do + } + override val messageUid: Long get() = delegate.msgBody.richText.attr!!.random.toLong() override val sourceMessage: MessageChain get() = delegate.toMessageChain() override val senderId: Long get() = delegate.msgHead.fromUin 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 1c0ea0752..b6f8fe122 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 @@ -294,6 +294,10 @@ internal class MessageSvc { override val sequenceId: Int get() = sequenceIdDeferred.getCompleted() + override suspend fun ensureSequenceIdAvailable() { + sequenceIdDeferred.join() + } + override fun toString(): String { return "" } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt index d84aed165..3db21c6b2 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt @@ -42,11 +42,6 @@ open class MessageReceipt( private val _isRecalled = atomic(false) - /** - * 判断消息是否已被撤回. - */ - val isRecalled: Boolean get() = _isRecalled.value - /** * 撤回这条消息. [recall] 或 [recallIn] 只能被调用一次. * 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 e5962e5bf..e0b7239dc 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 @@ -27,10 +27,17 @@ interface MessageSource : Message { companion object Key : Message.Key /** - * 序列号 + * 序列号. 若是机器人发出去的消息, 请先 [确保 sequenceId 可用][ensureSequenceIdAvailable] */ val sequenceId: Int + /** + * 等待 [sequenceId] 获取, 确保其可用. + * + * 若原消息发送失败, 这个方法会等待最多 3 秒随后抛出 [IllegalStateException] + */ + suspend fun ensureSequenceIdAvailable() + /** * 实际上是个随机数, 但服务器确实是用它当做 uid */ From 354cc2172da637c60ad6b9e8be56b66359c2d9d8 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 19:35:59 +0800 Subject: [PATCH 07/52] Japt: adapt for core update --- .../net/mamoe/mirai/japt/BlockingContact.java | 8 ++-- .../net/mamoe/mirai/japt/BlockingGroup.java | 33 +++++++++++++++++ .../java/net/mamoe/mirai/japt/BlockingQQ.java | 37 +++++++++++++++++++ .../japt/internal/BlockingContactsImpl.kt | 13 ++++--- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContact.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContact.java index 26faed221..6ac2875b5 100644 --- a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContact.java +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContact.java @@ -9,6 +9,7 @@ import net.mamoe.mirai.event.events.EventCancelledException; import net.mamoe.mirai.event.events.ImageUploadEvent; import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent; import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent; +import net.mamoe.mirai.message.MessageReceipt; import net.mamoe.mirai.message.data.Image; import net.mamoe.mirai.message.data.Message; import net.mamoe.mirai.message.data.MessageChain; @@ -42,8 +43,7 @@ public interface BlockingContact { * @see FriendMessageSendEvent 发送好友信息事件, cancellable * @see GroupMessageSendEvent 发送群消息事件. cancellable */ - // kotlin bug - void sendMessage(@NotNull MessageChain messages) throws EventCancelledException, IllegalStateException; + MessageReceipt sendMessage(@NotNull MessageChain messages) throws EventCancelledException, IllegalStateException; /** * 向这个对象发送消息. @@ -53,7 +53,7 @@ public interface BlockingContact { * @see FriendMessageSendEvent 发送好友信息事件, cancellable * @see GroupMessageSendEvent 发送群消息事件. cancellable */ - void sendMessage(@NotNull String message) throws EventCancelledException, IllegalStateException; + MessageReceipt sendMessage(@NotNull String message) throws EventCancelledException, IllegalStateException; /** * 向这个对象发送消息. @@ -63,7 +63,7 @@ public interface BlockingContact { * @see FriendMessageSendEvent 发送好友信息事件, cancellable * @see GroupMessageSendEvent 发送群消息事件. cancellable */ - void sendMessage(@NotNull Message message) throws EventCancelledException, IllegalStateException; + MessageReceipt sendMessage(@NotNull Message message) throws EventCancelledException, IllegalStateException; /** * 上传一个图片以备发送. diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java index 2ebd58f6b..a70b70921 100644 --- a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java @@ -3,6 +3,9 @@ package net.mamoe.mirai.japt; import net.mamoe.mirai.contact.*; import net.mamoe.mirai.data.MemberInfo; import net.mamoe.mirai.event.events.*; +import net.mamoe.mirai.message.MessageReceipt; +import net.mamoe.mirai.message.data.Message; +import net.mamoe.mirai.message.data.MessageChain; import net.mamoe.mirai.utils.MiraiExperimentalAPI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -152,6 +155,36 @@ public interface BlockingGroup extends BlockingContact { @Nullable BlockingMember getMemberOrNull(long id); + /** + * 向这个对象发送消息. + * + * @throws EventCancelledException 当发送消息事件被取消 + * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * @see MessageSendEvent.FriendMessageSendEvent 发送好友信息事件, cancellable + * @see MessageSendEvent.GroupMessageSendEvent 发送群消息事件. cancellable + */ + MessageReceipt sendMessage(@NotNull MessageChain messages) throws EventCancelledException, IllegalStateException; + + /** + * 向这个对象发送消息. + * + * @throws EventCancelledException 当发送消息事件被取消 + * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * @see MessageSendEvent.FriendMessageSendEvent 发送好友信息事件, cancellable + * @see MessageSendEvent.GroupMessageSendEvent 发送群消息事件. cancellable + */ + MessageReceipt sendMessage(@NotNull String message) throws EventCancelledException, IllegalStateException; + + /** + * 向这个对象发送消息. + * + * @throws EventCancelledException 当发送消息事件被取消 + * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * @see MessageSendEvent.FriendMessageSendEvent 发送好友信息事件, cancellable + * @see MessageSendEvent.GroupMessageSendEvent 发送群消息事件. cancellable + */ + MessageReceipt sendMessage(@NotNull Message message) throws EventCancelledException, IllegalStateException; + /** * 检查此 id 的群成员是否存在 */ diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingQQ.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingQQ.java index 7fb7afc0e..4108a4384 100644 --- a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingQQ.java +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingQQ.java @@ -1,8 +1,14 @@ package net.mamoe.mirai.japt; +import net.mamoe.mirai.contact.QQ; import net.mamoe.mirai.data.FriendNameRemark; import net.mamoe.mirai.data.PreviousNameList; import net.mamoe.mirai.data.Profile; +import net.mamoe.mirai.event.events.EventCancelledException; +import net.mamoe.mirai.event.events.MessageSendEvent; +import net.mamoe.mirai.message.MessageReceipt; +import net.mamoe.mirai.message.data.Message; +import net.mamoe.mirai.message.data.MessageChain; import net.mamoe.mirai.utils.MiraiExperimentalAPI; import org.jetbrains.annotations.NotNull; @@ -47,4 +53,35 @@ public interface BlockingQQ extends BlockingContact { @MiraiExperimentalAPI(message = "还未支持") @NotNull FriendNameRemark queryRemark(); + + /** + * 向这个对象发送消息. + * + * @throws EventCancelledException 当发送消息事件被取消 + * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * @see MessageSendEvent.FriendMessageSendEvent 发送好友信息事件, cancellable + * @see MessageSendEvent.GroupMessageSendEvent 发送群消息事件. cancellable + */ + MessageReceipt sendMessage(@NotNull MessageChain messages) throws EventCancelledException, IllegalStateException; + + /** + * 向这个对象发送消息. + * + * @throws EventCancelledException 当发送消息事件被取消 + * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * @see MessageSendEvent.FriendMessageSendEvent 发送好友信息事件, cancellable + * @see MessageSendEvent.GroupMessageSendEvent 发送群消息事件. cancellable + */ + MessageReceipt sendMessage(@NotNull String message) throws EventCancelledException, IllegalStateException; + + /** + * 向这个对象发送消息. + * + * @throws EventCancelledException 当发送消息事件被取消 + * @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出 + * @see MessageSendEvent.FriendMessageSendEvent 发送好友信息事件, cancellable + * @see MessageSendEvent.GroupMessageSendEvent 发送群消息事件. cancellable + */ + MessageReceipt sendMessage(@NotNull Message message) throws EventCancelledException, IllegalStateException; + } diff --git a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt index 193b57506..451d39d44 100644 --- a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt +++ b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt @@ -24,6 +24,7 @@ import net.mamoe.mirai.japt.BlockingBot import net.mamoe.mirai.japt.BlockingGroup import net.mamoe.mirai.japt.BlockingMember import net.mamoe.mirai.japt.BlockingQQ +import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.MiraiExperimentalAPI @@ -35,9 +36,9 @@ internal class BlockingQQImpl(private val delegate: QQ) : BlockingQQ { override fun getId(): Long = delegate.id override fun getNick(): String = delegate.nick - override fun sendMessage(messages: MessageChain) = runBlocking { delegate.sendMessage(messages) } - override fun sendMessage(message: String) = runBlocking { delegate.sendMessage(message.toMessage().toChain()) } - override fun sendMessage(message: Message) = runBlocking { delegate.sendMessage(message.toChain()) } + override fun sendMessage(messages: MessageChain): MessageReceipt = runBlocking { delegate.sendMessage(messages) } + override fun sendMessage(message: String): MessageReceipt = runBlocking { delegate.sendMessage(message.toMessage().toChain()) } + override fun sendMessage(message: Message): MessageReceipt = runBlocking { delegate.sendMessage(message.toChain()) } override fun uploadImage(image: ExternalImage): Image = runBlocking { delegate.uploadImage(image) } @MiraiExperimentalAPI @@ -51,9 +52,9 @@ internal class BlockingQQImpl(private val delegate: QQ) : BlockingQQ { } internal class BlockingGroupImpl(private val delegate: Group) : BlockingGroup { - override fun sendMessage(messages: MessageChain) = runBlocking { delegate.sendMessage(messages) } - override fun sendMessage(message: String) = runBlocking { delegate.sendMessage(message.toMessage().toChain()) } - override fun sendMessage(message: Message) = runBlocking { delegate.sendMessage(message.toChain()) } + override fun sendMessage(messages: MessageChain): MessageReceipt = runBlocking { delegate.sendMessage(messages) } + override fun sendMessage(message: String): MessageReceipt = runBlocking { delegate.sendMessage(message.toMessage().toChain()) } + override fun sendMessage(message: Message): MessageReceipt = runBlocking { delegate.sendMessage(message.toChain()) } override fun getOwner(): BlockingMember = delegate.owner.blocking() @MiraiExperimentalAPI override fun newMember(memberInfo: MemberInfo): Member = delegate.Member(memberInfo) From 2cd2591ff66f9a5ad825de45b696bc633ab6ada1 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 19:37:19 +0800 Subject: [PATCH 08/52] Add `ContactList.first` and ``ContactList.firstOrNull` --- .../kotlin/net.mamoe.mirai/contact/ContactList.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactList.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactList.kt index 5f459f5b9..c3e3deb11 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactList.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactList.kt @@ -40,6 +40,15 @@ class ContactList(@MiraiInternalAPI val delegate: LockFreeLinkedLis fun containsAll(elements: Collection): Boolean = elements.all { contains(it) } fun isEmpty(): Boolean = delegate.isEmpty() inline fun forEach(block: (C) -> Unit) = delegate.forEach(block) + fun first(): C { + forEach { return it } + throw NoSuchElementException() + } + + fun firstOrNull(): C? { + forEach { return it } + return null + } override fun toString(): String = delegate.joinToString(separator = ", ", prefix = "ContactList(", postfix = ")") } From 0015f3817c9accd93d38c3fc8277c3fea449cc8b Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 20:28:48 +0800 Subject: [PATCH 09/52] Unified group imageId --- .../mamoe/mirai/qqandroid/message/messages.kt | 9 ++-- .../net.mamoe.mirai/utils/ExternalImage.kt | 43 ++++++++++++++----- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt index 5d0068691..8f0f42630 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt @@ -15,6 +15,7 @@ import kotlinx.io.core.readUInt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm +import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.MiraiDebugAPI import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.discardExact @@ -249,7 +250,7 @@ internal fun MessageChain.toRichTextElems(): MutableList { internal class CustomFaceFromServer( internal val delegate: ImMsgBody.CustomFace ) : CustomFace() { - override val filepath: String get() = delegate.filePath + override val filepath: String = delegate.filePath override val fileId: Int get() = delegate.fileId override val serverIp: Int get() = delegate.serverIp override val serverPort: Int get() = delegate.serverPort @@ -265,14 +266,14 @@ internal class CustomFaceFromServer( override val size: Int get() = delegate.size override val original: Int get() = delegate.origin override val pbReserve: ByteArray get() = delegate.pbReserve - override val imageId: String get() = delegate.filePath + override val imageId: String = ExternalImage.generateImageId(delegate.md5, imageType) override fun equals(other: Any?): Boolean { return other is CustomFaceFromServer && other.filepath == this.filepath && other.md5.contentEquals(this.md5) } override fun hashCode(): Int { - return filepath.hashCode() + 31 * md5.hashCode() + return imageId.hashCode() + 31 * md5.hashCode() } } @@ -296,7 +297,7 @@ internal class NotOnlineImageFromServer( } override fun hashCode(): Int { - return resourceId.hashCode() + 31 * md5.hashCode() + return imageId.hashCode() + 31 * md5.hashCode() } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt index d389e4119..06447bf96 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt @@ -51,9 +51,39 @@ class ExternalImage( filename: String ): ExternalImage = ExternalImage(width, height, md5, format, data, data.remaining, filename) - fun generateUUID(md5: ByteArray): String{ + fun generateUUID(md5: ByteArray): String { return "${md5[0..3]}-${md5[4..5]}-${md5[6..7]}-${md5[8..9]}-${md5[10..15]}" } + + fun generateImageId(md5: ByteArray, imageType: Int): String { + return """{${generateUUID(md5)}}.${determineFormat(imageType)}""" + } + + fun determineImageType(format: String): Int { + return when (format) { + "jpg" -> 1000 + "png" -> 1001 + "webp" -> 1002 + "bmp" -> 1005 + "gig" -> 2000 + "apng" -> 2001 + "sharpp" -> 1004 + else -> 1000 // unsupported, just make it jpg + } + } + + fun determineFormat(imageType: Int): String { + return when (imageType) { + 1000 -> "jpg" + 1001 -> "png" + 1002 -> "webp" + 1005 -> "bmp" + 2000 -> "gig" + 2001 -> "apng" + 1004 -> "sharpp" + else -> "jpg" // unsupported, just make it jpg + } + } } val format: String = @@ -73,16 +103,7 @@ class ExternalImage( * SHARPP: 1004 */ val imageType: Int - get() = when (format) { - "jpg" -> 1000 - "png" -> 1001 - "webp" -> 1002 - "bmp" -> 1005 - "gig" -> 2000 - "apng" -> 2001 - "sharpp" -> 1004 - else -> 1000 // unsupported, just make it jpg - } + get() = determineImageType(format) override fun toString(): String = "[ExternalImage(${width}x$height $format)]" From 14f4f4588d53eb23e6610e7e01c8ae7efba619e6 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 20:29:48 +0800 Subject: [PATCH 10/52] Rename `delay` to `millis` --- .../kotlin/net.mamoe.mirai/contact/Group.kt | 12 +++---- .../net.mamoe.mirai/message/MessageReceipt.kt | 36 +++++++++++++++---- 2 files changed, 35 insertions(+), 13 deletions(-) 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 689611954..02c2d2c07 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 @@ -241,32 +241,32 @@ suspend inline fun Group.recall(message: MessageChain) = this.recall(message[Mes /** * 在一段时间后撤回这条消息. * - * @param delay 延迟的时间, 单位为毫秒 + * @param millis 延迟的时间, 单位为毫秒 * @param coroutineContext 额外的 [CoroutineContext] * @see recall */ fun Group.recallIn( message: MessageSource, - delay: Long, + millis: Long, coroutineContext: CoroutineContext = EmptyCoroutineContext ): Job = this.launch(coroutineContext + CoroutineName("MessageRecall")) { - kotlinx.coroutines.delay(delay) + kotlinx.coroutines.delay(millis) recall(message) } /** * 在一段时间后撤回这条消息. * - * @param delay 延迟的时间, 单位为毫秒 + * @param millis 延迟的时间, 单位为毫秒 * @param coroutineContext 额外的 [CoroutineContext] * @see recall */ fun Group.recallIn( message: MessageChain, - delay: Long, + millis: Long, coroutineContext: CoroutineContext = EmptyCoroutineContext ): Job = this.launch(coroutineContext + CoroutineName("MessageRecall")) { - kotlinx.coroutines.delay(delay) + kotlinx.coroutines.delay(millis) recall(message) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt index 3db21c6b2..6204e77c6 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt @@ -13,8 +13,7 @@ import kotlinx.atomicfu.atomic import kotlinx.coroutines.Job import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.* -import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.message.data.quote +import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.getValue import net.mamoe.mirai.utils.unsafeWeakRef @@ -67,17 +66,17 @@ open class MessageReceipt( /** * 撤回这条消息. [recall] 或 [recallIn] 只能被调用一次. * - * @param delay 延迟时间, 单位为毫秒 + * @param millis 延迟时间, 单位为毫秒 * * @throws IllegalStateException 当此消息已经被撤回或正计划撤回时 */ @UseExperimental(MiraiExperimentalAPI::class) - fun recallIn(delay: Long): Job { + fun recallIn(millis: Long): Job { @Suppress("BooleanLiteralArgument") if (_isRecalled.compareAndSet(false, true)) { when (val contact = target) { is Group -> { - return contact.recallIn(originalMessage, delay) + return contact.recallIn(originalMessage, millis) } is QQ -> { TODO() @@ -94,10 +93,33 @@ open class MessageReceipt( * * @throws IllegalStateException 当此消息不是群消息时 */ - @UseExperimental(MiraiExperimentalAPI::class) + @MiraiExperimentalAPI("unstable") open fun quote(): MessageChain { val target = target check(target is Group) { "quote is only available for GroupMessage" } return this.originalMessage.quote(target.botAsMember) } -} \ No newline at end of file + + /** + * 引用这条消息并回复. 仅群消息能被引用 + * + * @see MessageChain.quote 引用一条消息 + * + * @throws IllegalStateException 当此消息不是群消息时 + */ + @MiraiExperimentalAPI("unstable") + suspend fun quoteReply(message: MessageChain) { + target.sendMessage(this.quote() + message) + } +} + +@MiraiExperimentalAPI("unstable") +suspend inline fun MessageReceipt.quoteReply(message: Message) { + return this.quoteReply(message.toChain()) +} + +@MiraiExperimentalAPI("unstable") +suspend inline fun MessageReceipt.quoteReply(message: String) { + return this.quoteReply(message.toMessage().toChain()) +} + From b09dadce999269650482175a824146f09f6a1de8 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 20:29:57 +0800 Subject: [PATCH 11/52] Add `toString` --- .../network/protocol/packet/chat/receive/OnlinePush.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 0f92e0cca..06720d94e 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 @@ -51,7 +51,11 @@ internal class OnlinePush { internal class SendGroupMessageReceipt( val messageRandom: Int, val sequenceId: Int - ) : Packet, Event + ) : Packet, Event { + override fun toString(): String { + return "OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt(messageRandom=$messageRandom, sequenceId=$sequenceId)" + } + } @UseExperimental(ExperimentalStdlibApi::class) override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? { From 4f6691acc6fdd3b93a64cdaea127c029769a086c Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 20:30:10 +0800 Subject: [PATCH 12/52] Fix `MessageChain.addOrRemove` --- .../kotlin/net.mamoe.mirai/message/data/MessageChain.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e917fc625..9a4c9ad4e 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 @@ -68,7 +68,7 @@ interface MessageChain : Message, MutableList { /** * 先删除同类型的消息, 再添加 [message] */ -fun MessageChain.addOrRemove(message: Message) { +fun MessageChain.addOrRemove(message: T) { val clazz = message::class this.removeAll { clazz.isInstance(it) } this.add(message) From eda936c2bf7314014b0679b1c135377cbe9c0c87 Mon Sep 17 00:00:00 2001 From: ryoii Date: Fri, 21 Feb 2020 21:05:08 +0800 Subject: [PATCH 13/52] graphic plugins view --- .../controller/MiraiGraphicalUIController.kt | 8 +++++++ .../console/graphical/model/PluginModel.kt | 19 ++++++++++++++++ .../console/graphical/util/JFoenixAdaptor.kt | 4 ++++ .../console/graphical/view/PluginsView.kt | 22 +++++++++++++++++++ .../console/graphical/view/PrimaryView.kt | 4 +++- 5 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt create mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt index 422952d22..d70c5dd5f 100644 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt @@ -1,6 +1,7 @@ package net.mamoe.mirai.console.graphical.controller import javafx.application.Platform +import javafx.collections.ObservableList import javafx.stage.Modality import kotlinx.io.core.IoBuffer import net.mamoe.mirai.Bot @@ -8,6 +9,7 @@ import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsoleUI import net.mamoe.mirai.console.graphical.model.BotModel import net.mamoe.mirai.console.graphical.model.ConsoleInfo +import net.mamoe.mirai.console.graphical.model.PluginModel import net.mamoe.mirai.console.graphical.model.VerificationCodeModel import net.mamoe.mirai.console.graphical.view.VerificationCodeFragment import net.mamoe.mirai.utils.LoginSolver @@ -22,6 +24,7 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI { private val cache = mutableMapOf() val mainLog = observableListOf() val botList = observableListOf() + val pluginList: ObservableList by lazy(::getPluginsFromConsole) val consoleInfo = ConsoleInfo() suspend fun login(qq: String, psd: String) { @@ -70,6 +73,11 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI { } override fun createLoginSolver(): LoginSolver = loginSolver + + private fun getPluginsFromConsole(): ObservableList { + // TODO + return observableListOf() + } } class GraphicalLoginSolver : LoginSolver() { diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt new file mode 100644 index 000000000..21a59e66b --- /dev/null +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt @@ -0,0 +1,19 @@ +package net.mamoe.mirai.console.graphical.model + +import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject +import javafx.beans.property.SimpleBooleanProperty +import javafx.beans.property.SimpleStringProperty +import tornadofx.getValue +import tornadofx.setValue + +class PluginModel : RecursiveTreeObject() { + + val nameProperty = SimpleStringProperty(this, "nameProperty") + val name by nameProperty + + val descriptionProperty = SimpleStringProperty(this, "descriptionProperty") + val description by descriptionProperty + + val enabledProperty = SimpleBooleanProperty(this, "enabledProperty") + var enabled by enabledProperty +} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt index 5590d9c44..61dcde2ae 100644 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt @@ -1,6 +1,7 @@ package net.mamoe.mirai.console.graphical.util import com.jfoenix.controls.* +import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject import javafx.beans.value.ObservableValue import javafx.collections.ObservableList import javafx.event.EventTarget @@ -42,3 +43,6 @@ internal fun EventTarget.jfxListView(values: ObservableList? = null, op: else it.items = values } } + +fun ?> EventTarget.jfxTreeTableView(items: ObservableList? = null, op: JFXTreeTableView.() -> Unit = {}) + = JFXTreeTableView(RecursiveTreeItem(items, RecursiveTreeObject::getChildren)).attachTo(this, op) \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt new file mode 100644 index 000000000..5b3e4d163 --- /dev/null +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt @@ -0,0 +1,22 @@ +package net.mamoe.mirai.console.graphical.view + +import com.jfoenix.controls.JFXTreeTableColumn +import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController +import net.mamoe.mirai.console.graphical.model.PluginModel +import net.mamoe.mirai.console.graphical.util.jfxTreeTableView +import tornadofx.View + +class PluginsView : View() { + + private val controller = find() + val plugins = controller.pluginList + + override val root = jfxTreeTableView(plugins) { + columns.addAll( + JFXTreeTableColumn("插件名").apply { }, + JFXTreeTableColumn("版本").apply { }, + JFXTreeTableColumn("作者").apply { }, + JFXTreeTableColumn("介绍").apply { } + ) + } +} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt index 6ccca3816..56fa4b67a 100644 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt @@ -73,7 +73,9 @@ class PrimaryView : View() { this += find().root } - tab("Plugin") + tab("Plugins") { + this += find().root + } tab("Settings") From e580992e0bc7d0ad2f3c50300891026cf9e86875 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 21:07:00 +0800 Subject: [PATCH 14/52] Fix At --- .../mamoe/mirai/qqandroid/message/messages.kt | 25 +++++++++++++------ .../packet/chat/receive/MessageSvc.kt | 8 ++++-- .../net.mamoe.mirai/message/MessagePacket.kt | 4 +-- .../net.mamoe.mirai/message/data/AtAll.kt | 6 +++-- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt index 8f0f42630..9c1d52245 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt @@ -28,12 +28,13 @@ internal fun At.toJceData(): ImMsgBody.Text { return ImMsgBody.Text( str = text, attr6Buf = buildPacket { - writeShort(1) - writeShort(0) - writeShort(text.length.toShort()) - writeByte(1) - writeInt(target.toInt()) - writeShort(0) + // MessageForText$AtTroopMemberInfo + writeShort(1) // const + writeShort(0) // startPos + writeShort(text.length.toShort()) // textLen + writeByte(0) // flag, may=1 + writeInt(target.toInt()) // uin + writeShort(0) // const }.readBytes() ) } @@ -207,7 +208,15 @@ notOnlineImage=NotOnlineImage#2050019814 { private val atAllData = ImMsgBody.Elem( text = ImMsgBody.Text( str = "@全体成员", - attr6Buf = "00 01 00 00 00 05 01 00 00 00 00 00 00".hexToBytes() + attr6Buf = buildPacket { + // MessageForText$AtTroopMemberInfo + writeShort(1) // const + writeShort(0) // startPos + writeShort("@全体成员".length.toShort()) // textLen + writeByte(1) // flag, may=1 + writeInt(0) // uin + writeShort(0) // const + }.readBytes() ) ) @@ -225,7 +234,7 @@ internal fun MessageChain.toRichTextElems(): MutableList { this.forEach { when (it) { is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue))) - is At -> elements.add(ImMsgBody.Elem(text = it.toJceData())) + is At -> elements.add(ImMsgBody.Elem(text = it.toJceData())).also { elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " "))) } is CustomFaceFromFile -> elements.add(ImMsgBody.Elem(customFace = it.toJceData())) is CustomFaceFromServer -> elements.add(ImMsgBody.Elem(customFace = it.delegate)) is NotOnlineImageFromServer -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) 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 b6f8fe122..57bddbfdb 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 @@ -263,6 +263,9 @@ internal class MessageSvc { override fun toString(): String = "MessageSvc.PbSendMsg.Response.SUCCESS" } + /** + * 121: 被限制? 个别号才不能发 + */ data class Failed(val resultType: Int, val errorCode: Int, val errorMessage: String) : Response() { override fun toString(): String = "MessageSvc.PbSendMsg.Response.Failed(resultType=$resultType, errorCode=$errorCode, errorMessage=$errorMessage)" @@ -352,13 +355,12 @@ internal class MessageSvc { ): OutgoingPacket = buildOutgoingUniPacket(client) { val source = MessageSourceFromSend( - messageUid = Random.nextInt().absoluteValue.toLong() and 0xffffffff, + messageUid = Random.nextInt().absoluteValue.toLong(), senderId = client.uin, time = currentTimeSeconds + client.timeDifference, groupId = groupCode, sourceMessage = message ) - message.addOrRemove(source) ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) @@ -380,6 +382,8 @@ internal class MessageSvc { msgVia = 1 ) ) + + message.addOrRemove(source) } override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { 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 f11d244b6..5ad3974e6 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 @@ -112,9 +112,9 @@ abstract class MessagePacketBase(_bot: Bot) : /** * 创建 @ 这个账号的消息. 当且仅当消息为群消息时可用. 否则将会抛出 [IllegalArgumentException] */ - inline fun QQ.at(): At = At(this as? Member ?: error("`QQ.at` can only be used in GroupMessage")) + 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") + fun At.member(): Member = (this@MessagePacketBase as? GroupMessage)?.group?.get(this.target) ?: error("`At.member` can only be used in GroupMessage") // endregion diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt index 60dfdfecf..83e39948f 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt @@ -16,7 +16,9 @@ import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName /** - * "@全体成员" + * "@全体成员". + * + * 非会员每天只能发送 10 次 [AtAll]. 超出部分会被以普通文字看待. * * @see At at 单个群成员 */ @@ -26,7 +28,7 @@ object AtAll : Message, Message.Key { // 自动为消息补充 " " override fun followedBy(tail: Message): MessageChain { - if(tail is PlainText && tail.stringValue.startsWith(' ')){ + if (tail is PlainText && tail.stringValue.startsWith(' ')) { return super.followedBy(tail) } return super.followedBy(PlainText(" ")) + tail From e5546d9283516ac220bb69836c02d79d220e8e3e Mon Sep 17 00:00:00 2001 From: ryoii Date: Fri, 21 Feb 2020 21:20:58 +0800 Subject: [PATCH 15/52] graphic setting view --- .../controller/MiraiGraphicalUIController.kt | 7 ++++++ .../console/graphical/view/PrimaryView.kt | 10 +++----- .../console/graphical/view/SettingsView.kt | 23 +++++++++++++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt index d70c5dd5f..a9b031a88 100644 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt @@ -23,8 +23,13 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI { private val loginSolver = GraphicalLoginSolver() private val cache = mutableMapOf() val mainLog = observableListOf() + + val botList = observableListOf() val pluginList: ObservableList by lazy(::getPluginsFromConsole) + + val consoleConfig : Map by lazy(::getConfigFromConsole) + val consoleInfo = ConsoleInfo() suspend fun login(qq: String, psd: String) { @@ -78,6 +83,8 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI { // TODO return observableListOf() } + + private fun getConfigFromConsole() = MiraiConsole.MiraiProperties.config.asMap() } class GraphicalLoginSolver : LoginSolver() { diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt index 56fa4b67a..a0760cd6b 100644 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt @@ -69,15 +69,11 @@ class PrimaryView : View() { center = jfxTabPane { - tab("Login") { - this += find().root - } + tab("Login").content = find().root - tab("Plugins") { - this += find().root - } + tab("Plugins").content = find().root - tab("Settings") + tab("Settings").content = find().root logTab("Main", controller.mainLog) } diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt new file mode 100644 index 000000000..1c0b2968c --- /dev/null +++ b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt @@ -0,0 +1,23 @@ +package net.mamoe.mirai.console.graphical.view + +import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController +import net.mamoe.mirai.console.graphical.util.jfxTextfield +import tornadofx.View +import tornadofx.field +import tornadofx.fieldset +import tornadofx.form + +class SettingsView : View() { + + private val controller = find() + + override val root = form { + controller.consoleConfig.forEach { + fieldset { + field(it.key) { + jfxTextfield(it.value.toString()) { isEditable = false } + } + } + } + } +} \ No newline at end of file From c8ed80c0a5fb7bfd58f9f3a04d554c28551ca98c Mon Sep 17 00:00:00 2001 From: ryoii Date: Fri, 21 Feb 2020 21:26:09 +0800 Subject: [PATCH 16/52] rename SendMessageRouteModule to MessageRouteModule --- .../route/{SendMessageRouteModule.kt => MessageRouteModule.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/{SendMessageRouteModule.kt => MessageRouteModule.kt} (100%) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/SendMessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt similarity index 100% rename from mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/SendMessageRouteModule.kt rename to mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt From 03c614326954f77f4c6dd39bc065dd94844db6a3 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:28:57 +0800 Subject: [PATCH 17/52] Add `subscribingGet`, `CoroutineScope.subscribingGetAsync` --- .../kotlin/net.mamoe.mirai/event/linear.kt | 101 ++++++++++++++++++ .../net.mamoe.mirai/event/subscriber.kt | 3 + 2 files changed, 104 insertions(+) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt index 115c95c3e..e73d3e922 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt @@ -9,3 +9,104 @@ package net.mamoe.mirai.event +import kotlinx.coroutines.* +import net.mamoe.mirai.utils.MiraiExperimentalAPI +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/** + * 监听这个事件, 并尝试从这个事件中获取一个值. + * + * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. + * + * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 + * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [subscribingGet] 会返回这个值 + * + * @see subscribingGetAsync 本函数的异步版本 + */ +@MiraiExperimentalAPI +suspend inline fun subscribingGet( + timeoutMillis: Long = -1, + noinline filter: E.(E) -> R? +): R { + require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" } + return subscribingGetOrNull(timeoutMillis, filter) ?: error("timeout subscribingGet") +} + +/** + * 监听这个事件, 并尝试从这个事件中获取一个值. + * + * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. + * + * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 + * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [subscribingGet] 会返回这个值 + * + * @see subscribingGetAsync 本函数的异步版本 + */ +@MiraiExperimentalAPI +suspend inline fun subscribingGetOrNull( + timeoutMillis: Long = -1, + noinline filter: E.(E) -> R? +): R? { + require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" } + var result: R? = null + var resultThrowable: Throwable? = null + + if (timeoutMillis == -1L) { + @Suppress("DuplicatedCode") // for better performance + coroutineScope { + var listener: Listener? = null + listener = this.subscribe { + val value = try { + filter.invoke(this, it) + } catch (e: Exception) { + resultThrowable = e + return@subscribe ListeningStatus.STOPPED.also { listener!!.complete() } + } + + if (value != null) { + result = value + return@subscribe ListeningStatus.STOPPED.also { listener!!.complete() } + } else return@subscribe ListeningStatus.LISTENING + } + } + } else { + withTimeoutOrNull(timeoutMillis) { + var listener: Listener? = null + @Suppress("DuplicatedCode") // for better performance + listener = this.subscribe { + val value = try { + filter.invoke(this, it) + } catch (e: Exception) { + resultThrowable = e + return@subscribe ListeningStatus.STOPPED.also { listener!!.complete() } + } + + if (value != null) { + result = value + return@subscribe ListeningStatus.STOPPED.also { listener!!.complete() } + } else return@subscribe ListeningStatus.LISTENING + } + } + } + resultThrowable?.let { throw it } + return result +} + +/** + * 异步监听这个事件, 并尝试从这个事件中获取一个值. + * + * 若 [filter] 抛出了一个异常, [Deferred.await] 会抛出这个异常或. + * + * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 + * @param coroutineContext 额外的 [CoroutineContext] + * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [subscribingGet] 会返回这个值 + */ +@MiraiExperimentalAPI +inline fun CoroutineScope.subscribingGetAsync( + coroutineContext: CoroutineContext = EmptyCoroutineContext, + timeoutMillis: Long = -1, + noinline filter: E.(E) -> R? +): Deferred = this.async(coroutineContext) { + subscribingGet(timeoutMillis, filter) +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt index eeca3a407..9547437fd 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt @@ -101,6 +101,9 @@ interface Listener : CompletableJob { * * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext] * + * @see subscribingGet 监听一个事件, 并尝试从这个事件中获取一个值. + * @see subscribingGetAsync 异步监听一个事件, 并尝试从这个事件中获取一个值. + * * @see subscribeAlways 一直监听 * @see subscribeOnce 只监听一次 * From b26b3bbfbc574ecb76a045113844cdde66c29c8c Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:29:17 +0800 Subject: [PATCH 18/52] Fix `MemberImpl.equals` --- .../net/mamoe/mirai/qqandroid/ContactImpl.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index 159082173..ada894249 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -166,7 +166,7 @@ internal class QQImpl( } -@Suppress("MemberVisibilityCanBePrivate", "DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") +@Suppress("MemberVisibilityCanBePrivate") internal class MemberImpl( qq: QQImpl, group: GroupImpl, @@ -286,6 +286,20 @@ internal class MemberImpl( } } + override fun hashCode(): Int { + var result = bot.hashCode() + result = 31 * result + id.hashCode() + return result + } + + @Suppress("DuplicatedCode") + override fun equals(other: Any?): Boolean { // 不要删除. trust me + if (this === other) return true + if (other !is Contact) return false + if (this::class != other::class) return false + return this.id == other.id && this.bot == other.bot + } + override fun toString(): String { return "Member($id)" } From 8d115782657126d60f2a02b64d95968ea4b22570 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:29:58 +0800 Subject: [PATCH 19/52] Use newly introduced linear processing --- .../packet/chat/receive/MessageSvc.kt | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) 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 57bddbfdb..9d415b113 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 @@ -9,7 +9,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive -import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Deferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact @@ -19,11 +19,10 @@ 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.ListeningStatus import net.mamoe.mirai.event.events.BotJoinGroupEvent import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.MemberJoinEvent -import net.mamoe.mirai.event.subscribe +import net.mamoe.mirai.event.subscribingGetAsync import net.mamoe.mirai.message.FriendMessage import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSource @@ -279,17 +278,14 @@ internal class MessageSvc { override val groupId: Long, override val sourceMessage: MessageChain ) : MessageSource { - lateinit var sequenceIdDeferred: CompletableDeferred + lateinit var sequenceIdDeferred: Deferred + @UseExperimental(MiraiExperimentalAPI::class) fun startWaitingSequenceId(contact: Contact) { - sequenceIdDeferred = CompletableDeferred() - contact.subscribe { event -> - if (event.messageRandom == messageUid.toInt()) { - sequenceIdDeferred.complete(event.sequenceId) - return@subscribe ListeningStatus.STOPPED - } - - return@subscribe ListeningStatus.LISTENING + sequenceIdDeferred = contact.subscribingGetAsync { + if (it.messageRandom == messageUid.toInt()) { + it.sequenceId + } else null } } From 99586f226d36d2c319ea707a2dfd874c2872643f Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:30:39 +0800 Subject: [PATCH 20/52] Add `MessagePacket<*, *>.isContextIdenticalWith`, `MessagePacket<*, *>.nextMessage` --- .../net.mamoe.mirai/message/MessagePacket.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) 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 5ad3974e6..b3d4a3f03 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 @@ -21,6 +21,8 @@ import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.data.Packet import net.mamoe.mirai.event.events.BotEvent +import net.mamoe.mirai.event.subscribingGet +import net.mamoe.mirai.event.subscribingGetAsync import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.* import kotlin.jvm.JvmName @@ -109,6 +111,10 @@ abstract class MessagePacketBase(_bot: Bot) : suspend inline fun String.send() = this.toMessage().sendTo(subject) // endregion + operator fun get(at: Message.Key): M { + return this.message[at] + } + /** * 创建 @ 这个账号的消息. 当且仅当消息为群消息时可用. 否则将会抛出 [IllegalArgumentException] */ @@ -134,4 +140,30 @@ abstract class MessagePacketBase(_bot: Bot) : */ suspend inline fun Image.download(): ByteReadPacket = bot.run { download() } // endregion +} + +/** + * 判断两个 [MessagePacket] 的 [MessagePacket.sender] 和 [MessagePacket.subject] 是否相同 + */ +fun MessagePacket<*, *>.isContextIdenticalWith(another: MessagePacket<*, *>): Boolean { + return this.sender == another.sender && this.subject == another.subject +} + +/** + * 获取下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 都相同且通过 [筛选][filter] 的 [MessagePacket] + * + * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. + * + * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 + * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [subscribingGet] 会返回这个值 + * + * @see subscribingGetAsync 本函数的异步版本 + */ +suspend inline fun > P.nextMessage( + timeoutMillis: Long = -1, + crossinline filter: P.(P) -> Boolean +): P { + return subscribingGet(timeoutMillis) { + takeIf { this.isContextIdenticalWith(this@nextMessage) }?.takeIf { filter(it, it) } + } } \ No newline at end of file From eb258bbd5c74e425ee29c17d5e295ea2e557d9b7 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:30:55 +0800 Subject: [PATCH 21/52] Explicitly state return type --- .../net.mamoe.mirai/message/GroupMessage.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) 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 6f2b62a97..f14d0689e 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 @@ -9,6 +9,7 @@ package net.mamoe.mirai.message +import kotlinx.coroutines.Job import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.Event @@ -44,25 +45,25 @@ class GroupMessage( * 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息 * 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息 */ - suspend inline fun quoteReply(message: MessageChain) = reply(this.message.quote() + message) + suspend inline fun quoteReply(message: MessageChain): MessageReceipt = reply(this.message.quote() + message) - suspend inline fun quoteReply(message: Message) = reply(this.message.quote() + message) - suspend inline fun quoteReply(plain: String) = reply(this.message.quote() + plain) + suspend inline fun quoteReply(message: Message): MessageReceipt = reply(this.message.quote() + message) + suspend inline fun quoteReply(plain: String): MessageReceipt = reply(this.message.quote() + plain) @JvmName("reply2") - suspend inline fun String.quoteReply() = quoteReply(this) + suspend inline fun String.quoteReply(): MessageReceipt = quoteReply(this) @JvmName("reply2") - suspend inline fun Message.quoteReply() = quoteReply(this) + suspend inline fun Message.quoteReply(): MessageReceipt = quoteReply(this) @JvmName("reply2") - suspend inline fun MessageChain.quoteReply() = quoteReply(this) + suspend inline fun MessageChain.quoteReply(): MessageReceipt = quoteReply(this) suspend inline fun MessageChain.recall() = group.recall(this) suspend inline fun MessageSource.recall() = group.recall(this) - inline fun MessageSource.recallIn(delay: Long) = group.recallIn(this, delay) - inline fun MessageChain.recallIn(delay: Long) = group.recallIn(this, delay) + inline fun MessageSource.recallIn(delay: Long): Job = group.recallIn(this, delay) + inline fun MessageChain.recallIn(delay: Long): Job = group.recallIn(this, delay) override fun toString(): String = "GroupMessage(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)" From 5623f87ac5104d0c1b41f8151fbd155bcf250029 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:37:53 +0800 Subject: [PATCH 22/52] Update samples --- .../java/demo/subscribe/SubscribeSamples.kt | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt b/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt index 88931f882..555c0b611 100644 --- a/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt +++ b/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt @@ -14,16 +14,14 @@ package demo.subscribe import kotlinx.coroutines.CompletableJob import net.mamoe.mirai.Bot import net.mamoe.mirai.BotAccount -import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.contact.QQ +import net.mamoe.mirai.contact.isOperator import net.mamoe.mirai.contact.sendMessage import net.mamoe.mirai.event.* import net.mamoe.mirai.message.FriendMessage import net.mamoe.mirai.message.GroupMessage -import net.mamoe.mirai.message.data.AtAll -import net.mamoe.mirai.message.data.Image -import net.mamoe.mirai.message.data.PlainText -import net.mamoe.mirai.message.data.firstOrNull +import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.message.nextMessage import net.mamoe.mirai.message.sendAsImageTo import net.mamoe.mirai.qqandroid.Bot import net.mamoe.mirai.qqandroid.QQAndroid @@ -207,6 +205,25 @@ fun Bot.messageDSL() { // sender: QQ // it: String (来自 MessageChain.toString) // group: Group + + case("recall") { + reply("😎").recallIn(3000) // 3 秒后自动撤回这条消息 + } + + case("禁言") { + // 挂起当前协程, 等待下一条满足条件的消息. + // 发送 "禁言" 后需要再发送一条消息 at 一个人. + val value: At = nextMessage { message.any(At) }[At] + value.member().mute(10) + } + + startsWith("群名=") { + if (!sender.isOperator()) { + sender.mute(5) + return@startsWith + } + group.name = it + } } } From b8c35048a4c5d689c1a266d730cff89a1f49f7f0 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:38:16 +0800 Subject: [PATCH 23/52] Update docs --- .../src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt | 3 +++ .../commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) 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 74f0a1431..bce7e10b7 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 @@ -72,6 +72,9 @@ interface Member : QQ, Contact { /** * 禁言. * + * QQ 中最小操作和显示的时间都是一分钟. + * 机器人可以实现精确到秒, 会被客户端显示为 1 分钟但不影响实际禁言时间. + * * 管理员可禁言成员, 群主可禁言管理员和群员. * * @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常. 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 b3d4a3f03..b5ea0dac6 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 @@ -150,7 +150,7 @@ fun MessagePacket<*, *>.isContextIdenticalWith(another: MessagePacket<*, *>): Bo } /** - * 获取下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 都相同且通过 [筛选][filter] 的 [MessagePacket] + * 挂起当前协程, 等待下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 与 [P] 相同且通过 [筛选][filter] 的 [MessagePacket] * * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. * From 00fd47f12499e95d17abaf94c88de01fee7a01e4 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:39:41 +0800 Subject: [PATCH 24/52] Update docs --- .../src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt index e73d3e922..e6bca704c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt @@ -27,7 +27,7 @@ import kotlin.coroutines.EmptyCoroutineContext @MiraiExperimentalAPI suspend inline fun subscribingGet( timeoutMillis: Long = -1, - noinline filter: E.(E) -> R? + noinline filter: E.(E) -> R? // 不要 crossinline: crossinline 后 stacktrace 会不正常 ): R { require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" } return subscribingGetOrNull(timeoutMillis, filter) ?: error("timeout subscribingGet") @@ -46,7 +46,7 @@ suspend inline fun subscribingGet( @MiraiExperimentalAPI suspend inline fun subscribingGetOrNull( timeoutMillis: Long = -1, - noinline filter: E.(E) -> R? + noinline filter: E.(E) -> R? // 不要 crossinline: crossinline 后 stacktrace 会不正常 ): R? { require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" } var result: R? = null @@ -106,7 +106,7 @@ suspend inline fun subscribingGetOrNull( inline fun CoroutineScope.subscribingGetAsync( coroutineContext: CoroutineContext = EmptyCoroutineContext, timeoutMillis: Long = -1, - noinline filter: E.(E) -> R? + noinline filter: E.(E) -> R? // 不要 crossinline: crossinline 后 stacktrace 会不正常 ): Deferred = this.async(coroutineContext) { subscribingGet(timeoutMillis, filter) } \ No newline at end of file From fb25baa78d950a7e2c83a38cf49f961a3dbe2b82 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:41:22 +0800 Subject: [PATCH 25/52] Update docs --- .../src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt index e6bca704c..44b1b5e85 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt @@ -15,7 +15,7 @@ import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext /** - * 监听这个事件, 并尝试从这个事件中获取一个值. + * 挂起当前协程, 监听这个事件, 并尝试从这个事件中获取一个值. * * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. * @@ -34,7 +34,7 @@ suspend inline fun subscribingGet( } /** - * 监听这个事件, 并尝试从这个事件中获取一个值. + * 挂起当前协程, 监听这个事件, 并尝试从这个事件中获取一个值. * * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. * From ec248c8dd3e0b504e2a4fb7f8f56f924a2cac77f Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:44:07 +0800 Subject: [PATCH 26/52] Add unconditional `nextMessage` --- .../net.mamoe.mirai/message/MessagePacket.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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 b5ea0dac6..b8c3eb8fc 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 @@ -166,4 +166,21 @@ suspend inline fun > P.nextMessage( return subscribingGet(timeoutMillis) { takeIf { this.isContextIdenticalWith(this@nextMessage) }?.takeIf { filter(it, it) } } +} + +/** + * 挂起当前协程, 等待下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 与 [P] 相同的 [MessagePacket] + * + * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. + * + * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 + * + * @see subscribingGetAsync 本函数的异步版本 + */ +suspend inline fun > P.nextMessage( + timeoutMillis: Long = -1 +): P { + return subscribingGet(timeoutMillis) { + takeIf { this.isContextIdenticalWith(this@nextMessage) } + } } \ No newline at end of file From 06381e8ba21c63fad5491ddd010dc552917f23cc Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:50:51 +0800 Subject: [PATCH 27/52] Fix import --- .../src/main/java/demo/subscribe/SubscribeSamples.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt b/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt index 555c0b611..dc35014f7 100644 --- a/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt +++ b/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt @@ -14,6 +14,7 @@ package demo.subscribe import kotlinx.coroutines.CompletableJob import net.mamoe.mirai.Bot import net.mamoe.mirai.BotAccount +import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.isOperator import net.mamoe.mirai.contact.sendMessage @@ -23,8 +24,6 @@ import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.nextMessage import net.mamoe.mirai.message.sendAsImageTo -import net.mamoe.mirai.qqandroid.Bot -import net.mamoe.mirai.qqandroid.QQAndroid import net.mamoe.mirai.utils.FileBasedDeviceInfo import net.mamoe.mirai.utils.MiraiInternalAPI import java.io.File @@ -47,7 +46,7 @@ private fun readTestAccount(): BotAccount? { @Suppress("UNUSED_VARIABLE") suspend fun main() { - val bot = QQAndroid.Bot( // JVM 下也可以不写 `QQAndroid.` 引用顶层函数 + val bot = Bot( // JVM 下也可以不写 `QQAndroid.` 引用顶层函数 123456789, "123456" ) { From 068de99531b3c5c28314be0a17c6d7979137ac72 Mon Sep 17 00:00:00 2001 From: ryoii Date: Fri, 21 Feb 2020 22:51:36 +0800 Subject: [PATCH 28/52] http api prepare for recall with a dto --- .../net/mamoe/mirai/api/http/route/MessageRouteModule.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt index be9be7b03..8c0c69472 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt @@ -18,12 +18,12 @@ import io.ktor.response.respondText import io.ktor.routing.post import io.ktor.routing.routing import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient import net.mamoe.mirai.api.http.AuthedSession import net.mamoe.mirai.api.http.SessionManager import net.mamoe.mirai.api.http.data.* import net.mamoe.mirai.api.http.data.common.MessageChainDTO import net.mamoe.mirai.api.http.data.common.VerifyDTO -import net.mamoe.mirai.api.http.data.common.toDTO import net.mamoe.mirai.api.http.data.common.toMessageChain import net.mamoe.mirai.api.http.util.toJson import net.mamoe.mirai.contact.toList @@ -119,3 +119,8 @@ private data class SendImageDTO( val urls: List ) : VerifyDTO() +@Serializable +private class SendRetDTO( + @Transient val stateCode: StateCode = StateCode.Success, + val messageId: Long +) : StateCode(stateCode.code, stateCode.msg) From 2b7b8fa6c1f100fa5f0eed6ca569accb53bc64d9 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 22:59:52 +0800 Subject: [PATCH 29/52] Improve MessagePacket<*, *>.nextMessage --- .../kotlin/net.mamoe.mirai/message/MessagePacket.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 b8c3eb8fc..0bfd0a01e 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 @@ -162,10 +162,10 @@ fun MessagePacket<*, *>.isContextIdenticalWith(another: MessagePacket<*, *>): Bo suspend inline fun > P.nextMessage( timeoutMillis: Long = -1, crossinline filter: P.(P) -> Boolean -): P { +): MessageChain { return subscribingGet(timeoutMillis) { takeIf { this.isContextIdenticalWith(this@nextMessage) }?.takeIf { filter(it, it) } - } + }.message } /** @@ -179,8 +179,8 @@ suspend inline fun > P.nextMessage( */ suspend inline fun > P.nextMessage( timeoutMillis: Long = -1 -): P { +): MessageChain { return subscribingGet(timeoutMillis) { takeIf { this.isContextIdenticalWith(this@nextMessage) } - } + }.message } \ No newline at end of file From 989547d0fc1c3d967d4141088ea8b196e690679b Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Feb 2020 23:05:27 +0800 Subject: [PATCH 30/52] 0.19.1 --- CHANGELOG.md | 33 ++++++++++++++++++++++++++++++++- gradle.properties | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4031a124e..5096266aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ 开发版本. 频繁更新, 不保证高稳定性 +## `0.19.1` 2020/2/21 + +### mirai-core +- 支持机器人撤回群消息 (含自己发送的消息): `Group.recall`, `MessageReceipt.recall` +- 支持一定时间后自动撤回: `Group.recallIn`, `MessageReceipt.recallIn` +- `sendMessage` 返回 `MessageReceipt` 以实现撤回功能 +- 添加 `MessageChain.addOrRemove` +- 添加 `ContactList.firstOrNull`, `ContactList.first` +- 新的异步事件监听方式: `subscribingGetAsync` 启动一个协程并从一个事件从获取返回值到 `Deferred`. +- 新的线性事件监听方式: `subscribingGet` 挂起当前协程并从一个事件从获取返回值. + +##### 新的线性消息连续处理: `nextMessage` 挂起当前协程并等待下一条消息: +使用该示例, 发送两条消息, 一条为 "禁言", 另一条包含一个 At +```kotlin +case("禁言") { + val value: At = nextMessage { message.any(At) }[At] + value.member().mute(10) +} +``` +示例 2: +```kotlin +case("复读下一条") { + reply(nextMessage().message) +} +``` + +### mirai-core-qqandroid +- 修复一些情况下 `At` 无法发送的问题 +- 统一 ImageId: 群消息收到的 ImageId 均为 `{xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx}.jpg` 形式(固定长度 37) +- 支持成员主动离开事件的解析 (#51) + ## `0.18.0` 2020/2/20 ### mirai-core @@ -212,7 +243,7 @@ TIMPC ## `0.10.0` *2019/12/23* **事件优化** 更快的监听过程 -现在监听不再是 `suspend`, 而必须显式指定 `CoroutineScope`. 详见 [`Subscribers.kt`](mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt#L69) +现在监听不再是 `suspend`, 而必须显式指定 `CoroutineScope`. 详见 `Subscribers.kt` 删除原本的 bot.subscribe 等监听模式. **其他** diff --git a/gradle.properties b/gradle.properties index 97dc4d58d..751900c55 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # style guide kotlin.code.style=official # config -mirai_version=0.18.0 +mirai_version=0.19.1 mirai_japt_version=1.0.1 kotlin.incremental.multiplatform=true kotlin.parallel.tasks.in.project=true From 876da07144e834c81b4bee46d4022c0af6c95233 Mon Sep 17 00:00:00 2001 From: ryoii Date: Sat, 22 Feb 2020 00:40:40 +0800 Subject: [PATCH 31/52] tool man fixes bugs which he doesnt know at all --- .../network/QQAndroidBotNetworkHandler.kt | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index 4746a5fff..57782c6f5 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -374,12 +374,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler // with generic type, less mistakes 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<*>) { packet.forEach { handlePacket(null, it, commandName, sequenceId) } } + handlePacket(packetFactory, packet, commandName, sequenceId) } } @@ -388,29 +388,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler */ 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)) { - listener.complete(packet) - } - } - - // check top-level cancelling - if (packet != null && PacketReceivedEvent(packet).broadcast().isCancelled) { - return - } - - - // broadcast - if (packet is Event) { - if (packet is BroadcastControllable) { - if (packet.shouldBroadcast) packet.broadcast() - } else { - packet.broadcast() - } - - if (packet is CancellableEvent && packet.isCancelled) return - } - if (packet != null && (bot.logger.isEnabled || logger.isEnabled)) { val logMessage = "Received: ${packet.toString().replace("\n", """\n""").replace("\r", "")}" @@ -419,12 +396,32 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } else logger.verbose(logMessage) } + packetListeners.forEach { listener -> + if (listener.filter(commandName, sequenceId) && packetListeners.remove(listener)) { + listener.complete(packet) + } + } + packetFactory?.run { when (this) { is OutgoingPacketFactory

-> bot.handle(packet) is IncomingPacketFactory

-> bot.handle(packet, sequenceId)?.sendWithoutExpect() } } + + if (packet != null && PacketReceivedEvent(packet).broadcast().isCancelled) { + return + } + + if (packet is Event) { + if (packet is BroadcastControllable) { + if (packet.shouldBroadcast) packet.broadcast() + } else { + packet.broadcast() + } + + if (packet is CancellableEvent && packet.isCancelled) return + } } /** From 24eab8f76c459f86261cbcbacf4c18a6d1c6948c Mon Sep 17 00:00:00 2001 From: ryoii Date: Sat, 22 Feb 2020 00:46:40 +0800 Subject: [PATCH 32/52] rename EventType_CN.md to EventType_CH.md --- mirai-api-http/{EventType_CN.md => EventType_CH.md} | 0 mirai-api-http/README_CH.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename mirai-api-http/{EventType_CN.md => EventType_CH.md} (100%) diff --git a/mirai-api-http/EventType_CN.md b/mirai-api-http/EventType_CH.md similarity index 100% rename from mirai-api-http/EventType_CN.md rename to mirai-api-http/EventType_CH.md diff --git a/mirai-api-http/README_CH.md b/mirai-api-http/README_CH.md index 8af771a53..a102a1b90 100644 --- a/mirai-api-http/README_CH.md +++ b/mirai-api-http/README_CH.md @@ -408,7 +408,7 @@ Content-Type:multipart/form-data ### 事件类型一览 -[事件类型一览](./EventType_CN.md) +[事件类型一览](EventType_CH.md) > 事件为Bot被动接收的信息,无法主动构建 From 616957430aef6ac99e4083e723e6e50be3d8b0d3 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 22 Feb 2020 10:59:27 +0800 Subject: [PATCH 33/52] Remove `MessageSource.sourceMessage` --- .../qqandroid/message/MessageSourceFromMsg.kt | 5 ++--- .../protocol/packet/chat/receive/MessageSvc.kt | 14 +++++++------- .../net.mamoe.mirai/message/data/MessageSource.kt | 5 ----- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt index f966bc71b..540186716 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt @@ -10,7 +10,6 @@ package net.mamoe.mirai.qqandroid.message import net.mamoe.mirai.contact.Group -import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.qqandroid.io.serialization.loadAs import net.mamoe.mirai.qqandroid.io.serialization.toByteArray @@ -29,7 +28,7 @@ internal inline class MessageSourceFromServer( } override val messageUid: Long get() = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!! - override val sourceMessage: MessageChain get() = delegate.toMessageChain() + // override val sourceMessage: MessageChain get() = delegate.toMessageChain() override val senderId: Long get() = delegate.senderUin override val groupId: Long get() = Group.calculateGroupCodeByGroupUin(delegate.toUin) @@ -46,7 +45,7 @@ internal inline class MessageSourceFromMsg( } override val messageUid: Long get() = delegate.msgBody.richText.attr!!.random.toLong() - override val sourceMessage: MessageChain get() = delegate.toMessageChain() + // override val sourceMessage: MessageChain get() = delegate.toMessageChain() override val senderId: Long get() = delegate.msgHead.fromUin override val groupId: Long get() = delegate.msgHead.groupInfo!!.groupCode 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 9d415b113..94e37d2fa 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 @@ -275,10 +275,10 @@ internal class MessageSvc { override val messageUid: Long, override val time: Long, override val senderId: Long, - override val groupId: Long, - override val sourceMessage: MessageChain + override val groupId: Long// , + // override val sourceMessage: MessageChain ) : MessageSource { - lateinit var sequenceIdDeferred: Deferred + private lateinit var sequenceIdDeferred: Deferred @UseExperimental(MiraiExperimentalAPI::class) fun startWaitingSequenceId(contact: Contact) { @@ -317,8 +317,8 @@ internal class MessageSvc { messageUid = Random.nextInt().absoluteValue.toLong() and 0xffffffff, senderId = client.uin, time = currentTimeSeconds + client.timeDifference, - groupId = 0, - sourceMessage = message + groupId = 0// + // sourceMessage = message ) message.addOrRemove(source) @@ -354,8 +354,8 @@ internal class MessageSvc { messageUid = Random.nextInt().absoluteValue.toLong(), senderId = client.uin, time = currentTimeSeconds + client.timeDifference, - groupId = groupCode, - sourceMessage = message + groupId = groupCode//, + // sourceMessage = message ) ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) 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 e0b7239dc..467802330 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 @@ -58,11 +58,6 @@ interface MessageSource : Message { */ val groupId: Long - /** - * 原消息内容 - */ - val sourceMessage: MessageChain - /** * 固定返回空字符串 ("") */ From 0e30d3d69ba29d1013f16ebb65acd73cead16295 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 22 Feb 2020 11:36:19 +0800 Subject: [PATCH 34/52] Better memory management --- .../net/mamoe/mirai/qqandroid/ContactImpl.kt | 25 +++++++++---------- .../packet/chat/receive/OnlinePush.kt | 2 -- .../net/mamoe/mirai/message/MessagePacket.kt | 3 +-- .../net.mamoe.mirai/message/FriendMessage.kt | 8 ++++-- .../net.mamoe.mirai/message/GroupMessage.kt | 7 +++--- .../net.mamoe.mirai/message/MessagePacket.kt | 9 ++++--- .../net/mamoe/mirai/message/MessagePacket.kt | 3 +-- 7 files changed, 29 insertions(+), 28 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index ada894249..2d740a1f4 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -306,20 +306,19 @@ internal class MemberImpl( } internal class MemberInfoImpl( - private val jceInfo: StTroopMemberInfo, - private val groupOwnerId: Long + jceInfo: StTroopMemberInfo, + groupOwnerId: Long ) : MemberInfo { - override val uin: Long get() = jceInfo.memberUin - override val nameCard: String get() = jceInfo.sName ?: "" - override val nick: String get() = jceInfo.nick - override val permission: MemberPermission - get() = when { - jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER - jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR - else -> MemberPermission.MEMBER - } - override val specialTitle: String get() = jceInfo.sSpecialTitle ?: "" - override val muteTimestamp: Int get() = jceInfo.dwShutupTimestap?.toInt() ?: 0 + override val uin: Long = jceInfo.memberUin + override val nameCard: String = jceInfo.sName ?: "" + override val nick: String = jceInfo.nick + override val permission: MemberPermission = when { + jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER + jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR + else -> MemberPermission.MEMBER + } + override val specialTitle: String = jceInfo.sSpecialTitle ?: "" + override val muteTimestamp: Int = jceInfo.dwShutupTimestap?.toInt() ?: 0 } /** 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 06720d94e..8f1ab906c 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 @@ -74,8 +74,6 @@ internal class OnlinePush { // println(pbPushMsg.msg.msgBody.richText.contentToString()) val flags = extraInfo?.flags ?: 0 return GroupMessage( - bot = bot, - group = group, senderName = pbPushMsg.msg.msgHead.groupInfo.groupCard, sender = group[pbPushMsg.msg.msgHead.fromUin], message = pbPushMsg.msg.toMessageChain(), diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt index 25d9e7640..a9172742c 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt @@ -9,7 +9,6 @@ package net.mamoe.mirai.message -import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.utils.MiraiInternalAPI @@ -18,7 +17,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI * 平台相关扩展 */ @UseExperimental(MiraiInternalAPI::class) -actual abstract class MessagePacket actual constructor(bot: Bot) : MessagePacketBase(bot) { +actual abstract class MessagePacket actual constructor() : MessagePacketBase() { // suspend inline fun uploadImage(image: Bitmap): Image = subject.uploadImage(image) //suspend inline fun uploadImage(image: URL): Image = subject.uploadImage(image) //suspend inline fun uploadImage(image: Input): Image = subject.uploadImage(image) 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 8ead2fe6a..d4d03120b 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,12 +13,16 @@ 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.getValue +import net.mamoe.mirai.utils.unsafeWeakRef class FriendMessage( bot: Bot, - override val sender: QQ, + sender: QQ, override val message: MessageChain -) : MessagePacket(bot), BroadcastControllable { +) : MessagePacket(), BroadcastControllable { + override val sender: QQ by sender.unsafeWeakRef() + override val bot: Bot get() = sender.bot override val subject: QQ get() = sender override fun toString(): String = "FriendMessage(sender=${sender.id}, message=$message)" 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 f14d0689e..5752b8755 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 @@ -22,8 +22,6 @@ import kotlin.jvm.JvmName @Suppress("unused", "NOTHING_TO_INLINE") class GroupMessage( - bot: Bot, - group: Group, val senderName: String, /** * 发送方权限. @@ -31,9 +29,10 @@ class GroupMessage( val permission: MemberPermission, sender: Member, override val message: MessageChain -) : MessagePacket(bot), Event { - val group: Group by group.unsafeWeakRef() +) : MessagePacket(), Event { override val sender: Member by sender.unsafeWeakRef() + val group: Group get() = sender.group + override val bot: Bot get() = sender.bot override val subject: Group get() = group 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 0bfd0a01e..36c19d890 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 @@ -32,18 +32,19 @@ import kotlin.jvm.JvmName * 请查看各平台的 `actual` 实现的说明. */ @UseExperimental(MiraiInternalAPI::class) -expect abstract class MessagePacket(bot: Bot) : MessagePacketBase +expect abstract class MessagePacket() : MessagePacketBase /** * 仅内部使用, 请使用 [MessagePacket] */ // Tips: 在 IntelliJ 中 (左侧边栏) 打开 `Structure`, 可查看类结构 @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") @MiraiInternalAPI -abstract class MessagePacketBase(_bot: Bot) : Packet, BotEvent { +abstract class MessagePacketBase : Packet, BotEvent { /** * 接受到这条消息的 */ - override val bot: Bot by _bot.unsafeWeakRef() + @WeakRefProperty + abstract override val bot: Bot /** * 消息事件主体. @@ -53,6 +54,7 @@ abstract class MessagePacketBase(_bot: Bot) : * * 在回复消息时, 可通过 [subject] 作为回复对象 */ + @WeakRefProperty abstract val subject: TSubject /** @@ -60,6 +62,7 @@ abstract class MessagePacketBase(_bot: Bot) : * * 在好友消息时为 [QQ] 的实例, 在群消息时为 [Member] 的实例 */ + @WeakRefProperty abstract val sender: TSender /** diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt index 6d3ca3cbc..389598f07 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.withContext import kotlinx.io.core.Input import kotlinx.io.core.use import kotlinx.io.streams.inputStream -import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.message.data.Image @@ -35,7 +34,7 @@ import javax.imageio.ImageIO * JVM 平台相关扩展 */ @UseExperimental(MiraiInternalAPI::class) -actual abstract class MessagePacket actual constructor(bot: Bot) : MessagePacketBase(bot) { +actual abstract class MessagePacket actual constructor() : MessagePacketBase() { // region 上传图片 suspend inline fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image) From 0015b7bcc1d7bd17588e89bea5e5fd3cb7212196 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 22 Feb 2020 13:48:13 +0800 Subject: [PATCH 35/52] Make `MessageSource.messageUid` int --- .../mirai/api/http/data/common/MessageDTO.kt | 2 +- .../mirai/api/http/queue/MessageQueue.kt | 2 +- .../net/mamoe/mirai/qqandroid/ContactImpl.kt | 12 ++-- .../qqandroid/message/MessageSourceFromMsg.kt | 9 +-- .../network/protocol/data/proto/MsgSvc.kt | 2 +- .../packet/chat/receive/MessageSvc.kt | 70 ++++++++++++------- .../net.mamoe.mirai/message/MessageReceipt.kt | 7 +- .../message/data/MessageSource.kt | 2 +- 8 files changed, 65 insertions(+), 41 deletions(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt index 61ebc46f8..ad0404169 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt @@ -98,7 +98,7 @@ fun MessageChainDTO.toMessageChain(contact: Contact) = @UseExperimental(ExperimentalUnsignedTypes::class) fun Message.toDTO() = when (this) { - is MessageSource -> MessageSourceDTO(messageUid) + is MessageSource -> MessageSourceDTO(messageUid.toLong() and 0xFFffFFff) is At -> AtDTO(target, display) is AtAll -> AtAllDTO(0L) is Face -> FaceDTO(id) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt index eee8f85de..23fb76b90 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt @@ -44,6 +44,6 @@ class MessageQueue : ConcurrentLinkedDeque() { } private fun addCache(msg: GroupMessage) { - quoteCache[msg.message[MessageSource].messageUid] = msg + quoteCache[msg.message[MessageSource].messageUid.toLong() and 0xFFffFFff] = msg } } \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index 2d740a1f4..dc783581c 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -70,16 +70,17 @@ internal class QQImpl( if (event.isCancelled) { throw EventCancelledException("cancelled by FriendMessageSendEvent") } + lateinit var source: MessageSource bot.network.run { check( MessageSvc.PbSendMsg.ToFriend( bot.client, id, event.message - ).sendAndExpect() is MessageSvc.PbSendMsg.Response.SUCCESS + ) { source = it }.sendAndExpect() is MessageSvc.PbSendMsg.Response.SUCCESS ) { "send message failed" } } - return MessageReceipt(message, this) + return MessageReceipt(message, source, this) } override suspend fun uploadImage(image: ExternalImage): Image = try { @@ -552,12 +553,13 @@ internal class GroupImpl( if (event.isCancelled) { throw EventCancelledException("cancelled by FriendMessageSendEvent") } + lateinit var source: MessageSource bot.network.run { - val response = MessageSvc.PbSendMsg.ToGroup( + val response: MessageSvc.PbSendMsg.Response = MessageSvc.PbSendMsg.ToGroup( bot.client, id, event.message - ).sendAndExpect() + ) { source = it }.sendAndExpect() check( response is MessageSvc.PbSendMsg.Response.SUCCESS ) { "send message failed: $response" } @@ -566,7 +568,7 @@ internal class GroupImpl( ((message.last() as MessageSource) as MessageSvc.PbSendMsg.MessageSourceFromSend) .startWaitingSequenceId(this) - return MessageReceipt(message, this) + return MessageReceipt(message, source, this) } override suspend fun uploadImage(image: ExternalImage): Image = try { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt index 540186716..44065d95b 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceFromMsg.kt @@ -27,7 +27,7 @@ internal inline class MessageSourceFromServer( // nothing to do } - override val messageUid: Long get() = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!! + override val messageUid: Int get() = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.toInt() // override val sourceMessage: MessageChain get() = delegate.toMessageChain() override val senderId: Long get() = delegate.senderUin override val groupId: Long get() = Group.calculateGroupCodeByGroupUin(delegate.toUin) @@ -44,7 +44,7 @@ internal inline class MessageSourceFromMsg( // nothing to do } - override val messageUid: Long get() = delegate.msgBody.richText.attr!!.random.toLong() + override val messageUid: Int get() = delegate.msgBody.richText.attr!!.random // override val sourceMessage: MessageChain get() = delegate.toMessageChain() override val senderId: Long get() = delegate.msgHead.fromUin override val groupId: Long get() = delegate.msgHead.groupInfo!!.groupCode @@ -62,7 +62,7 @@ internal inline class MessageSourceFromMsg( type = 0, time = delegate.msgHead.msgTime, pbReserve = SourceMsg.ResvAttr( - origUids = messageUid + origUids = messageUid.toLong() and 0xffFFffFF ).toByteArray(SourceMsg.ResvAttr.serializer()), srcMsg = MsgComm.Msg( msgHead = MsgComm.MsgHead( @@ -72,7 +72,8 @@ internal inline class MessageSourceFromMsg( c2cCmd = delegate.msgHead.c2cCmd, msgSeq = delegate.msgHead.msgSeq, msgTime = delegate.msgHead.msgTime, - msgUid = messageUid, // ok + msgUid = messageUid.toLong() and 0xffFFffFF + , // ok groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode), isSrcMsg = true ), diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgSvc.kt index 3007c6036..712ffe1d8 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgSvc.kt @@ -26,7 +26,7 @@ internal class MsgSvc : ProtoBuf { internal class PbGetMsgResp( @SerialId(1) val result: Int = 0, @SerialId(2) val errmsg: String = "", - @SerialId(3) val syncCookie: ByteArray = EMPTY_BYTE_ARRAY, + @SerialId(3) val syncCookie: ByteArray? = EMPTY_BYTE_ARRAY, @SerialId(4) val syncFlag: SyncFlag, @SerialId(5) val uinPairMsgs: List? = null, @SerialId(6) val bindUin: Long = 0L, 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 94e37d2fa..0c79e4345 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 @@ -272,7 +272,7 @@ internal class MessageSvc { } internal class MessageSourceFromSend( - override val messageUid: Long, + override val messageUid: Int, override val time: Long, override val senderId: Long, override val groupId: Long// , @@ -283,7 +283,7 @@ internal class MessageSvc { @UseExperimental(MiraiExperimentalAPI::class) fun startWaitingSequenceId(contact: Contact) { sequenceIdDeferred = contact.subscribingGetAsync { - if (it.messageRandom == messageUid.toInt()) { + if (it.messageRandom == messageUid) { it.sequenceId } else null } @@ -302,25 +302,34 @@ internal class MessageSvc { } } - /** - * 发送好友消息 - */ - @Suppress("FunctionName") - fun ToFriend( + inline fun ToFriend( client: QQAndroidClient, toUin: Long, - message: MessageChain - ): OutgoingPacket = buildOutgoingUniPacket(client) { - ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) - + message: MessageChain, + crossinline sourceCallback: (MessageSource) -> Unit + ): OutgoingPacket { val source = MessageSourceFromSend( - messageUid = Random.nextInt().absoluteValue.toLong() and 0xffffffff, + messageUid = Random.nextInt().absoluteValue, senderId = client.uin, time = currentTimeSeconds + client.timeDifference, groupId = 0// // sourceMessage = message ) - message.addOrRemove(source) + sourceCallback(source) + return ToFriend(client, toUin, message, source) + } + + /** + * 发送好友消息 + */ + @Suppress("FunctionName") + private fun ToFriend( + client: QQAndroidClient, + toUin: Long, + message: MessageChain, + source: MessageSource + ): OutgoingPacket = buildOutgoingUniPacket(client) { + ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) ///return@buildOutgoingUniPacket writeProtoBuf( @@ -333,13 +342,32 @@ internal class MessageSvc { ) ), msgSeq = client.atomicNextMessageSequenceId(), - msgRand = source.messageUid.toInt(), + msgRand = source.messageUid, syncCookie = SyncCookie(time = source.time).toByteArray(SyncCookie.serializer()) // msgVia = 1 ) ) } + + inline fun ToGroup( + client: QQAndroidClient, + groupCode: Long, + message: MessageChain, + sourceCallback: (MessageSource) -> Unit + ): OutgoingPacket { + + val source = MessageSourceFromSend( + messageUid = Random.nextInt().absoluteValue, + senderId = client.uin, + time = currentTimeSeconds + client.timeDifference, + groupId = groupCode//, + // sourceMessage = message + ) + sourceCallback(source) + return ToGroup(client, groupCode, message, source) + } + /** * 发送群消息 */ @@ -347,17 +375,9 @@ internal class MessageSvc { fun ToGroup( client: QQAndroidClient, groupCode: Long, - message: MessageChain + message: MessageChain, + source: MessageSource ): OutgoingPacket = buildOutgoingUniPacket(client) { - - val source = MessageSourceFromSend( - messageUid = Random.nextInt().absoluteValue.toLong(), - senderId = client.uin, - time = currentTimeSeconds + client.timeDifference, - groupId = groupCode//, - // sourceMessage = message - ) - ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) // DebugLogger.debug("sending group message: " + message.toRichTextElems().contentToString()) @@ -373,7 +393,7 @@ internal class MessageSvc { ) ), msgSeq = client.atomicNextMessageSequenceId(), - msgRand = source.messageUid.toInt(), + msgRand = source.messageUid, syncCookie = EMPTY_BYTE_ARRAY, msgVia = 1 ) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt index 6204e77c6..d760a88f9 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt @@ -28,6 +28,7 @@ import net.mamoe.mirai.utils.unsafeWeakRef */ open class MessageReceipt( val originalMessage: MessageChain, + private val source: MessageSource, target: C ) { init { @@ -53,7 +54,7 @@ open class MessageReceipt( if (_isRecalled.compareAndSet(false, true)) { when (val contact = target) { is Group -> { - contact.recall(originalMessage) + contact.recall(source) } is QQ -> { TODO() @@ -76,7 +77,7 @@ open class MessageReceipt( if (_isRecalled.compareAndSet(false, true)) { when (val contact = target) { is Group -> { - return contact.recallIn(originalMessage, millis) + return contact.recallIn(source, millis) } is QQ -> { TODO() @@ -97,7 +98,7 @@ open class MessageReceipt( open fun quote(): MessageChain { val target = target check(target is Group) { "quote is only available for GroupMessage" } - return this.originalMessage.quote(target.botAsMember) + return this.source.quote(target.botAsMember) } /** 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 467802330..c0ecb35cd 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 @@ -41,7 +41,7 @@ interface MessageSource : Message { /** * 实际上是个随机数, 但服务器确实是用它当做 uid */ - val messageUid: Long + val messageUid: Int /** * 发送时间, 单位为秒 From 801e3a68b1780b71ec333d0a98facd781cf5955a Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 22 Feb 2020 13:48:26 +0800 Subject: [PATCH 36/52] Less memory usage --- .../commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt index cb7dac2e7..42216bb30 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt @@ -13,7 +13,7 @@ import kotlinx.io.pool.DefaultPool import kotlinx.io.pool.ObjectPool internal const val DEFAULT_BYTE_ARRAY_POOL_SIZE = 256 -internal const val DEFAULT_BYTE_ARRAY_SIZE = 81920 +internal const val DEFAULT_BYTE_ARRAY_SIZE = 81920 / 2 val ByteArrayPool: ObjectPool = ByteArrayPoolImpl From 32dc95a6e49d72991b6b6e783f20ca788a2913e5 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 22 Feb 2020 13:48:40 +0800 Subject: [PATCH 37/52] Add `LockFreeLinkedList.iterator` --- .../kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt index 0212d8a48..1acc1a462 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt @@ -68,6 +68,10 @@ fun LockFreeLinkedList.asSequence(): Sequence { } } +operator fun LockFreeLinkedList.iterator(): Iterator { + return asSequence().iterator() +} + /** * 构建链表结构然后转为 [LockFreeLinkedList] */ From 7a217f6f65be01411e305cfe25701a2a3891859d Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 22 Feb 2020 14:32:52 +0800 Subject: [PATCH 38/52] Add `MessageChain.messageUid`, `MessageChain.sequenceId` --- .../kotlin/net.mamoe.mirai/contact/Contact.kt | 2 +- .../net.mamoe.mirai/message/data/MessageChain.kt | 2 +- .../net.mamoe.mirai/message/data/MessageSource.kt | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) 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 68224bdd0..fb1fc8942 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 @@ -48,7 +48,7 @@ interface Contact : CoroutineScope { val id: Long /** - * 向这个对象发送消息. 发送成功后 [message] 中会添加 [MessageSource], 此后可以 [引用回复][MessageReceipt.quote](仅群聊)或 [撤回][MessageReceipt.recall] 这条消息. + * 向这个对象发送消息. * * @see FriendMessageSendEvent 发送好友信息事件, cancellable * @see GroupMessageSendEvent 发送群消息事件. cancellable 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 9a4c9ad4e..f66376bb3 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 @@ -209,7 +209,7 @@ fun MessageChain.firstOrNull(key: Message.Key): M? = when (key) * @throws [NoSuchElementException] 如果找不到该类型的实例 */ @Suppress("UNCHECKED_CAST") -fun MessageChain.first(key: Message.Key): M = firstOrNull(key) ?: error("unknown key: $key") +fun MessageChain.first(key: Message.Key): M = firstOrNull(key) ?: throw NoSuchElementException("no such element: $key") /** * 获取第一个 [M] 类型的 [Message] 实例 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 c0ecb35cd..d7a047a30 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 @@ -62,4 +62,14 @@ interface MessageSource : Message { * 固定返回空字符串 ("") */ override fun toString(): String -} \ No newline at end of file +} + +/** + * 消息唯一标识符. 实际上是个随机数, 但服务器确实是用它当做 uid + */ +val MessageChain.messageUid get() = this[MessageSource].messageUid + +/** + * 消息序列号, 可能来自服务器也可以发送时赋值, 不唯一. + */ +val MessageChain.sequenceId get() = this[MessageSource].sequenceId \ No newline at end of file From 377c5c66a07f186b8b8532d8dfb3644dec8e5ce1 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 22 Feb 2020 15:18:15 +0800 Subject: [PATCH 39/52] Remove `Message.eq` override --- .../commonMain/kotlin/net.mamoe.mirai/message/data/At.kt | 4 ---- .../commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt | 4 ---- .../kotlin/net.mamoe.mirai/message/data/Image.kt | 5 ----- .../kotlin/net.mamoe.mirai/message/data/MessageChain.kt | 6 ------ .../kotlin/net.mamoe.mirai/message/data/PlainText.kt | 7 ------- .../commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt | 4 ---- 6 files changed, 30 deletions(-) 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 949b7e6ae..c08baf3b4 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 @@ -34,10 +34,6 @@ class At @MiraiInternalAPI constructor(val target: Long, val display: String) : companion object Key : Message.Key - override fun eq(other: Message): Boolean { - return other is At && other.target == this.target - } - // 自动为消息补充 " " override fun followedBy(tail: Message): MessageChain { 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 69ab4596c..0578b7c9d 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 @@ -167,8 +167,4 @@ class Face(val id: Int) : Message { const val shouqiang: Int = 169 const val qingwa: Int = 170 } - - override fun eq(other: Message): Boolean { - return other is Face && other.id == this.id - } } \ 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 f4e6dd98f..d11c80488 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 @@ -49,11 +49,6 @@ sealed class Image : Message { final override fun toString(): String { return "[mirai:$imageId]" } - - final override fun eq(other: Message): Boolean { - return if (other is Image) return other.imageId == this.imageId - else this.toString() == other.toString() - } } abstract class CustomFace : Image() { 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 f66376bb3..cf6420cc3 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 @@ -57,12 +57,6 @@ interface MessageChain : Message, MutableList { * @param key 由各个类型消息的伴生对象持有. 如 [PlainText.Key] */ operator fun get(key: Message.Key): M = first(key) - - override fun eq(other: Message): Boolean { - if (other is MessageChain && other.size != this.size) - return false - return this.toString() == other.toString() - } } /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt index e7dbe30ee..d13061536 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt @@ -25,13 +25,6 @@ inline class PlainText(val stringValue: String) : Message { override fun toString(): String = stringValue companion object Key : Message.Key

- - override fun eq(other: Message): Boolean { - if (other is MessageChain) { - return other eq this.toString() - } - return other is PlainText && other.stringValue == this.stringValue - } } /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt index d8805a6d7..27a4829d3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt @@ -27,10 +27,6 @@ inline class XMLMessage(val stringValue: String) : Message, SingleOnly { override fun followedBy(tail: Message): Nothing = error("XMLMessage Message cannot be followed") override fun toString(): String = stringValue - - override fun eq(other: Message): Boolean { - return other is XMLMessage && other.stringValue == this.stringValue - } } /** From f6e95e49b9c736b1b33a7db2be239a2be5c1d4c8 Mon Sep 17 00:00:00 2001 From: jasonczc <jasonczc@qq.com> Date: Sat, 22 Feb 2020 16:07:31 +0800 Subject: [PATCH 40/52] update docs --- docs/guide_build_for_mirai.md | 186 +++++++++++++++++++++++++++++++++ docs/guide_subscribe_events.md | 2 + 2 files changed, 188 insertions(+) create mode 100644 docs/guide_build_for_mirai.md diff --git a/docs/guide_build_for_mirai.md b/docs/guide_build_for_mirai.md new file mode 100644 index 000000000..6bda52948 --- /dev/null +++ b/docs/guide_build_for_mirai.md @@ -0,0 +1,186 @@ +# Mirai Guide - Build For Mirai + +由于Mirai项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020-02-22```,对应版本```0.19.1``` + +本页面采用Kotlin作为开发语言,**若你希望使用 Java 开发**, 请参阅: [mirai-japt](mirai-japt/README.md) + +本页面是[Mirai Guide - Subscribe Events](/docs/guide_subscribe_events.md)的后续Guide + +## build.gradle + +我们首先来看一下完整的```build.gradle```文件 + +```groovy +plugins { + id 'java' + id 'org.jetbrains.kotlin.jvm' version '1.3.61' +} + +group 'test' +version '1.0-SNAPSHOT' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() + jcenter() +} + +dependencies { + implementation 'net.mamoe:mirai-core-qqandroid-jvm:0.19.1' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + testCompile group: 'junit', name: 'junit', version: '4.12' +} + +compileKotlin { + kotlinOptions.jvmTarget = "1.8" +} +compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" +} +``` + +使用gradle直接打包,不会将依赖也打包进去 + +因此,我们引入一些插件进行打包 + +### ShadowJar + +shadowJar支持将依赖也打包到Jar内,下面介绍用法。 + +#### 1.buildscript + +首先声明buildScript + +```groovy +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0' + } +} +``` + +在plugin前加入以上语句 + + + +#### 2.在plugins中进行插件的使用 + +将原本的plugins + +```groovy +plugins { + id 'java' + id 'org.jetbrains.kotlin.jvm' version '1.3.61' +} +``` + +覆盖为 + +```groovy +plugins { + id 'java' + id 'org.jetbrains.kotlin.jvm' version '1.3.61' + id 'com.github.johnrengelman.shadow' version '5.2.0'//使用shadow对依赖进行打包 +} + +apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'java' +``` + + + +#### 3.添加shadowJar + +在文件底部添加 + +```groovy +shadowJar { + // 生成包的命名规则: baseName-version-classifier.jar + manifest { + attributes( + 'Main-Class': 'net.mamoe.mirai.simpleloader.MyLoaderKt'//入口点 + ) + } + + // 将 build.gradle 打入到 jar 中, 方便查看依赖包版本 + from("./"){ + include 'build.gradle' + } +} +``` + + + +#### 4.运行build + +在IDEA中点击```ShadowJar```左侧的run按钮(绿色小三角),build完成后在```build\libs```中找到jar + + + +至此,build.gradle内的内容是 + +```groovy +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0' + } +} + + +plugins { + id 'java' + id 'org.jetbrains.kotlin.jvm' version '1.3.61' + id 'com.github.johnrengelman.shadow' version '5.2.0'//使用shadow对依赖进行打包 +} + +apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'java' + +group 'test' +version '1.0-SNAPSHOT' + +sourceCompatibility = 1.8 + + +repositories { + mavenCentral() + jcenter() +} + +dependencies { + implementation 'net.mamoe:mirai-core-qqandroid-jvm:0.19.1' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + testCompile group: 'junit', name: 'junit', version: '4.12' +} + + +compileKotlin { + kotlinOptions.jvmTarget = "1.8" +} +compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" +} + +shadowJar { + // 生成包的命名规则: baseName-version-classifier.jar + manifest { + attributes( + 'Main-Class': 'net.mamoe.mirai.simpleloader.MyLoaderKt' + ) + } + + // 将 build.gradle 打入到 jar 中, 方便查看依赖包版本 + from("./"){ + include 'build.gradle' + } +} + +``` + diff --git a/docs/guide_subscribe_events.md b/docs/guide_subscribe_events.md index 36c062890..077e755c0 100644 --- a/docs/guide_subscribe_events.md +++ b/docs/guide_subscribe_events.md @@ -112,3 +112,5 @@ suspend fun main() { } ``` +下面可以参阅[Mirai Guide - Build For Mirai](/docs/guide_build_for_mirai.md),对你的Mirai应用进行打包 + From 8624cb89eb7724add604a41b12fcf9250bd57d62 Mon Sep 17 00:00:00 2001 From: jasonczc <jasonczc@qq.com> Date: Sat, 22 Feb 2020 16:10:19 +0800 Subject: [PATCH 41/52] update docs --- docs/guide_getting_started.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/guide_getting_started.md b/docs/guide_getting_started.md index f705ccefd..98c360a6b 100644 --- a/docs/guide_getting_started.md +++ b/docs/guide_getting_started.md @@ -26,6 +26,8 @@ JDK要求6以上 ### 2 新建Gradle项目 +*使用gradle项目可能需要代理,在IDEA的```settings```->```proxy settings```中可以设置 + - 在```File->new project```中选择```Gradle``` - 在面板中的```Additional Libraries and Frameworks```中勾选```Java```以及```Kotlin/JVM``` - 点击```next```,填入```GroupId```与```ArtifactId```(对于测试项目来说,可随意填写) From 3d8485fee58c1dbcf447c722384ba53de9c708e1 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Sat, 22 Feb 2020 16:22:49 +0800 Subject: [PATCH 42/52] Fix `.last` bug --- .../kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt | 5 ++--- .../network/protocol/packet/chat/receive/MessageSvc.kt | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index dc783581c..873725f57 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -553,7 +553,7 @@ internal class GroupImpl( if (event.isCancelled) { throw EventCancelledException("cancelled by FriendMessageSendEvent") } - lateinit var source: MessageSource + lateinit var source: MessageSvc.PbSendMsg.MessageSourceFromSend bot.network.run { val response: MessageSvc.PbSendMsg.Response = MessageSvc.PbSendMsg.ToGroup( bot.client, @@ -565,8 +565,7 @@ internal class GroupImpl( ) { "send message failed: $response" } } - ((message.last() as MessageSource) as MessageSvc.PbSendMsg.MessageSourceFromSend) - .startWaitingSequenceId(this) + source.startWaitingSequenceId(this) return MessageReceipt(message, source, this) } 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 0c79e4345..694dd7277 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 @@ -354,7 +354,7 @@ internal class MessageSvc { client: QQAndroidClient, groupCode: Long, message: MessageChain, - sourceCallback: (MessageSource) -> Unit + sourceCallback: (MessageSourceFromSend) -> Unit ): OutgoingPacket { val source = MessageSourceFromSend( From 8b2f2ac89aacd3f9e65ea41bf4b2a1cc0c2b6e0c Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Sat, 22 Feb 2020 16:32:58 +0800 Subject: [PATCH 43/52] Fix ambiguous doc --- .../src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt | 3 +-- mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) 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 02c2d2c07..8b1c1ed42 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 @@ -23,7 +23,6 @@ import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSource -import net.mamoe.mirai.message.data.quote import net.mamoe.mirai.utils.MiraiExperimentalAPI import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -173,7 +172,7 @@ interface Group : Contact, CoroutineScope { fun Member(memberInfo: MemberInfo): Member /** - * 向这个对象发送消息. 发送成功后 [message] 中会添加 [MessageSource], 此后可以 [引用回复][quote] 或 [撤回][recall] 这条消息. + * 向这个对象发送消息. * * @see FriendMessageSendEvent 发送好友信息事件, cancellable * @see GroupMessageSendEvent 发送群消息事件. cancellable diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt index 9dcd5aa03..3e39fd3c8 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt @@ -21,7 +21,6 @@ import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.MiraiExperimentalAPI /** @@ -76,7 +75,7 @@ interface QQ : Contact, CoroutineScope { suspend fun queryRemark(): FriendNameRemark /** - * 向这个对象发送消息. 发送成功后 [message] 中会添加 [MessageSource], 此后可以 [撤回][recall] 这条消息. + * 向这个对象发送消息. * * @see FriendMessageSendEvent 发送好友信息事件, cancellable * @see GroupMessageSendEvent 发送群消息事件. cancellable From ec759f0fd257cf2db5972307856bd0e8cf7cd101 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Sat, 22 Feb 2020 16:55:41 +0800 Subject: [PATCH 44/52] Add `huawei` repo --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index e9b389fc3..d42e39273 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ buildscript { repositories { mavenLocal() + maven { url "https://mirrors.huaweicloud.com/repository/maven" } jcenter() mavenCentral() google() @@ -40,6 +41,7 @@ allprojects { repositories { mavenLocal() + maven { url "https://mirrors.huaweicloud.com/repository/maven" } jcenter() mavenCentral() google() From dc50787b4006456d1b2d053cdaf72ab720bd337b Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Sat, 22 Feb 2020 16:56:35 +0800 Subject: [PATCH 45/52] Remove unnecessary comments --- build.gradle | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build.gradle b/build.gradle index d42e39273..c48df075d 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,6 @@ buildscript { } dependencies { - // Do try to waste your time. classpath 'com.android.tools.build:gradle:3.5.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath("com.github.jengelman.gradle.plugins:shadow:5.2.0") @@ -33,11 +32,6 @@ allprojects { group = "net.mamoe" version = getProperty("mirai_version") -// tasks.withType(KotlinCompile).all { task -> -// task.kotlinOptions{ -// jvmTarget = '1.6' -// } -// } repositories { mavenLocal() From 87af0cd0f21d4d44821bc195834e2f72a55d6891 Mon Sep 17 00:00:00 2001 From: ryoii <ryoii@foxmail.com> Date: Sat, 22 Feb 2020 16:37:59 +0800 Subject: [PATCH 46/52] http handle any throwable exception --- .../main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt index 4292c972a..474095612 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt @@ -21,6 +21,7 @@ import io.ktor.http.HttpStatusCode import io.ktor.http.content.PartData import io.ktor.request.receive import io.ktor.response.defaultTextContentType +import io.ktor.response.respond import io.ktor.response.respondText import io.ktor.routing.Route import io.ktor.routing.route @@ -141,6 +142,9 @@ internal inline fun Route.intercept(crossinline blk: suspend PipelineContext<Uni call.respondStateCode(StateCode.PermissionDenied) } catch (e: IllegalAccessException) { call.respondStateCode(StateCode(400, e.message), HttpStatusCode.BadRequest) + } catch (e: Throwable) { + e.printStackTrace() + call.respond(HttpStatusCode.InternalServerError, e.message!!) } } From 63c3a64ceb3f78b74c3007651f5136742f4b0357 Mon Sep 17 00:00:00 2001 From: ryoii <ryoii@foxmail.com> Date: Sat, 22 Feb 2020 17:46:17 +0800 Subject: [PATCH 47/52] http api quote cache --- .../net/mamoe/mirai/api/http/queue/MessageQueue.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt index 23fb76b90..bd15f6030 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt @@ -15,16 +15,17 @@ import net.mamoe.mirai.api.http.data.common.toDTO import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.data.MessageSource -import java.util.concurrent.ConcurrentHashMap +import net.mamoe.mirai.utils.firstKey import java.util.concurrent.ConcurrentLinkedDeque class MessageQueue : ConcurrentLinkedDeque<BotEvent>() { - val quoteCache = ConcurrentHashMap<Long, GroupMessage>() + val quoteCacheSize = 4096 + val quoteCache = LinkedHashMap<Long, GroupMessage>() fun fetch(size: Int): List<EventDTO> { var count = size - quoteCache.clear() + val ret = ArrayList<EventDTO>(count) while (!this.isEmpty() && count > 0) { val event = pop() @@ -44,6 +45,9 @@ class MessageQueue : ConcurrentLinkedDeque<BotEvent>() { } private fun addCache(msg: GroupMessage) { - quoteCache[msg.message[MessageSource].messageUid.toLong() and 0xFFffFFff] = msg + quoteCache[msg.message[MessageSource].messageUid.toLong()] = msg + if (quoteCache.size > quoteCacheSize) { + quoteCache.remove(quoteCache.firstKey()) + } } } \ No newline at end of file From bebcf1aacb8490d534eec8aa5830426e82d2dcb1 Mon Sep 17 00:00:00 2001 From: ryoii <ryoii@foxmail.com> Date: Sat, 22 Feb 2020 17:47:38 +0800 Subject: [PATCH 48/52] Http api fix MessageSource lost and ignore Unknown type message --- .../mamoe/mirai/api/http/data/common/MessageDTO.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt index ad0404169..fa2d41d8b 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt @@ -64,7 +64,7 @@ data class XmlDTO(val xml: String) : MessageDTO() @Serializable @SerialName("Unknown") -data class UnknownMessageDTO(val text: String) : MessageDTO() +object UnknownMessageDTO : MessageDTO() /* * Abstract Class @@ -88,9 +88,12 @@ fun MessagePacket<*, *>.toDTO() = when (this) { is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender)) else -> IgnoreEventDTO }.apply { - if (this is MessagePacketDTO) { - messageChain = mutableListOf<MessageDTO>().also { ls -> message.foreachContent { ls.add(it.toDTO()) } } - } + if (this is MessagePacketDTO) { messageChain = message.toDTOChain() } + // else: `this` is bot event +} + +fun MessageChain.toDTOChain() = mutableListOf(this[MessageSource].toDTO()).apply { + foreachContent { content -> content.toDTO().takeUnless { it == UnknownMessageDTO }?.let(::add) } } fun MessageChainDTO.toMessageChain(contact: Contact) = @@ -105,7 +108,7 @@ fun Message.toDTO() = when (this) { is PlainText -> PlainDTO(stringValue) is Image -> ImageDTO(imageId) is XMLMessage -> XmlDTO(stringValue) - else -> UnknownMessageDTO("未知消息类型") + else -> UnknownMessageDTO } @UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) From 43e0c3b4149741c474a7207cd31d06b034d62cc6 Mon Sep 17 00:00:00 2001 From: ryoii <ryoii@foxmail.com> Date: Sat, 22 Feb 2020 18:02:13 +0800 Subject: [PATCH 49/52] Http api rename private method addCache --- .../kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt index bd15f6030..5d4e03399 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt @@ -37,14 +37,15 @@ class MessageQueue : ConcurrentLinkedDeque<BotEvent>() { } } + // TODO: 等FriendMessage支持quote if (event is GroupMessage) { - addCache(event) + addQuoteCache(event) } } return ret } - private fun addCache(msg: GroupMessage) { + private fun addQuoteCache(msg: GroupMessage) { quoteCache[msg.message[MessageSource].messageUid.toLong()] = msg if (quoteCache.size > quoteCacheSize) { quoteCache.remove(quoteCache.firstKey()) From b92956f112d1bc8e9dceeb7d8e208e43cea4c8b2 Mon Sep 17 00:00:00 2001 From: ryoii <ryoii@foxmail.com> Date: Sat, 22 Feb 2020 18:32:00 +0800 Subject: [PATCH 50/52] Http api calculate messageId with uid and seq --- .../kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt | 4 +++- .../kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt index fa2d41d8b..d87d7c999 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt @@ -99,9 +99,11 @@ fun MessageChain.toDTOChain() = mutableListOf(this[MessageSource].toDTO()).apply fun MessageChainDTO.toMessageChain(contact: Contact) = MessageChain().apply { this@toMessageChain.forEach { add(it.toMessage(contact)) } } +internal fun MessageSource.calMessageId() = (messageUid.toLong() shl 32) or (sequenceId.toLong() and 0xFFFFFFFF) + @UseExperimental(ExperimentalUnsignedTypes::class) fun Message.toDTO() = when (this) { - is MessageSource -> MessageSourceDTO(messageUid.toLong() and 0xFFffFFff) + is MessageSource -> MessageSourceDTO(calMessageId()) is At -> AtDTO(target, display) is AtAll -> AtAllDTO(0L) is Face -> FaceDTO(id) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt index 5d4e03399..b2eb44fd2 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.api.http.queue import net.mamoe.mirai.api.http.data.common.EventDTO import net.mamoe.mirai.api.http.data.common.IgnoreEventDTO +import net.mamoe.mirai.api.http.data.common.calMessageId import net.mamoe.mirai.api.http.data.common.toDTO import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.message.GroupMessage @@ -46,7 +47,7 @@ class MessageQueue : ConcurrentLinkedDeque<BotEvent>() { } private fun addQuoteCache(msg: GroupMessage) { - quoteCache[msg.message[MessageSource].messageUid.toLong()] = msg + quoteCache[msg.message[MessageSource].calMessageId()] = msg if (quoteCache.size > quoteCacheSize) { quoteCache.remove(quoteCache.firstKey()) } From 06c69ad6cc869b641c7373ed8a9d2c42321439cb Mon Sep 17 00:00:00 2001 From: ryoii <ryoii@foxmail.com> Date: Sat, 22 Feb 2020 18:53:42 +0800 Subject: [PATCH 51/52] Http api rename `MessageSourceDTO.uid` to `MessageSourceDTO.id` --- mirai-api-http/README_CH.md | 4 ++-- .../kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mirai-api-http/README_CH.md b/mirai-api-http/README_CH.md index a102a1b90..274d7cd84 100644 --- a/mirai-api-http/README_CH.md +++ b/mirai-api-http/README_CH.md @@ -430,13 +430,13 @@ Content-Type:multipart/form-data ```json5 { "type": "Source", - "uid": 123456 + "id": 123456 } ``` | 名字 | 类型 | 说明 | | ---- | ---- | ------------------------------------------------------------ | -| uid | Long | 消息的识别号,用于引用回复(Source类型只在群消息中返回,且永远为chain的第一个元素) | +| id | Long | 消息的识别号,用于引用回复(Source类型只在群消息中返回,且永远为chain的第一个元素) | #### At diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt index d87d7c999..a4a30d82f 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt @@ -36,7 +36,7 @@ data class GroupMessagePacketDTO(val sender: MemberDTO) : MessagePacketDTO() // Message @Serializable @SerialName("Source") -data class MessageSourceDTO(val uid: Long) : MessageDTO() +data class MessageSourceDTO(val id: Long) : MessageDTO() @Serializable @SerialName("At") From 241cf2d57941403444c271f53a32bc51d47fd3d9 Mon Sep 17 00:00:00 2001 From: ryoii <ryoii@foxmail.com> Date: Sat, 22 Feb 2020 19:18:48 +0800 Subject: [PATCH 52/52] Http api ready for recall --- .../mamoe/mirai/api/http/data/StateCode.kt | 3 ++- .../api/http/route/MessageRouteModule.kt | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/StateCode.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/StateCode.kt index 51766c1cb..9777d5144 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/StateCode.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/StateCode.kt @@ -1,9 +1,10 @@ package net.mamoe.mirai.api.http.data import kotlinx.serialization.Serializable +import net.mamoe.mirai.api.http.data.common.DTO @Serializable -open class StateCode(val code: Int, var msg: String) { +open class StateCode(val code: Int, var msg: String) : DTO { object Success : StateCode(0, "success") // 成功 object NoBot : StateCode(2, "指定Bot不存在") object IllegalSession : StateCode(3, "Session失效或不存在") diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt index 8c0c69472..8e3fa1647 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt @@ -11,9 +11,11 @@ package net.mamoe.mirai.api.http.route import io.ktor.application.Application import io.ktor.application.call +import io.ktor.http.HttpStatusCode import io.ktor.http.content.readAllParts import io.ktor.http.content.streamProvider import io.ktor.request.receiveMultipart +import io.ktor.response.respond import io.ktor.response.respondText import io.ktor.routing.post import io.ktor.routing.routing @@ -45,14 +47,12 @@ fun Application.messageModule() { it.session.bot.getFriend(it.target).apply { sendMessage(it.messageChain.toMessageChain(this)) // this aka QQ } - call.respondStateCode(StateCode.Success) } miraiVerify<SendDTO>("/sendGroupMessage") { it.session.bot.getGroup(it.target).apply { sendMessage(it.messageChain.toMessageChain(this)) // this aka Group } - call.respondStateCode(StateCode.Success) } miraiVerify<SendDTO>("/quoteMessage") { @@ -100,6 +100,11 @@ fun Application.messageModule() { } ?: throw IllegalAccessException("图片上传错误") } ?: throw IllegalAccessException("未知错误") } + + miraiVerify<RecallDTO>("recall") { + // TODO + call.respond(HttpStatusCode.NotFound, "未完成") + } } } @@ -121,6 +126,13 @@ private data class SendImageDTO( @Serializable private class SendRetDTO( - @Transient val stateCode: StateCode = StateCode.Success, - val messageId: Long + val messageId: Long, + @Transient val stateCode: StateCode = Success ) : StateCode(stateCode.code, stateCode.msg) + +@Serializable +private data class RecallDTO( + override val sessionKey: String, + val target: Long, + val sender: Long +) : VerifyDTO()