消息元素与消息链
在 simbot 中, 消息是一个重要的类型之一。
消息 Message
Message
即代表一个消息, 它可能是 Message.Element
或 Messages
。
消息元素 Message.Element
是一个消息链的最小单元, 代表了一个有具体含义的消息。
标准消息元素 StandardMessage
核心库中定义了一些常见的、泛用型较高的消息类型, 它们都继承自 StandardMessage
接口。 这其中的类型有一些可能会在部分场景下被特殊处理, 例如 PlainText
。
- PlainText
接口类型, 纯文本消息。代表一段只存在文本的消息。 一般不需要自己实现, 可以使用类型
Text
。- text
纯文本消息的文本内容。
- Text
PlainText
的标准实现类型。val text = "text".toText() val emptyText = Text() val text1 = Text { "text" }val text = Text.of("text");- MentionMessage
代表一个描述“提及”的消息。常见表现形式即为
At
。- At
一个 “艾特(
@
)” 消息。- target
被提及的目标的ID
- type
提及的形式, 如果有特殊形式, 则由解析它的实现者来解释含义, 例如QQ频道中除了提及用户, 还能提及子频道, 那么它就会解析
type="channel"
的类型。 默认为user
。
val at = At(1.ID)var at = At.of(Identifies.of(1));- AtAll
一个“提及所有”的消息。是
object
单例类型。val atAll = AtAllvar atAll = AtAll.INSTANCE;
- Image
接口类型, 一个代表图片消息元素类型。 图片消息可能被分为 离线图片
OfflineImage
和 远端图片RemoteImage
。- OfflineImage
接口类型, 实现
Image
, 代表一个离线(本地)图片消息元素类型。通常是用来发送的消息类型。OfflineImage
的实现类不保证可以序列化。- 基于 ByteArray
一个基于
ByteArray
的OfflineImage
, 是多平台实现。bytes: ByteArray = ... val image = bytes.toOfflineImage();byte[] bytes = ...; var image = OfflineImage.ofBytes(bytes);- 基于 Resource
一个基于
Resource
的OfflineImage
, 是多平台实现。 在 JVM 中, 可以通过Resources
提供的工厂函数来基于不同的源构建Resource
, 例如使用File
或Path
。resource: Resource = ... val image = resource.toOfflineImage();Resource resource = ...; var image = OfflineImage.ofResource(resource);当通过
Resource
构建OfflineImage
时, 根据Resource
的类型可能会得到不同的结果。 例如在 JVM 平台下, 如果提供了一个PathResource
, 则会实际得到一个直接基于Path
的OfflineImage
实现。
- RemoteImage
接口类型, 一个远程图片消息元素类型。
RemoteImage
一般出现在接收到的消息事件里, 由提供事件的实现者(例如某个组件下的插件)进行实现。 一般情况下不需要普通开发者实现或直接构建RemoteImage
。- RemoteUrlAwareImage
接口类型, 继承
RemoteImage
, 代表这个远程图片可以获得其链接。
- EmoticonMessage
接口类型, 表示某种表情符号的消息元素类型。
- Emoji
一个
emoji
表情。Emoji
主要服务于那些只能提供指定范围内emoji
表情的场景, 例如针对某个消息的reaction
。- Face
一个表情。一般代表平台提供的自带系统表情。
- MessageReference
一个用于表示“消息引用”的元素。 默认只有一个抽象属性
id
来表示引用目标的消息ID。- MessageIdReference
一个仅实现
id
的MessageReference
简单实现。
扩展消息元素
除了标准的消息元素实现以外, 不同的组件、插件, 都有可能会提供更多的元素扩展, 比如QQ频道组件中提供与 Ark
消息相关的元素实现。 这些额外的消息元素实现的序列化信息会被注册在 Component.serializersModule
中, 并在安装时被一并加载到 Application
里。
消息链 Messages
一个 消息链。 消息链 Messages
本质上就是一组 Message.Element
集合。 消息链是不可变的 。它通过 plus
与其他消息元素或消息链重新组合为新的消息链。
创建 Messages
序列化
simbot 中所有的序列化相关实现均基于 Kotlinx serialization
, Messages
也不例外。 Messages
会被作为一个 List<Message.Element>
基于多态进行序列化, 因此当需要进行序列化的时候, 请确保消息链中的所有消息元素均支持序列化。
你可以:
通过
Messages.standardSerializersModule
得到所有标准消息元素的多态序列化信息。通过
Application.components.serializersModule
得到已经注册的所有组件的序列化信息的聚合产物, 其中理应包含由组件定义的额外扩展的信息。通过
Messages.serializer
得到针对Messages
的序列化器。
以 Json 序列化为例:
事件消息内容 MessageContent
MessageContent
是从 MessageEvent
事件中接收到的消息内容类型。
你可以在其中得到一些与消息相关的信息。
- id
这个消息的ID。如果没有什么可以作为ID的, 那么可能是一个随机ID。
- messages
Messages
类型, 此事件中解析出来的消息链。- plainText
从接收到的消息中提取出的纯文本内容(一般来讲是
PlainText
类型的消息元素)拼接在一起的结果。- delete(...)
删除这个消息。“删除”也可以理解为撤回, 如果平台某种类型的消息内容不支持被删除, 则可能会抛出
UnsupportedOperationException
。- reference()
尝试获取一个消息引用
MessageReference
。reference()
在明确不支持或直接通过messages
寻找获取时, 不会发生挂起。否则当需要通过网络查询结果时会产生挂起。reference
所得结果不一定是messages
中的元素。 如上所述,如果需要通过网络查询才能得到结果,则reference()
的结果不会包含在messages
中。如果实现者尚未针对性地实现此API,则默认逻辑为: 从
messages
中寻找第一个类型为MessageReference
的元素。如果实现者的所属平台不存在、不支持 消息引用 的概念,则可能始终得到
null
。如果实现者的所属平台有明确的 消息引用 概念,但是无法通过
MessageReference
这个类型进行表述, 则使用reference
时应当抛出信息明确的UnsupportedOperationException
异常。
- referenceMessage()
根据 消息引用 (或具体实现内部的某种真实引用) 查询此引用的源消息。
如果实现者尚未实现此功能,或
reference
返回null
, 则referenceMessage
的结果为null
。如果实现的对应平台明确存在引用的概念、但由于各种原因无法查询引用源消息时,
referenceMessage
将会抛出UnsupportedOperationException
。否则,将根据具体地引用信息查询并得到其对应地
MessageContent
。 与reference
不同,referenceMessage
大概率会产生网络请求和挂起行为, 但具体行为还是以具体实现为准。
消息的发送
消息的发送功能主要定义在 SendSupport
和 ReplySupport
这两个接口中, 命名为 send
或 reply
。 它们含义不同, 主要面向实现的目标也不同, 但是核心的功能是相同的:发送消息。
ReplySupport
已经在 事件-消息事件 中出现过了, 那么这里便使用 SendSupport
做介绍。
send
支持使用字符串文本、 Messages
和 MessageContent
这三个类型作为发的消息内容。
发送回执 MessageReceipt
当一个消息发送成功没有出错时, 便会返回一个回执 MessageReceipt
。 MessageReceipt
继承了 DeleteSupport
, 因此也可以使用 delete(...)
来进行“删除”行为, 同样的, 如果平台的实现不支持, 也可能会抛出 UnsupportedOperationException
。
标准回执类型 StandardMessageReceipt
MessageReceipt
有一个特殊类型实现: StandardMessageReceipt
, 它定义了两个子类型:
SingleMessageReceipt
AggregatedMessageReceipt
顾名思义, 它们分别代表为一个独立回执和一个聚合回执。其中, 聚合回执是对多个独立回执的聚合。
为什么要分这两种类型?因为在平台实现中, 你使用一次 send
或 reply
发送消息, 不一定真的只发送了一条消息。
举个例子, 你创建了一个包含3张图片消息的消息链并发送, 但是平台的底层API只支持一次发一个图片, 那么这时候组件的实现中就会发送三条消息, 并得到三个实际上的发送结果。而将这三个结果“聚合”为一个结果返回给调用处, 便是 StandardMessageReceipt
中这两个类型的主要作用。