From fae51eea2413afb00ba23c6793e59e4127e46f75 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sat, 29 Oct 2022 17:43:38 +0800 Subject: [PATCH 01/15] feat: remove essence message --- .../src/commonMain/kotlin/contact/Group.kt | 10 +++++ .../commonMain/kotlin/contact/GroupImpl.kt | 13 ++++++ .../network/protocol/packet/PacketFactory.kt | 1 + .../packet/chat/TroopEssenceMsgManager.kt | 45 ++++++++++++++++--- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt index 76436b58dee..13352159ec6 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt @@ -228,6 +228,16 @@ public interface Group : Contact, CoroutineScope, FileSupported, AudioSupported */ public suspend fun setEssenceMessage(source: MessageSource): Boolean + /** + * 取消一条群精华消息, 需要管理员或群主权限. + * 操作成功返回 `true`. + * + * @throws PermissionDeniedException 没有权限时抛出 + * + * @since 2.14 + */ + public suspend fun removeEssenceMessage(source: MessageSource): Boolean + public companion object { /** * 将一条消息设置为群精华消息, 需要管理员或群主权限. diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt index 8f312ae48d1..16ccfc6c86d 100644 --- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt @@ -389,6 +389,19 @@ internal abstract class CommonGroupImpl constructor( return result.success } + override suspend fun removeEssenceMessage(source: MessageSource): Boolean { + checkBotPermission(MemberPermission.ADMINISTRATOR) + val result = bot.network.sendAndExpect( + TroopEssenceMsgManager.SetEssence( + bot.client, + this@CommonGroupImpl.uin, + source.internalIds.first(), + source.ids.first() + ), 5000, 2 + ) + return result.success + } + override fun toString(): String = "Group($id)" } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt index 70c1826804c..34c86949c71 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -162,6 +162,7 @@ internal object KnownPacketFactories { TroopManagement.Kick, TroopManagement.SwitchAnonymousChat, TroopEssenceMsgManager.SetEssence, + TroopEssenceMsgManager.RemoveEssence, NudgePacket, Heartbeat.Alive, PbMessageSvc.PbMsgWithDraw, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopEssenceMsgManager.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopEssenceMsgManager.kt index c89f9645d3a..9e708d91c14 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopEssenceMsgManager.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopEssenceMsgManager.kt @@ -28,15 +28,48 @@ import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf * */ internal class TroopEssenceMsgManager { - internal object SetEssence : OutgoingPacketFactory("OidbSvc.0xeac_1") { - internal data class Response(val success: Boolean, val msg: String?) : Packet + internal data class Response(val success: Boolean, val msg: String?) : Packet + + internal object SetEssence : OutgoingPacketFactory("OidbSvc.0xeac_1") { + + operator fun invoke( + client: QQAndroidClient, + troopUin: Long, + msgRandom: Int, + msgSeq: Int + ) = buildOutgoingUniPacket(client) { + writeProtoBuf( + OidbSso.OIDBSSOPkg.serializer(), OidbSso.OIDBSSOPkg( + command = 3756, + result = 0, + serviceType = 1, + bodybuffer = Oidb0xeac.ReqBody( + groupCode = troopUin, + msgSeq = msgSeq.and(-1), + msgRandom = msgRandom + ).toByteArray(Oidb0xeac.ReqBody.serializer()), + ) + ) + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + readProtoBuf(OidbSso.OIDBSSOPkg.serializer()).let { pkg -> + pkg.bodybuffer.loadAs(Oidb0xeac.RspBody.serializer()).let { data -> + return Response(data.errorCode == 0, data.wording) + } + } + + } + } + + internal object RemoveEssence : OutgoingPacketFactory("OidbSvc.0xeac_2") { operator fun invoke( client: QQAndroidClient, troopUin: Long, - msg_random: Int, - msg_seq: Int + msgRandom: Int, + msgSeq: Int ) = buildOutgoingUniPacket(client) { writeProtoBuf( OidbSso.OIDBSSOPkg.serializer(), OidbSso.OIDBSSOPkg( @@ -45,8 +78,8 @@ internal class TroopEssenceMsgManager { serviceType = 1, bodybuffer = Oidb0xeac.ReqBody( groupCode = troopUin, - msgSeq = msg_seq.and(-1), - msgRandom = msg_random + msgSeq = msgSeq.and(-1), + msgRandom = msgRandom ).toByteArray(Oidb0xeac.ReqBody.serializer()), ) ) From 277c3edce664f405205f17395cc483e02ff69179 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sat, 29 Oct 2022 19:41:51 +0800 Subject: [PATCH 02/15] feat: Essences --- .../src/commonMain/kotlin/contact/Group.kt | 8 ++ .../contact/essence/EssenceMessageRecord.kt | 27 +++++ .../kotlin/contact/essence/Essences.kt | 21 ++++ .../src/contact/essence/MockEssences.kt | 16 +++ .../src/internal/contact/MockGroupImpl.kt | 11 ++ .../internal/contact/essence/MockEssences.kt | 42 +++++++ .../kotlin/contact/essence/EssencesImpl.kt | 109 ++++++++++++++++++ .../contact/essence/GroupDigestProtocol.kt | 109 ++++++++++++++++++ .../kotlin/contact/essence/EssencesImpl.kt | 26 +++++ .../kotlin/contact/essence/EssencesImpl.kt | 18 +++ 10 files changed, 387 insertions(+) create mode 100644 mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt create mode 100644 mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt create mode 100644 mirai-core-mock/src/contact/essence/MockEssences.kt create mode 100644 mirai-core-mock/src/internal/contact/essence/MockEssences.kt create mode 100644 mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt create mode 100644 mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt create mode 100644 mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt create mode 100644 mirai-core/src/nativeMain/kotlin/contact/essence/EssencesImpl.kt diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt index 13352159ec6..dced9d77008 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt @@ -17,6 +17,7 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.active.GroupActive import net.mamoe.mirai.contact.announcement.Announcements +import net.mamoe.mirai.contact.essence.Essences import net.mamoe.mirai.contact.file.RemoteFiles import net.mamoe.mirai.event.events.* import net.mamoe.mirai.message.MessageReceipt @@ -238,6 +239,13 @@ public interface Group : Contact, CoroutineScope, FileSupported, AudioSupported */ public suspend fun removeEssenceMessage(source: MessageSource): Boolean + /** + * 群精华消息相关功能接口 + * + * @since 2.14 + */ + public val essences: Essences + public companion object { /** * 将一条消息设置为群精华消息, 需要管理员或群主权限. diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt new file mode 100644 index 00000000000..c0783fd4b84 --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2022 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/dev/LICENSE + */ + +package net.mamoe.mirai.contact.essence + +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.NormalMember +import net.mamoe.mirai.message.data.MessageSource + +public class EssenceMessageRecord( + public val group: Group, + public val sender: NormalMember?, + public val senderId: Long, + public val senderNick: String, + public val senderTime: Int, + public val operator: NormalMember?, + public val operatorId: Long, + public val operatorNick: String, + public val operatorTime: Int, + public val source: MessageSource +) \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt new file mode 100644 index 00000000000..8d3eae53cfd --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2022 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/dev/LICENSE + */ + +package net.mamoe.mirai.contact.essence + +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.utils.Streamable + +public interface Essences : Streamable { + public suspend fun page(start: Int, limit: Int): List + + public suspend fun add(source: MessageSource) + + public suspend fun remove(source: MessageSource) +} \ No newline at end of file diff --git a/mirai-core-mock/src/contact/essence/MockEssences.kt b/mirai-core-mock/src/contact/essence/MockEssences.kt new file mode 100644 index 00000000000..d69cd6933ac --- /dev/null +++ b/mirai-core-mock/src/contact/essence/MockEssences.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2019-2022 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/dev/LICENSE + */ + +package net.mamoe.mirai.mock.contact.essence + +import net.mamoe.mirai.contact.essence.Essences + +public interface MockEssences : Essences { + +} \ No newline at end of file diff --git a/mirai-core-mock/src/internal/contact/MockGroupImpl.kt b/mirai-core-mock/src/internal/contact/MockGroupImpl.kt index 465e213e6ab..746c1976689 100644 --- a/mirai-core-mock/src/internal/contact/MockGroupImpl.kt +++ b/mirai-core-mock/src/internal/contact/MockGroupImpl.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.runBlocking import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.announcement.OfflineAnnouncement import net.mamoe.mirai.contact.announcement.buildAnnouncementParameters +import net.mamoe.mirai.contact.essence.Essences import net.mamoe.mirai.contact.file.RemoteFiles import net.mamoe.mirai.data.GroupHonorType import net.mamoe.mirai.data.MemberInfo @@ -30,7 +31,9 @@ import net.mamoe.mirai.mock.contact.MockGroup import net.mamoe.mirai.mock.contact.MockGroupControlPane import net.mamoe.mirai.mock.contact.MockNormalMember import net.mamoe.mirai.mock.contact.active.MockGroupActive +import net.mamoe.mirai.mock.contact.essence.MockEssences import net.mamoe.mirai.mock.internal.contact.active.MockGroupActiveImpl +import net.mamoe.mirai.mock.internal.contact.essence.MockEssencesImpl import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcToGroup import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc import net.mamoe.mirai.mock.utils.broadcastBlocking @@ -334,9 +337,17 @@ internal class MockGroupImpl( resource.mockUploadVoice(bot) override suspend fun setEssenceMessage(source: MessageSource): Boolean { + essences.add(source) return true } + override suspend fun removeEssenceMessage(source: MessageSource): Boolean { + essences.remove(source) + return true + } + + override val essences: MockEssences = MockEssencesImpl(this) + @Deprecated("Please use files instead.", replaceWith = ReplaceWith("files.root")) @Suppress("OverridingDeprecatedMember", "DEPRECATION") override val filesRoot: RemoteFile by lazy { diff --git a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt new file mode 100644 index 00000000000..f2ea8ebfe5a --- /dev/null +++ b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2022 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/dev/LICENSE + */ + +package net.mamoe.mirai.mock.internal.contact.essence + +import kotlinx.coroutines.flow.Flow +import net.mamoe.mirai.contact.essence.EssenceMessageRecord +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.mock.contact.essence.MockEssences +import net.mamoe.mirai.mock.internal.contact.MockGroupImpl +import java.util.stream.Stream + +internal class MockEssencesImpl( + private val group: MockGroupImpl +) : MockEssences { + + override suspend fun page(start: Int, limit: Int): List { + TODO("Not yet implemented") + } + + override suspend fun add(source: MessageSource) { + TODO("Not yet implemented") + } + + override suspend fun remove(source: MessageSource) { + TODO("Not yet implemented") + } + + override fun asFlow(): Flow { + TODO("Not yet implemented") + } + + override fun asStream(): Stream { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt new file mode 100644 index 00000000000..cf97f94c613 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2019-2022 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/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.essence + +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.isActive +import net.mamoe.mirai.contact.essence.EssenceMessageRecord +import net.mamoe.mirai.contact.essence.Essences +import net.mamoe.mirai.internal.contact.GroupImpl +import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.utils.MiraiLogger + +internal expect class EssencesImpl( + group: GroupImpl, + logger: MiraiLogger, +) : CommonEssencesImpl + +internal abstract class CommonEssencesImpl( + protected val group: GroupImpl, + protected val logger: MiraiLogger, +) : Essences { + + private fun source(message: DigestMessage): MessageSource { + return group.bot.buildMessageSource(MessageSourceKind.GROUP) { + ids = intArrayOf(message.msgSeq) + internalIds = intArrayOf(message.msgRandom) + time = message.senderTime + + fromId = message.senderUin + targetId = group.id + + messages(message.msgContent.map { content -> + when (content.msgType) { + 1 -> PlainText(content.text) + 3 -> { + // TODO: image url -> md5 -> image_id + content.imageUrl + Image("") + } + else -> { + // XXX: + emptyMessageChain() + } + } + }) + } + } + + private fun record(message: DigestMessage): EssenceMessageRecord { + return EssenceMessageRecord( + group = group, + sender = group[message.senderUin], + senderId = message.senderUin, + senderNick = message.senderNick, + senderTime = message.senderTime, + operator = group[message.addDigestUin], + operatorId = message.addDigestUin, + operatorNick = message.addDigestNick, + operatorTime = message.addDigestTime, + source = source(message = message) + ) + } + + override suspend fun page(start: Int, limit: Int): List { + val page = group.bot.getDigestList( + groupCode = group.id, + pageStart = start, + pageLimit = limit + ) + + return page.messages.map(this::record) + } + + override suspend fun add(source: MessageSource) { + TODO("Not yet implemented") + } + + override suspend fun remove(source: MessageSource) { + TODO("Not yet implemented") + } + + override fun asFlow(): Flow { + return flow { + var offset = 0 + while (currentCoroutineContext().isActive) { + val page = group.bot.getDigestList( + groupCode = group.id, + pageStart = offset, + pageLimit = 30 + ) + for (message in page.messages) { + emit(source(message)) + } + if (page.isEnd) break + if (page.messages.isEmpty()) break + offset += 30 + } + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt new file mode 100644 index 00000000000..83548c0fdc8 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2019-2022 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/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.essence + +import io.ktor.client.request.* +import io.ktor.client.statement.* +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.contact.active.defaultJson +import net.mamoe.mirai.internal.network.components.HttpClientProvider +import net.mamoe.mirai.internal.network.psKey +import net.mamoe.mirai.internal.network.sKey +import net.mamoe.mirai.utils.CheckableResponseA +import net.mamoe.mirai.utils.JsonStruct +import net.mamoe.mirai.utils.loadAs + +@Serializable +internal data class DigestData( + @SerialName("data") val `data`: JsonElement = JsonNull, + @SerialName("retmsg") override val errorMessage: String, + @SerialName("retcode") override val errorCode: Int +) : CheckableResponseA(), JsonStruct + +@Serializable +internal data class DigestList( + @SerialName("group_role") + val role: Int = 0, + @SerialName("is_end") + val isEnd: Boolean = false, + @SerialName("msg_list") + val messages: List = emptyList(), + @SerialName("show_tips") + val showTips: Boolean = false +) + +@Serializable +internal data class DigestMessage( + @SerialName("add_digest_nick") + val addDigestNick: String = "", + @SerialName("add_digest_time") + val addDigestTime: Int = 0, + @SerialName("add_digest_uin") + val addDigestUin: Long = 0, + @SerialName("group_code") + val groupCode: String = "", + @SerialName("msg_content") + val msgContent: List = emptyList(), + @SerialName("msg_random") + val msgRandom: Int = 0, + @SerialName("msg_seq") + val msgSeq: Int = 0, + @SerialName("sender_nick") + val senderNick: String = "", + @SerialName("sender_time") + val senderTime: Int = 0, + @SerialName("sender_uin") + val senderUin: Long = 0 +) + +@Serializable +internal data class DigestMessageContent( + @SerialName("image_thumbnail_url") + val imageThumbnailUrl: String = "", + @SerialName("image_url") + val imageUrl: String = "", + @SerialName("msg_type") + val msgType: Int = 0, + @SerialName("text") + val text: String = "" +) + +private fun DigestData.loadData(serializer: KSerializer): T { + return try { + defaultJson.decodeFromJsonElement(serializer, this.data) + } catch (cause: Exception) { + throw IllegalStateException(message = "parse digest data error, status: $errorCode - $errorMessage", cause) + } +} + +internal suspend fun QQAndroidBot.getDigestList( + groupCode: Long, pageStart: Int, pageLimit: Int +): DigestList { + return components[HttpClientProvider].getHttpClient().get { + url("https://qun.qq.com/cgi-bin/group_digest/digest_list") + parameter("group_code", groupCode) + parameter("page_start", pageStart) + parameter("page_limit", pageLimit) + parameter("bkn", client.wLoginSigInfo.bkn) + + headers { + // ktor bug + append( + "cookie", + "uin=o${id}; skey=${sKey}; p_uin=o${id}; p_skey=${psKey(host)};" + ) + } + }.bodyAsText().loadAs(DigestData.serializer()).loadData(DigestList.serializer()) +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt new file mode 100644 index 00000000000..f4bb82fc89c --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2019-2022 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/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.essence + +import kotlinx.coroutines.flow.Flow +import net.mamoe.mirai.internal.contact.GroupImpl +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.utils.MiraiLogger +import java.util.stream.Stream + +internal actual class EssencesImpl actual constructor( + group: GroupImpl, + logger: MiraiLogger, +) : CommonEssencesImpl(group, logger) { + + override fun asStream(): Stream { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/nativeMain/kotlin/contact/essence/EssencesImpl.kt new file mode 100644 index 00000000000..3d618a9d7e2 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/contact/essence/EssencesImpl.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2019-2022 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/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.essence + +import net.mamoe.mirai.internal.contact.GroupImpl +import net.mamoe.mirai.utils.MiraiLogger + +internal actual class EssencesImpl actual constructor( + group: GroupImpl, + logger: MiraiLogger, +) : CommonEssencesImpl(group, logger) \ No newline at end of file From 31682c78d6fa2a11500babe93f4ef25a81451296 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sat, 26 Nov 2022 20:42:30 +0800 Subject: [PATCH 03/15] add: share and remove --- .../android/api/android.api | 23 ++++++++ .../compatibility-validation/jvm/api/jvm.api | 23 ++++++++ .../kotlin/contact/essence/Essences.kt | 8 ++- .../src/internal/contact/MockGroupImpl.kt | 3 +- .../internal/contact/essence/MockEssences.kt | 9 +--- .../kotlin/contact/essence/EssencesImpl.kt | 22 +++++--- .../contact/essence/GroupDigestProtocol.kt | 54 +++++++++++++++++++ 7 files changed, 125 insertions(+), 17 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index 55316d25e80..d92d12ef761 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -379,6 +379,7 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin public abstract fun getBotAsMember ()Lnet/mamoe/mirai/contact/NormalMember; public fun getBotMuteRemaining ()I public fun getBotPermission ()Lnet/mamoe/mirai/contact/MemberPermission; + public abstract fun getEssences ()Lnet/mamoe/mirai/contact/essence/Essences; public abstract fun getId ()J public abstract fun getMembers ()Lnet/mamoe/mirai/contact/ContactList; public abstract fun getName ()Ljava/lang/String; @@ -387,6 +388,8 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin public abstract fun getSettings ()Lnet/mamoe/mirai/contact/GroupSettings; public fun quit ()Z public abstract fun quit (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;)Z + public abstract fun removeEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt; public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt; @@ -900,6 +903,26 @@ public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt { public static final fun getBot (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;)Lnet/mamoe/mirai/Bot; } +public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { + public fun (Lnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;JLjava/lang/String;ILnet/mamoe/mirai/contact/NormalMember;JLjava/lang/String;ILnet/mamoe/mirai/message/data/MessageSource;)V + public final fun getGroup ()Lnet/mamoe/mirai/contact/Group; + public final fun getOperator ()Lnet/mamoe/mirai/contact/NormalMember; + public final fun getOperatorId ()J + public final fun getOperatorNick ()Ljava/lang/String; + public final fun getOperatorTime ()I + public final fun getSender ()Lnet/mamoe/mirai/contact/NormalMember; + public final fun getSenderId ()J + public final fun getSenderNick ()Ljava/lang/String; + public final fun getSenderTime ()I + public final fun getSource ()Lnet/mamoe/mirai/message/data/MessageSource; +} + +public abstract interface class net/mamoe/mirai/contact/essence/Essences : net/mamoe/mirai/utils/Streamable { + public abstract fun page (IILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun remove (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun share (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + public abstract interface class net/mamoe/mirai/contact/file/AbsoluteFile : net/mamoe/mirai/contact/file/AbsoluteFileFolder { public abstract fun getExpiryTime ()J public abstract fun getMd5 ()[B diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 1d69ea44e0f..41375ed1db1 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -379,6 +379,7 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin public abstract fun getBotAsMember ()Lnet/mamoe/mirai/contact/NormalMember; public fun getBotMuteRemaining ()I public fun getBotPermission ()Lnet/mamoe/mirai/contact/MemberPermission; + public abstract fun getEssences ()Lnet/mamoe/mirai/contact/essence/Essences; public abstract fun getId ()J public abstract fun getMembers ()Lnet/mamoe/mirai/contact/ContactList; public abstract fun getName ()Ljava/lang/String; @@ -387,6 +388,8 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin public abstract fun getSettings ()Lnet/mamoe/mirai/contact/GroupSettings; public fun quit ()Z public abstract fun quit (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;)Z + public abstract fun removeEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt; public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt; @@ -900,6 +903,26 @@ public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt { public static final fun getBot (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;)Lnet/mamoe/mirai/Bot; } +public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { + public fun (Lnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;JLjava/lang/String;ILnet/mamoe/mirai/contact/NormalMember;JLjava/lang/String;ILnet/mamoe/mirai/message/data/MessageSource;)V + public final fun getGroup ()Lnet/mamoe/mirai/contact/Group; + public final fun getOperator ()Lnet/mamoe/mirai/contact/NormalMember; + public final fun getOperatorId ()J + public final fun getOperatorNick ()Ljava/lang/String; + public final fun getOperatorTime ()I + public final fun getSender ()Lnet/mamoe/mirai/contact/NormalMember; + public final fun getSenderId ()J + public final fun getSenderNick ()Ljava/lang/String; + public final fun getSenderTime ()I + public final fun getSource ()Lnet/mamoe/mirai/message/data/MessageSource; +} + +public abstract interface class net/mamoe/mirai/contact/essence/Essences : net/mamoe/mirai/utils/Streamable { + public abstract fun page (IILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun remove (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun share (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + public abstract interface class net/mamoe/mirai/contact/file/AbsoluteFile : net/mamoe/mirai/contact/file/AbsoluteFileFolder { public abstract fun getExpiryTime ()J public abstract fun getMd5 ()[B diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt index 8d3eae53cfd..fae4b53e850 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt @@ -12,10 +12,14 @@ package net.mamoe.mirai.contact.essence import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.Streamable -public interface Essences : Streamable { +/** + * + * @since 2.14 + */ +public interface Essences : Streamable { public suspend fun page(start: Int, limit: Int): List - public suspend fun add(source: MessageSource) + public suspend fun share(source: MessageSource) public suspend fun remove(source: MessageSource) } \ No newline at end of file diff --git a/mirai-core-mock/src/internal/contact/MockGroupImpl.kt b/mirai-core-mock/src/internal/contact/MockGroupImpl.kt index 746c1976689..c006c64aa79 100644 --- a/mirai-core-mock/src/internal/contact/MockGroupImpl.kt +++ b/mirai-core-mock/src/internal/contact/MockGroupImpl.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.runBlocking import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.announcement.OfflineAnnouncement import net.mamoe.mirai.contact.announcement.buildAnnouncementParameters -import net.mamoe.mirai.contact.essence.Essences import net.mamoe.mirai.contact.file.RemoteFiles import net.mamoe.mirai.data.GroupHonorType import net.mamoe.mirai.data.MemberInfo @@ -337,7 +336,7 @@ internal class MockGroupImpl( resource.mockUploadVoice(bot) override suspend fun setEssenceMessage(source: MessageSource): Boolean { - essences.add(source) + essences.share(source) return true } diff --git a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt index f2ea8ebfe5a..d96ac70be48 100644 --- a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt +++ b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt @@ -14,7 +14,6 @@ import net.mamoe.mirai.contact.essence.EssenceMessageRecord import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.mock.contact.essence.MockEssences import net.mamoe.mirai.mock.internal.contact.MockGroupImpl -import java.util.stream.Stream internal class MockEssencesImpl( private val group: MockGroupImpl @@ -24,7 +23,7 @@ internal class MockEssencesImpl( TODO("Not yet implemented") } - override suspend fun add(source: MessageSource) { + override suspend fun share(source: MessageSource) { TODO("Not yet implemented") } @@ -32,11 +31,7 @@ internal class MockEssencesImpl( TODO("Not yet implemented") } - override fun asFlow(): Flow { - TODO("Not yet implemented") - } - - override fun asStream(): Stream { + override fun asFlow(): Flow { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index cf97f94c613..e40962a1a0b 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -47,7 +47,7 @@ internal abstract class CommonEssencesImpl( Image("") } else -> { - // XXX: + // XXX: unknown message type emptyMessageChain() } } @@ -80,15 +80,25 @@ internal abstract class CommonEssencesImpl( return page.messages.map(this::record) } - override suspend fun add(source: MessageSource) { - TODO("Not yet implemented") + override suspend fun share(source: MessageSource) { + val share = group.bot.shareDigest( + groupCode = group.id, + msgSeq = source.ids.first(), + msgRandom = source.internalIds.first(), + targetGroupCode = 0 + ) + share.shareKey } override suspend fun remove(source: MessageSource) { - TODO("Not yet implemented") + group.bot.cancelDigest( + groupCode = group.id, + msgSeq = source.ids.first(), + msgRandom = source.internalIds.first() + ) } - override fun asFlow(): Flow { + override fun asFlow(): Flow { return flow { var offset = 0 while (currentCoroutineContext().isActive) { @@ -98,7 +108,7 @@ internal abstract class CommonEssencesImpl( pageLimit = 30 ) for (message in page.messages) { - emit(source(message)) + emit(record(message)) } if (page.isEnd) break if (page.messages.isEmpty()) break diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt index 83548c0fdc8..4d8658f5f31 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt @@ -28,6 +28,7 @@ import net.mamoe.mirai.utils.loadAs @Serializable internal data class DigestData( @SerialName("data") val `data`: JsonElement = JsonNull, + @SerialName("wording") val reason: String = "", @SerialName("retmsg") override val errorMessage: String, @SerialName("retcode") override val errorCode: Int ) : CheckableResponseA(), JsonStruct @@ -80,6 +81,12 @@ internal data class DigestMessageContent( val text: String = "" ) +@Serializable +internal data class DigestShare( + @SerialName("share_key") + val shareKey: String = "" +) + private fun DigestData.loadData(serializer: KSerializer): T { return try { defaultJson.decodeFromJsonElement(serializer, this.data) @@ -106,4 +113,51 @@ internal suspend fun QQAndroidBot.getDigestList( ) } }.bodyAsText().loadAs(DigestData.serializer()).loadData(DigestList.serializer()) +} + +internal suspend fun QQAndroidBot.cancelDigest( + groupCode: Long, msgSeq: Int, msgRandom: Int +) { + val data = components[HttpClientProvider].getHttpClient().get { + url("https://qun.qq.com/cgi-bin/group_digest/cancel_digest") + parameter("group_code", groupCode) + parameter("msg_seq", msgSeq) + parameter("msg_random", msgRandom) + parameter("bkn", client.wLoginSigInfo.bkn) + + headers { + // ktor bug + append( + "cookie", + "uin=o${id}; skey=${sKey}; p_uin=o${id}; p_skey=${psKey(host)};" + ) + } + }.bodyAsText().loadAs(DigestData.serializer()) + + when (data.errorCode) { + 0, 11007, 11001 -> Unit + else -> throw IllegalStateException(message = "cancel digest error, status: ${data.errorCode} - ${data.errorMessage}, reason: ${data.reason}") + } +} + + +internal suspend fun QQAndroidBot.shareDigest( + groupCode: Long, msgSeq: Int, msgRandom: Int, targetGroupCode: Long +): DigestShare { + return components[HttpClientProvider].getHttpClient().get { + url("https://qun.qq.com/cgi-bin/group_digest/share_digest") + parameter("group_code", groupCode) + parameter("msg_seq", msgSeq) + parameter("msg_random", msgRandom) + parameter("target_group_code", targetGroupCode) + parameter("bkn", client.wLoginSigInfo.bkn) + + headers { + // ktor bug + append( + "cookie", + "uin=o${id}; skey=${sKey}; p_uin=o${id}; p_skey=${psKey(host)};" + ) + } + }.bodyAsText().loadAs(DigestData.serializer()).loadData(DigestShare.serializer()) } \ No newline at end of file From 4611c9527f85a67607591c19ff0da55e15fc675b Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sat, 26 Nov 2022 21:02:51 +0800 Subject: [PATCH 04/15] fix: impl --- mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt | 9 +++++++++ .../jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt | 10 +--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt index 16ccfc6c86d..61f91f9c334 100644 --- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt @@ -19,6 +19,7 @@ import net.mamoe.mirai.LowLevelApi import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.active.GroupActive import net.mamoe.mirai.contact.announcement.Announcements +import net.mamoe.mirai.contact.essence.Essences import net.mamoe.mirai.contact.file.RemoteFiles import net.mamoe.mirai.data.GroupHonorType import net.mamoe.mirai.data.GroupInfo @@ -28,6 +29,7 @@ import net.mamoe.mirai.event.events.* import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.contact.active.GroupActiveImpl import net.mamoe.mirai.internal.contact.announcement.AnnouncementsImpl +import net.mamoe.mirai.internal.contact.essence.EssencesImpl import net.mamoe.mirai.internal.contact.file.RemoteFilesImpl import net.mamoe.mirai.internal.contact.info.MemberInfoImpl import net.mamoe.mirai.internal.message.contextualBugReportException @@ -402,6 +404,13 @@ internal abstract class CommonGroupImpl constructor( return result.success } + override val essences: Essences by lazy { + EssencesImpl( + this as GroupImpl, + bot.network.logger.subLogger("Group $id"), + ) + } + override fun toString(): String = "Group($id)" } diff --git a/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt index f4bb82fc89c..3d618a9d7e2 100644 --- a/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt @@ -9,18 +9,10 @@ package net.mamoe.mirai.internal.contact.essence -import kotlinx.coroutines.flow.Flow import net.mamoe.mirai.internal.contact.GroupImpl -import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.MiraiLogger -import java.util.stream.Stream internal actual class EssencesImpl actual constructor( group: GroupImpl, logger: MiraiLogger, -) : CommonEssencesImpl(group, logger) { - - override fun asStream(): Stream { - TODO("Not yet implemented") - } -} \ No newline at end of file +) : CommonEssencesImpl(group, logger) \ No newline at end of file From 511228249258b53c40c41dbd918366bacce5d084 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sat, 26 Nov 2022 23:41:43 +0800 Subject: [PATCH 05/15] fix: arguments --- .../commonMain/kotlin/contact/essence/GroupDigestProtocol.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt index 4d8658f5f31..f941c8ada9b 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt @@ -91,7 +91,7 @@ private fun DigestData.loadData(serializer: KSerializer): T { return try { defaultJson.decodeFromJsonElement(serializer, this.data) } catch (cause: Exception) { - throw IllegalStateException(message = "parse digest data error, status: $errorCode - $errorMessage", cause) + throw IllegalStateException("parse digest data error, status: $errorCode - $errorMessage", cause) } } @@ -136,7 +136,7 @@ internal suspend fun QQAndroidBot.cancelDigest( when (data.errorCode) { 0, 11007, 11001 -> Unit - else -> throw IllegalStateException(message = "cancel digest error, status: ${data.errorCode} - ${data.errorMessage}, reason: ${data.reason}") + else -> throw IllegalStateException("cancel digest error, status: ${data.errorCode} - ${data.errorMessage}, reason: ${data.reason}") } } From 3f38c439ed8fffd03da49cbf3cc4791d2ac6e63f Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sun, 18 Dec 2022 05:33:56 +0800 Subject: [PATCH 06/15] feat: image url to image --- .../kotlin/contact/essence/EssencesImpl.kt | 26 +++++++++++++++---- .../contact/essence/GroupDigestProtocol.kt | 16 +++--------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index e40962a1a0b..25778b5d155 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -13,11 +13,15 @@ import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.isActive +import kotlinx.serialization.json.int +import kotlinx.serialization.json.intOrNull +import kotlinx.serialization.json.jsonPrimitive import net.mamoe.mirai.contact.essence.EssenceMessageRecord import net.mamoe.mirai.contact.essence.Essences import net.mamoe.mirai.internal.contact.GroupImpl import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.warning internal expect class EssencesImpl( group: GroupImpl, @@ -39,15 +43,27 @@ internal abstract class CommonEssencesImpl( targetId = group.id messages(message.msgContent.map { content -> - when (content.msgType) { - 1 -> PlainText(content.text) + when (content.getValue("msg_type").jsonPrimitive.intOrNull) { + 1 -> PlainText(content = content.getValue("text").jsonPrimitive.content) + 2 -> Face(id = content.getValue("face_index").jsonPrimitive.int) 3 -> { - // TODO: image url -> md5 -> image_id - content.imageUrl - Image("") + val url = content.getValue("image_url").jsonPrimitive.content + val (md5, ext) = IMAGE_MD5_REGEX.find(url)!!.destructured + val imageId = buildString { + append(md5) + insert(8,"-") + insert(13,"-") + insert(18,"-") + insert(23,"-") + insert(0, "{") + append("}.") + append(ext.replace("jpeg", "jpg")) + } + Image(imageId) } else -> { // XXX: unknown message type + logger.warning { "unknown digest message type for $content" } emptyMessageChain() } } diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt index f941c8ada9b..8124eac21fa 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt @@ -16,6 +16,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.contact.active.defaultJson import net.mamoe.mirai.internal.network.components.HttpClientProvider @@ -56,7 +57,7 @@ internal data class DigestMessage( @SerialName("group_code") val groupCode: String = "", @SerialName("msg_content") - val msgContent: List = emptyList(), + val msgContent: List = emptyList(), @SerialName("msg_random") val msgRandom: Int = 0, @SerialName("msg_seq") @@ -69,17 +70,7 @@ internal data class DigestMessage( val senderUin: Long = 0 ) -@Serializable -internal data class DigestMessageContent( - @SerialName("image_thumbnail_url") - val imageThumbnailUrl: String = "", - @SerialName("image_url") - val imageUrl: String = "", - @SerialName("msg_type") - val msgType: Int = 0, - @SerialName("text") - val text: String = "" -) +internal val IMAGE_MD5_REGEX: Regex = """([0-9a-fA-F]{32})\.([0-9a-zA-Z]+)""".toRegex() @Serializable internal data class DigestShare( @@ -140,7 +131,6 @@ internal suspend fun QQAndroidBot.cancelDigest( } } - internal suspend fun QQAndroidBot.shareDigest( groupCode: Long, msgSeq: Int, msgRandom: Int, targetGroupCode: Long ): DigestShare { From 35b20c9674ea957bd1b808021b9f9c46daf09261 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Wed, 21 Dec 2022 19:19:27 +0800 Subject: [PATCH 07/15] add: doc --- .../android/api/android.api | 4 ++ .../compatibility-validation/jvm/api/jvm.api | 4 ++ .../contact/essence/EssenceMessageRecord.kt | 20 ++++++++- .../kotlin/contact/essence/Essences.kt | 43 ++++++++++++++++++- .../src/contact/essence/MockEssences.kt | 8 ++++ .../src/internal/contact/MockGroupImpl.kt | 4 +- .../internal/contact/essence/MockEssences.kt | 7 ++- .../kotlin/contact/essence/EssencesImpl.kt | 4 +- 8 files changed, 88 insertions(+), 6 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index d92d12ef761..b636af68615 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -915,11 +915,15 @@ public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { public final fun getSenderNick ()Ljava/lang/String; public final fun getSenderTime ()I public final fun getSource ()Lnet/mamoe/mirai/message/data/MessageSource; + public fun toString ()Ljava/lang/String; } public abstract interface class net/mamoe/mirai/contact/essence/Essences : net/mamoe/mirai/utils/Streamable { + public fun page (II)Ljava/util/List; public abstract fun page (IILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun remove (Lnet/mamoe/mirai/message/data/MessageSource;)V public abstract fun remove (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun share (Lnet/mamoe/mirai/message/data/MessageSource;)Ljava/lang/String; public abstract fun share (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 41375ed1db1..051cb5d035b 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -915,11 +915,15 @@ public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { public final fun getSenderNick ()Ljava/lang/String; public final fun getSenderTime ()I public final fun getSource ()Lnet/mamoe/mirai/message/data/MessageSource; + public fun toString ()Ljava/lang/String; } public abstract interface class net/mamoe/mirai/contact/essence/Essences : net/mamoe/mirai/utils/Streamable { + public fun page (II)Ljava/util/List; public abstract fun page (IILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun remove (Lnet/mamoe/mirai/message/data/MessageSource;)V public abstract fun remove (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun share (Lnet/mamoe/mirai/message/data/MessageSource;)Ljava/lang/String; public abstract fun share (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt index c0783fd4b84..f31f0c22099 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt @@ -13,6 +13,20 @@ import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.NormalMember import net.mamoe.mirai.message.data.MessageSource +/** + * 精华消息记录 + * @since 2.14 + * @param group 记录的群聊 + * @param sender 消息的发送者 + * @param senderId 消息的发送者的ID + * @param senderNick 消息的发送者的Nick + * @param senderTime 消息的发送的时间 * + * @param operator 设置精华的操作者 + * @param operatorId 设置精华的操作者的ID + * @param operatorNick 设置精华的操作者的Nick + * @param operatorTime 设置精华的时间 + * @param source 消息源 + */ public class EssenceMessageRecord( public val group: Group, public val sender: NormalMember?, @@ -24,4 +38,8 @@ public class EssenceMessageRecord( public val operatorNick: String, public val operatorTime: Int, public val source: MessageSource -) \ No newline at end of file +) { + override fun toString(): String { + return "EssenceMessageRecord(group=${group}, sender=${senderNick}(${senderId}), senderTime=${senderTime}, operator=${operatorNick}(${operatorId}), operatorTime=${operatorTime})" + } +} \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt index fae4b53e850..33d651ca8fd 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt @@ -9,17 +9,58 @@ package net.mamoe.mirai.contact.essence +import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.utils.NotStableForInheritance import net.mamoe.mirai.utils.Streamable /** + * 表示一个群精华消息管理. + * + * ## 获取 [Essences] 实例 + * + * 只可以通过 [Group.essences] 获取一个群的精华消息管理, 即 [Essences] 实例. + * + * ### 获取精华消息列表 + * + * 通过 [asFlow] 或 `asStream` 可以获取到*惰性*流, 在从流中收集数据时才会请求服务器获取数据. 通常建议在 Kotlin 使用协程的 [asFlow], 在 Java 使用 `asStream`. + * + * 若要获取全部精华消息列表, 可使用 [toList]. + * + * ### 获取精华消息分享链接 + * + * 通过 [share] 可以获得一个精华消息的分享链接 + * + * ### 移除精华消息 + * + * 通过 [remove] 可以从列表中移除指定精华消息 (WEB API) * * @since 2.14 */ +@NotStableForInheritance public interface Essences : Streamable { + + /** + * 按页获取精华消息记录 + * @param start 起始Index + * @param limit 页大小 + */ + @JvmBlockingBridge public suspend fun page(start: Int, limit: Int): List - public suspend fun share(source: MessageSource) + /** + * 分享精华消息 + * @param source 要分享的消息源 + * @return 分享URL + */ + @JvmBlockingBridge + public suspend fun share(source: MessageSource): String + /** + * 移除精华消息 + * @param source 要移除的消息源 + */ + @JvmBlockingBridge public suspend fun remove(source: MessageSource) } \ No newline at end of file diff --git a/mirai-core-mock/src/contact/essence/MockEssences.kt b/mirai-core-mock/src/contact/essence/MockEssences.kt index d69cd6933ac..14e2abcb9c7 100644 --- a/mirai-core-mock/src/contact/essence/MockEssences.kt +++ b/mirai-core-mock/src/contact/essence/MockEssences.kt @@ -9,8 +9,16 @@ package net.mamoe.mirai.mock.contact.essence +import net.mamoe.mirai.contact.NormalMember import net.mamoe.mirai.contact.essence.Essences +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.mock.MockBotDSL public interface MockEssences : Essences { + /** + * 直接以 [actor] 的身份设置一条精华消息 + */ + @MockBotDSL + public fun mockSetEssences(source: MessageSource, actor: NormalMember) } \ No newline at end of file diff --git a/mirai-core-mock/src/internal/contact/MockGroupImpl.kt b/mirai-core-mock/src/internal/contact/MockGroupImpl.kt index c006c64aa79..125c2271d44 100644 --- a/mirai-core-mock/src/internal/contact/MockGroupImpl.kt +++ b/mirai-core-mock/src/internal/contact/MockGroupImpl.kt @@ -336,11 +336,13 @@ internal class MockGroupImpl( resource.mockUploadVoice(bot) override suspend fun setEssenceMessage(source: MessageSource): Boolean { - essences.share(source) + checkBotPermission(MemberPermission.ADMINISTRATOR) + essences.mockSetEssences(source, this.botAsMember) return true } override suspend fun removeEssenceMessage(source: MessageSource): Boolean { + checkBotPermission(MemberPermission.ADMINISTRATOR) essences.remove(source) return true } diff --git a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt index d96ac70be48..284fc1c7eea 100644 --- a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt +++ b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.mock.internal.contact.essence import kotlinx.coroutines.flow.Flow +import net.mamoe.mirai.contact.NormalMember import net.mamoe.mirai.contact.essence.EssenceMessageRecord import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.mock.contact.essence.MockEssences @@ -19,11 +20,15 @@ internal class MockEssencesImpl( private val group: MockGroupImpl ) : MockEssences { + override fun mockSetEssences(source: MessageSource, actor: NormalMember) { + TODO("Not yet implemented") + } + override suspend fun page(start: Int, limit: Int): List { TODO("Not yet implemented") } - override suspend fun share(source: MessageSource) { + override suspend fun share(source: MessageSource): String { TODO("Not yet implemented") } diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index 25778b5d155..c65465f6a56 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -96,14 +96,14 @@ internal abstract class CommonEssencesImpl( return page.messages.map(this::record) } - override suspend fun share(source: MessageSource) { + override suspend fun share(source: MessageSource): String { val share = group.bot.shareDigest( groupCode = group.id, msgSeq = source.ids.first(), msgRandom = source.internalIds.first(), targetGroupCode = 0 ) - share.shareKey + return "https://qun.qq.com/essence/share?_wv=3&_wwv=128&_wvx=2&sharekey=${share.shareKey}" } override suspend fun remove(source: MessageSource) { From 2649bb1b016865b9a98b188986eac4dda55e320c Mon Sep 17 00:00:00 2001 From: cssxsh Date: Wed, 4 Jan 2023 16:09:31 +0800 Subject: [PATCH 08/15] fix: doc --- .../android/api/android.api | 4 +-- .../compatibility-validation/jvm/api/jvm.api | 4 +-- .../kotlin/contact/essence/Essences.kt | 8 +++-- .../internal/contact/essence/MockEssences.kt | 29 +++++++++++++++---- .../kotlin/contact/essence/EssencesImpl.kt | 13 +++------ .../kotlin/contact/essence/EssencesImpl.kt | 18 ------------ .../kotlin/contact/essence/EssencesImpl.kt | 18 ------------ 7 files changed, 36 insertions(+), 58 deletions(-) delete mode 100644 mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt delete mode 100644 mirai-core/src/nativeMain/kotlin/contact/essence/EssencesImpl.kt diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index b636af68615..1a94cf871ca 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -919,8 +919,8 @@ public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { } public abstract interface class net/mamoe/mirai/contact/essence/Essences : net/mamoe/mirai/utils/Streamable { - public fun page (II)Ljava/util/List; - public abstract fun page (IILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun getPage (II)Ljava/util/List; + public abstract fun getPage (IILkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun remove (Lnet/mamoe/mirai/message/data/MessageSource;)V public abstract fun remove (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun share (Lnet/mamoe/mirai/message/data/MessageSource;)Ljava/lang/String; diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 051cb5d035b..3fa966dcd5e 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -919,8 +919,8 @@ public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { } public abstract interface class net/mamoe/mirai/contact/essence/Essences : net/mamoe/mirai/utils/Streamable { - public fun page (II)Ljava/util/List; - public abstract fun page (IILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun getPage (II)Ljava/util/List; + public abstract fun getPage (IILkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun remove (Lnet/mamoe/mirai/message/data/MessageSource;)V public abstract fun remove (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun share (Lnet/mamoe/mirai/message/data/MessageSource;)Ljava/lang/String; diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt index 33d651ca8fd..c4446875e7b 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt @@ -43,22 +43,24 @@ public interface Essences : Streamable { /** * 按页获取精华消息记录 - * @param start 起始Index + * @param start 起始索引 * @param limit 页大小 */ @JvmBlockingBridge - public suspend fun page(start: Int, limit: Int): List + public suspend fun getPage(start: Int, limit: Int): List /** * 分享精华消息 * @param source 要分享的消息源 - * @return 分享URL + * @throws IllegalStateException [source] 不为精华消息时将会触发异常 + * @return 分享 URL */ @JvmBlockingBridge public suspend fun share(source: MessageSource): String /** * 移除精华消息 + * @throws IllegalStateException [source] 不为精华消息或权限不足时将会触发异常 * @param source 要移除的消息源 */ @JvmBlockingBridge diff --git a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt index 284fc1c7eea..52be65db332 100644 --- a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt +++ b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt @@ -10,33 +10,50 @@ package net.mamoe.mirai.mock.internal.contact.essence import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow import net.mamoe.mirai.contact.NormalMember import net.mamoe.mirai.contact.essence.EssenceMessageRecord import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.mock.contact.essence.MockEssences import net.mamoe.mirai.mock.internal.contact.MockGroupImpl +import net.mamoe.mirai.utils.ConcurrentHashMap +import net.mamoe.mirai.utils.currentTimeSeconds internal class MockEssencesImpl( private val group: MockGroupImpl ) : MockEssences { + private val cache: MutableMap = ConcurrentHashMap() + override fun mockSetEssences(source: MessageSource, actor: NormalMember) { - TODO("Not yet implemented") + val record = EssenceMessageRecord( + group = group, + sender = group[source.fromId], + senderId = source.fromId, + senderNick = group[source.fromId]?.nick.orEmpty(), + senderTime = source.time, + operator = actor, + operatorId = actor.id, + operatorNick = actor.nick, + operatorTime = currentTimeSeconds().toInt(), + source = source + ) + cache[source] = record } - override suspend fun page(start: Int, limit: Int): List { - TODO("Not yet implemented") + override suspend fun getPage(start: Int, limit: Int): List { + return cache.values.toList().subList(start, start + limit) } override suspend fun share(source: MessageSource): String { - TODO("Not yet implemented") + return "https://qun.qq.com/essence/share?_wv=3&_wwv=128&_wvx=2&sharekey=" } override suspend fun remove(source: MessageSource) { - TODO("Not yet implemented") + cache.remove(source) } override fun asFlow(): Flow { - TODO("Not yet implemented") + return cache.values.asFlow() } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index c65465f6a56..a546c4d6356 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -23,14 +23,9 @@ import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.warning -internal expect class EssencesImpl( - group: GroupImpl, - logger: MiraiLogger, -) : CommonEssencesImpl - -internal abstract class CommonEssencesImpl( - protected val group: GroupImpl, - protected val logger: MiraiLogger, +internal class EssencesImpl( + internal val group: GroupImpl, + internal val logger: MiraiLogger, ) : Essences { private fun source(message: DigestMessage): MessageSource { @@ -86,7 +81,7 @@ internal abstract class CommonEssencesImpl( ) } - override suspend fun page(start: Int, limit: Int): List { + override suspend fun getPage(start: Int, limit: Int): List { val page = group.bot.getDigestList( groupCode = group.id, pageStart = start, diff --git a/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt deleted file mode 100644 index 3d618a9d7e2..00000000000 --- a/mirai-core/src/jvmBaseMain/kotlin/contact/essence/EssencesImpl.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2019-2022 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/dev/LICENSE - */ - -package net.mamoe.mirai.internal.contact.essence - -import net.mamoe.mirai.internal.contact.GroupImpl -import net.mamoe.mirai.utils.MiraiLogger - -internal actual class EssencesImpl actual constructor( - group: GroupImpl, - logger: MiraiLogger, -) : CommonEssencesImpl(group, logger) \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/nativeMain/kotlin/contact/essence/EssencesImpl.kt deleted file mode 100644 index 3d618a9d7e2..00000000000 --- a/mirai-core/src/nativeMain/kotlin/contact/essence/EssencesImpl.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2019-2022 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/dev/LICENSE - */ - -package net.mamoe.mirai.internal.contact.essence - -import net.mamoe.mirai.internal.contact.GroupImpl -import net.mamoe.mirai.utils.MiraiLogger - -internal actual class EssencesImpl actual constructor( - group: GroupImpl, - logger: MiraiLogger, -) : CommonEssencesImpl(group, logger) \ No newline at end of file From 45e9dd2bebcbf88ebf4cd638ae7340b985098800 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sun, 8 Jan 2023 14:30:00 +0800 Subject: [PATCH 09/15] Copyright: 2023 --- mirai-core-api/src/commonMain/kotlin/contact/Group.kt | 2 +- .../kotlin/contact/essence/EssenceMessageRecord.kt | 2 +- .../src/commonMain/kotlin/contact/essence/Essences.kt | 7 ++++--- mirai-core-mock/src/contact/essence/MockEssences.kt | 2 +- .../src/internal/contact/essence/MockEssences.kt | 2 +- mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt | 2 +- .../src/commonMain/kotlin/contact/essence/EssencesImpl.kt | 2 +- .../kotlin/contact/essence/GroupDigestProtocol.kt | 2 +- .../kotlin/network/protocol/packet/PacketFactory.kt | 2 +- .../kotlin/network/protocol/packet/chat/TroopManagement.kt | 2 +- 10 files changed, 13 insertions(+), 12 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt index dced9d77008..70f5f5d387e 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt index f31f0c22099..01cd06df6c8 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt index c4446875e7b..3d7c7cf3ca3 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. @@ -43,8 +43,9 @@ public interface Essences : Streamable { /** * 按页获取精华消息记录 - * @param start 起始索引 - * @param limit 页大小 + * @param start 起始索引 从 0 开始 + * @param limit 页大小 返回的记录最大数量,最大取 50 + * @throws IllegalStateException [limit] 过大或其他参数错误时会触发异常 */ @JvmBlockingBridge public suspend fun getPage(start: Int, limit: Int): List diff --git a/mirai-core-mock/src/contact/essence/MockEssences.kt b/mirai-core-mock/src/contact/essence/MockEssences.kt index 14e2abcb9c7..5fd55ec32f5 100644 --- a/mirai-core-mock/src/contact/essence/MockEssences.kt +++ b/mirai-core-mock/src/contact/essence/MockEssences.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. diff --git a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt index 52be65db332..efbc4b6c7c0 100644 --- a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt +++ b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt index 61f91f9c334..26bf74ed905 100644 --- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index a546c4d6356..30b0a9c9697 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt index 8124eac21fa..f46ba924231 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt index 34c86949c71..797b6f605f6 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopManagement.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopManagement.kt index 95316c6d115..9474d768531 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopManagement.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/TroopManagement.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Mamoe Technologies and contributors. + * Copyright 2019-2023 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. From 5d2ecd33c8fcf91809c7a2009fafcd8896b8f057 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sun, 19 Feb 2023 20:05:58 +0800 Subject: [PATCH 10/15] remove: method removeEssenceMessage --- .../android/api/android.api | 2 -- .../compatibility-validation/jvm/api/jvm.api | 2 -- .../src/commonMain/kotlin/contact/Group.kt | 12 +-------- .../contact/essence/EssenceMessageRecord.kt | 2 +- .../kotlin/contact/essence/Essences.kt | 2 +- .../src/internal/contact/MockGroupImpl.kt | 6 ----- .../internal/contact/essence/MockEssences.kt | 2 +- .../commonMain/kotlin/contact/GroupImpl.kt | 25 ++++++++++--------- .../kotlin/contact/essence/EssencesImpl.kt | 3 +++ 9 files changed, 20 insertions(+), 36 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index 80e8fa32bdc..bf8f686dddb 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -388,8 +388,6 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin public abstract fun getSettings ()Lnet/mamoe/mirai/contact/GroupSettings; public fun quit ()Z public abstract fun quit (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun removeEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;)Z - public abstract fun removeEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt; public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt; diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index defc35158a2..04ed8bcb635 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -388,8 +388,6 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin public abstract fun getSettings ()Lnet/mamoe/mirai/contact/GroupSettings; public fun quit ()Z public abstract fun quit (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun removeEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;)Z - public abstract fun removeEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt; public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt; diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt index 9e12737439e..ea7e314c66a 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt @@ -230,20 +230,10 @@ public interface Group : Contact, CoroutineScope, FileSupported, AudioSupported, */ public suspend fun setEssenceMessage(source: MessageSource): Boolean - /** - * 取消一条群精华消息, 需要管理员或群主权限. - * 操作成功返回 `true`. - * - * @throws PermissionDeniedException 没有权限时抛出 - * - * @since 2.14 - */ - public suspend fun removeEssenceMessage(source: MessageSource): Boolean - /** * 群精华消息相关功能接口 * - * @since 2.14 + * @since 2.15 */ public val essences: Essences diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt index 01cd06df6c8..3d6a9a02da5 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt @@ -15,7 +15,7 @@ import net.mamoe.mirai.message.data.MessageSource /** * 精华消息记录 - * @since 2.14 + * @since 2.15 * @param group 记录的群聊 * @param sender 消息的发送者 * @param senderId 消息的发送者的ID diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt index 3d7c7cf3ca3..1a550972e01 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/Essences.kt @@ -36,7 +36,7 @@ import net.mamoe.mirai.utils.Streamable * * 通过 [remove] 可以从列表中移除指定精华消息 (WEB API) * - * @since 2.14 + * @since 2.15 */ @NotStableForInheritance public interface Essences : Streamable { diff --git a/mirai-core-mock/src/internal/contact/MockGroupImpl.kt b/mirai-core-mock/src/internal/contact/MockGroupImpl.kt index 4ed60673ef4..280ec2949b2 100644 --- a/mirai-core-mock/src/internal/contact/MockGroupImpl.kt +++ b/mirai-core-mock/src/internal/contact/MockGroupImpl.kt @@ -344,12 +344,6 @@ internal class MockGroupImpl( return true } - override suspend fun removeEssenceMessage(source: MessageSource): Boolean { - checkBotPermission(MemberPermission.ADMINISTRATOR) - essences.remove(source) - return true - } - override val essences: MockEssences = MockEssencesImpl(this) @Deprecated("Please use files instead.", replaceWith = ReplaceWith("files.root")) diff --git a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt index efbc4b6c7c0..eb0bf806800 100644 --- a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt +++ b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt @@ -46,7 +46,7 @@ internal class MockEssencesImpl( } override suspend fun share(source: MessageSource): String { - return "https://qun.qq.com/essence/share?_wv=3&_wwv=128&_wvx=2&sharekey=" + return "https://qun.qq.com/essence/share?_wv=3&_wwv=128&_wvx=2&sharekey=..." } override suspend fun remove(source: MessageSource) { diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt index 2ba258e6e82..cbd48659c10 100644 --- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt @@ -395,18 +395,19 @@ internal abstract class CommonGroupImpl constructor( override val roamingMessages: RoamingMessages by lazy { RoamingMessagesImplGroup(this) } - override suspend fun removeEssenceMessage(source: MessageSource): Boolean { - checkBotPermission(MemberPermission.ADMINISTRATOR) - val result = bot.network.sendAndExpect( - TroopEssenceMsgManager.SetEssence( - bot.client, - this@CommonGroupImpl.uin, - source.internalIds.first(), - source.ids.first() - ), 5000, 2 - ) - return result.success - } + // 鉴于在 [essences] 中 有相同的功能的 Web API 所以此方法移除 +// override suspend fun removeEssenceMessage(source: MessageSource): Boolean { +// checkBotPermission(MemberPermission.ADMINISTRATOR) +// val result = bot.network.sendAndExpect( +// TroopEssenceMsgManager.RemoveEssence( +// bot.client, +// this@CommonGroupImpl.uin, +// source.internalIds.first(), +// source.ids.first() +// ), 5000, 2 +// ) +// return result.success +// } override val essences: Essences by lazy { EssencesImpl( diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index 30b0a9c9697..354826d81ed 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -16,6 +16,8 @@ import kotlinx.coroutines.isActive import kotlinx.serialization.json.int import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonPrimitive +import net.mamoe.mirai.contact.MemberPermission +import net.mamoe.mirai.contact.checkBotPermission import net.mamoe.mirai.contact.essence.EssenceMessageRecord import net.mamoe.mirai.contact.essence.Essences import net.mamoe.mirai.internal.contact.GroupImpl @@ -102,6 +104,7 @@ internal class EssencesImpl( } override suspend fun remove(source: MessageSource) { + group.checkBotPermission(MemberPermission.ADMINISTRATOR) group.bot.cancelDigest( groupCode = group.id, msgSeq = source.ids.first(), From d0a35dcb7b4d456358d23544b0075ce9e8d6a2a7 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Sun, 19 Feb 2023 21:09:46 +0800 Subject: [PATCH 11/15] feat: lazy load source --- .../android/api/android.api | 2 +- .../compatibility-validation/jvm/api/jvm.api | 2 +- .../contact/essence/EssenceMessageRecord.kt | 17 ++++- .../internal/contact/essence/MockEssences.kt | 2 +- .../kotlin/contact/essence/EssencesImpl.kt | 71 +++++++++++-------- .../contact/essence/GroupDigestProtocol.kt | 7 ++ 6 files changed, 65 insertions(+), 36 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index bf8f686dddb..0a55cd8bd1d 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -902,7 +902,6 @@ public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt { } public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { - public fun (Lnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;JLjava/lang/String;ILnet/mamoe/mirai/contact/NormalMember;JLjava/lang/String;ILnet/mamoe/mirai/message/data/MessageSource;)V public final fun getGroup ()Lnet/mamoe/mirai/contact/Group; public final fun getOperator ()Lnet/mamoe/mirai/contact/NormalMember; public final fun getOperatorId ()J @@ -913,6 +912,7 @@ public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { public final fun getSenderNick ()Ljava/lang/String; public final fun getSenderTime ()I public final fun getSource ()Lnet/mamoe/mirai/message/data/MessageSource; + public final fun getSource (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun toString ()Ljava/lang/String; } diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 04ed8bcb635..c130940d2fe 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -902,7 +902,6 @@ public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt { } public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { - public fun (Lnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;JLjava/lang/String;ILnet/mamoe/mirai/contact/NormalMember;JLjava/lang/String;ILnet/mamoe/mirai/message/data/MessageSource;)V public final fun getGroup ()Lnet/mamoe/mirai/contact/Group; public final fun getOperator ()Lnet/mamoe/mirai/contact/NormalMember; public final fun getOperatorId ()J @@ -913,6 +912,7 @@ public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { public final fun getSenderNick ()Ljava/lang/String; public final fun getSenderTime ()I public final fun getSource ()Lnet/mamoe/mirai/message/data/MessageSource; + public final fun getSource (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun toString ()Ljava/lang/String; } diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt index 3d6a9a02da5..5ce04d72c6f 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt @@ -9,9 +9,11 @@ package net.mamoe.mirai.contact.essence +import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.NormalMember import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.utils.MiraiInternalApi /** * 精华消息记录 @@ -25,9 +27,8 @@ import net.mamoe.mirai.message.data.MessageSource * @param operatorId 设置精华的操作者的ID * @param operatorNick 设置精华的操作者的Nick * @param operatorTime 设置精华的时间 - * @param source 消息源 */ -public class EssenceMessageRecord( +public class EssenceMessageRecord @MiraiInternalApi constructor( public val group: Group, public val sender: NormalMember?, public val senderId: Long, @@ -37,9 +38,19 @@ public class EssenceMessageRecord( public val operatorId: Long, public val operatorNick: String, public val operatorTime: Int, - public val source: MessageSource + private val loadMessageSource: suspend () -> MessageSource ) { override fun toString(): String { return "EssenceMessageRecord(group=${group}, sender=${senderNick}(${senderId}), senderTime=${senderTime}, operator=${operatorNick}(${operatorId}), operatorTime=${operatorTime})" } + + /** + * 获取消息源 + * + * **注意** 当精华消息中包含 图片 时,会尝试将其下载然后重新上传, 以保证可用性 + */ + @JvmBlockingBridge + public suspend fun getSource(): MessageSource { + return loadMessageSource() + } } \ No newline at end of file diff --git a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt index eb0bf806800..321d419c83b 100644 --- a/mirai-core-mock/src/internal/contact/essence/MockEssences.kt +++ b/mirai-core-mock/src/internal/contact/essence/MockEssences.kt @@ -36,7 +36,7 @@ internal class MockEssencesImpl( operatorId = actor.id, operatorNick = actor.nick, operatorTime = currentTimeSeconds().toInt(), - source = source + loadMessageSource = { source } ) cache[source] = record } diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index 354826d81ed..546c522ce65 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.isActive +import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.int import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonPrimitive @@ -22,15 +23,50 @@ import net.mamoe.mirai.contact.essence.EssenceMessageRecord import net.mamoe.mirai.contact.essence.Essences import net.mamoe.mirai.internal.contact.GroupImpl import net.mamoe.mirai.message.data.* -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.warning +import net.mamoe.mirai.utils.* +import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource internal class EssencesImpl( internal val group: GroupImpl, internal val logger: MiraiLogger, ) : Essences { - private fun source(message: DigestMessage): MessageSource { + private suspend fun parse(content: JsonObject): Message { + return when (content.getValue("msg_type").jsonPrimitive.intOrNull) { + 1 -> PlainText(content = content.getValue("text").jsonPrimitive.content) + 2 -> Face(id = content.getValue("face_index").jsonPrimitive.int) + 3 -> { + val url = content.getValue("image_url").jsonPrimitive.content + + try { + // url -> bytes -> group.upload + val bytes = group.bot.downloadEssenceMessageImage(url) + bytes.toExternalResource().use { group.uploadImage(it) } + } catch (cause: Exception) { + logger.debug({ "essence message image $url download fail." }, cause) + val (md5, ext) = IMAGE_MD5_REGEX.find(url)!!.destructured + val imageId = buildString { + append(md5) + insert(8,"-") + insert(13,"-") + insert(18,"-") + insert(23,"-") + insert(0, "{") + append("}.") + append(ext.replace("jpeg", "jpg")) + } + Image(imageId) + } + } + else -> { + // XXX: unknown message type + logger.warning { "unknown digest message type for $content" } + emptyMessageChain() + } + } + } + + private suspend fun source(message: DigestMessage): MessageSource { return group.bot.buildMessageSource(MessageSourceKind.GROUP) { ids = intArrayOf(message.msgSeq) internalIds = intArrayOf(message.msgRandom) @@ -39,32 +75,7 @@ internal class EssencesImpl( fromId = message.senderUin targetId = group.id - messages(message.msgContent.map { content -> - when (content.getValue("msg_type").jsonPrimitive.intOrNull) { - 1 -> PlainText(content = content.getValue("text").jsonPrimitive.content) - 2 -> Face(id = content.getValue("face_index").jsonPrimitive.int) - 3 -> { - val url = content.getValue("image_url").jsonPrimitive.content - val (md5, ext) = IMAGE_MD5_REGEX.find(url)!!.destructured - val imageId = buildString { - append(md5) - insert(8,"-") - insert(13,"-") - insert(18,"-") - insert(23,"-") - insert(0, "{") - append("}.") - append(ext.replace("jpeg", "jpg")) - } - Image(imageId) - } - else -> { - // XXX: unknown message type - logger.warning { "unknown digest message type for $content" } - emptyMessageChain() - } - } - }) + messages(message.msgContent.map { content -> parse(content) }) } } @@ -79,7 +90,7 @@ internal class EssencesImpl( operatorId = message.addDigestUin, operatorNick = message.addDigestNick, operatorTime = message.addDigestTime, - source = source(message = message) + loadMessageSource = { source(message = message) } ) } diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt index f46ba924231..dce157f3818 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/GroupDigestProtocol.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.contact.essence +import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.client.statement.* import kotlinx.serialization.KSerializer @@ -150,4 +151,10 @@ internal suspend fun QQAndroidBot.shareDigest( ) } }.bodyAsText().loadAs(DigestData.serializer()).loadData(DigestShare.serializer()) +} + +internal suspend fun QQAndroidBot.downloadEssenceMessageImage(urlString: String): ByteArray { + return components[HttpClientProvider].getHttpClient().get { + url(urlString) + }.body() } \ No newline at end of file From ecf4f445a9eb58527190b641b8f8b379ffa5120d Mon Sep 17 00:00:00 2001 From: cssxsh Date: Mon, 6 Mar 2023 17:21:25 +0800 Subject: [PATCH 12/15] add: no parse --- .../android/api/android.api | 2 + .../compatibility-validation/jvm/api/jvm.api | 2 + .../contact/essence/EssenceMessageRecord.kt | 18 +++++++- .../kotlin/contact/essence/EssencesImpl.kt | 45 ++++++++++++------- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index 0a55cd8bd1d..7234cb6fe0c 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -902,6 +902,8 @@ public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt { } public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { + public final fun getFullSource ()Lnet/mamoe/mirai/message/data/MessageSource; + public final fun getFullSource (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun getGroup ()Lnet/mamoe/mirai/contact/Group; public final fun getOperator ()Lnet/mamoe/mirai/contact/NormalMember; public final fun getOperatorId ()J diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index c130940d2fe..10b6763326d 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -902,6 +902,8 @@ public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt { } public final class net/mamoe/mirai/contact/essence/EssenceMessageRecord { + public final fun getFullSource ()Lnet/mamoe/mirai/message/data/MessageSource; + public final fun getFullSource (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun getGroup ()Lnet/mamoe/mirai/contact/Group; public final fun getOperator ()Lnet/mamoe/mirai/contact/NormalMember; public final fun getOperatorId ()J diff --git a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt index 5ce04d72c6f..0a848525d9a 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/essence/EssenceMessageRecord.kt @@ -38,7 +38,7 @@ public class EssenceMessageRecord @MiraiInternalApi constructor( public val operatorId: Long, public val operatorNick: String, public val operatorTime: Int, - private val loadMessageSource: suspend () -> MessageSource + private val loadMessageSource: suspend (parse: Boolean) -> MessageSource ) { override fun toString(): String { return "EssenceMessageRecord(group=${group}, sender=${senderNick}(${senderId}), senderTime=${senderTime}, operator=${operatorNick}(${operatorId}), operatorTime=${operatorTime})" @@ -47,10 +47,24 @@ public class EssenceMessageRecord @MiraiInternalApi constructor( /** * 获取消息源 * + * 其中的 [MessageSource.originalMessage] 将会尝试以加载为原消息格式 + * * **注意** 当精华消息中包含 图片 时,会尝试将其下载然后重新上传, 以保证可用性 + * + * @see getSource + */ + @JvmBlockingBridge + public suspend fun getFullSource(): MessageSource { + return loadMessageSource(true) + } + + /** + * 获取消息源 + * + * @see getFullSource */ @JvmBlockingBridge public suspend fun getSource(): MessageSource { - return loadMessageSource() + return loadMessageSource(false) } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index 546c522ce65..fae5cd0cfc8 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -66,31 +66,44 @@ internal class EssencesImpl( } } - private suspend fun source(message: DigestMessage): MessageSource { + private fun plain(content: JsonObject): String { + return when (content.getValue("msg_type").jsonPrimitive.intOrNull) { + 1 -> content.getValue("text").jsonPrimitive.content + 2 -> Face(id = content.getValue("face_index").jsonPrimitive.int).content + 3 -> "[图片]" + else -> "" + } + } + + private suspend fun source(digests: DigestMessage, parse: Boolean): MessageSource { return group.bot.buildMessageSource(MessageSourceKind.GROUP) { - ids = intArrayOf(message.msgSeq) - internalIds = intArrayOf(message.msgRandom) - time = message.senderTime + ids = intArrayOf(digests.msgSeq) + internalIds = intArrayOf(digests.msgRandom) + time = digests.senderTime - fromId = message.senderUin + fromId = digests.senderUin targetId = group.id - messages(message.msgContent.map { content -> parse(content) }) + if (parse) { + messages(digests.msgContent.map { content -> parse(content) }) + } else { + messages(digests.msgContent.joinToString { content -> plain(content) }.toPlainText()) + } } } - private fun record(message: DigestMessage): EssenceMessageRecord { + private fun record(digests: DigestMessage): EssenceMessageRecord { return EssenceMessageRecord( group = group, - sender = group[message.senderUin], - senderId = message.senderUin, - senderNick = message.senderNick, - senderTime = message.senderTime, - operator = group[message.addDigestUin], - operatorId = message.addDigestUin, - operatorNick = message.addDigestNick, - operatorTime = message.addDigestTime, - loadMessageSource = { source(message = message) } + sender = group[digests.senderUin], + senderId = digests.senderUin, + senderNick = digests.senderNick, + senderTime = digests.senderTime, + operator = group[digests.addDigestUin], + operatorId = digests.addDigestUin, + operatorNick = digests.addDigestNick, + operatorTime = digests.addDigestTime, + loadMessageSource = { source(digests = digests, parse = false) } ) } From cd3ce5e64eca09ff020d3a8037a44ece3b0955d3 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Mon, 6 Mar 2023 17:27:44 +0800 Subject: [PATCH 13/15] add: sendAndExpect try --- .../kotlin/contact/essence/EssencesImpl.kt | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index fae5cd0cfc8..d80c54c0ed9 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.contact.essence +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -22,6 +23,7 @@ import net.mamoe.mirai.contact.checkBotPermission import net.mamoe.mirai.contact.essence.EssenceMessageRecord import net.mamoe.mirai.contact.essence.Essences import net.mamoe.mirai.internal.contact.GroupImpl +import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopEssenceMsgManager import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource @@ -129,11 +131,23 @@ internal class EssencesImpl( override suspend fun remove(source: MessageSource) { group.checkBotPermission(MemberPermission.ADMINISTRATOR) - group.bot.cancelDigest( - groupCode = group.id, - msgSeq = source.ids.first(), - msgRandom = source.internalIds.first() - ) + try { + val result = group.bot.network.sendAndExpect( + TroopEssenceMsgManager.RemoveEssence( + group.bot.client, + group.uin, + source.internalIds.first(), + source.ids.first() + ), 5000, 2 + ) + check(result.success) { result.msg ?: "移除精华消息失败" } + } catch (_: CancellationException) { + group.bot.cancelDigest( + groupCode = group.id, + msgSeq = source.ids.first(), + msgRandom = source.internalIds.first() + ) + } } override fun asFlow(): Flow { From f2a21191c0e79da2f2160bad1e154a344133144e Mon Sep 17 00:00:00 2001 From: cssxsh Date: Tue, 21 Mar 2023 21:06:17 +0800 Subject: [PATCH 14/15] fix: remove throw --- .../kotlin/contact/essence/EssencesImpl.kt | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index d80c54c0ed9..30ce96ed500 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -9,7 +9,6 @@ package net.mamoe.mirai.internal.contact.essence -import kotlinx.coroutines.CancellationException import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -131,22 +130,25 @@ internal class EssencesImpl( override suspend fun remove(source: MessageSource) { group.checkBotPermission(MemberPermission.ADMINISTRATOR) - try { - val result = group.bot.network.sendAndExpect( - TroopEssenceMsgManager.RemoveEssence( - group.bot.client, - group.uin, - source.internalIds.first(), - source.ids.first() - ), 5000, 2 - ) - check(result.success) { result.msg ?: "移除精华消息失败" } - } catch (_: CancellationException) { - group.bot.cancelDigest( - groupCode = group.id, - msgSeq = source.ids.first(), - msgRandom = source.internalIds.first() - ) + val result = group.bot.network.sendAndExpect( + TroopEssenceMsgManager.RemoveEssence( + group.bot.client, + group.uin, + source.internalIds.first(), + source.ids.first() + ), 5000, 2 + ) + if (result.success.not()) { + try { + group.bot.cancelDigest( + groupCode = group.id, + msgSeq = source.ids.first(), + msgRandom = source.internalIds.first() + ) + } catch (cause: IllegalStateException) { + cause.addSuppressed(IllegalStateException(result.msg)) + throw cause + } } } From 408935f9661920c5ecdfaebe9dcf795402e3f2e4 Mon Sep 17 00:00:00 2001 From: cssxsh Date: Tue, 21 Mar 2023 21:20:15 +0800 Subject: [PATCH 15/15] fix: parse IMAGE_MD5_REGEX --- .../src/commonMain/kotlin/contact/essence/EssencesImpl.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt index 30ce96ed500..3811f317d89 100644 --- a/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/essence/EssencesImpl.kt @@ -45,7 +45,8 @@ internal class EssencesImpl( bytes.toExternalResource().use { group.uploadImage(it) } } catch (cause: Exception) { logger.debug({ "essence message image $url download fail." }, cause) - val (md5, ext) = IMAGE_MD5_REGEX.find(url)!!.destructured + val match = IMAGE_MD5_REGEX.find(url) ?: return emptyMessageChain() + val (md5, ext) = match.destructured val imageId = buildString { append(md5) insert(8,"-")