mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-02 04:30:25 +08:00
Add docs, rearrange implementations
This commit is contained in:
parent
716e3ade48
commit
09524da1e9
@ -24,10 +24,7 @@ import kotlinx.serialization.UnstableDefault
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.json.int
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.BotImpl
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.ThisApiMustBeUsedInWithConnectionLockBlock
|
||||
import net.mamoe.mirai.*
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.*
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
@ -82,10 +79,18 @@ internal class QQAndroidBot constructor(
|
||||
|
||||
@OptIn(LowLevelAPI::class)
|
||||
override suspend fun acceptNewFriendRequest(event: NewFriendRequestEvent) {
|
||||
check(event.bot === this) {
|
||||
"the request $event is from Bot ${event.bot.id} but you are responding with bot ${this.id}"
|
||||
}
|
||||
|
||||
check(event.responded.compareAndSet(false, true)) {
|
||||
"the request $this has already been responded"
|
||||
}
|
||||
|
||||
check(!friends.contains(event.fromId)) {
|
||||
"the request $event is outdated: You had already responded it on another device."
|
||||
}
|
||||
|
||||
network.run {
|
||||
NewContact.SystemMsgNewFriend.Action(
|
||||
bot.client,
|
||||
@ -100,8 +105,16 @@ internal class QQAndroidBot constructor(
|
||||
}
|
||||
|
||||
override suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean) {
|
||||
check(event.bot === this) {
|
||||
"the request $event is from Bot ${event.bot.id} but you are responding with bot ${this.id}"
|
||||
}
|
||||
|
||||
check(event.responded.compareAndSet(false, true)) {
|
||||
"the request $this has already been responded"
|
||||
"the request $event has already been responded"
|
||||
}
|
||||
|
||||
check(!friends.contains(event.fromId)) {
|
||||
"the request $event is outdated: You had already responded it on another device."
|
||||
}
|
||||
|
||||
network.run {
|
||||
@ -116,10 +129,16 @@ internal class QQAndroidBot constructor(
|
||||
|
||||
@OptIn(LowLevelAPI::class)
|
||||
override suspend fun acceptMemberJoinRequest(event: MemberJoinRequestEvent) {
|
||||
@Suppress("DuplicatedCode")
|
||||
checkGroupPermission(event.bot, event.group) { event::class.simpleName ?: "<anonymous class>" }
|
||||
check(event.responded.compareAndSet(false, true)) {
|
||||
"the request $this has already been responded"
|
||||
}
|
||||
|
||||
check(!event.group.members.contains(event.fromId)) {
|
||||
"the request $this is outdated: Another operator has already responded it."
|
||||
}
|
||||
|
||||
network.run {
|
||||
NewContact.SystemMsgNewGroup.Action(
|
||||
bot.client,
|
||||
@ -137,11 +156,17 @@ internal class QQAndroidBot constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
@OptIn(LowLevelAPI::class)
|
||||
override suspend fun rejectMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) {
|
||||
checkGroupPermission(event.bot, event.group) { event::class.simpleName ?: "<anonymous class>" }
|
||||
check(event.responded.compareAndSet(false, true)) {
|
||||
"the request $this has already been responded"
|
||||
}
|
||||
|
||||
check(!event.group.members.contains(event.fromId)) {
|
||||
"the request $this is outdated: Another operator has already responded it."
|
||||
}
|
||||
network.run {
|
||||
NewContact.SystemMsgNewGroup.Action(
|
||||
bot.client,
|
||||
@ -152,7 +177,22 @@ internal class QQAndroidBot constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun checkGroupPermission(eventBot: Bot, eventGroup: Group, eventName: () -> String) {
|
||||
val group = this.getGroupOrNull(eventGroup.id)
|
||||
?: kotlin.run {
|
||||
if (this == eventBot) {
|
||||
error("A ${eventName()} is outdated. Group ${eventGroup.id} not found for bot ${this.id}. " +
|
||||
"This is because bot isn't in the group anymore")
|
||||
} else {
|
||||
error("A ${eventName()} is from bot ${eventBot.id}, but you are trying to respond it using bot ${this.id} who isn't a member of the group ${eventGroup.id}")
|
||||
}
|
||||
}
|
||||
|
||||
group.checkBotPermissionOperator()
|
||||
}
|
||||
|
||||
override suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) {
|
||||
checkGroupPermission(event.bot, event.group) { event::class.simpleName ?: "<anonymous class>" }
|
||||
check(event.responded.compareAndSet(false, true)) {
|
||||
"the request $this has already been responded"
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ import kotlin.jvm.JvmSynthetic
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
open class MessageReceipt<out C : Contact>(
|
||||
/**
|
||||
* 指代发送出去的消息
|
||||
* 指代发送出去的消息.
|
||||
*/
|
||||
val source: OnlineMessageSource.Outgoing,
|
||||
/**
|
||||
|
@ -12,11 +12,9 @@
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
|
||||
private const val displayA = "@全体成员"
|
||||
|
||||
@ -50,15 +48,6 @@ object AtAll :
|
||||
}
|
||||
|
||||
// 自动为消息补充 " "
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE")
|
||||
@JvmName("followedBy")
|
||||
@JvmSynthetic
|
||||
override fun followedBy1(tail: Message): CombinedMessage {
|
||||
return followedByInternalForBinaryCompatibility(tail)
|
||||
}
|
||||
|
||||
override fun followedBy(tail: Message): MessageChain {
|
||||
if (tail is PlainText && tail.stringValue.startsWith(' ')) {
|
||||
return super.followedBy(tail)
|
||||
|
@ -8,6 +8,8 @@
|
||||
*/
|
||||
|
||||
@file:Suppress("unused", "NOTHING_TO_INLINE")
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils") // since 0.39.1
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
@ -15,10 +17,7 @@ import net.mamoe.mirai.message.data.PokeMessage.Types
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
import kotlin.jvm.JvmField
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmStatic
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
import kotlin.jvm.*
|
||||
|
||||
/**
|
||||
* 一些特殊的消息
|
||||
|
@ -19,7 +19,6 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.BotImpl
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
@ -38,7 +37,7 @@ import kotlin.jvm.JvmSynthetic
|
||||
*
|
||||
* ### [toString] 和 [contentToString]
|
||||
* - [toString] 固定返回 `[mirai:image:<ID>]` 格式字符串, 其中 `<ID>` 代表 [imageId].
|
||||
* - [contentToString] 固定返回 `"[图片]"`
|
||||
* - [contentToString] 固定返回 ```"[图片]"```
|
||||
*
|
||||
* ### 上传和发送图片
|
||||
* @see Contact.uploadImage 上传 [图片文件][ExternalImage] 并得到 [Image] 消息
|
||||
|
@ -8,6 +8,8 @@
|
||||
*/
|
||||
|
||||
@file:Suppress("MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE", "NOTHING_TO_INLINE")
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
@ -17,6 +19,7 @@ import net.mamoe.mirai.message.data.Message.Key
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
|
||||
@ -313,6 +316,8 @@ interface SingleMessage : Message, CharSequence, Comparable<String> {
|
||||
/**
|
||||
* 消息元数据, 即不含内容的元素.
|
||||
*
|
||||
* 所有子类的 [contentToString] 都应该返回空字符串.
|
||||
*
|
||||
* @see MessageSource 消息源
|
||||
* @see QuoteReply 引用回复
|
||||
* @see CustomMessageMetadata 自定义元数据
|
||||
@ -328,11 +333,12 @@ interface MessageMetadata : SingleMessage {
|
||||
|
||||
/**
|
||||
* 约束一个 [MessageChain] 中只存在这一种类型的元素. 新元素将会替换旧元素, 保持原顺序.
|
||||
*
|
||||
* 实现此接口的元素将会在连接时自动处理替换.
|
||||
*/
|
||||
@SinceMirai("0.34.0")
|
||||
interface ConstrainSingle<out M : Message> : MessageMetadata {
|
||||
val key: Message.Key<M>
|
||||
val key: Key<M>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,9 +142,7 @@ 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
|
||||
}
|
||||
if (!block(it)) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
@ -158,9 +156,7 @@ 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
|
||||
}
|
||||
if (block(it)) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@ -29,6 +29,25 @@ import kotlin.jvm.JvmSynthetic
|
||||
/**
|
||||
* 消息源, 它存在于 [MessageChain] 中, 用于表示这个消息的来源.
|
||||
*
|
||||
*
|
||||
* ### 组成
|
||||
* MessageSource 由 metadata (元数据), form & target, content 组成
|
||||
*
|
||||
* #### metadata
|
||||
* - [id] 消息 id (序列号)
|
||||
* - [internalId] 消息内部 id
|
||||
* - [time] 时间
|
||||
*
|
||||
* 官方客户端通过 metadata 这三个数据定位消息, 撤回和引用回复都是如此.
|
||||
*
|
||||
* #### form & target
|
||||
* - [fromId] 消息发送人
|
||||
* - [targetId] 消息发送目标
|
||||
*
|
||||
* #### content
|
||||
* - [originalMessage] 消息内容
|
||||
*
|
||||
*
|
||||
* 消息源可用于 [引用回复][QuoteReply] 或 [撤回][Bot.recall].
|
||||
*
|
||||
* @see Bot.recall 撤回一条消息
|
||||
@ -52,50 +71,74 @@ sealed class MessageSource : Message, MessageMetadata, ConstrainSingle<MessageSo
|
||||
abstract val bot: Bot
|
||||
|
||||
/**
|
||||
* 消息 id.
|
||||
* 此值在同一会话中唯一且有顺序.
|
||||
* 消息 id (序列号).
|
||||
**
|
||||
* #### 值域
|
||||
* 值的范围约为 [UShort] 的范围.
|
||||
*
|
||||
* #### 顺序
|
||||
* 群消息的 id 由服务器维护. 好友消息的 id 由 mirai 维护.
|
||||
*
|
||||
* - 在同一个群的消息中此值随每条消息递增 1.
|
||||
* - 在好友消息中无法保证每次都递增 1. 也可能会产生大幅跳过的情况.
|
||||
*/
|
||||
abstract val id: Int
|
||||
|
||||
/**
|
||||
* 内部 id, 仅用于 [Bot.constructMessageSource]
|
||||
* 值没有顺序, 也可能为 0, 取决于服务器是否提供.
|
||||
* 在事件中和在引用中无法保证同一条消息的 [internalId] 相同.
|
||||
* 内部 id. 仅用于协议模块使用.
|
||||
*
|
||||
* 仅用于协议实现.
|
||||
* 在撤回消息和引用回复时均需使用此 id.
|
||||
*
|
||||
* 值没有顺序, 也可能为 0, 取决于服务器是否提供.
|
||||
*
|
||||
* 在事件中和在引用中无法保证同一条消息的 [internalId] 相同.
|
||||
*/
|
||||
@SinceMirai("0.39.0")
|
||||
abstract val internalId: Int
|
||||
|
||||
/**
|
||||
* 发送时间时间戳, 单位为秒.
|
||||
* 撤回好友消息时需要
|
||||
*
|
||||
* 时间戳可能来自服务器, 也可能来自 mirai, 且无法保证两者时间同步.
|
||||
*
|
||||
* 撤回消息时需要此值.
|
||||
*/
|
||||
abstract val time: Int
|
||||
|
||||
/**
|
||||
* 发送人.
|
||||
* 当 [OnlineMessageSource.Outgoing] 时为 [机器人][Bot.id]
|
||||
* 当 [OnlineMessageSource.Incoming] 时为发信 [目标好友][QQ.id] 或 [群][Group.id]
|
||||
* 当 [OfflineMessageSource] 时为 [机器人][Bot.id], 发信 [目标好友][QQ.id] 或 [群][Group.id] (取决于 [OfflineMessageSource.kind])
|
||||
*
|
||||
* - 当 [OnlineMessageSource.Outgoing] 时为 [机器人][Bot.id]
|
||||
* - 当 [OnlineMessageSource.Incoming] 时为发信 [目标好友][QQ.id] 或 [群][Group.id]
|
||||
* - 当 [OfflineMessageSource] 时为 [机器人][Bot.id], 发信 [目标好友][QQ.id] 或 [群][Group.id] (取决于 [OfflineMessageSource.kind])
|
||||
*/
|
||||
abstract val fromId: Long
|
||||
|
||||
/**
|
||||
* 发送目标.
|
||||
* 当 [OnlineMessageSource.Outgoing] 时为发信 [目标好友][QQ.id] 或 [群][Group.id] 或 [临时消息][Member.id]
|
||||
* 当 [OnlineMessageSource.Incoming] 时为 [机器人][Bot.id]
|
||||
* 当 [OfflineMessageSource] 时为 [机器人][Bot.id], 发信 [目标好友][QQ.id] 或 [群][Group.id] 或 [临时消息][Member.id] (取决于 [OfflineMessageSource.kind])
|
||||
* 消息发送目标.
|
||||
*
|
||||
* - 当 [OnlineMessageSource.Outgoing] 时为发信 [目标好友][QQ.id] 或 [群][Group.id] 或 [临时消息][Member.id]
|
||||
* - 当 [OnlineMessageSource.Incoming] 时为 [机器人][Bot.id]
|
||||
* - 当 [OfflineMessageSource] 时为 [机器人][Bot.id], 发信 [目标好友][QQ.id] 或 [群][Group.id] 或 [临时消息][Member.id] (取决于 [OfflineMessageSource.kind])
|
||||
*/
|
||||
abstract val targetId: Long // groupCode / friendUin / memberUin
|
||||
|
||||
/**
|
||||
* 原消息内容.
|
||||
*
|
||||
* 此属性是 **lazy** 的: 它只会在第一次调用时初始化, 因为需要反序列化服务器发来的整个包, 相当于接收了一条新消息.
|
||||
*/
|
||||
@LazyProperty
|
||||
abstract val originalMessage: MessageChain
|
||||
|
||||
/**
|
||||
* 返回 `"[mirai:source:$id,$internalId]"`
|
||||
*/
|
||||
final override fun toString(): String = "[mirai:source:$id,$internalId]"
|
||||
|
||||
/**
|
||||
* 返回空字符串, 因 [MessageMetadata] 的约束.
|
||||
*/
|
||||
final override fun contentToString(): String = ""
|
||||
}
|
||||
|
||||
@ -105,7 +148,8 @@ sealed class MessageSource : Message, MessageMetadata, ConstrainSingle<MessageSo
|
||||
* 拥有对象化的 [sender], [target], 也可以直接 [recall] 和 [quote]
|
||||
*
|
||||
* ### 来源
|
||||
* **必定是一个发出去的消息或接收到的消息的 [MessageChain] 中的一个元数据 [MessageMetadata].**
|
||||
* - 当 bot 主动发送消息时, 产生 (由协议模块主动构造) [OnlineMessageSource.Outgoing]
|
||||
* - 当 bot 接收消息时, 产生 (由协议模块根据服务器的提供的信息构造) [OnlineMessageSource.Incoming]
|
||||
*
|
||||
* #### 机器人主动发送消息
|
||||
* 当机器人 [主动发出消息][Member.sendMessage], 将会得到一个 [消息回执][MessageReceipt].
|
||||
@ -115,6 +159,8 @@ sealed class MessageSource : Message, MessageMetadata, ConstrainSingle<MessageSo
|
||||
* 当机器人接收一条消息 [ContactMessage], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源.
|
||||
*
|
||||
*
|
||||
* ### 实现
|
||||
* 此类的所有子类都有协议模块实现. 不要自行实现它们, 否则将无法发送
|
||||
*
|
||||
* @see OnlineMessageSource.toOffline 转为 [OfflineMessageSource]
|
||||
*/
|
||||
@ -144,7 +190,7 @@ sealed class OnlineMessageSource : MessageSource() {
|
||||
abstract val subject: Contact
|
||||
|
||||
/**
|
||||
* 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource]
|
||||
* 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource], 可通过 [MessageReceipt] 获得.
|
||||
*/
|
||||
sealed class Outgoing : OnlineMessageSource() {
|
||||
companion object Key : Message.Key<Outgoing> {
|
||||
@ -266,6 +312,8 @@ sealed class OnlineMessageSource : MessageSource() {
|
||||
/**
|
||||
* 由一条消息中的 [QuoteReply] 得到的 [MessageSource].
|
||||
* 此消息源可能来自一条与机器人无关的消息. 因此无法提供对象化的 `sender` 或 `target` 获取.
|
||||
*
|
||||
* @see buildMessageSource 构建一个 [OfflineMessageSource]
|
||||
*/
|
||||
@SinceMirai("0.33.0")
|
||||
abstract class OfflineMessageSource : MessageSource() {
|
||||
@ -290,6 +338,9 @@ abstract class OfflineMessageSource : MessageSource() {
|
||||
// final override fun toString(): String = "OfflineMessageSource(sender=$senderId, target=$targetId)"
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是发送给群, 或从群接收的消息的消息源
|
||||
*/
|
||||
// inline for future removal
|
||||
inline fun MessageSource.isAboutGroup(): Boolean {
|
||||
return when (this) {
|
||||
@ -298,6 +349,9 @@ inline fun MessageSource.isAboutGroup(): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是发送给临时会话, 或从临时会话接收的消息的消息源
|
||||
*/
|
||||
inline fun MessageSource.isAboutTemp(): Boolean {
|
||||
return when (this) {
|
||||
is OnlineMessageSource -> subject is Member
|
||||
@ -305,6 +359,9 @@ inline fun MessageSource.isAboutTemp(): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是发送给好友, 或从好友接收的消息的消息源
|
||||
*/
|
||||
// inline for future removal
|
||||
inline fun MessageSource.isAboutFriend(): Boolean {
|
||||
return when (this) {
|
||||
@ -315,6 +372,7 @@ inline fun MessageSource.isAboutFriend(): Boolean {
|
||||
|
||||
/**
|
||||
* 引用这条消息
|
||||
* @see QuoteReply
|
||||
*/
|
||||
fun MessageSource.quote(): QuoteReply {
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
@ -323,17 +381,37 @@ fun MessageSource.quote(): QuoteReply {
|
||||
|
||||
/**
|
||||
* 引用这条消息
|
||||
* @see QuoteReply
|
||||
*/
|
||||
fun MessageChain.quote(): QuoteReply {
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
return QuoteReply(this.source as? OnlineMessageSource ?: error("only online messages can be quoted"))
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
|
||||
*
|
||||
* [Bot] 撤回自己的消息不需要权限.
|
||||
* [Bot] 撤回群员的消息需要管理员权限.
|
||||
*
|
||||
* @throws PermissionDeniedException 当 [Bot] 无权限操作时
|
||||
* @throws IllegalStateException 当这条消息已经被撤回时 (仅同步主动操作)
|
||||
*
|
||||
* @see Bot.recall
|
||||
*/
|
||||
@JvmSynthetic
|
||||
suspend inline fun MessageSource.recall() = bot.recall(this)
|
||||
|
||||
/**
|
||||
* 撤回这条消息
|
||||
* 在一段时间后撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
|
||||
*
|
||||
* [Bot] 撤回自己的消息不需要权限.
|
||||
* [Bot] 撤回群员的消息需要管理员权限.
|
||||
*
|
||||
* @throws PermissionDeniedException 当 [Bot] 无权限操作时
|
||||
* @throws IllegalStateException 当这条消息已经被撤回时 (仅同步主动操作)
|
||||
*
|
||||
* @see Bot.recall
|
||||
*/
|
||||
@JvmSynthetic
|
||||
inline fun MessageSource.recallIn(
|
||||
@ -345,7 +423,7 @@ inline fun MessageSource.recallIn(
|
||||
|
||||
/**
|
||||
* 消息 id.
|
||||
* 仅从服务器接收的消息才可以获取 id
|
||||
* 仅从服务器接收的消息才可以获取
|
||||
*
|
||||
* @see MessageSource.id
|
||||
*/
|
||||
@ -353,6 +431,36 @@ inline fun MessageSource.recallIn(
|
||||
inline val MessageChain.id: Int
|
||||
get() = this.source.id
|
||||
|
||||
/**
|
||||
* 消息内部 id.
|
||||
* 仅从服务器接收的消息才可以获取
|
||||
*
|
||||
* @see MessageSource.id
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val MessageChain.internalId: Int
|
||||
get() = this.source.internalId
|
||||
|
||||
/**
|
||||
* 消息时间.
|
||||
* 仅从服务器接收的消息才可以获取
|
||||
*
|
||||
* @see MessageSource.id
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val MessageChain.time: Int
|
||||
get() = this.source.time
|
||||
|
||||
/**
|
||||
* 消息内部 id.
|
||||
* 仅从服务器接收的消息才可以获取
|
||||
*
|
||||
* @see MessageSource.id
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val MessageChain.bot: Bot
|
||||
get() = this.source.bot
|
||||
|
||||
/**
|
||||
* 获取这条消息源
|
||||
* 仅从服务器接收的消息才可以获取消息源
|
||||
@ -361,9 +469,31 @@ inline val MessageChain.id: Int
|
||||
inline val MessageChain.source: MessageSource
|
||||
get() = this[MessageSource]
|
||||
|
||||
/**
|
||||
* 撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
|
||||
*
|
||||
* [Bot] 撤回自己的消息不需要权限.
|
||||
* [Bot] 撤回群员的消息需要管理员权限.
|
||||
*
|
||||
* @throws PermissionDeniedException 当 [Bot] 无权限操作时
|
||||
* @throws IllegalStateException 当这条消息已经被撤回时 (仅同步主动操作)
|
||||
*
|
||||
* @see Bot.recall
|
||||
*/
|
||||
@JvmSynthetic
|
||||
suspend inline fun MessageChain.recall() = this.source.recall()
|
||||
|
||||
/**
|
||||
* 在一段时间后撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
|
||||
*
|
||||
* [Bot] 撤回自己的消息不需要权限.
|
||||
* [Bot] 撤回群员的消息需要管理员权限.
|
||||
*
|
||||
* @throws PermissionDeniedException 当 [Bot] 无权限操作时
|
||||
* @throws IllegalStateException 当这条消息已经被撤回时 (仅同步主动操作)
|
||||
*
|
||||
* @see Bot.recall
|
||||
*/
|
||||
@JvmSynthetic
|
||||
inline fun MessageChain.recallIn(
|
||||
millis: Long,
|
||||
|
@ -25,6 +25,9 @@ import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
|
||||
/**
|
||||
* 将在线消息源转换为离线消息源.
|
||||
*/
|
||||
@SinceMirai("0.39.0")
|
||||
@JvmName("toOfflineMessageSource")
|
||||
fun OnlineMessageSource.toOffline(): OfflineMessageSource =
|
||||
@ -60,6 +63,14 @@ interface MessageSourceAmender {
|
||||
var internalId: Int
|
||||
|
||||
var originalMessage: MessageChain
|
||||
|
||||
/** 从另一个 [MessageSource] 中复制 [id], [internalId], [time]*/
|
||||
@SinceMirai("0.39.2")
|
||||
fun metadataFrom(another: MessageSource) {
|
||||
this.id = another.id
|
||||
this.internalId = another.internalId
|
||||
this.time = another.time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,7 +15,6 @@ package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -29,10 +28,19 @@ import kotlin.jvm.JvmSynthetic
|
||||
/**
|
||||
* 引用回复.
|
||||
*
|
||||
* 可以引用一条群消息并发送给一个好友, 或是引用好友消息发送给群.
|
||||
* 可以引用自己发出的消息. 详见 [MessageReceipt.quote]
|
||||
* 支持引用任何一条消息发送给任何人.
|
||||
*
|
||||
* @see MessageSource 获取更多信息
|
||||
* #### [source] 的类型:
|
||||
* - 在发送引用回复时, [source] 类型为 [OnlineMessageSource] 或 [OfflineMessageSource]
|
||||
* - 在接收引用回复时, [source] 类型一定为 [OfflineMessageSource]
|
||||
*
|
||||
* #### 原消息内容
|
||||
* 引用回复的原消息内容完全由 [source] 中 [MessageSource.originalMessage] 控制, 客户端不会自行寻找原消息.
|
||||
*
|
||||
* #### 客户端内跳转
|
||||
* 客户端在跳转原消息时, 会通过 [MessageSource.id] 等 metadata
|
||||
*
|
||||
* @see MessageSource 获取有关消息源的更多信息
|
||||
*/
|
||||
@OptIn(MiraiExperimentalAPI::class)
|
||||
@SinceMirai("0.33.0")
|
||||
@ -50,26 +58,52 @@ class QuoteReply(val source: MessageSource) : Message, MessageMetadata, Constrai
|
||||
override fun hashCode(): Int = source.hashCode()
|
||||
}
|
||||
|
||||
/**
|
||||
* @see MessageSource.id
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val QuoteReply.id: Int
|
||||
get() = source.id
|
||||
|
||||
/**
|
||||
* @see MessageSource.internalId
|
||||
*/
|
||||
@SinceMirai("0.39.2")
|
||||
@get:JvmSynthetic
|
||||
inline val QuoteReply.internalId: Int
|
||||
get() = source.internalId
|
||||
|
||||
/**
|
||||
* @see MessageSource.fromId
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val QuoteReply.fromId: Long
|
||||
get() = source.fromId
|
||||
|
||||
/**
|
||||
* @see MessageSource.targetId
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val QuoteReply.targetId: Long
|
||||
get() = source.targetId
|
||||
|
||||
/**
|
||||
* @see MessageSource.originalMessage
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val QuoteReply.originalMessage: MessageChain
|
||||
get() = source.originalMessage
|
||||
|
||||
/**
|
||||
* @see MessageSource.time
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val QuoteReply.time: Int
|
||||
get() = source.time
|
||||
|
||||
/**
|
||||
* @see MessageSource.bot
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
inline val QuoteReply.bot: Bot
|
||||
get() = source.bot
|
||||
@ -78,6 +112,9 @@ inline val QuoteReply.bot: Bot
|
||||
@JvmSynthetic
|
||||
suspend inline fun QuoteReply.recall() = this.source.recall()
|
||||
|
||||
/**
|
||||
* 在一段时间后撤回这条消息.
|
||||
*/
|
||||
@JvmOverloads
|
||||
inline fun QuoteReply.recallIn(
|
||||
millis: Long,
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmName("HummerMessageKt")
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
|
||||
|
||||
/*
|
||||
因为文件改名为做的兼容
|
||||
*/
|
||||
|
||||
@PlannedRemoval("1.0.0")
|
||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@JvmName("flash")
|
||||
@SinceMirai("0.33.0")
|
||||
inline fun Image.flash2(): FlashImage = FlashImage(this)
|
||||
|
||||
@PlannedRemoval("1.0.0")
|
||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@JvmName("flash")
|
||||
@JvmSynthetic
|
||||
@SinceMirai("0.33.0")
|
||||
inline fun GroupImage.flash2(): GroupFlashImage = FlashImage(this) as GroupFlashImage
|
||||
|
||||
@PlannedRemoval("1.0.0")
|
||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@JvmName("flash")
|
||||
@JvmSynthetic
|
||||
@SinceMirai("0.33.0")
|
||||
inline fun FriendImage.flash2(): FriendFlashImage = FlashImage(this) as FriendFlashImage
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmName("MessageKt")
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
/*
|
||||
因为文件改名为做的兼容
|
||||
*/
|
||||
|
||||
@PlannedRemoval("1.0.0")
|
||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@JvmName("isPlain")
|
||||
inline fun Message.isPlain2(): Boolean = this is PlainText
|
||||
|
||||
@PlannedRemoval("1.0.0")
|
||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@JvmName("isNotPlain")
|
||||
inline fun Message.isNotPlain2(): Boolean = this !is PlainText
|
||||
|
||||
@PlannedRemoval("1.0.0")
|
||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@JvmName("repeat")
|
||||
// inline: for future removal
|
||||
inline fun Message.repeat2(count: Int): MessageChain {
|
||||
if (this is ConstrainSingle<*>) {
|
||||
// fast-path
|
||||
return this.asMessageChain()
|
||||
}
|
||||
return buildMessageChain(count) {
|
||||
add(this@repeat2)
|
||||
}
|
||||
}
|
@ -42,6 +42,8 @@ inline fun buildMessageChain(initialSize: Int, block: MessageChainBuilder.() ->
|
||||
/**
|
||||
* [MessageChain] 构建器.
|
||||
* 多个连续的 [String] 会被连接为单个 [PlainText] 以优化性能.
|
||||
*
|
||||
*
|
||||
* **注意:** 无并发安全性.
|
||||
*
|
||||
* @see buildMessageChain 推荐使用
|
||||
@ -91,6 +93,7 @@ open class MessageChainBuilder private constructor(
|
||||
return addAll(elements.flatten())
|
||||
}
|
||||
|
||||
@JvmSynthetic
|
||||
operator fun Message.unaryPlus() {
|
||||
checkBuilt()
|
||||
flushCache()
|
||||
@ -98,22 +101,26 @@ open class MessageChainBuilder private constructor(
|
||||
}
|
||||
|
||||
|
||||
@JvmSynthetic
|
||||
operator fun String.unaryPlus() {
|
||||
checkBuilt()
|
||||
add(this)
|
||||
}
|
||||
|
||||
@JvmSynthetic // they should use add
|
||||
operator fun plusAssign(plain: String) {
|
||||
checkBuilt()
|
||||
withCache { append(plain) }
|
||||
}
|
||||
|
||||
@JvmSynthetic // they should use add
|
||||
operator fun plusAssign(message: Message) {
|
||||
checkBuilt()
|
||||
flushCache()
|
||||
this.add(message)
|
||||
}
|
||||
|
||||
@JvmSynthetic // they should use add
|
||||
operator fun plusAssign(message: SingleMessage) { // avoid resolution ambiguity
|
||||
checkBuilt()
|
||||
flushCache()
|
||||
@ -125,6 +132,7 @@ open class MessageChainBuilder private constructor(
|
||||
withCache { append(plain) }
|
||||
}
|
||||
|
||||
@JvmSynthetic // they should use add
|
||||
operator fun plusAssign(charSequence: CharSequence) {
|
||||
checkBuilt()
|
||||
withCache { append(charSequence) }
|
||||
|
@ -311,9 +311,11 @@ internal class SingleMessageChainImpl constructor(
|
||||
|
||||
|
||||
@SharedImmutable
|
||||
@get:JvmSynthetic
|
||||
internal val EMPTY_BYTE_ARRAY = ByteArray(0)
|
||||
|
||||
|
||||
@JvmSynthetic
|
||||
@Suppress("NOTHING_TO_INLINE") // no stack waste
|
||||
internal inline fun Char.hexDigitToByte(): Int {
|
||||
return when (this) {
|
||||
@ -324,6 +326,7 @@ internal inline fun Char.hexDigitToByte(): Int {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmSynthetic
|
||||
internal fun String.skipToSecondHyphen(): Int {
|
||||
var count = 0
|
||||
this.forEachIndexed { index, c ->
|
||||
@ -332,6 +335,7 @@ internal fun String.skipToSecondHyphen(): Int {
|
||||
error("cannot find two hyphens")
|
||||
}
|
||||
|
||||
@JvmSynthetic
|
||||
internal fun String.imageIdToMd5(offset: Int): ByteArray {
|
||||
val result = ByteArray(16)
|
||||
var cur = 0
|
||||
|
Loading…
Reference in New Issue
Block a user