mirror of
https://github.com/mamoe/mirai.git
synced 2024-12-31 07:29:45 +08:00
[Review] Messages:
- Optimize empty message chain on builders - Remove `QuoteReply.bot` - Code style - Docs
This commit is contained in:
parent
b4b70f028b
commit
a2d9dbfcc4
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
* Copyright 2019-2021 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.
|
||||
@ -21,7 +21,7 @@ import net.mamoe.mirai.Bot
|
||||
* @see Contact
|
||||
* @see Bot
|
||||
*/
|
||||
public interface ContactOrBot : CoroutineScope {
|
||||
public interface ContactOrBot : CoroutineScope { // TODO: 2021/1/10 Make sealed interface in Kotlin 1.5
|
||||
/**
|
||||
* QQ 号或群号.
|
||||
*/
|
||||
|
@ -11,10 +11,12 @@ package net.mamoe.mirai.message
|
||||
|
||||
import kotlinx.serialization.ContextualSerializer
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.*
|
||||
import net.mamoe.mirai.internal.message.MessageSerializersImpl
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import kotlin.reflect.KClass
|
||||
@ -22,6 +24,13 @@ import kotlin.reflect.KClass
|
||||
/**
|
||||
* 消息序列化器.
|
||||
*
|
||||
* [MessageSerializers] 存放 [SerializersModule], 用于协助 [SingleMessage.Serializer] 的多态序列化.
|
||||
*
|
||||
* 要序列化一个 [MessageChain], 请使用内建的 [MessageChain.serializeToJsonString]
|
||||
*
|
||||
* @see serializersModule
|
||||
*
|
||||
*
|
||||
* @see SingleMessage.Serializer
|
||||
* @see MessageChain.Serializer
|
||||
*
|
||||
@ -31,12 +40,17 @@ public interface MessageSerializers {
|
||||
/**
|
||||
* 包含 [SingleMessage] 多态序列化和 [Message] [ContextualSerializer] 信息的 [SerializersModule].
|
||||
*
|
||||
* 在序列化消息时都需要
|
||||
* 在序列化消息时都需要提供给相关 [Json] 配置的 [Json.serializersModule]. 如通过:
|
||||
* ```
|
||||
* val json = Json {
|
||||
* serializesModule = MessageSerializers.serializersModule
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public val serializersModule: SerializersModule
|
||||
|
||||
/**
|
||||
* 注册一个 [SerializersModuleBuilder.contextual] 和 [SingleMessage] 多态域的 [PolymorphicModuleBuilder.subclass]
|
||||
* 注册一个 [SerializersModuleBuilder.contextual] 和 [SingleMessage] 多态域的 [PolymorphicModuleBuilder.subclass].
|
||||
*
|
||||
* 相当于
|
||||
* ```
|
||||
@ -45,6 +59,9 @@ public interface MessageSerializers {
|
||||
* subclass(baseClass, serializer)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* 若要自己实现消息类型, 务必在这里注册对应序列化器, 否则在 [MessageChain.serializeToJsonString] 时将会出错.
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
public fun <M : SingleMessage> registerSerializer(baseClass: KClass<M>, serializer: KSerializer<M>)
|
||||
|
@ -18,8 +18,6 @@ import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.code.CodableMessage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
|
||||
private const val displayA = "@全体成员"
|
||||
|
||||
/**
|
||||
* "@全体成员".
|
||||
*
|
||||
@ -34,30 +32,25 @@ private const val displayA = "@全体成员"
|
||||
@Serializable
|
||||
public object AtAll :
|
||||
MessageContent, CodableMessage {
|
||||
public const val display: String = displayA
|
||||
public const val display: String = "@全体成员"
|
||||
public const val SERIAL_NAME: String = "AtAll"
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
public override fun toString(): String = "[mirai:atall]"
|
||||
public override fun contentToString(): String = display
|
||||
public override fun equals(other: Any?): Boolean {
|
||||
return other === this
|
||||
}
|
||||
override fun contentToString(): String = display
|
||||
override fun toString(): String = "[mirai:atall]"
|
||||
override fun toMiraiCode(): String = toString()
|
||||
|
||||
override fun toMiraiCode(): String {
|
||||
return toString()
|
||||
}
|
||||
override fun hashCode(): Int = display.hashCode()
|
||||
override fun equals(other: Any?): Boolean = other === this
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
builder.append(toString())
|
||||
}
|
||||
|
||||
public override fun hashCode(): Int {
|
||||
return display.hashCode()
|
||||
}
|
||||
|
||||
// 自动为消息补充 " "
|
||||
@JvmSynthetic
|
||||
public override fun followedBy(tail: Message): MessageChain {
|
||||
if (tail is PlainText && tail.content.startsWith(' ')) {
|
||||
return super<MessageContent>.followedBy(tail)
|
||||
|
@ -192,6 +192,8 @@ public fun <T : CustomMessage> T.toByteArray(): ByteArray {
|
||||
* 2. 添加伴生对象, 继承 [CustomMessage.ProtoBufSerializerFactory] 或 [CustomMessage.JsonSerializerFactory], 或 [CustomMessage.Factory]
|
||||
* 3. 在需要解析消息前调用一次伴生对象以注册
|
||||
*
|
||||
* 注意: 这是实验性 API. 可能会在未来发生变动.
|
||||
*
|
||||
* @see CustomMessage 查看更多信息
|
||||
* @see ConstrainSingle 可实现此接口以保证消息链中只存在一个元素
|
||||
*/
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
@ -28,20 +29,21 @@ import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
public data class Face(public val id: Int) : // used in delegation
|
||||
MessageContent, CodableMessage {
|
||||
|
||||
public override fun toString(): String = "[mirai:face:$id]"
|
||||
public val name: String get() = contentToString().let { it.substring(1, it.length - 1) }
|
||||
public override fun contentToString(): String = names.getOrElse(id) { "[表情]" }
|
||||
|
||||
override fun toString(): String = toMiraiCode()
|
||||
override fun contentToString(): String = names.getOrElse(id) { "[表情]" }
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
builder.append("[mirai:face:").append(id).append(']')
|
||||
}
|
||||
|
||||
public override fun equals(other: Any?): Boolean = other is Face && other.id == this.id
|
||||
public override fun hashCode(): Int = id
|
||||
override fun equals(other: Any?): Boolean = other is Face && other.id == this.id
|
||||
override fun hashCode(): Int = id
|
||||
|
||||
//Auto generated
|
||||
@Suppress("NonAsciiCharacters", "unused", "SpellCheckingInspection", "all")
|
||||
@Suppress("NonAsciiCharacters", "unused", "SpellCheckingInspection", "all", "ObjectPropertyName")
|
||||
public companion object {
|
||||
public const val SERIAL_NAME: String = "Face"
|
||||
|
||||
@ -480,7 +482,10 @@ public data class Face(public val id: Int) : // used in delegation
|
||||
public const val 请: Int = QING
|
||||
public const val ZHENG_YAN: Int = 289
|
||||
public const val 睁眼: Int = ZHENG_YAN
|
||||
internal val names: Array<String> = Array(290) { "[表情]" }
|
||||
|
||||
|
||||
@JvmField
|
||||
public val names: Array<String> = Array(290) { "[表情]" }
|
||||
|
||||
init {
|
||||
names[JING_YA] = "[惊讶]"
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.code.CodableMessage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
/**
|
||||
* 闪照. 闪照的内容取决于 [image] 代表的图片.
|
||||
*
|
||||
* ## 构造闪照
|
||||
*
|
||||
* 需要首先获得普通图片才能构造闪照. 详见 [Image].
|
||||
*
|
||||
* - 使用 [FlashImage.from] 将普通图片转换为闪照.
|
||||
* - 在 Kotlin 使用类构造器顶层函数 `FlashImage(image)`.
|
||||
* - 在 Kotlin 使用扩展 [Image.flash].
|
||||
*
|
||||
* ## mirai 码支持
|
||||
* 格式: [mirai:flash:*[Image.imageId]*]
|
||||
*
|
||||
* @see Image 查看图片相关信息
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName(FlashImage.SERIAL_NAME)
|
||||
public data class FlashImage(
|
||||
/**
|
||||
* 闪照的内容图片, 即一个普通图片.
|
||||
*/
|
||||
@SerialName("imageId")
|
||||
@Serializable(Image.AsStringSerializer::class)
|
||||
public val image: Image
|
||||
) : MessageContent, HummerMessage, CodableMessage, ConstrainSingle {
|
||||
override val key: MessageKey<FlashImage> get() = Key
|
||||
|
||||
private val stringValue: String by lazy(LazyThreadSafetyMode.NONE) { "[mirai:flash:${image.imageId}]" }
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
builder.append(stringValue)
|
||||
}
|
||||
|
||||
override fun toMiraiCode(): String = stringValue
|
||||
override fun toString(): String = stringValue
|
||||
override fun contentToString(): String = "[闪照]"
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, FlashImage>(HummerMessage, { it.safeCast() }) {
|
||||
public const val SERIAL_NAME: String = "FlashImage"
|
||||
|
||||
/**
|
||||
* 将普通图片转换为闪照.
|
||||
*
|
||||
* @param imageId 图片 id, 详见 [Image.imageId]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun from(imageId: String): FlashImage = FlashImage(Image(imageId))
|
||||
|
||||
/**
|
||||
* 将普通图片转换为闪照.
|
||||
*
|
||||
* @see Image.flash
|
||||
*/
|
||||
@JvmStatic
|
||||
public inline fun from(image: Image): FlashImage = FlashImage(image)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将普通图片转换为闪照.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun FlashImage(imageId: String): FlashImage = FlashImage.from(imageId)
|
||||
|
||||
/**
|
||||
* 将普通图片转换为闪照.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun Image.flash(): FlashImage = FlashImage(this)
|
@ -352,7 +352,7 @@ public class ForwardMessageBuilder private constructor(
|
||||
*/
|
||||
public var currentTime: Int = currentTimeSeconds().toInt()
|
||||
|
||||
public inner class BuilderNode : ForwardMessage.INode {
|
||||
public inner class BuilderNode internal constructor() : ForwardMessage.INode {
|
||||
|
||||
/**
|
||||
* 发送人 [User.id]
|
||||
|
@ -13,368 +13,22 @@
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.code.CodableMessage
|
||||
import net.mamoe.mirai.message.code.internal.appendStringAsMiraiCode
|
||||
import net.mamoe.mirai.message.data.VipFace.Kind
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.castOrNull
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
/**
|
||||
* 一些特殊的消息
|
||||
*
|
||||
* 注意, [HummerMessage] 类型不稳定, 但它的子类如 [PokeMessage] 是稳定的.
|
||||
*
|
||||
* @see PokeMessage 戳一戳
|
||||
* @see FlashImage 闪照
|
||||
* @see MarketFace 商城表情
|
||||
* @see VipFace VIP 表情
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
public interface HummerMessage : MessageContent, ConstrainSingle {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<MessageContent, HummerMessage>(MessageContent, { it.castOrNull() })
|
||||
// has service type etc.
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
///////////// POKE MESSAGE /////////////
|
||||
////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 戳一戳. 可以发送给好友或群.
|
||||
*
|
||||
* ## mirai 码支持
|
||||
* 格式: [mirai:poke:*[name]*,*[pokeType]*,*[id]*]
|
||||
*
|
||||
* @see PokeMessage.Companion 使用伴生对象中的常量
|
||||
*/
|
||||
@SerialName(PokeMessage.SERIAL_NAME)
|
||||
@Serializable
|
||||
public data class PokeMessage @MiraiInternalApi constructor(
|
||||
/**
|
||||
* 仅 mirai, 显示的名称
|
||||
*/
|
||||
public val name: String,
|
||||
|
||||
public val pokeType: Int, // 'type' is used by serialization
|
||||
public val id: Int
|
||||
) : HummerMessage, CodableMessage {
|
||||
override val key: MessageKey<HummerMessage>
|
||||
get() = Key
|
||||
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, PokeMessage>(HummerMessage, { it.castOrNull() }) {
|
||||
|
||||
public const val SERIAL_NAME: String = "PokeMessage"
|
||||
|
||||
/** 戳一戳 */
|
||||
@JvmField
|
||||
public val ChuoYiChuo: PokeMessage = PokeMessage("戳一戳", 1, -1)
|
||||
|
||||
/** 戳一戳 */
|
||||
@JvmField
|
||||
@Deprecated("Use ChuoYiChuo", replaceWith = ReplaceWith("ChuoYiChuo"))
|
||||
public val Poke: PokeMessage = ChuoYiChuo
|
||||
|
||||
/** 比心 */
|
||||
@JvmField
|
||||
public val BiXin: PokeMessage = PokeMessage("比心", 2, -1)
|
||||
|
||||
/** 比心 */
|
||||
@JvmField
|
||||
@Deprecated("Use BiXin", replaceWith = ReplaceWith("BiXin"))
|
||||
public val ShowLove: PokeMessage = BiXin
|
||||
|
||||
/** 点赞 */
|
||||
@JvmField
|
||||
public val DianZan: PokeMessage = PokeMessage("点赞", 3, -1)
|
||||
|
||||
/** 点赞 */
|
||||
@JvmField
|
||||
@Deprecated("Use DianZan", replaceWith = ReplaceWith("DianZan"))
|
||||
public val Like: PokeMessage = DianZan
|
||||
|
||||
/** 心碎 */
|
||||
@JvmField
|
||||
public val XinSui: PokeMessage = PokeMessage("心碎", 4, -1)
|
||||
|
||||
/** 心碎 */
|
||||
@JvmField
|
||||
@Deprecated("Use XinSui", replaceWith = ReplaceWith("XinSui"))
|
||||
public val Heartbroken: PokeMessage = XinSui
|
||||
|
||||
/** 666 */
|
||||
@JvmField
|
||||
public val LiuLiuLiu: PokeMessage = PokeMessage("666", 5, -1)
|
||||
|
||||
/** 666 */
|
||||
@JvmField
|
||||
@Deprecated("Use LiuLiuLiu", replaceWith = ReplaceWith("LiuLiuLiu"))
|
||||
public val SixSixSix: PokeMessage = LiuLiuLiu
|
||||
|
||||
/** 放大招 */
|
||||
@JvmField
|
||||
public val FangDaZhao: PokeMessage = PokeMessage("放大招", 6, -1)
|
||||
|
||||
/** 宝贝球 (SVIP) */
|
||||
@JvmField
|
||||
public val BaoBeiQiu: PokeMessage = PokeMessage("宝贝球", 126, 2011)
|
||||
|
||||
/** 玫瑰花 (SVIP) */
|
||||
@JvmField
|
||||
public val Rose: PokeMessage = PokeMessage("玫瑰花", 126, 2007)
|
||||
|
||||
/** 召唤术 (SVIP) */
|
||||
@JvmField
|
||||
public val ZhaoHuanShu: PokeMessage = PokeMessage("召唤术", 126, 2006)
|
||||
|
||||
/** 让你皮 (SVIP) */
|
||||
@JvmField
|
||||
public val RangNiPi: PokeMessage = PokeMessage("让你皮", 126, 2009)
|
||||
|
||||
/** 结印 (SVIP) */
|
||||
@JvmField
|
||||
public val JieYin: PokeMessage = PokeMessage("结印", 126, 2005)
|
||||
|
||||
/** 手雷 (SVIP) */
|
||||
@JvmField
|
||||
public val ShouLei: PokeMessage = PokeMessage("手雷", 126, 2004)
|
||||
|
||||
/** 勾引 */
|
||||
@JvmField
|
||||
public val GouYin: PokeMessage = PokeMessage("勾引", 126, 2003)
|
||||
|
||||
/** 抓一下 (SVIP) */
|
||||
@JvmField
|
||||
public val ZhuaYiXia: PokeMessage = PokeMessage("抓一下", 126, 2001)
|
||||
|
||||
/** 碎屏 (SVIP) */
|
||||
@JvmField
|
||||
public val SuiPing: PokeMessage = PokeMessage("碎屏", 126, 2002)
|
||||
|
||||
/** 敲门 (SVIP) */
|
||||
@JvmField
|
||||
public val QiaoMen: PokeMessage = PokeMessage("敲门", 126, 2002)
|
||||
|
||||
|
||||
/**
|
||||
* 所有类型数组
|
||||
*/
|
||||
@JvmField
|
||||
public val values: Array<PokeMessage> = arrayOf(
|
||||
ChuoYiChuo, BiXin, DianZan, XinSui, LiuLiuLiu,
|
||||
FangDaZhao, BaoBeiQiu, Rose, ZhaoHuanShu, RangNiPi,
|
||||
JieYin, ShouLei, GouYin, ZhuaYiXia, SuiPing
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private val stringValue = "[mirai:poke:$name,$pokeType,$id]"
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
builder.append("[mirai:poke:").appendStringAsMiraiCode(name)
|
||||
.append(',').append(pokeType).append(',').append(id)
|
||||
.append(']')
|
||||
}
|
||||
|
||||
override fun toString(): String = stringValue
|
||||
override fun contentToString(): String = "[戳一戳]"
|
||||
//businessType=0x00000001(1)
|
||||
//pbElem=08 01 18 00 20 FF FF FF FF 0F 2A 00 32 00 38 00 50 00
|
||||
//serviceType=0x00000002(2)
|
||||
}
|
||||
////////////////////////////////////
|
||||
////////// MARKET FACE /////////////
|
||||
////////////////////////////////////
|
||||
/**
|
||||
* 商城表情
|
||||
*
|
||||
* 目前不支持直接发送,可支持转发,但其取决于表情是否可使用.
|
||||
*/
|
||||
public interface MarketFace : HummerMessage {
|
||||
public val name: String
|
||||
public val id: Int
|
||||
|
||||
override val key: MessageKey<MarketFace>
|
||||
get() = Key
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, MarketFace>(HummerMessage, { it.safeCast() }) {
|
||||
public const val SERIAL_NAME: String = "MarketFace"
|
||||
}
|
||||
|
||||
override fun contentToString(): String = name
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
///////////// VIP FACE /////////////
|
||||
////////////////////////////////////
|
||||
|
||||
/**
|
||||
* VIP 表情.
|
||||
*
|
||||
* 不支持发送.
|
||||
*
|
||||
* ## mirai 码支持
|
||||
* 格式: [mirai:vipface:*[Kind.id]*,*[Kind.name]*,*[count]*]
|
||||
*
|
||||
* @see VipFace.Key 使用伴生对象中的常量
|
||||
*/
|
||||
@SerialName(VipFace.SERIAL_NAME)
|
||||
@Serializable
|
||||
public data class VipFace @MiraiInternalApi constructor(
|
||||
/**
|
||||
* 使用 [Companion] 中常量.
|
||||
*/
|
||||
public val kind: Kind,
|
||||
public val count: Int
|
||||
) : HummerMessage, CodableMessage {
|
||||
@Serializable
|
||||
public data class Kind(
|
||||
val id: Int,
|
||||
val name: String
|
||||
) {
|
||||
public override fun toString(): String {
|
||||
return "$id,$name"
|
||||
}
|
||||
}
|
||||
|
||||
override val key: MessageKey<VipFace>
|
||||
get() = Key
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, VipFace>(HummerMessage, { it.safeCast() }) {
|
||||
|
||||
public const val SERIAL_NAME: String = "VipFace"
|
||||
|
||||
@JvmField
|
||||
public val LiuLian: Kind = 9 to "榴莲"
|
||||
|
||||
@JvmField
|
||||
public val PingDiGuo: Kind = 1 to "平底锅"
|
||||
|
||||
@JvmField
|
||||
public val ChaoPiao: Kind = 12 to "钞票"
|
||||
|
||||
@JvmField
|
||||
public val LueLueLue: Kind = 10 to "略略略"
|
||||
|
||||
@JvmField
|
||||
public val ZhuTou: Kind = 4 to "猪头"
|
||||
|
||||
@JvmField
|
||||
public val BianBian: Kind = 6 to "便便"
|
||||
|
||||
@JvmField
|
||||
public val ZhaDan: Kind = 5 to "炸弹"
|
||||
|
||||
@JvmField
|
||||
public val AiXin: Kind = 2 to "爱心"
|
||||
|
||||
@JvmField
|
||||
public val HaHa: Kind = 3 to "哈哈"
|
||||
|
||||
@JvmField
|
||||
public val DianZan: Kind = 1 to "点赞"
|
||||
|
||||
@JvmField
|
||||
public val QinQin: Kind = 7 to "亲亲"
|
||||
|
||||
@JvmField
|
||||
public val YaoWan: Kind = 8 to "药丸"
|
||||
|
||||
@JvmField
|
||||
public val values: Array<Kind> = arrayOf(
|
||||
LiuLian, PingDiGuo, ChaoPiao, LueLueLue, ZhuTou,
|
||||
BianBian, ZhaDan, AiXin, HaHa, DianZan, QinQin, YaoWan
|
||||
)
|
||||
|
||||
private infix fun Int.to(name: String): Kind = Kind(this, name)
|
||||
}
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
builder.append(stringValue)
|
||||
}
|
||||
|
||||
private val stringValue = "[mirai:vipface:$kind,$count]"
|
||||
|
||||
override fun toString(): String = stringValue
|
||||
override fun contentToString(): String = "[${kind.name}]x$count"
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
///////////// FLASH IMAGE /////////////
|
||||
///////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* 闪照
|
||||
*
|
||||
* ## mirai 码支持
|
||||
* 格式: [mirai:flash:*[Image.imageId]*]
|
||||
*
|
||||
* @see Image.flash 转换普通图片为闪照
|
||||
*
|
||||
* @see Image 查看图片相关信息
|
||||
*/
|
||||
@SerialName(FlashImage.SERIAL_NAME)
|
||||
@Serializable
|
||||
public data class FlashImage(
|
||||
/**
|
||||
* 闪照的内容图片, 即一个普通图片.
|
||||
*/
|
||||
@SerialName("imageId")
|
||||
@Serializable(Image.AsStringSerializer::class)
|
||||
public val image: Image
|
||||
) : MessageContent, HummerMessage, CodableMessage, ConstrainSingle {
|
||||
override val key: MessageKey<FlashImage>
|
||||
get() = Key
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, FlashImage>(HummerMessage, { it.safeCast() }) {
|
||||
public const val SERIAL_NAME: String = "FlashImage"
|
||||
|
||||
/**
|
||||
* 将普通图片转换为闪照.
|
||||
*
|
||||
* @param imageId 图片 id, 详见 [Image.imageId]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun from(imageId: String): FlashImage = FlashImage(Image(imageId))
|
||||
|
||||
/**
|
||||
* 将普通图片转换为闪照.
|
||||
*
|
||||
* @see Image.flash
|
||||
*/
|
||||
@JvmStatic
|
||||
public inline fun from(image: Image): FlashImage = FlashImage(image)
|
||||
}
|
||||
|
||||
private val stringValue: String by lazy(LazyThreadSafetyMode.NONE) { "[mirai:flash:${image.imageId}]" }
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
builder.append(stringValue)
|
||||
}
|
||||
|
||||
override fun toMiraiCode(): String = stringValue
|
||||
public override fun toString(): String = stringValue
|
||||
public override fun contentToString(): String = "[闪照]"
|
||||
}
|
||||
|
||||
/**
|
||||
* 将普通图片转换为闪照.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun FlashImage(imageId: String): FlashImage = FlashImage.from(imageId)
|
||||
|
||||
/**
|
||||
* 将普通图片转换为闪照.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun Image.flash(): FlashImage = FlashImage(this)
|
||||
}
|
@ -88,14 +88,12 @@ public interface Image : Message, MessageContent, CodableMessage {
|
||||
*/
|
||||
public val imageId: String
|
||||
|
||||
@kotlinx.serialization.Serializer(forClass = Image::class)
|
||||
public object AsStringSerializer : KSerializer<Image> by String.serializer().mapPrimitive(
|
||||
SERIAL_NAME,
|
||||
serialize = { imageId },
|
||||
deserialize = { Image(it) },
|
||||
)
|
||||
|
||||
@kotlinx.serialization.Serializer(forClass = Image::class)
|
||||
public object Serializer : KSerializer<Image> by FallbackSerializer("Image")
|
||||
|
||||
@MiraiInternalApi
|
||||
@ -133,7 +131,7 @@ public interface Image : Message, MessageContent, CodableMessage {
|
||||
* - 当图片为从服务器接收的消息中的图片时, 可以直接获取下载链接, 本函数不会挂起协程.
|
||||
* - 其他情况下协程可能会挂起并向服务器查询下载链接, 或不挂起并拼接一个链接.
|
||||
*
|
||||
* @return 原图 HTTP 下载链接 (非 HTTPS)
|
||||
* @return 原图 HTTP 下载链接
|
||||
* @throws IllegalStateException 当无任何 [Bot] 在线时抛出 (因为无法获取相关协议)
|
||||
*/
|
||||
@JvmStatic
|
||||
@ -199,6 +197,11 @@ public interface Image : Message, MessageContent, CodableMessage {
|
||||
@JvmSynthetic
|
||||
public inline fun Image(imageId: String): Image = Image.fromId(imageId)
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Internals
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 计算图片的 md5 校验值.
|
||||
*
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
/**
|
||||
* 商城表情
|
||||
*
|
||||
* 目前不支持直接发送,可保存接收到的来自官方客户端的商城表情然后转发.
|
||||
*/
|
||||
public interface MarketFace : HummerMessage {
|
||||
/**
|
||||
* 如 `[开心]`
|
||||
*/
|
||||
public val name: String
|
||||
|
||||
/**
|
||||
* 内部 id.
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
public val id: Int
|
||||
|
||||
override val key: MessageKey<MarketFace> get() = Key
|
||||
|
||||
override fun contentToString(): String = name
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, MarketFace>(HummerMessage, { it.safeCast() }) {
|
||||
public const val SERIAL_NAME: String = "MarketFace"
|
||||
}
|
||||
}
|
@ -35,7 +35,27 @@ import kotlin.internal.LowPriorityInOverloadResolution
|
||||
* - [MessageContent] 含内容的消息, 包括: [纯文本][PlainText], [@群员][At], [@全体成员][AtAll] 等.
|
||||
* - [MessageChain]: 不可变消息链, 链表形式链接的多个 [SingleMessage] 实例.
|
||||
*
|
||||
* #### 在 Kotlin 使用 [Message]:
|
||||
* ## 获得 [Message]
|
||||
*
|
||||
* 请先根据实际需求确定需要的类型.
|
||||
*
|
||||
*
|
||||
* - [PlainText]: 纯文本
|
||||
* - [Image]: 图片
|
||||
* - [Face]: 原生表情
|
||||
* - [At]: 一个群成员的引用
|
||||
* - [AtAll]: 全体成员的引用
|
||||
* - [QuoteReply]: 一条消息的引用
|
||||
* - [RichMessage]: 富文本消息, 如 [XML 和 JSON][ServiceMessage], [小程序][LightApp]
|
||||
* - [FlashImage]: 闪照
|
||||
* - [PokeMessage]: 戳一戳 (消息)
|
||||
* - [VipFace]: VIP 表情
|
||||
* - [CustomMessage]: 自定义消息类型
|
||||
* - ...
|
||||
*
|
||||
* ## 使用 [Message]
|
||||
*
|
||||
* ### 在 Kotlin 使用 [Message]:
|
||||
* 这与使用 [String] 的使用非常类似.
|
||||
*
|
||||
* - 比较 [SingleMessage] 与 [String]:
|
||||
@ -48,7 +68,9 @@ import kotlin.internal.LowPriorityInOverloadResolution
|
||||
* ```
|
||||
* 但注意: 不能 `String + Message`. 只能 `Message + String`
|
||||
*
|
||||
* #### 发送消息
|
||||
*
|
||||
*
|
||||
* ### 发送消息
|
||||
* - 通过 [Contact] 中的成员函数: [Contact.sendMessage]
|
||||
* - 通过 [Message] 的扩展函数: [Message.sendTo]
|
||||
* - 在 [MessageEvent] 中使用 [MessageEvent.reply] 等捷径
|
||||
@ -68,7 +90,7 @@ import kotlin.internal.LowPriorityInOverloadResolution
|
||||
*
|
||||
* @see Contact.sendMessage 发送消息
|
||||
*/
|
||||
public interface Message {
|
||||
public interface Message { // TODO: 2021/1/10 Make sealed interface in Kotlin 1.5
|
||||
|
||||
/**
|
||||
* 将 `this` 和 [tail] 连接.
|
||||
@ -222,26 +244,26 @@ public suspend inline operator fun Message.plus(another: Flow<Message>): Message
|
||||
* 单个消息元素. 与之相对的是 [MessageChain], 是多个 [SingleMessage] 的集合.
|
||||
*/
|
||||
@Serializable(SingleMessage.Serializer::class)
|
||||
public interface SingleMessage : Message {
|
||||
@kotlinx.serialization.Serializer(forClass = SingleMessage::class)
|
||||
public object Serializer :
|
||||
KSerializer<SingleMessage> by PolymorphicSerializer(SingleMessage::class)
|
||||
public interface SingleMessage : Message { // TODO: 2021/1/10 Make sealed interface in Kotlin 1.5
|
||||
public object Serializer : KSerializer<SingleMessage> by PolymorphicSerializer(SingleMessage::class)
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息元数据, 即不含内容的元素.
|
||||
*
|
||||
* 这种类型的 [Message] 只表示一条消息的属性. 其子类为 [MessageSource], [QuoteReply]
|
||||
* 这种类型的 [Message] 只表示一条消息的属性. 其子类为 [MessageSource], [QuoteReply] 和 [CustomMessageMetadata]
|
||||
*
|
||||
* 所有子类的 [contentToString] 都应该返回空字符串.
|
||||
*
|
||||
* 要获取详细信息, 查看 [MessageChain].
|
||||
*
|
||||
* @see MessageSource 消息源
|
||||
* @see QuoteReply 引用回复
|
||||
* @see CustomMessageMetadata 自定义元数据
|
||||
*
|
||||
* @see ConstrainSingle 约束一个 [MessageChain] 中只存在这一种类型的元素
|
||||
*/
|
||||
public interface MessageMetadata : SingleMessage {
|
||||
public interface MessageMetadata : SingleMessage { // TODO: 2021/1/10 Make sealed interface in Kotlin 1.5
|
||||
/**
|
||||
* 返回空字符串
|
||||
*/
|
||||
@ -253,10 +275,8 @@ public interface MessageMetadata : SingleMessage {
|
||||
*
|
||||
* 实现此接口的元素将会在连接时自动处理替换.
|
||||
*
|
||||
* @see AbstractMessageKey
|
||||
* @see AbstractPolymorphicMessageKey
|
||||
*
|
||||
* @see MessageSource
|
||||
* 要获取有关键的信息, 查看 [MessageKey].
|
||||
* 要获取有关约束的处理方式, 查看 [AbstractPolymorphicMessageKey].
|
||||
*/
|
||||
public interface ConstrainSingle : SingleMessage {
|
||||
/**
|
||||
@ -280,7 +300,7 @@ public interface ConstrainSingle : SingleMessage {
|
||||
* @see ForwardMessage 合并转发
|
||||
* @see Voice 语音
|
||||
*/
|
||||
public interface MessageContent : SingleMessage {
|
||||
public interface MessageContent : SingleMessage { // TODO: 2021/1/10 Make sealed interface in Kotlin 1.5
|
||||
public companion object Key : AbstractMessageKey<MessageContent>({ it.safeCast() })
|
||||
}
|
||||
|
||||
|
@ -27,56 +27,160 @@ import net.mamoe.mirai.message.MessageSerializers
|
||||
import net.mamoe.mirai.message.code.CodableMessage
|
||||
import net.mamoe.mirai.message.code.MiraiCode
|
||||
import net.mamoe.mirai.message.code.MiraiCode.parseMiraiCode
|
||||
import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.recall
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.recallIn
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
import java.util.stream.Stream
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
/**
|
||||
* 消息链. 空的实现为 [EmptyMessageChain]
|
||||
* 消息链, `List<SingleMessage>`, 即 [单个消息元素][SingleMessage] 的有序集合.
|
||||
*
|
||||
* 要获取更多消息相关的信息, 查看 [Message]
|
||||
* [MessageChain] 代表一条完整的聊天中的消息, 可包含 [带内容的消息 `MessageContent`][MessageContent]
|
||||
* 和 [不带内容的元数据 `MessageMetadata`][MessageMetadata].
|
||||
*
|
||||
* ### 构造消息链
|
||||
* - [buildMessageChain][buildMessageChain]: 使用构建器
|
||||
* - [Message.plus][Message.plus]: 将两个消息相连成为一个消息链
|
||||
* - [toMessageChain][toMessageChain] 将 [Iterable], [Array] 等类型转换为 [MessageChain]
|
||||
* - [messageChainOf][messageChainOf] 类似 [listOf], 将多个 [Message] 构造为 [MessageChain]
|
||||
* # 元素类型
|
||||
*
|
||||
* @see get 获取消息链中一个类型的元素, 不存在时返回 `null`
|
||||
* @see getOrFail 获取消息链中一个类型的元素, 不存在时抛出异常 [NoSuchElementException]
|
||||
* @see MessageSource.quote 引用这条消息
|
||||
* @see MessageSource.recall 撤回这条消息 (仅限来自 [MessageEvent] 的消息)
|
||||
* [MessageContent] 如 [纯文字][PlainText], [图片][Image], [语音][Voice], 是能被用户看到的内容.
|
||||
*
|
||||
* @see buildMessageChain 构造一个 [MessageChain]
|
||||
* @see Message.toMessageChain 将单个 [Message] 转换为 [MessageChain]
|
||||
* @see toMessageChain 将 [Iterable] 或 [Sequence] 委托为 [MessageChain]
|
||||
*
|
||||
* @see forEachContent 遍历内容
|
||||
* @see allContent 判断是否每一个 [MessageContent] 都满足条件
|
||||
* @see noneContent 判断是否每一个 [MessageContent] 都不满足条件
|
||||
* [MessageMetadata] 是用来形容这条消息的状态的数据, 因此称为 *元数据 (metadata)*.
|
||||
* 元数据目前只分为 [消息来源 `MessageSource`][MessageSource] 和 [引用回复 `QuoteReply`][QuoteReply].
|
||||
*
|
||||
* @see orNull 属性委托扩展
|
||||
* @see orElse 属性委托扩展
|
||||
* @see getValue 属性委托扩展
|
||||
* @see flatten 扁平化
|
||||
* [MessageSource] 存储这条消息的发送人, 接收人, 识别 ID (服务器提供), 发送时间等信息.
|
||||
* **[MessageSource] 是精确的**. 凭 [MessageSource] 就可以在服务器上定位一条消息, 因此可以用来 [撤回消息][MessageSource.recall].
|
||||
*
|
||||
* @see MiraiCode mirai 码
|
||||
* [QuoteReply] 是一个标记, 表示这条消息引用了另一条消息 (在官方客户端中可通过 "回复" 功能发起引用). [QuoteReply.source] 则指代那条被引用的消息.
|
||||
* 由于 [MessageSource] 是精确的, 如果对 [QuoteReply.source] 使用 [MessageSource.recall], 则可以撤回那条被引用的消息.
|
||||
*
|
||||
*
|
||||
* # 获得消息链
|
||||
*
|
||||
* 在消息事件中可以获得消息内容作为 [MessageChain]: [MessageEvent.message]
|
||||
*
|
||||
* 在主动发送消息时, 可使用如下方案.
|
||||
*
|
||||
* ## 在 Kotlin 构造消息链
|
||||
* - 获取不包含任何元素的消息链: [EmptyMessageChain]
|
||||
* - [messageChainOf][messageChainOf]: 类似 [listOf], 将多个 [Message] 构造为 [MessageChain]:
|
||||
* ```
|
||||
* val chain = messageChainOf(PlainText("..."), Image("..."), ...)
|
||||
* ```
|
||||
* - [buildMessageChain][buildMessageChain]: 使用 DSL 构建器.
|
||||
* ```
|
||||
* val chain = buildMessageChain {
|
||||
* +"你想要的图片是:"
|
||||
* +Image("...")
|
||||
* }
|
||||
* ```
|
||||
* - [Message.plus][Message.plus]: 将两个消息相连成为一个消息链:
|
||||
* ```
|
||||
* val chain = PlainText("Hello ") + PlainText("Mirai!") // chain: MessageChain
|
||||
* ```
|
||||
* - [toMessageChain][toMessageChain] 将 [Iterable], [Array], [Sequence], [Iterator], [Flow], [Stream] 转换为 [MessageChain].
|
||||
* 相关定义为:
|
||||
* ```
|
||||
* public fun Sequence<Message>.toMessageChain(): MessageChain
|
||||
* public fun Iterable<Message>.toMessageChain(): MessageChain
|
||||
* public fun Iterator<Message>.toMessageChain(): MessageChain
|
||||
* public fun Stream<Message>.toMessageChain(): MessageChain
|
||||
* public fun Flow<Message>.toMessageChain(): MessageChain
|
||||
* public fun Array<Message>.toMessageChain(): MessageChain
|
||||
* ```
|
||||
* - [Message.toMessageChain][Message.toMessageChain] 将单个 [Message] 包装成一个单元素的 [MessageChain]
|
||||
*
|
||||
* ## 在 Java 构造消息链
|
||||
* - `MessageUtils.newChain`: 有多个重载, 相关定义如下:
|
||||
* ```java
|
||||
* public static MessageChain newChain(Message messages...)
|
||||
* public static MessageChain newChain(Iterable<Message> iterable)
|
||||
* public static MessageChain newChain(Iterator<Message> iterator)
|
||||
* public static MessageChain newChain(Stream<Message> stream)
|
||||
* public static MessageChain newChain(Message[] array)
|
||||
* ```
|
||||
* - [Message.plus][Message.plus]: 将两个消息相连成为一个消息链:
|
||||
* ```java
|
||||
* MessageChain chain = new PlainText("Hello ").plus(new PlainText("Mirai!"))
|
||||
* ```
|
||||
* - [MessageChainBuilder]:
|
||||
* ```java
|
||||
* MessageChainBuilder builder = MessageChainBuilder.create();
|
||||
* builder.append(new PlainText("Hello "));
|
||||
* builder.append(new PlainText(" Mirai!"));
|
||||
* MessageChain chain = builder.build();
|
||||
* ```
|
||||
*
|
||||
* # 元素唯一性
|
||||
*
|
||||
* 部分消息类型如 [语音][Voice], [小程序][LightApp] 在官方客户端限制中只允许单独存在于一条消息. 在创建 [MessageChain] 时这种限制会被体现.
|
||||
*
|
||||
* 当添加只允许单独存在的消息元素到一个消息链时, 已有的元素可能会被删除或替换. 详见 [AbstractPolymorphicMessageKey] 和 [ConstrainSingle].
|
||||
*
|
||||
* # 操作 [MessageChain]
|
||||
*
|
||||
* [MessageChain] 继承 `List<SingleMessage>`. 可以以 [List] 的方式处理 [MessageChain].
|
||||
*
|
||||
* 额外地, 若要获取一个 [ConstrainSingle] 的元素, 可以通过 [ConstrainSingle.key]:
|
||||
* ```
|
||||
* val quote = chain[QuoteReply] // Kotlin
|
||||
*
|
||||
* QuoteReply quote = chain.get(QuoteReply.Key) // Java
|
||||
* ```
|
||||
*
|
||||
* 相关地还可以使用 [MessageChain.contains] 和 [MessageChain.getOrFail]
|
||||
*
|
||||
* ## Kotlin 扩展
|
||||
*
|
||||
* ### 属性委托
|
||||
* ```
|
||||
* val at: At? by chain.orNull()
|
||||
* val at: At by chain.orElse { /* 返回一个 At */ }
|
||||
* val at: At by chain
|
||||
* ```
|
||||
*
|
||||
* ### 筛选得到 [Sequence] 与 [List]
|
||||
* - [MessageChain.contentsSequence]
|
||||
* - [MessageChain.metadataSequence]
|
||||
* - [MessageChain.contentsList]
|
||||
* - [MessageChain.metadataList]
|
||||
*
|
||||
*
|
||||
* ## 序列化
|
||||
*
|
||||
* ### kotlinx-serialization 序列化
|
||||
*
|
||||
* - 使用 [MessageChain.serializeToJsonString] 将 [MessageChain] 序列化为 JSON [String].
|
||||
* - 使用 [MessageChain.deserializeFromJsonString] 将 JSON [String] 反序列化为 [MessageChain].
|
||||
*
|
||||
* ### Mirai Code 序列化
|
||||
*
|
||||
* 详见 [MiraiCode]
|
||||
*
|
||||
* - 使用 [MessageChain.toMiraiCode] 将 [MessageChain] 序列化为 Mirai Code [String].
|
||||
* - 使用 [MessageChain.toMiraiCode] 将 Mirai Code [String] 反序列化为 [MessageChain].
|
||||
*
|
||||
*
|
||||
* ## 撤回和引用
|
||||
* - [MessageSource.quote]
|
||||
* - [MessageSource.recall]
|
||||
* - [MessageSource.recallIn]
|
||||
* - `MessageChain.quote`
|
||||
* - `MessageChain.recall`
|
||||
* - `MessageChain.recallIn`
|
||||
*/
|
||||
@Serializable(MessageChain.Serializer::class)
|
||||
public interface MessageChain : Message, List<SingleMessage>, RandomAccess, CodableMessage {
|
||||
/**
|
||||
* 元素数量. [EmptyMessageChain] 不参加计数.
|
||||
*/
|
||||
public override val size: Int
|
||||
public interface MessageChain :
|
||||
Message, List<SingleMessage>, RandomAccess, CodableMessage { // TODO: 2021/1/10 Make sealed interface in Kotlin 1.5
|
||||
|
||||
/**
|
||||
* 获取第一个类型为 [key] 的 [Message] 实例. 若不存在此实例, 返回 `null`.
|
||||
*
|
||||
* 此方法仅适用于 [ConstrainSingle] 的消息类型, 如 [MessageSource]
|
||||
* 此方法目前仅适用于 [ConstrainSingle] 的消息类型, 如 [MessageSource].
|
||||
*
|
||||
* ### Kotlin 使用方法
|
||||
* ```
|
||||
@ -98,6 +202,32 @@ public interface MessageChain : Message, List<SingleMessage>, RandomAccess, Coda
|
||||
public operator fun <M : SingleMessage> get(key: MessageKey<M>): M? =
|
||||
asSequence().mapNotNull { key.safeCast.invoke(it) }.firstOrNull()
|
||||
|
||||
/**
|
||||
* 当存在 [ConstrainSingle.key] 为 [key] 的 [SingleMessage] 实例时返回 `true`.
|
||||
*
|
||||
* 此方法目前仅适用于 [ConstrainSingle] 的消息类型, 如 [MessageSource].
|
||||
*
|
||||
* ### Kotlin 使用方法
|
||||
* ```
|
||||
* val chain: MessageChain = ...
|
||||
*
|
||||
* if (chain.contains(QuoteReply)) {
|
||||
* // 包含引用回复
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ### Java 使用方法
|
||||
* ```java
|
||||
* MessageChain chain = ...
|
||||
* if (chain.contains(QuoteReply.Key)) {
|
||||
* // 包含引用回复
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param key 由各个类型消息的伴生对象持有. 如 [MessageSource.Key]
|
||||
*
|
||||
* @see MessageChain.getOrFail 在找不到此类型的元素时抛出 [NoSuchElementException]
|
||||
*/
|
||||
public operator fun <M : SingleMessage> contains(key: MessageKey<M>): Boolean =
|
||||
asSequence().any { key.safeCast.invoke(it) != null }
|
||||
|
||||
@ -106,7 +236,14 @@ public interface MessageChain : Message, List<SingleMessage>, RandomAccess, Coda
|
||||
forEach { it.safeCast<CodableMessage>()?.appendMiraiCodeTo(builder) }
|
||||
}
|
||||
|
||||
@kotlinx.serialization.Serializer(MessageChain::class)
|
||||
/**
|
||||
* 将 [MessageChain] 作为 `List<SingleMessage>` 序列化. 使用 [多态序列化][Polymorphic].
|
||||
*
|
||||
* 在实践时请提供 [MessageSerializers.serializersModule] 到指定 [SerialFormat].
|
||||
*
|
||||
* @see ListSerializer
|
||||
* @see MessageSerializers
|
||||
*/
|
||||
public object Serializer : KSerializer<MessageChain> {
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
private val delegate = ListSerializer(PolymorphicSerializer(SingleMessage::class))
|
||||
@ -115,7 +252,6 @@ public interface MessageChain : Message, List<SingleMessage>, RandomAccess, Coda
|
||||
override fun serialize(encoder: Encoder, value: MessageChain): Unit = delegate.serialize(encoder, value)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
public companion object {
|
||||
private fun getDefaultJson() = Json {
|
||||
serializersModule =
|
||||
@ -191,20 +327,27 @@ public interface MessageChain : Message, List<SingleMessage>, RandomAccess, Coda
|
||||
/**
|
||||
* 不含任何元素的 [MessageChain].
|
||||
*/
|
||||
public object EmptyMessageChain : MessageChain, Iterator<SingleMessage>, List<SingleMessage> by emptyList() {
|
||||
public override val size: Int get() = 0
|
||||
public override fun toString(): String = ""
|
||||
public override fun contentToString(): String = ""
|
||||
public override fun equals(other: Any?): Boolean = other === this
|
||||
@Serializable(MessageChain.Serializer::class)
|
||||
public object EmptyMessageChain : MessageChain, List<SingleMessage> by emptyList() {
|
||||
override val size: Int get() = 0
|
||||
|
||||
public override fun iterator(): Iterator<SingleMessage> = this
|
||||
public override fun hasNext(): Boolean = false
|
||||
public override fun next(): SingleMessage = throw NoSuchElementException("EmptyMessageChain is empty.")
|
||||
override fun toString(): String = ""
|
||||
override fun contentToString(): String = ""
|
||||
override fun toMiraiCode(): String = ""
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean = other === this
|
||||
override fun hashCode(): Int = 1
|
||||
|
||||
override fun iterator(): Iterator<SingleMessage> = EmptyMessageChainIterator
|
||||
|
||||
private object EmptyMessageChainIterator : Iterator<SingleMessage> {
|
||||
override fun hasNext(): Boolean = false
|
||||
override fun next(): Nothing = throw NoSuchElementException("EmptyMessageChain is empty.")
|
||||
}
|
||||
}
|
||||
|
||||
// region accessors
|
||||
@ -214,53 +357,37 @@ public object EmptyMessageChain : MessageChain, Iterator<SingleMessage>, List<Si
|
||||
*
|
||||
* @param key 由各个类型消息的伴生对象持有. 如 [MessageSource.Key]
|
||||
*/
|
||||
@JvmOverloads
|
||||
@JvmSynthetic
|
||||
public inline fun <M : SingleMessage> MessageChain.getOrFail(
|
||||
key: MessageKey<M>,
|
||||
crossinline lazyMessage: (key: MessageKey<M>) -> String = { key.toString() }
|
||||
): M = get(key) ?: throw NoSuchElementException(lazyMessage(key))
|
||||
|
||||
|
||||
/**
|
||||
* 遍历每一个 [消息内容][MessageContent]
|
||||
* 获取 `Sequence<MessageContent>`
|
||||
* 相当于 `this.asSequence().filterIsInstance<MessageContent>()`
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun MessageChain.forEachContent(block: (MessageContent) -> Unit) {
|
||||
for (element in this) {
|
||||
if (element !is MessageMetadata) {
|
||||
check(element is MessageContent) { "internal error: Message must be either MessageMetadata or MessageContent" }
|
||||
block(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
public fun MessageChain.contentsSequence(): Sequence<MessageContent> =
|
||||
this.asSequence().filterIsInstance<MessageContent>()
|
||||
|
||||
/**
|
||||
* 如果每一个 [消息内容][MessageContent] 都满足 [block], 返回 `true`
|
||||
* 获取 `Sequence<MessageMetadata>`
|
||||
* 相当于 `this.asSequence().filterIsInstance<MessageMetadata>()`
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun MessageChain.allContent(block: (MessageContent) -> Boolean): Boolean {
|
||||
this.forEach {
|
||||
if (it !is MessageMetadata) {
|
||||
check(it is MessageContent) { "internal error: Message must be either MessageMetadata or MessageContent" }
|
||||
if (!block(it)) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
public fun MessageChain.metadataSequence(): Sequence<MessageMetadata> =
|
||||
this.asSequence().filterIsInstance<MessageMetadata>()
|
||||
|
||||
/**
|
||||
* 如果每一个 [消息内容][MessageContent] 都不满足 [block], 返回 `true`
|
||||
* 筛选 [MessageMetadata]
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun MessageChain.noneContent(block: (MessageContent) -> Boolean): Boolean {
|
||||
this.forEach {
|
||||
if (it !is MessageMetadata) {
|
||||
check(it is MessageContent) { "internal error: Message must be either MessageMetadata or MessageContent" }
|
||||
if (block(it)) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
public fun MessageChain.metadataList(): List<MessageMetadata> = this.filterIsInstance<MessageMetadata>()
|
||||
|
||||
/**
|
||||
* 筛选 [MessageContent]
|
||||
*/
|
||||
public fun MessageChain.contentsList(): List<MessageContent> = this.filterIsInstance<MessageContent>()
|
||||
|
||||
|
||||
/**
|
||||
@ -307,7 +434,8 @@ public inline fun messageChainOf(vararg messages: Message): MessageChain = messa
|
||||
* 扁平化 [this] 并创建一个 [MessageChain].
|
||||
*/
|
||||
@JvmName("newChain")
|
||||
public fun Sequence<Message>.toMessageChain(): MessageChain = MessageChainImpl(this.constrainSingleMessages())
|
||||
public fun Sequence<Message>.toMessageChain(): MessageChain =
|
||||
createMessageChainImplOptimized(this.constrainSingleMessages())
|
||||
|
||||
/**
|
||||
* 扁平化 [this] 并创建一个 [MessageChain].
|
||||
@ -415,3 +543,77 @@ public inline fun <reified T : R, R : SingleMessage?> MessageChain.orElse(
|
||||
): OrNullDelegate<R> = OrNullDelegate<R>(this.firstIsInstanceOrNull<T>() ?: lazyDefault())
|
||||
|
||||
// endregion delegate
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Deprecated
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* 遍历每一个 [消息内容][MessageContent]
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@Deprecated(
|
||||
"Use operations on contentsSequence instead.",
|
||||
ReplaceWith(
|
||||
"this.contentsSequence().forEach(block)",
|
||||
"net.mamoe.mirai.message.data.contentsSequence"
|
||||
),
|
||||
DeprecationLevel.ERROR,
|
||||
)
|
||||
@PlannedRemoval("2.0.0")
|
||||
public inline fun MessageChain.forEachContent(block: (MessageContent) -> Unit) {
|
||||
for (element in this) {
|
||||
if (element !is MessageMetadata) {
|
||||
check(element is MessageContent) { "internal error: Message must be either MessageMetadata or MessageContent" }
|
||||
block(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果每一个 [消息内容][MessageContent] 都满足 [block], 返回 `true`
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@Deprecated(
|
||||
"Use operations on contentsSequence instead.",
|
||||
ReplaceWith(
|
||||
"this.contentsSequence().all(block)",
|
||||
"net.mamoe.mirai.message.data.contentsSequence"
|
||||
),
|
||||
DeprecationLevel.ERROR,
|
||||
)
|
||||
@PlannedRemoval("2.0.0")
|
||||
public inline fun MessageChain.allContent(block: (MessageContent) -> Boolean): Boolean {
|
||||
this.forEach {
|
||||
if (it !is MessageMetadata) {
|
||||
check(it is MessageContent) { "internal error: Message must be either MessageMetadata or MessageContent" }
|
||||
if (!block(it)) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果每一个 [消息内容][MessageContent] 都不满足 [block], 返回 `true`
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@Deprecated(
|
||||
"Use operations on contentsSequence instead.",
|
||||
ReplaceWith(
|
||||
"this.contentsSequence().none(block)",
|
||||
"net.mamoe.mirai.message.data.contentsSequence"
|
||||
),
|
||||
DeprecationLevel.ERROR,
|
||||
)
|
||||
@PlannedRemoval("2.0.0")
|
||||
public inline fun MessageChain.noneContent(block: (MessageContent) -> Boolean): Boolean {
|
||||
this.forEach {
|
||||
if (it !is MessageMetadata) {
|
||||
check(it is MessageContent) { "internal error: Message must be either MessageMetadata or MessageContent" }
|
||||
if (block(it)) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ public class MessageChainBuilder private constructor(
|
||||
// avoid resolution to extensions
|
||||
public fun asMessageChain(): MessageChain {
|
||||
this.flushCache()
|
||||
return MessageChainImpl(this.constrainSingleMessages())
|
||||
return createMessageChainImplOptimized(this.constrainSingleMessages())
|
||||
}
|
||||
|
||||
/** 同 [asMessageChain] */
|
||||
|
@ -25,12 +25,10 @@ import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.event.events.MessageEvent
|
||||
import net.mamoe.mirai.internal.message.MessageSourceSerializerImpl
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.isAboutFriend
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.isAboutGroup
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.isAboutStranger
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.isAboutTemp
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.recall
|
||||
import net.mamoe.mirai.utils.LazyProperty
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
/**
|
||||
@ -58,7 +56,7 @@ import net.mamoe.mirai.utils.safeCast
|
||||
*
|
||||
* ## 使用
|
||||
*
|
||||
* 消息源可用于 [引用回复][QuoteReply] 或 [撤回][IMirai.recallMessage].
|
||||
* 消息源可用于 [引用回复][MessageSource.quote] 或 [撤回][MessageSource.recall].
|
||||
*
|
||||
* @see IMirai.recallMessage 撤回一条消息
|
||||
* @see MessageSource.quote 引用这条消息, 创建 [MessageChain]
|
||||
@ -256,7 +254,7 @@ public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle {
|
||||
@JvmStatic
|
||||
public inline fun MessageSource.isAboutFriend(): Boolean {
|
||||
return when (this) {
|
||||
is OnlineMessageSource -> subject !is Group && subject !is Member
|
||||
is OnlineMessageSource -> subject is Friend
|
||||
is OfflineMessageSource -> kind == MessageSourceKind.FRIEND
|
||||
}
|
||||
}
|
||||
@ -277,208 +275,52 @@ public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle {
|
||||
}
|
||||
}
|
||||
|
||||
public inline val MessageSource.bot: Bot
|
||||
get() = when (this) {
|
||||
is OnlineMessageSource -> bot
|
||||
is OfflineMessageSource -> Bot.getInstance(botId)
|
||||
}
|
||||
|
||||
public inline val MessageSource.botOrNull: Bot?
|
||||
get() = when (this) {
|
||||
is OnlineMessageSource -> bot
|
||||
is OfflineMessageSource -> Bot.getInstanceOrNull(botId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线消息的 [MessageSource].
|
||||
* 拥有对象化的 [sender], [target], 也可以直接 [recallMessage] 和 [quote]
|
||||
*
|
||||
* ### 来源
|
||||
* - 当 bot 主动发送消息时, 产生 (由协议模块主动构造) [OnlineMessageSource.Outgoing]
|
||||
* - 当 bot 接收消息时, 产生 (由协议模块根据服务器的提供的信息构造) [OnlineMessageSource.Incoming]
|
||||
*
|
||||
* #### 机器人主动发送消息
|
||||
* 当机器人 [主动发出消息][Member.sendMessage], 将会得到一个 [消息回执][MessageReceipt].
|
||||
* 此回执的 [消息源][MessageReceipt.source] 即为一个 [外向消息源][OnlineMessageSource.Outgoing], 代表着刚刚发出的那条消息的来源.
|
||||
*
|
||||
* #### 机器人接受消息
|
||||
* 当机器人接收一条消息 [MessageEvent], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源.
|
||||
*
|
||||
*
|
||||
* ### 实现
|
||||
* 此类的所有子类都有协议模块实现. 不要自行实现它们, 否则将无法发送
|
||||
*
|
||||
* @see OnlineMessageSource.toOffline 转为 [OfflineMessageSource]
|
||||
* 消息来源类型
|
||||
*/
|
||||
public sealed class OnlineMessageSource : MessageSource() {
|
||||
public companion object Key : AbstractMessageKey<OnlineMessageSource>({ it.safeCast() })
|
||||
|
||||
/**
|
||||
* @see botId
|
||||
*/
|
||||
public abstract val bot: Bot
|
||||
final override val botId: Long get() = bot.id
|
||||
|
||||
/**
|
||||
* 消息发送人. 可能为 [机器人][Bot] 或 [好友][Friend] 或 [群员][Member].
|
||||
* 即类型必定为 [Bot], [Friend] 或 [Member]
|
||||
*/
|
||||
public abstract val sender: ContactOrBot
|
||||
|
||||
/**
|
||||
* 消息发送目标. 可能为 [机器人][Bot] 或 [好友][Friend] 或 [群][Group].
|
||||
* 即类型必定为 [Bot], [Friend] 或 [Group]
|
||||
*/
|
||||
public abstract val target: ContactOrBot
|
||||
|
||||
/**
|
||||
* 消息主体. 群消息时为 [Group]. 好友消息时为 [Friend], 临时消息为 [Member]
|
||||
* 不论是机器人接收的消息还是发送的消息, 此属性都指向机器人能进行回复的目标.
|
||||
*/
|
||||
public abstract val subject: Contact
|
||||
|
||||
/*
|
||||
* 以下子类型仅是覆盖了 [target], [subject], [sender] 等的类型
|
||||
*/
|
||||
|
||||
/**
|
||||
* 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource], 可通过 [MessageReceipt] 获得.
|
||||
*/
|
||||
public sealed class Outgoing : OnlineMessageSource() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<OnlineMessageSource, Outgoing>(OnlineMessageSource, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Bot
|
||||
public abstract override val target: Contact
|
||||
|
||||
public final override val fromId: Long get() = sender.id
|
||||
public final override val targetId: Long get() = target.id
|
||||
|
||||
public abstract class ToFriend : Outgoing() {
|
||||
public companion object Key : AbstractPolymorphicMessageKey<Outgoing, ToFriend>(Outgoing, { it.safeCast() })
|
||||
|
||||
public abstract override val target: Friend
|
||||
public final override val subject: Friend get() = target
|
||||
// final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.ids})"
|
||||
}
|
||||
|
||||
public abstract class ToStranger : Outgoing() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Outgoing, ToStranger>(Outgoing, { it.safeCast() })
|
||||
|
||||
public abstract override val target: Stranger
|
||||
public final override val subject: Stranger get() = target
|
||||
// final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.ids})"
|
||||
}
|
||||
|
||||
public abstract class ToTemp : Outgoing() {
|
||||
public companion object Key : AbstractPolymorphicMessageKey<Outgoing, ToTemp>(Outgoing, { it.safeCast() })
|
||||
|
||||
public abstract override val target: Member
|
||||
public val group: Group get() = target.group
|
||||
public final override val subject: Member get() = target
|
||||
}
|
||||
|
||||
public abstract class ToGroup : Outgoing() {
|
||||
public companion object Key : AbstractPolymorphicMessageKey<Outgoing, ToGroup>(Outgoing, { it.safeCast() })
|
||||
|
||||
public abstract override val target: Group
|
||||
public final override val subject: Group get() = target
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收到的一条消息的 [MessageSource]
|
||||
*/
|
||||
public sealed class Incoming : OnlineMessageSource() {
|
||||
public abstract override val sender: User
|
||||
|
||||
public final override val fromId: Long get() = sender.id
|
||||
public final override val targetId: Long get() = target.id
|
||||
|
||||
public abstract class FromFriend : Incoming() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Incoming, FromFriend>(Incoming, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Friend
|
||||
public final override val subject: Friend get() = sender
|
||||
public final override val target: Bot get() = sender.bot
|
||||
// final override fun toString(): String = "OnlineMessageSource.FromFriend(from=${sender.ids})"
|
||||
}
|
||||
|
||||
public abstract class FromTemp : Incoming() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Incoming, FromTemp>(Incoming, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Member
|
||||
public inline val group: Group get() = sender.group
|
||||
public final override val subject: Member get() = sender
|
||||
public final override val target: Bot get() = sender.bot
|
||||
}
|
||||
|
||||
public abstract class FromStranger : Incoming() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Incoming, FromStranger>(Incoming, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Stranger
|
||||
public final override val subject: Stranger get() = sender
|
||||
public final override val target: Bot get() = sender.bot
|
||||
}
|
||||
|
||||
public abstract class FromGroup : Incoming() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Incoming, FromGroup>(Incoming, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Member
|
||||
public final override val subject: Group get() = sender.group
|
||||
public final override val target: Group get() = group
|
||||
public inline val group: Group get() = sender.group
|
||||
}
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<OnlineMessageSource, FromTemp>(OnlineMessageSource, { it.safeCast() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 由一条消息中的 [QuoteReply] 得到的 [MessageSource].
|
||||
* 此消息源可能来自一条与机器人无关的消息. 因此无法提供对象化的 `sender` 或 `target` 获取.
|
||||
*
|
||||
* @see buildMessageSource 构建一个 [OfflineMessageSource]
|
||||
* @see IMirai.constructMessageSource
|
||||
* @see OnlineMessageSource.toOffline
|
||||
*/
|
||||
public abstract class OfflineMessageSource : MessageSource() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<MessageSource, OfflineMessageSource>(MessageSource, { it.safeCast() })
|
||||
|
||||
/**
|
||||
* 消息种类
|
||||
*/
|
||||
public abstract val kind: MessageSourceKind
|
||||
}
|
||||
|
||||
@Serializable
|
||||
public enum class MessageSourceKind {
|
||||
/**
|
||||
* 群消息
|
||||
*/
|
||||
GROUP,
|
||||
|
||||
/**
|
||||
* 好友消息
|
||||
*/
|
||||
FRIEND,
|
||||
|
||||
/**
|
||||
* 来自群成员的临时会话消息
|
||||
*/
|
||||
TEMP,
|
||||
|
||||
/**
|
||||
* 来自陌生人的消息
|
||||
*/
|
||||
STRANGER
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [MessageSourceKind]
|
||||
*/
|
||||
public val MessageSource.kind: MessageSourceKind
|
||||
get() = when (this) {
|
||||
is OnlineMessageSource -> kind
|
||||
is OfflineMessageSource -> kind
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [MessageSourceKind]
|
||||
*/
|
||||
public val OnlineMessageSource.kind: MessageSourceKind
|
||||
get() = when {
|
||||
isAboutGroup() -> MessageSourceKind.GROUP
|
||||
isAboutFriend() -> MessageSourceKind.FRIEND
|
||||
isAboutTemp() -> MessageSourceKind.TEMP
|
||||
isAboutStranger() -> MessageSourceKind.STRANGER
|
||||
else -> error("Internal error: OnlineMessageSource.kind reached an unexpected clause")
|
||||
get() = when (subject) {
|
||||
is Group -> MessageSourceKind.GROUP
|
||||
is Friend -> MessageSourceKind.FRIEND
|
||||
is Member -> MessageSourceKind.TEMP
|
||||
is Stranger -> MessageSourceKind.STRANGER
|
||||
else -> error("Internal error: OnlineMessageSource.kind reached an unexpected clause, subject=$subject")
|
||||
}
|
||||
|
||||
// For MessageChain, no need to expose to Java.
|
||||
@ -548,3 +390,191 @@ public inline val MessageChain.source: MessageSource
|
||||
@get:JvmSynthetic
|
||||
public inline val MessageChain.sourceOrNull: MessageSource?
|
||||
get() = this[MessageSource]
|
||||
|
||||
/**
|
||||
* 根据 [MessageSource.botId] 从 [Bot.getInstance] 获取 [Bot]
|
||||
*/
|
||||
public inline val MessageSource.bot: Bot
|
||||
get() = when (this) {
|
||||
is OnlineMessageSource -> bot
|
||||
is OfflineMessageSource -> Bot.getInstance(botId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 [MessageSource.botId] 从 [Bot.getInstanceOrNull] 获取 [Bot]
|
||||
*/
|
||||
public inline val MessageSource.botOrNull: Bot?
|
||||
get() = when (this) {
|
||||
is OnlineMessageSource -> bot
|
||||
is OfflineMessageSource -> Bot.getInstanceOrNull(botId)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 在线消息的 [MessageSource].
|
||||
* 拥有对象化的 [sender], [target], 也可以直接 [recallMessage] 和 [quote]
|
||||
*
|
||||
* ### 来源
|
||||
* - 当 bot 主动发送消息时, 产生 (由协议模块主动构造) [OnlineMessageSource.Outgoing]
|
||||
* - 当 bot 接收消息时, 产生 (由协议模块根据服务器的提供的信息构造) [OnlineMessageSource.Incoming]
|
||||
*
|
||||
* #### 机器人主动发送消息
|
||||
* 当机器人 [主动发出消息][Member.sendMessage], 将会得到一个 [消息回执][MessageReceipt].
|
||||
* 此回执的 [消息源][MessageReceipt.source] 即为一个 [外向消息源][OnlineMessageSource.Outgoing], 代表着刚刚发出的那条消息的来源.
|
||||
*
|
||||
* #### 机器人接受消息
|
||||
* 当机器人接收一条消息 [MessageEvent], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源.
|
||||
*
|
||||
*
|
||||
* ### 实现
|
||||
* 此类的所有子类都有协议模块实现. 不要自行实现它们, 否则将无法发送
|
||||
*
|
||||
* @see OnlineMessageSource.toOffline 转为 [OfflineMessageSource]
|
||||
*/
|
||||
public sealed class OnlineMessageSource : MessageSource() { // TODO: 2021/1/10 Extract to separate file in Kotlin 1.5
|
||||
public companion object Key : AbstractMessageKey<OnlineMessageSource>({ it.safeCast() })
|
||||
|
||||
/**
|
||||
* @see botId
|
||||
*/
|
||||
public abstract val bot: Bot
|
||||
final override val botId: Long get() = bot.id
|
||||
|
||||
/**
|
||||
* 消息发送人. 可能为 [机器人][Bot] 或 [好友][Friend] 或 [群员][Member].
|
||||
* 即类型必定为 [Bot], [Friend] 或 [Member]
|
||||
*/
|
||||
public abstract val sender: ContactOrBot
|
||||
|
||||
/**
|
||||
* 消息发送目标. 可能为 [机器人][Bot] 或 [好友][Friend] 或 [群][Group].
|
||||
* 即类型必定为 [Bot], [Friend] 或 [Group]
|
||||
*/
|
||||
public abstract val target: ContactOrBot
|
||||
|
||||
/**
|
||||
* 消息主体. 群消息时为 [Group]. 好友消息时为 [Friend], 临时消息为 [Member]
|
||||
* 不论是机器人接收的消息还是发送的消息, 此属性都指向机器人能进行回复的目标.
|
||||
*/
|
||||
public abstract val subject: Contact
|
||||
|
||||
/*
|
||||
* 以下子类型仅是覆盖了 [target], [subject], [sender] 等的类型
|
||||
*/
|
||||
|
||||
/**
|
||||
* 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource], 可通过 [MessageReceipt] 获得.
|
||||
*/
|
||||
public sealed class Outgoing : OnlineMessageSource() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<OnlineMessageSource, Outgoing>(OnlineMessageSource, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Bot
|
||||
public abstract override val target: Contact
|
||||
|
||||
public final override val fromId: Long get() = sender.id
|
||||
public final override val targetId: Long get() = target.id
|
||||
|
||||
public abstract class ToFriend @MiraiInternalApi constructor() : Outgoing() {
|
||||
public companion object Key : AbstractPolymorphicMessageKey<Outgoing, ToFriend>(Outgoing, { it.safeCast() })
|
||||
|
||||
public abstract override val target: Friend
|
||||
public final override val subject: Friend get() = target
|
||||
// final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.ids})"
|
||||
}
|
||||
|
||||
public abstract class ToStranger @MiraiInternalApi constructor() : Outgoing() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Outgoing, ToStranger>(Outgoing, { it.safeCast() })
|
||||
|
||||
public abstract override val target: Stranger
|
||||
public final override val subject: Stranger get() = target
|
||||
// final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.ids})"
|
||||
}
|
||||
|
||||
public abstract class ToTemp @MiraiInternalApi constructor() : Outgoing() {
|
||||
public companion object Key : AbstractPolymorphicMessageKey<Outgoing, ToTemp>(Outgoing, { it.safeCast() })
|
||||
|
||||
public abstract override val target: Member
|
||||
public val group: Group get() = target.group
|
||||
public final override val subject: Member get() = target
|
||||
}
|
||||
|
||||
public abstract class ToGroup @MiraiInternalApi constructor() : Outgoing() {
|
||||
public companion object Key : AbstractPolymorphicMessageKey<Outgoing, ToGroup>(Outgoing, { it.safeCast() })
|
||||
|
||||
public abstract override val target: Group
|
||||
public final override val subject: Group get() = target
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收到的一条消息的 [MessageSource]
|
||||
*/
|
||||
public sealed class Incoming : OnlineMessageSource() {
|
||||
public abstract override val sender: User
|
||||
|
||||
public final override val fromId: Long get() = sender.id
|
||||
public final override val targetId: Long get() = target.id
|
||||
|
||||
public abstract class FromFriend @MiraiInternalApi constructor() : Incoming() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Incoming, FromFriend>(Incoming, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Friend
|
||||
public final override val subject: Friend get() = sender
|
||||
public final override val target: Bot get() = sender.bot
|
||||
// final override fun toString(): String = "OnlineMessageSource.FromFriend(from=${sender.ids})"
|
||||
}
|
||||
|
||||
public abstract class FromTemp @MiraiInternalApi constructor() : Incoming() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Incoming, FromTemp>(Incoming, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Member
|
||||
public inline val group: Group get() = sender.group
|
||||
public final override val subject: Member get() = sender
|
||||
public final override val target: Bot get() = sender.bot
|
||||
}
|
||||
|
||||
public abstract class FromStranger @MiraiInternalApi constructor() : Incoming() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Incoming, FromStranger>(Incoming, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Stranger
|
||||
public final override val subject: Stranger get() = sender
|
||||
public final override val target: Bot get() = sender.bot
|
||||
}
|
||||
|
||||
public abstract class FromGroup @MiraiInternalApi constructor() : Incoming() {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Incoming, FromGroup>(Incoming, { it.safeCast() })
|
||||
|
||||
public abstract override val sender: Member
|
||||
public final override val subject: Group get() = sender.group
|
||||
public final override val target: Group get() = group
|
||||
public inline val group: Group get() = sender.group
|
||||
}
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<OnlineMessageSource, FromTemp>(OnlineMessageSource, { it.safeCast() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 由一条消息中的 [QuoteReply] 得到的 [MessageSource].
|
||||
* 此消息源可能来自一条与机器人无关的消息. 因此无法提供对象化的 `sender` 或 `target` 获取.
|
||||
*
|
||||
* @see buildMessageSource 构建一个 [OfflineMessageSource]
|
||||
* @see IMirai.constructMessageSource
|
||||
* @see OnlineMessageSource.toOffline
|
||||
*/
|
||||
public abstract class OfflineMessageSource : MessageSource() { // TODO: 2021/1/10 Extract to separate file in Kotlin 1.5
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<MessageSource, OfflineMessageSource>(MessageSource, { it.safeCast() })
|
||||
|
||||
/**
|
||||
* 消息种类
|
||||
*/
|
||||
public abstract val kind: MessageSourceKind
|
||||
}
|
160
mirai-core-api/src/commonMain/kotlin/message/data/PokeMessage.kt
Normal file
160
mirai-core-api/src/commonMain/kotlin/message/data/PokeMessage.kt
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.code.CodableMessage
|
||||
import net.mamoe.mirai.message.code.internal.appendStringAsMiraiCode
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.castOrNull
|
||||
|
||||
/**
|
||||
* 戳一戳. 可以发送给好友或群.
|
||||
*
|
||||
* ## mirai 码支持
|
||||
* 格式: [mirai:poke:*[name]*,*[pokeType]*,*[id]*]
|
||||
*
|
||||
* @see PokeMessage.Companion 使用伴生对象中的常量
|
||||
*/
|
||||
@SerialName(PokeMessage.SERIAL_NAME)
|
||||
@Serializable
|
||||
public data class PokeMessage @MiraiInternalApi constructor(
|
||||
/**
|
||||
* 仅 mirai, 显示的名称
|
||||
*/
|
||||
public val name: String,
|
||||
|
||||
public val pokeType: Int, // 'type' is used by serialization
|
||||
public val id: Int
|
||||
) : HummerMessage, CodableMessage {
|
||||
override val key: MessageKey<HummerMessage> get() = Key
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
builder.append("[mirai:poke:").appendStringAsMiraiCode(name)
|
||||
.append(',').append(pokeType).append(',').append(id)
|
||||
.append(']')
|
||||
}
|
||||
|
||||
override fun toString(): String = "[mirai:poke:$name,$pokeType,$id]"
|
||||
override fun contentToString(): String = "[戳一戳]"
|
||||
//businessType=0x00000001(1)
|
||||
//pbElem=08 01 18 00 20 FF FF FF FF 0F 2A 00 32 00 38 00 50 00
|
||||
//serviceType=0x00000002(2)
|
||||
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, PokeMessage>(HummerMessage, { it.castOrNull() }) {
|
||||
|
||||
public const val SERIAL_NAME: String = "PokeMessage"
|
||||
|
||||
/** 戳一戳 */
|
||||
@JvmField
|
||||
public val ChuoYiChuo: PokeMessage = PokeMessage("戳一戳", 1, -1)
|
||||
|
||||
/** 戳一戳 */
|
||||
@JvmField
|
||||
@Deprecated("Use ChuoYiChuo", replaceWith = ReplaceWith("ChuoYiChuo"))
|
||||
public val Poke: PokeMessage = ChuoYiChuo
|
||||
|
||||
/** 比心 */
|
||||
@JvmField
|
||||
public val BiXin: PokeMessage = PokeMessage("比心", 2, -1)
|
||||
|
||||
/** 比心 */
|
||||
@JvmField
|
||||
@Deprecated("Use BiXin", replaceWith = ReplaceWith("BiXin"))
|
||||
public val ShowLove: PokeMessage = BiXin
|
||||
|
||||
/** 点赞 */
|
||||
@JvmField
|
||||
public val DianZan: PokeMessage = PokeMessage("点赞", 3, -1)
|
||||
|
||||
/** 点赞 */
|
||||
@JvmField
|
||||
@Deprecated("Use DianZan", replaceWith = ReplaceWith("DianZan"))
|
||||
public val Like: PokeMessage = DianZan
|
||||
|
||||
/** 心碎 */
|
||||
@JvmField
|
||||
public val XinSui: PokeMessage = PokeMessage("心碎", 4, -1)
|
||||
|
||||
/** 心碎 */
|
||||
@JvmField
|
||||
@Deprecated("Use XinSui", replaceWith = ReplaceWith("XinSui"))
|
||||
public val Heartbroken: PokeMessage = XinSui
|
||||
|
||||
/** 666 */
|
||||
@JvmField
|
||||
public val LiuLiuLiu: PokeMessage = PokeMessage("666", 5, -1)
|
||||
|
||||
/** 666 */
|
||||
@JvmField
|
||||
@Deprecated("Use LiuLiuLiu", replaceWith = ReplaceWith("LiuLiuLiu"))
|
||||
public val SixSixSix: PokeMessage = LiuLiuLiu
|
||||
|
||||
/** 放大招 */
|
||||
@JvmField
|
||||
public val FangDaZhao: PokeMessage = PokeMessage("放大招", 6, -1)
|
||||
|
||||
/** 宝贝球 (SVIP) */
|
||||
@JvmField
|
||||
public val BaoBeiQiu: PokeMessage = PokeMessage("宝贝球", 126, 2011)
|
||||
|
||||
/** 玫瑰花 (SVIP) */
|
||||
@JvmField
|
||||
public val Rose: PokeMessage = PokeMessage("玫瑰花", 126, 2007)
|
||||
|
||||
/** 召唤术 (SVIP) */
|
||||
@JvmField
|
||||
public val ZhaoHuanShu: PokeMessage = PokeMessage("召唤术", 126, 2006)
|
||||
|
||||
/** 让你皮 (SVIP) */
|
||||
@JvmField
|
||||
public val RangNiPi: PokeMessage = PokeMessage("让你皮", 126, 2009)
|
||||
|
||||
/** 结印 (SVIP) */
|
||||
@JvmField
|
||||
public val JieYin: PokeMessage = PokeMessage("结印", 126, 2005)
|
||||
|
||||
/** 手雷 (SVIP) */
|
||||
@JvmField
|
||||
public val ShouLei: PokeMessage = PokeMessage("手雷", 126, 2004)
|
||||
|
||||
/** 勾引 */
|
||||
@JvmField
|
||||
public val GouYin: PokeMessage = PokeMessage("勾引", 126, 2003)
|
||||
|
||||
/** 抓一下 (SVIP) */
|
||||
@JvmField
|
||||
public val ZhuaYiXia: PokeMessage = PokeMessage("抓一下", 126, 2001)
|
||||
|
||||
/** 碎屏 (SVIP) */
|
||||
@JvmField
|
||||
public val SuiPing: PokeMessage = PokeMessage("碎屏", 126, 2002)
|
||||
|
||||
/** 敲门 (SVIP) */
|
||||
@JvmField
|
||||
public val QiaoMen: PokeMessage = PokeMessage("敲门", 126, 2002)
|
||||
|
||||
|
||||
/**
|
||||
* 所有类型数组
|
||||
*/
|
||||
@JvmField
|
||||
public val values: Array<PokeMessage> = arrayOf(
|
||||
ChuoYiChuo, BiXin, DianZan, XinSui, LiuLiuLiu,
|
||||
FangDaZhao, BaoBeiQiu, Rose, ZhaoHuanShu, RangNiPi,
|
||||
JieYin, ShouLei, GouYin, ZhuaYiXia, SuiPing
|
||||
)
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.recall
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
@ -25,17 +24,17 @@ import net.mamoe.mirai.utils.safeCast
|
||||
*
|
||||
* 支持引用任何一条消息发送给任何人.
|
||||
*
|
||||
* #### 元数据
|
||||
* ### 元数据
|
||||
* [QuoteReply] 被作为 [MessageMetadata], 因为它不包含实际的消息内容, 且只能在消息中单独存在.
|
||||
*
|
||||
* #### [source] 的类型:
|
||||
* ### [source] 的类型:
|
||||
* - 在发送引用回复时, [source] 类型为 [OnlineMessageSource] 或 [OfflineMessageSource]
|
||||
* - 在接收引用回复时, [source] 类型一定为 [OfflineMessageSource]
|
||||
*
|
||||
* #### 原消息内容
|
||||
* ### 原消息内容
|
||||
* 引用回复的原消息内容完全由 [source] 中 [MessageSource.originalMessage] 控制, 客户端不会自行寻找原消息.
|
||||
*
|
||||
* #### 客户端内跳转
|
||||
* ### 客户端内跳转
|
||||
* 客户端在跳转原消息时, 会通过 [MessageSource.ids] 等 metadata
|
||||
*
|
||||
* @see MessageSource 获取有关消息源的更多信息
|
||||
@ -58,13 +57,6 @@ public data class QuoteReply(
|
||||
public override fun hashCode(): Int = source.hashCode()
|
||||
}
|
||||
|
||||
/**
|
||||
* @see MessageSource.bot
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
public inline val QuoteReply.bot: Bot
|
||||
get() = source.bot
|
||||
|
||||
/**
|
||||
* 撤回引用的源消息
|
||||
*/
|
||||
|
110
mirai-core-api/src/commonMain/kotlin/message/data/VipFace.kt
Normal file
110
mirai-core-api/src/commonMain/kotlin/message/data/VipFace.kt
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.code.CodableMessage
|
||||
import net.mamoe.mirai.message.data.VipFace.Kind
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
/**
|
||||
* VIP 表情.
|
||||
*
|
||||
* 不支持发送.
|
||||
*
|
||||
* ## mirai 码支持
|
||||
* 格式: [mirai:vipface:*[Kind.id]*,*[Kind.name]*,*[count]*]
|
||||
*
|
||||
* @see VipFace.Key 使用伴生对象中的常量
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName(VipFace.SERIAL_NAME)
|
||||
public data class VipFace @MiraiInternalApi constructor(
|
||||
/**
|
||||
* 使用 [Companion] 中常量.
|
||||
*/
|
||||
public val kind: Kind,
|
||||
public val count: Int
|
||||
) : HummerMessage, CodableMessage {
|
||||
override val key: MessageKey<VipFace> get() = Key
|
||||
|
||||
@MiraiExperimentalApi
|
||||
override fun appendMiraiCodeTo(builder: StringBuilder) {
|
||||
builder.append("[mirai:vipface:").append(kind).append(',').append(count).append(']')
|
||||
}
|
||||
|
||||
override fun toString(): String = "[mirai:vipface:$kind,$count]"
|
||||
override fun contentToString(): String = "[${kind.name}]x$count"
|
||||
|
||||
@Serializable
|
||||
public data class Kind(
|
||||
val id: Int,
|
||||
val name: String
|
||||
) {
|
||||
public override fun toString(): String {
|
||||
return "$id,$name"
|
||||
}
|
||||
}
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, VipFace>(HummerMessage, { it.safeCast() }) {
|
||||
|
||||
public const val SERIAL_NAME: String = "VipFace"
|
||||
|
||||
@JvmField
|
||||
public val LiuLian: Kind = 9 to "榴莲"
|
||||
|
||||
@JvmField
|
||||
public val PingDiGuo: Kind = 1 to "平底锅"
|
||||
|
||||
@JvmField
|
||||
public val ChaoPiao: Kind = 12 to "钞票"
|
||||
|
||||
@JvmField
|
||||
public val LueLueLue: Kind = 10 to "略略略"
|
||||
|
||||
@JvmField
|
||||
public val ZhuTou: Kind = 4 to "猪头"
|
||||
|
||||
@JvmField
|
||||
public val BianBian: Kind = 6 to "便便"
|
||||
|
||||
@JvmField
|
||||
public val ZhaDan: Kind = 5 to "炸弹"
|
||||
|
||||
@JvmField
|
||||
public val AiXin: Kind = 2 to "爱心"
|
||||
|
||||
@JvmField
|
||||
public val HaHa: Kind = 3 to "哈哈"
|
||||
|
||||
@JvmField
|
||||
public val DianZan: Kind = 1 to "点赞"
|
||||
|
||||
@JvmField
|
||||
public val QinQin: Kind = 7 to "亲亲"
|
||||
|
||||
@JvmField
|
||||
public val YaoWan: Kind = 8 to "药丸"
|
||||
|
||||
@JvmField
|
||||
public val values: Array<Kind> = arrayOf(
|
||||
LiuLian, PingDiGuo, ChaoPiao, LueLueLue, ZhuTou,
|
||||
BianBian, ZhaDan, AiXin, HaHa, DianZan, QinQin, YaoWan
|
||||
)
|
||||
|
||||
private inline infix fun Int.to(name: String): Kind = Kind(this, name)
|
||||
}
|
||||
}
|
@ -51,8 +51,8 @@ internal fun Message.contentEqualsStrictImpl(another: Message, ignoreCase: Boole
|
||||
/**
|
||||
* 逐个判断非 [PlainText] 的 [Message] 是否 [equals]
|
||||
*/
|
||||
this.forEachContent { thisElement ->
|
||||
if (thisElement is PlainText) return@forEachContent
|
||||
this.contentsSequence().forEach { thisElement ->
|
||||
if (thisElement is PlainText) return@forEach
|
||||
for (it in anotherIterator) {
|
||||
if (it is PlainText || it !is MessageContent) continue
|
||||
if (thisElement != it) return false
|
||||
@ -158,6 +158,14 @@ internal fun <M : SingleMessage> MessageChain.getImpl(key: MessageKey<M>): M? {
|
||||
return this.asSequence().mapNotNull { key.safeCast.invoke(it) }.firstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [EmptyMessageChain] if [delegate] is empty, otherwise [MessageChainImpl]
|
||||
*/
|
||||
internal fun createMessageChainImplOptimized(delegate: List<SingleMessage>): MessageChain {
|
||||
return if (delegate.isEmpty()) EmptyMessageChain
|
||||
else MessageChainImpl(delegate)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 [Collection] 作为委托的 [MessageChain]
|
||||
*/
|
||||
@ -182,7 +190,7 @@ internal data class MessageChainImpl constructor(
|
||||
@Suppress("FunctionName") // source compatibility with 1.x
|
||||
internal fun MessageChainImplBySequence(
|
||||
delegate: Sequence<SingleMessage> // 可以有重复 ConstrainSingle
|
||||
): MessageChain = MessageChainImpl(delegate.constrainSingleMessages())
|
||||
): MessageChain = createMessageChainImplOptimized(delegate.constrainSingleMessages())
|
||||
|
||||
|
||||
//////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user