Fix ambiguous message event names. Fix #299.

Binary compatibility until 1.2.0.

Migrations:
- `MessagePacket` deprecated in favor of `MessageEvent`
- `MessagePacketBase` deprecated in favor of `MessageEvent`
- `ContactMessage` -> `MessageEvent`
- `FriendMessage` -> `FriendMessageEvent`
- `GroupMessage` -> `GroupMessageEvent`
- `TempMessage` -> `TempMessageEvent`
This commit is contained in:
Him188 2020-05-05 13:55:01 +08:00
parent 66da881ebb
commit 4ee27f2069
18 changed files with 585 additions and 483 deletions

View File

@ -59,17 +59,17 @@ internal fun Contact.logMessageSent(message: Message) {
}
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
internal fun ContactMessage.logMessageReceived() {
internal fun MessageEvent.logMessageReceived() {
when (this) {
is GroupMessage -> bot.logger.verbose {
is GroupMessageEvent -> bot.logger.verbose {
"[${group.name.singleLine()}(${group.id})] ${senderName.singleLine()}(${sender.id}) -> ${message.toString()
.singleLine()}"
}
is TempMessage -> bot.logger.verbose {
is TempMessageEvent -> bot.logger.verbose {
"[${group.name.singleLine()}(${group.id})] ${senderName.singleLine()}(Temp ${sender.id}) -> ${message.toString()
.singleLine()}"
}
is FriendMessage -> bot.logger.verbose {
is FriendMessageEvent -> bot.logger.verbose {
"${sender.nick.singleLine()}(${sender.id}) -> ${message.toString().singleLine()}"
}
}

View File

@ -22,7 +22,7 @@ import kotlinx.io.core.use
import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.event.events.BotOnlineEvent
import net.mamoe.mirai.message.ContactMessage
import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.UnsupportedSMSLoginException
import net.mamoe.mirai.network.WrongPasswordException
@ -495,7 +495,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
is Packet.NoLog -> {
// nothing to do
}
is ContactMessage -> packet.logMessageReceived()
is MessageEvent -> packet.logMessageReceived()
is Event -> bot.logger.verbose { "Event: ${packet.toString().singleLine()}" }
else -> logger.verbose { "Packet: ${packet.toString().singleLine()}" }
}

View File

@ -29,8 +29,8 @@ import net.mamoe.mirai.event.events.BotJoinGroupEvent
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.event.events.MemberJoinEvent
import net.mamoe.mirai.getFriendOrNull
import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.message.TempMessage
import net.mamoe.mirai.message.FriendMessageEvent
import net.mamoe.mirai.message.TempMessageEvent
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.contact.GroupImpl
@ -274,7 +274,7 @@ internal class MessageSvc {
friend.lastMessageSequence.loop { instant ->
if (msg.msgHead.msgSeq > instant) {
if (friend.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) {
return@mapNotNull FriendMessage(
return@mapNotNull FriendMessageEvent(
friend,
msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true),
msg.msgHead.msgTime
@ -297,7 +297,7 @@ internal class MessageSvc {
member.lastMessageSequence.loop { instant ->
if (msg.msgHead.msgSeq > instant) {
if (member.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) {
return@mapNotNull TempMessage(
return@mapNotNull TempMessageEvent(
member,
msg.toMessageChain(
bot,

View File

@ -23,7 +23,7 @@ import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.getFriendOrNull
import net.mamoe.mirai.getGroupOrNull
import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.message.GroupMessageEvent
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.contact.*
import net.mamoe.mirai.qqandroid.message.contextualBugReportException
@ -109,7 +109,7 @@ internal class OnlinePush {
}
val flags = extraInfo?.flags ?: 0
return GroupMessage(
return GroupMessageEvent(
senderName = name.also {
if (it != sender.nameCard) {
val origin = sender._nameCard

View File

@ -16,7 +16,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.message.FriendMessageEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.toMessage
@ -29,7 +29,7 @@ import kotlin.jvm.JvmSynthetic
* 对于同一个 [Bot], 任何一个人的 [Friend] 实例都是单一的.
* [Friend] 无法通过任何方式直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
*
* @see FriendMessage
* @see FriendMessageEvent
*/
@Suppress("DEPRECATION_ERROR")
abstract class Friend : User(), CoroutineScope {

View File

@ -17,10 +17,10 @@ package net.mamoe.mirai.event
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.internal.*
import net.mamoe.mirai.message.ContactMessage
import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.message.TempMessage
import net.mamoe.mirai.message.FriendMessageEvent
import net.mamoe.mirai.message.GroupMessageEvent
import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.message.TempMessageEvent
import net.mamoe.mirai.message.data.*
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
@ -31,7 +31,7 @@ import kotlin.jvm.JvmSynthetic
* 消息事件的处理器.
*
* :
* 接受者 T [ContactMessage]
* 接受者 T [MessageEvent]
* 参数 String 转为字符串了的消息 ([Message.toString])
*/
typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
@ -47,7 +47,7 @@ typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
* @see subscribeFriendMessages
*/
@MessageDsl
open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
open class MessageSubscribersBuilder<M : MessageEvent, out Ret, R : RR, RR>(
/**
* 用于 [MessageListener] 无返回值的替代.
*/
@ -231,7 +231,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
/** 如果是这个人发的消息. 消息目前只会是群消息 */
@MessageDsl
fun sentBy(name: String): ListeningFilter = content { this is GroupMessage && this.senderName == name }
fun sentBy(name: String): ListeningFilter = content { this is GroupMessageEvent && this.senderName == name }
/** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
@MessageDsl
@ -247,36 +247,37 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
/** 如果是好友发来的消息 */
@MessageDsl
fun sentByFriend(onEvent: MessageListener<FriendMessage, R>): Ret =
content({ this is FriendMessage }) { onEvent(this as FriendMessage, it) }
fun sentByFriend(onEvent: MessageListener<FriendMessageEvent, R>): Ret =
content({ this is FriendMessageEvent }) { onEvent(this as FriendMessageEvent, it) }
/** 如果是好友发来的消息 */
@MessageDsl
fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessage }
fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessageEvent }
/** 如果是好友发来的消息 */
/** 如果是群临时会话消息 */
@MessageDsl
fun sentByTemp(): ListeningFilter = newListeningFilter { this is TempMessage }
fun sentByTemp(): ListeningFilter = newListeningFilter { this is TempMessageEvent }
/** 如果是管理员或群主发的消息 */
@MessageDsl
fun sentByOperator(): ListeningFilter = content { this is GroupMessage && sender.permission.isOperator() }
fun sentByOperator(): ListeningFilter = content { this is GroupMessageEvent && sender.permission.isOperator() }
/** 如果是管理员发的消息 */
@MessageDsl
fun sentByAdministrator(): ListeningFilter = content { this is GroupMessage && sender.permission.isAdministrator() }
fun sentByAdministrator(): ListeningFilter =
content { this is GroupMessageEvent && sender.permission.isAdministrator() }
/** 如果是群主发的消息 */
@MessageDsl
fun sentByOwner(): ListeningFilter = content { this is GroupMessage && sender.isOwner() }
fun sentByOwner(): ListeningFilter = content { this is GroupMessageEvent && sender.isOwner() }
/** 如果是来自这个群的消息 */
@MessageDsl
fun sentFrom(groupId: Long): ListeningFilter = content { this is GroupMessage && group.id == groupId }
fun sentFrom(groupId: Long): ListeningFilter = content { this is GroupMessageEvent && group.id == groupId }
/** 如果是来自这个群的消息 */
@MessageDsl
fun sentFrom(group: Group): ListeningFilter = content { this is GroupMessage && group.id == group.id }
fun sentFrom(group: Group): ListeningFilter = content { this is GroupMessageEvent && group.id == group.id }
/** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At] */
@MessageDsl

View File

@ -12,7 +12,7 @@ package net.mamoe.mirai.event.internal
import net.mamoe.mirai.event.MessageDsl
import net.mamoe.mirai.event.MessageListener
import net.mamoe.mirai.event.MessageSubscribersBuilder
import net.mamoe.mirai.message.ContactMessage
import net.mamoe.mirai.message.MessageEvent
/*
@ -20,13 +20,13 @@ import net.mamoe.mirai.message.ContactMessage
*/
@MessageDsl
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.content(
internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.content(
filter: M.(String) -> Boolean,
onEvent: MessageListener<M, RR>
): Ret =
subscriber(filter) { onEvent(this, it) }
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.endsWithImpl(
internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.endsWithImpl(
suffix: String,
removeSuffix: Boolean = true,
trim: Boolean = true,
@ -46,7 +46,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
}
}
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.startsWithImpl(
internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.startsWithImpl(
prefix: String,
removePrefix: Boolean = true,
trim: Boolean = true,
@ -64,7 +64,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
}
}
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAllImpl(
internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAllImpl(
sub: Array<out String>,
ignoreCase: Boolean = false,
trim: Boolean = true
@ -76,7 +76,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
content { sub.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
}
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAnyImpl(
internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAnyImpl(
vararg sub: String,
ignoreCase: Boolean = false,
trim: Boolean = true
@ -86,7 +86,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
content { list.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
} else content { sub.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.caseImpl(
internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.caseImpl(
equals: String,
ignoreCase: Boolean = false,
trim: Boolean = true
@ -99,7 +99,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
}
}
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsImpl(
internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsImpl(
sub: String,
ignoreCase: Boolean = false,
trim: Boolean = true,

View File

@ -12,7 +12,7 @@
package net.mamoe.mirai.event
import kotlinx.coroutines.*
import net.mamoe.mirai.message.ContactMessage
import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.isContextIdenticalWith
@ -61,7 +61,7 @@ import kotlin.jvm.JvmSynthetic
*/
@SinceMirai("0.29.0")
@Suppress("unused")
suspend inline fun <reified T : ContactMessage> T.whileSelectMessages(
suspend inline fun <reified T : MessageEvent> T.whileSelectMessages(
timeoutMillis: Long = -1,
filterContext: Boolean = true,
crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, Boolean>.() -> Unit
@ -74,7 +74,7 @@ suspend inline fun <reified T : ContactMessage> T.whileSelectMessages(
@MiraiExperimentalAPI
@SinceMirai("0.29.0")
@JvmName("selectMessages1")
suspend inline fun <reified T : ContactMessage> T.selectMessagesUnit(
suspend inline fun <reified T : MessageEvent> T.selectMessagesUnit(
timeoutMillis: Long = -1,
filterContext: Boolean = true,
crossinline selectBuilder: @MessageDsl MessageSelectBuilderUnit<T, Unit>.() -> Unit
@ -104,7 +104,7 @@ suspend inline fun <reified T : ContactMessage> T.selectMessagesUnit(
@SinceMirai("0.29.0")
@Suppress("unused") // false positive
// @BuilderInference // https://youtrack.jetbrains.com/issue/KT-37716
suspend inline fun <reified T : ContactMessage, R> T.selectMessages(
suspend inline fun <reified T : MessageEvent, R> T.selectMessages(
timeoutMillis: Long = -1,
filterContext: Boolean = true,
// @BuilderInference
@ -121,7 +121,7 @@ suspend inline fun <reified T : ContactMessage, R> T.selectMessages(
*/
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
@SinceMirai("0.29.0")
abstract class MessageSelectBuilder<M : ContactMessage, R> @PublishedApi internal constructor(
abstract class MessageSelectBuilder<M : MessageEvent, R> @PublishedApi internal constructor(
ownerMessagePacket: M,
stub: Any?,
subscriber: (M.(String) -> Boolean, MessageListener<M, Any?>) -> Unit
@ -236,7 +236,7 @@ abstract class MessageSelectBuilder<M : ContactMessage, R> @PublishedApi interna
* @see MessageSubscribersBuilder 查看上层 API
*/
@SinceMirai("0.29.0")
abstract class MessageSelectBuilderUnit<M : ContactMessage, R> @PublishedApi internal constructor(
abstract class MessageSelectBuilderUnit<M : MessageEvent, R> @PublishedApi internal constructor(
private val ownerMessagePacket: M,
stub: Any?,
subscriber: (M.(String) -> Boolean, MessageListener<M, Any?>) -> Unit
@ -480,7 +480,7 @@ internal val ExceptionHandlerIgnoringCancellationException = CoroutineExceptionH
@PublishedApi
@BuilderInference
@OptIn(ExperimentalTypeInference::class)
internal suspend inline fun <reified T : ContactMessage, R> T.selectMessagesImpl(
internal suspend inline fun <reified T : MessageEvent, R> T.selectMessagesImpl(
timeoutMillis: Long = -1,
isUnit: Boolean,
filterContext: Boolean = true,
@ -579,7 +579,7 @@ internal suspend inline fun <reified T : ContactMessage, R> T.selectMessagesImpl
@Suppress("unused")
@PublishedApi
internal suspend inline fun <reified T : ContactMessage> T.whileSelectMessagesImpl(
internal suspend inline fun <reified T : MessageEvent> T.whileSelectMessagesImpl(
timeoutMillis: Long = -1,
filterContext: Boolean = true,
crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, Boolean>.() -> Unit

View File

@ -17,10 +17,10 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.message.ContactMessage
import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.message.TempMessage
import net.mamoe.mirai.message.FriendMessageEvent
import net.mamoe.mirai.message.GroupMessageEvent
import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.message.TempMessageEvent
import net.mamoe.mirai.utils.SinceMirai
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
@ -28,7 +28,7 @@ import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
typealias MessagePacketSubscribersBuilder = MessageSubscribersBuilder<ContactMessage, Listener<ContactMessage>, Unit, Unit>
typealias MessagePacketSubscribersBuilder = MessageSubscribersBuilder<MessageEvent, Listener<MessageEvent>, Unit, Unit>
/**
* 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
@ -47,7 +47,7 @@ fun <R> CoroutineScope.subscribeMessages(
}
return MessagePacketSubscribersBuilder(Unit)
{ filter, messageListener: MessageListener<ContactMessage, Unit> ->
{ filter, messageListener: MessageListener<MessageEvent, Unit> ->
// subscribeAlways 即注册一个监听器. 这个监听器收到消息后就传递给 [messageListener]
// messageListener 即为 DSL 里 `contains(...) { }`, `startsWith(...) { }` 的代码块.
subscribeAlways(coroutineContext, concurrencyKind) {
@ -59,7 +59,7 @@ fun <R> CoroutineScope.subscribeMessages(
}.run(listeners)
}
typealias GroupMessageSubscribersBuilder = MessageSubscribersBuilder<GroupMessage, Listener<GroupMessage>, Unit, Unit>
typealias GroupMessageSubscribersBuilder = MessageSubscribersBuilder<GroupMessageEvent, Listener<GroupMessageEvent>, Unit, Unit>
/**
* 订阅来自所有 [Bot] 的所有群消息事件
@ -84,7 +84,7 @@ fun <R> CoroutineScope.subscribeGroupMessages(
}.run(listeners)
}
typealias FriendMessageSubscribersBuilder = MessageSubscribersBuilder<FriendMessage, Listener<FriendMessage>, Unit, Unit>
typealias FriendMessageSubscribersBuilder = MessageSubscribersBuilder<FriendMessageEvent, Listener<FriendMessageEvent>, Unit, Unit>
/**
* 订阅来自所有 [Bot] 的所有好友消息事件
@ -109,7 +109,7 @@ fun <R> CoroutineScope.subscribeFriendMessages(
}.run(listeners)
}
typealias TempMessageSubscribersBuilder = MessageSubscribersBuilder<TempMessage, Listener<TempMessage>, Unit, Unit>
typealias TempMessageSubscribersBuilder = MessageSubscribersBuilder<TempMessageEvent, Listener<TempMessageEvent>, Unit, Unit>
/**
* 订阅来自所有 [Bot] 的所有临时会话消息事件

View File

@ -1,383 +0,0 @@
/*
* 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:Suppress(
"EXPERIMENTAL_UNSIGNED_LITERALS",
"EXPERIMENTAL_API_USAGE",
"unused",
"INVISIBLE_REFERENCE",
"INVISIBLE_MEMBER"
)
@file:OptIn(MiraiInternalAPI::class)
package net.mamoe.mirai.message
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.utils.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
/**
* 一个消息事件.
*
* 它是一个 [BotEvent], 因此可以被 [监听][Bot.subscribe]
*
* 支持的消息类型:
* - [群消息事件][GroupMessage]
* - [好友消息事件][FriendMessage]
* - [临时会话消息事件][TempMessage]
*
* @see isContextIdenticalWith 判断语境是否相同
*/
@Suppress("DEPRECATION")
@SinceMirai("0.32.0")
abstract class ContactMessage : MessagePacket<User, Contact>(), BotEvent
/**
* 一条从服务器接收到的消息事件.
* 请查看各平台的 `actual` 实现的说明.
*/
@Suppress("DEPRECATION")
@Deprecated(
message = "use ContactMessage",
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
expect abstract class MessagePacket<TSender : User, TSubject : Contact> constructor() :
MessagePacketBase<TSender, TSubject>
/**
* 仅内部使用, 请使用 [ContactMessage]
*/ // Tips: 在 IntelliJ 中 (左侧边栏) 打开 `Structure`, 可查看类结构
@Deprecated(
message = "use ContactMessage",
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
abstract class MessagePacketBase<out TSender : User, out TSubject : Contact> : Packet, BotEvent, AbstractEvent() {
/**
* 接受到这条消息的
*/
abstract override val bot: Bot
/**
* 消息事件主体.
*
* - 对于好友消息, 这个属性为 [Friend] 的实例, [sender] 引用相同;
* - 对于临时会话消息, 这个属性为 [Member] 的实例, [sender] 引用相同;
* - 对于群消息, 这个属性为 [Group] 的实例, [GroupMessage.group] 引用相同
*
* 在回复消息时, 可通过 [subject] 作为回复对象
*/
abstract val subject: TSubject
/**
* 发送人.
*
* 在好友消息时为 [Friend] 的实例, 在群消息时为 [Member] 的实例
*/
abstract val sender: TSender
abstract val senderName: String
/**
* 消息内容
*/
abstract val message: MessageChain
/**
* 消息发送时间 (由服务器提供)
*/
@SinceMirai("0.39.0")
abstract val time: Int
/**
* 消息源
*/
open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
// region 发送 Message
/**
* 给这个消息事件的主体发送消息
* 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
*/
suspend inline fun reply(message: Message): MessageReceipt<TSubject> =
subject.sendMessage(message.asMessageChain()) as MessageReceipt<TSubject>
suspend inline fun reply(plain: String): MessageReceipt<TSubject> =
subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject>
// endregion
// region 图片
suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
suspend inline fun ExternalImage.send(): MessageReceipt<TSubject> = this.sendTo(subject)
suspend inline fun Image.send(): MessageReceipt<TSubject> = this.sendTo(subject)
suspend inline fun Message.send(): MessageReceipt<TSubject> = this.sendTo(subject)
suspend inline fun String.send(): MessageReceipt<TSubject> = this.toMessage().sendTo(subject)
// endregion
// region 引用回复
/**
* 给这个消息事件的主体发送引用回复消息
* 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
*/
suspend inline fun quoteReply(message: MessageChain): MessageReceipt<TSubject> =
reply(this.message.quote() + message)
suspend inline fun quoteReply(message: Message): MessageReceipt<TSubject> = reply(this.message.quote() + message)
suspend inline fun quoteReply(plain: String): MessageReceipt<TSubject> = reply(this.message.quote() + plain)
@JvmName("reply2")
suspend inline fun String.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
@JvmName("reply2")
suspend inline fun Message.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
@JvmName("reply2")
suspend inline fun MessageChain.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
// endregion
inline operator fun <M : Message> get(at: Message.Key<M>): M {
return this.message[at]
}
inline fun At.isBot(): Boolean = target == bot.id
// endregion
// region 下载图片
/**
* 获取图片下载链接
*
* @return "http://gchat.qpic.cn/gchatpic_new/..."
*/
suspend inline fun Image.url(): String = bot.queryImageUrl(this@url)
// endregion
}
/**
* 判断两个 [MessagePacket] [MessagePacket.sender] [MessagePacket.subject] 是否相同
*/
@SinceMirai("0.29.0")
fun ContactMessage.isContextIdenticalWith(another: ContactMessage): Boolean {
return this.sender == another.sender && this.subject == another.subject && this.bot == another.bot
}
/**
* 挂起当前协程, 等待下一条 [MessagePacket.sender] [MessagePacket.subject] [this] 相同且通过 [筛选][filter] [MessagePacket]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @param filter 过滤器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
*
* @see syncFromEvent
*/
@JvmSynthetic
suspend inline fun <reified P : ContactMessage> P.nextMessage(
timeoutMillis: Long = -1,
crossinline filter: suspend P.(P) -> Boolean
): MessageChain {
return syncFromEvent<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessage) }?.takeIf { filter(it, it) }
}.message
}
/**
* 挂起当前协程, 等待下一条 [MessagePacket.sender] [MessagePacket.subject] [this] 相同且通过 [筛选][filter] [MessagePacket]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @param filter 过滤器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
* @return 消息链. 超时时返回 `null`
*
* @see syncFromEventOrNull
*/
@JvmSynthetic
suspend inline fun <reified P : ContactMessage> P.nextMessageOrNull(
timeoutMillis: Long = -1,
crossinline filter: suspend P.(P) -> Boolean
): MessageChain? {
return syncFromEventOrNull<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }?.takeIf { filter(it, it) }
}?.message
}
/**
* 挂起当前协程, 等待下一条 [MessagePacket.sender] [MessagePacket.subject] [this] 相同的 [MessagePacket]
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
*
* @throws TimeoutCancellationException
*
* @see syncFromEvent
*/
@JvmSynthetic
suspend inline fun <reified P : ContactMessage> P.nextMessage(
timeoutMillis: Long = -1
): MessageChain {
return syncFromEvent<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessage) }
}.message
}
/**
* @see nextMessage
* @throws TimeoutCancellationException
*/
@JvmSynthetic
inline fun <reified P : ContactMessage> P.nextMessageAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<MessageChain> {
return this.bot.async(coroutineContext) {
syncFromEvent<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageAsync) }
}.message
}
}
/**
* @see nextMessage
*/
@JvmSynthetic
inline fun <reified P : ContactMessage> P.nextMessageAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext,
crossinline filter: suspend P.(P) -> Boolean
): Deferred<MessageChain> {
return this.bot.async(coroutineContext) {
syncFromEvent<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageAsync) }
.takeIf { filter(this, this) }
}.message
}
}
/**
* 挂起当前协程, 等待下一条 [MessagePacket.sender] [MessagePacket.subject] [this] 相同的 [MessagePacket]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @return 消息链. 超时时返回 `null`
*
* @see syncFromEventOrNull
*/
@JvmSynthetic
suspend inline fun <reified P : ContactMessage> P.nextMessageOrNull(
timeoutMillis: Long = -1
): MessageChain? {
return syncFromEventOrNull<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }
}?.message
}
/**
* @see nextMessageOrNull
*/
@JvmSynthetic
inline fun <reified P : ContactMessage> P.nextMessageOrNullAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<MessageChain?> {
return this.bot.async(coroutineContext) {
syncFromEventOrNull<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageOrNullAsync) }
}?.message
}
}
/**
* 挂起当前协程, 等待下一条 [MessagePacket.sender] [MessagePacket.subject] [this] 相同的 [MessagePacket]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
*
* @see syncFromEvent
* @see whileSelectMessages
* @see selectMessages
*/
@JvmSynthetic
suspend inline fun <reified M : Message> ContactMessage.nextMessageContaining(
timeoutMillis: Long = -1
): M {
return syncFromEvent<ContactMessage, ContactMessage>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContaining) }
.takeIf { this.message.anyIsInstance<M>() }
}.message.firstIsInstance()
}
@JvmSynthetic
inline fun <reified M : Message> ContactMessage.nextMessageContainingAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<M> {
return this.bot.async(coroutineContext) {
@Suppress("RemoveExplicitTypeArguments")
syncFromEvent<ContactMessage, ContactMessage>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingAsync) }
.takeIf { this.message.anyIsInstance<M>() }
}.message.firstIsInstance<M>()
}
}
/**
* 挂起当前协程, 等待下一条 [MessagePacket.sender] [MessagePacket.subject] [this] 相同并含有 [M] 类型的消息的 [MessagePacket]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @return 指定类型的消息. 超时时返回 `null`
*
* @see syncFromEventOrNull
*/
@JvmSynthetic
suspend inline fun <reified M : Message> ContactMessage.nextMessageContainingOrNull(
timeoutMillis: Long = -1
): M? {
return syncFromEventOrNull<ContactMessage, ContactMessage>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNull) }
.takeIf { this.message.anyIsInstance<M>() }
}?.message?.firstIsInstance()
}
@JvmSynthetic
inline fun <reified M : Message> ContactMessage.nextMessageContainingOrNullAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<M?> {
return this.bot.async(coroutineContext) {
syncFromEventOrNull<ContactMessage, ContactMessage>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNullAsync) }
.takeIf { this.message.anyIsInstance<M>() }
}?.message?.firstIsInstance<M>()
}
}

View File

@ -18,17 +18,21 @@ import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.message.data.source
import net.mamoe.mirai.utils.PlannedRemoval
/**
* 好友消息事件
* 机器人收到的好友消息的事件
*
* @see MessageEvent
*/
class FriendMessage constructor(
class FriendMessageEvent constructor(
override val sender: Friend,
override val message: MessageChain,
override val time: Int
) : ContactMessage(), BroadcastControllable {
) : @PlannedRemoval("1.2.0") FriendMessage(), BroadcastControllable {
init {
val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message")
val source =
message.getOrNull(MessageSource) ?: throw IllegalArgumentException("Cannot find MessageSource from message")
check(source is OnlineMessageSource.Incoming.FromFriend) { "source provided to a FriendMessage must be an instance of OnlineMessageSource.Incoming.FromFriend" }
}
@ -37,5 +41,5 @@ class FriendMessage constructor(
override val senderName: String get() = sender.nick
override val source: OnlineMessageSource.Incoming.FromFriend get() = message.source as OnlineMessageSource.Incoming.FromFriend
override fun toString(): String = "FriendMessage(sender=${sender.id}, message=$message)"
override fun toString(): String = "FriendMessageEvent(sender=${sender.id}, message=$message)"
}

View File

@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("DEPRECATION_ERROR", "unused", "NOTHING_TO_INLINE")
package net.mamoe.mirai.message
import net.mamoe.mirai.Bot
@ -15,14 +17,14 @@ import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.PlannedRemoval
/**
* 群消息事件
* 机器人收到的群消息事件
*
* @see ContactMessage
* @see MessageEvent
*/
@Suppress("unused", "NOTHING_TO_INLINE")
class GroupMessage(
class GroupMessageEvent(
override val senderName: String,
/**
* 发送方权限.
@ -31,13 +33,13 @@ class GroupMessage(
override val sender: Member,
override val message: MessageChain,
override val time: Int
) : ContactMessage(), Event {
) : @PlannedRemoval("1.2.0") GroupMessage(), Event {
init {
val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message")
check(source is OnlineMessageSource.Incoming.FromGroup) { "source provided to a GroupMessage must be an instance of OnlineMessageSource.Incoming.FromGroup" }
}
val group: Group get() = sender.group
inline val group: Group get() = sender.group
override val bot: Bot get() = sender.bot
override val subject: Group get() = group
@ -47,5 +49,5 @@ class GroupMessage(
inline fun At.asMember(): Member = group[this.target]
override fun toString(): String =
"GroupMessage(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)"
"GroupMessageEvent(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)"
}

View File

@ -0,0 +1,221 @@
/*
* 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:Suppress(
"EXPERIMENTAL_UNSIGNED_LITERALS",
"EXPERIMENTAL_API_USAGE",
"unused",
"DECLARATION_CANT_BE_INLINED", "UNCHECKED_CAST", "NOTHING_TO_INLINE"
)
@file:OptIn(MiraiInternalAPI::class)
@file:JvmMultifileClass
@file:JvmName("MessageEventKt")
package net.mamoe.mirai.message
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.AbstractEvent
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.utils.*
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
/**
* 一个 (收到的) 消息事件.
*
* 它是一个 [BotEvent], 因此可以被 [监听][Bot.subscribe]
*
* 支持的消息类型:
* - [群消息事件][GroupMessageEvent]
* - [好友消息事件][FriendMessageEvent]
* - [临时会话消息事件][TempMessageEvent]
*
* @see isContextIdenticalWith 判断语境是否相同
*/
@Suppress("DEPRECATION_ERROR")
@SinceMirai("0.32.0")
abstract class MessageEvent : @PlannedRemoval("1.2.0") ContactMessage(),
BotEvent, MessageEventExtensions<User, Contact> {
/**
* 与这个消息事件相关的 [Bot]
*/
abstract override val bot: Bot
/**
* 消息事件主体.
*
* - 对于好友消息, 这个属性为 [Friend] 的实例, [sender] 引用相同;
* - 对于临时会话消息, 这个属性为 [Member] 的实例, [sender] 引用相同;
* - 对于群消息, 这个属性为 [Group] 的实例, [GroupMessageEvent.group] 引用相同
*
* 在回复消息时, 可通过 [subject] 作为回复对象
*/
abstract override val subject: Contact
/**
* 发送人.
*
* 在好友消息时为 [Friend] 的实例, 在群消息时为 [Member] 的实例
*/
abstract override val sender: User
abstract val senderName: String
/** 消息内容 */
abstract override val message: MessageChain
/** 消息发送时间 (由服务器提供) */
@SinceMirai("0.39.0")
abstract val time: Int
/** 消息源 */
open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
}
/** 消息事件的扩展函数 */
@Suppress("EXPOSED_SUPER_INTERFACE") // Functions are visible
interface MessageEventExtensions<out TSender : User, out TSubject : Contact> :
MessageEventPlatformExtensions<TSender, TSubject> {
// region 发送 Message
/**
* 给这个消息事件的主体发送消息
* 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
*/
@JvmSynthetic
suspend inline fun reply(message: Message): MessageReceipt<TSubject> =
subject.sendMessage(message.asMessageChain()) as MessageReceipt<TSubject>
@JvmSynthetic
suspend inline fun reply(plain: String): MessageReceipt<TSubject> =
subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject>
// endregion
@JvmSynthetic
suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
@JvmSynthetic
suspend inline fun ExternalImage.send(): MessageReceipt<TSubject> = this.sendTo(subject)
@JvmSynthetic
suspend inline fun Image.send(): MessageReceipt<TSubject> = this.sendTo(subject)
@JvmSynthetic
suspend inline fun Message.send(): MessageReceipt<TSubject> = this.sendTo(subject)
@JvmSynthetic
suspend inline fun String.send(): MessageReceipt<TSubject> = this.toMessage().sendTo(subject)
// region 引用回复
/**
* 给这个消息事件的主体发送引用回复消息
* 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
*/
@JvmSynthetic
suspend inline fun quoteReply(message: MessageChain): MessageReceipt<TSubject> =
reply(this.message.quote() + message)
@JvmSynthetic
suspend inline fun quoteReply(message: Message): MessageReceipt<TSubject> = reply(this.message.quote() + message)
@JvmSynthetic
suspend inline fun quoteReply(plain: String): MessageReceipt<TSubject> = reply(this.message.quote() + plain)
@JvmSynthetic
inline operator fun <M : Message> get(at: Message.Key<M>): M {
return this.message[at]
}
@JvmSynthetic
inline fun At.isBot(): Boolean = target == bot.id
/**
* 获取图片下载链接
* @return "http://gchat.qpic.cn/gchatpic_new/..."
*/
@JvmSynthetic
suspend inline fun Image.url(): String = bot.queryImageUrl(this@url)
}
/** 一个消息事件在各平台的相关扩展. 请使用 [MessageEventExtensions] */
internal expect interface MessageEventPlatformExtensions<out TSender : User, out TSubject : Contact> {
val subject: TSubject
val sender: TSender
val message: MessageChain
val bot: Bot
}
/**
* 已废弃, 请使用 [MessageEvent]
*/
@PlannedRemoval("1.2.0")
@Deprecated(
message = "use MessageEvent",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.ERROR
)
abstract class MessagePacketBase<out TSender : User, out TSubject : Contact> : Packet, BotEvent, AbstractEvent()
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use MessageEvent instead",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.ERROR
)
@Suppress("DEPRECATION_ERROR")
abstract class MessagePacket : MessagePacketBase<User, Contact>(),
BotEvent, MessageEventExtensions<User, Contact>
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use MessageEvent instead",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.ERROR
)
@Suppress("DEPRECATION_ERROR")
abstract class ContactMessage : MessagePacket(),
BotEvent, MessageEventExtensions<User, Contact>
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use FriendMessageEvent instead",
replaceWith = ReplaceWith("FriendMessageEvent", "net.mamoe.mirai.message.FriendMessageEvent"),
level = DeprecationLevel.ERROR
)
@Suppress("DEPRECATION_ERROR")
abstract class FriendMessage : MessageEvent()
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use GroupMessageEvent instead",
replaceWith = ReplaceWith("GroupMessageEvent", "net.mamoe.mirai.message.GroupMessageEvent"),
level = DeprecationLevel.ERROR
)
@Suppress("DEPRECATION_ERROR")
abstract class GroupMessage : MessageEvent()
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use TempMessageEvent instead",
replaceWith = ReplaceWith("TempMessageEvent", "net.mamoe.mirai.message.TempMessageEvent"),
level = DeprecationLevel.ERROR
)
abstract class TempMessage : MessageEvent()

View File

@ -1,3 +1,5 @@
@file:Suppress("DEPRECATION_ERROR", "unused", "NOTHING_TO_INLINE")
package net.mamoe.mirai.message
import net.mamoe.mirai.Bot
@ -12,14 +14,16 @@ import net.mamoe.mirai.message.data.source
import net.mamoe.mirai.utils.SinceMirai
/**
* 临时会话消息
* 机器人收到的群临时会话消息的事件
*
* @see MessageEvent
*/
@SinceMirai("0.35.0")
class TempMessage(
class TempMessageEvent(
override val sender: Member,
override val message: MessageChain,
override val time: Int
) : ContactMessage(), BroadcastControllable {
) : TempMessage(), BroadcastControllable {
init {
val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message")
check(source is OnlineMessageSource.Incoming.FromTemp) { "source provided to a TempMessage must be an instance of OnlineMessageSource.Incoming.FromTemp" }
@ -32,5 +36,5 @@ class TempMessage(
override val source: OnlineMessageSource.Incoming.FromTemp get() = message.source as OnlineMessageSource.Incoming.FromTemp
override fun toString(): String =
"TempMessage(sender=${sender.id} from group(${sender.group.id}), message=$message)"
"TempMessageEvent(sender=${sender.id} from group(${sender.group.id}), message=$message)"
}

View File

@ -12,8 +12,11 @@
package net.mamoe.mirai.message.data
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.ContactMessage
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.contact.nameCardOrNick
import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.message.data.ForwardMessage.DisplayStrategy
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
@ -72,7 +75,7 @@ import kotlin.jvm.JvmSynthetic
*
* ### 构造
* - 使用 [DSL][buildForwardMessage]
* - 通过 [ContactMessage] 集合转换: [toForwardMessage]
* - 通过 [MessageEvent] 集合转换: [toForwardMessage]
*
* @see buildForwardMessage
*/
@ -186,7 +189,7 @@ class ForwardMessage @JvmOverloads constructor(
*/
@SinceMirai("0.39.0")
@JvmOverloads
fun Iterable<ContactMessage>.toForwardMessage(displayStrategy: DisplayStrategy = DisplayStrategy): ForwardMessage {
fun Iterable<MessageEvent>.toForwardMessage(displayStrategy: DisplayStrategy = DisplayStrategy): ForwardMessage {
val iterator = this.iterator()
if (!iterator.hasNext()) return ForwardMessage(emptyList(), displayStrategy)
return ForwardMessage(
@ -236,7 +239,7 @@ inline fun buildForwardMessage(
*/
@SinceMirai("0.39.0")
@JvmSynthetic
inline fun ContactMessage.buildForwardMessage(
inline fun MessageEvent.buildForwardMessage(
context: Contact = this.subject,
displayStrategy: DisplayStrategy = DisplayStrategy,
block: ForwardMessageBuilder.() -> Unit

View File

@ -16,7 +16,7 @@ package net.mamoe.mirai.message.data
import kotlinx.coroutines.Job
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.ContactMessage
import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.recallIn
import net.mamoe.mirai.utils.LazyProperty
@ -159,7 +159,7 @@ sealed class MessageSource : Message, MessageMetadata, ConstrainSingle<MessageSo
* 此回执的 [消息源][MessageReceipt.source] 即为一个 [外向消息源][OnlineMessageSource.Outgoing], 代表着刚刚发出的那条消息的来源.
*
* #### 机器人接受消息
* 当机器人接收一条消息 [ContactMessage], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源.
* 当机器人接收一条消息 [MessageEvent], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源.
*
*
* ### 实现
@ -356,7 +356,7 @@ fun MessageSource.quote(): QuoteReply {
}
/**
* 引用这条消息. 仅从服务器接收的消息 (即来自 [ContactMessage]) 才可以通过这个方式被引用.
* 引用这条消息. 仅从服务器接收的消息 (即来自 [MessageEvent]) 才可以通过这个方式被引用.
* @see QuoteReply
*/
fun MessageChain.quote(): QuoteReply {

View File

@ -0,0 +1,243 @@
/*
* 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:JvmMultifileClass
@file:JvmName("MessageEventKt")
@file:Suppress("unused")
package net.mamoe.mirai.message
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
import net.mamoe.mirai.event.selectMessages
import net.mamoe.mirai.event.syncFromEvent
import net.mamoe.mirai.event.syncFromEventOrNull
import net.mamoe.mirai.event.whileSelectMessages
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.anyIsInstance
import net.mamoe.mirai.message.data.firstIsInstance
import net.mamoe.mirai.utils.PlannedRemoval
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
/**
* 判断两个 [MessageEvent] [MessageEvent.sender] [MessageEvent.subject] 是否相同
*/
fun MessageEvent.isContextIdenticalWith(another: MessageEvent): Boolean {
return this.sender == another.sender && this.subject == another.subject
}
/**
* 挂起当前协程, 等待下一条 [MessageEvent.sender] [MessageEvent.subject] [this] 相同且通过 [筛选][filter] [MessageEvent]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @param filter 过滤器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
*
* @see syncFromEvent
*/
@JvmSynthetic
suspend inline fun <reified P : MessageEvent> P.nextMessage(
timeoutMillis: Long = -1,
crossinline filter: suspend P.(P) -> Boolean
): MessageChain {
return syncFromEvent<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessage) }?.takeIf { filter(it, it) }
}.message
}
/**
* 挂起当前协程, 等待下一条 [MessageEvent.sender] [MessageEvent.subject] [this] 相同且通过 [筛选][filter] [MessageEvent]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @param filter 过滤器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
* @return 消息链. 超时时返回 `null`
*
* @see syncFromEventOrNull
*/
@JvmSynthetic
suspend inline fun <reified P : MessageEvent> P.nextMessageOrNull(
timeoutMillis: Long = -1,
crossinline filter: suspend P.(P) -> Boolean
): MessageChain? {
return syncFromEventOrNull<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }?.takeIf { filter(it, it) }
}?.message
}
/**
* 挂起当前协程, 等待下一条 [MessageEvent.sender] [MessageEvent.subject] [this] 相同的 [MessageEvent]
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
*
* @throws TimeoutCancellationException
*
* @see syncFromEvent
*/
@JvmSynthetic
suspend inline fun <reified P : MessageEvent> P.nextMessage(
timeoutMillis: Long = -1
): MessageChain {
return syncFromEvent<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessage) }
}.message
}
/**
* @see nextMessage
* @throws TimeoutCancellationException
*/
@JvmSynthetic
inline fun <reified P : MessageEvent> P.nextMessageAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<MessageChain> {
return this.bot.async(coroutineContext) {
syncFromEvent<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageAsync) }
}.message
}
}
/**
* @see nextMessage
*/
@JvmSynthetic
inline fun <reified P : MessageEvent> P.nextMessageAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext,
crossinline filter: suspend P.(P) -> Boolean
): Deferred<MessageChain> {
return this.bot.async(coroutineContext) {
syncFromEvent<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageAsync) }
.takeIf { filter(this, this) }
}.message
}
}
/**
* 挂起当前协程, 等待下一条 [MessageEvent.sender] [MessageEvent.subject] [this] 相同的 [MessageEvent]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @return 消息链. 超时时返回 `null`
*
* @see syncFromEventOrNull
*/
@JvmSynthetic
suspend inline fun <reified P : MessageEvent> P.nextMessageOrNull(
timeoutMillis: Long = -1
): MessageChain? {
return syncFromEventOrNull<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }
}?.message
}
/**
* @see nextMessageOrNull
*/
@JvmSynthetic
inline fun <reified P : MessageEvent> P.nextMessageOrNullAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<MessageChain?> {
return this.bot.async(coroutineContext) {
syncFromEventOrNull<P, P>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageOrNullAsync) }
}?.message
}
}
/**
* 挂起当前协程, 等待下一条 [MessageEvent.sender] [MessageEvent.subject] [this] 相同的 [MessageEvent]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
*
* @see syncFromEvent
* @see whileSelectMessages
* @see selectMessages
*/
@JvmSynthetic
suspend inline fun <reified M : Message> MessageEvent.nextMessageContaining(
timeoutMillis: Long = -1
): M {
return syncFromEvent<MessageEvent, MessageEvent>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContaining) }
.takeIf { this.message.anyIsInstance<M>() }
}.message.firstIsInstance()
}
@JvmSynthetic
inline fun <reified M : Message> MessageEvent.nextMessageContainingAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<M> {
return this.bot.async(coroutineContext) {
@Suppress("RemoveExplicitTypeArguments")
syncFromEvent<MessageEvent, MessageEvent>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingAsync) }
.takeIf { this.message.anyIsInstance<M>() }
}.message.firstIsInstance<M>()
}
}
/**
* 挂起当前协程, 等待下一条 [MessageEvent.sender] [MessageEvent.subject] [this] 相同并含有 [M] 类型的消息的 [MessageEvent]
*
* [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @return 指定类型的消息. 超时时返回 `null`
*
* @see syncFromEventOrNull
*/
@JvmSynthetic
suspend inline fun <reified M : Message> MessageEvent.nextMessageContainingOrNull(
timeoutMillis: Long = -1
): M? {
return syncFromEventOrNull<MessageEvent, MessageEvent>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNull) }
.takeIf { this.message.anyIsInstance<M>() }
}?.message?.firstIsInstance()
}
@JvmSynthetic
inline fun <reified M : Message> MessageEvent.nextMessageContainingOrNullAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<M?> {
return this.bot.async(coroutineContext) {
syncFromEventOrNull<MessageEvent, MessageEvent>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNullAsync) }
.takeIf { this.message.anyIsInstance<M>() }
}?.message?.firstIsInstance<M>()
}
}
@PlannedRemoval("1.2.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@Suppress("DEPRECATION_ERROR")
@JvmSynthetic
fun ContactMessage.isContextIdenticalWith(another: ContactMessage): Boolean {
return this.sender == another.sender && this.subject == another.subject && this.bot == another.bot
}

View File

@ -7,50 +7,45 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("unused")
@file:Suppress("unused", "DECLARATION_CANT_BE_INLINED")
package net.mamoe.mirai.message
import kotlinx.coroutines.io.ByteWriteChannel
import kotlinx.io.core.Input
import kotlinx.io.core.Output
import kotlinx.io.core.use
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.copyAndClose
import net.mamoe.mirai.utils.copyTo
import net.mamoe.mirai.message.data.MessageChain
import java.awt.image.BufferedImage
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.net.URL
/**
* 一条从服务器接收到的消息事件.
* JVM 平台相关扩展
* 消息事件在 JVM 平台的扩展
* @see MessageEventExtensions
*/
@Suppress("DEPRECATION")
@Deprecated(
message = "use ContactMessage",
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual constructor() :
MessagePacketBase<TSender, TSubject>() {
internal actual interface MessageEventPlatformExtensions<out TSender : User, out TSubject : Contact> {
actual val subject: TSubject
actual val sender: TSender
actual val message: MessageChain
actual val bot: Bot
// region 上传图片
@JvmSynthetic
suspend inline fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)
@JvmSynthetic
suspend inline fun uploadImage(image: URL): Image = subject.uploadImage(image)
@JvmSynthetic
suspend inline fun uploadImage(image: Input): Image = subject.uploadImage(image)
@JvmSynthetic
suspend inline fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
@JvmSynthetic
suspend inline fun uploadImage(image: File): Image = subject.uploadImage(image)
// endregion
@ -58,12 +53,16 @@ actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual c
// region 发送图片
@JvmSynthetic
suspend inline fun sendImage(image: BufferedImage): MessageReceipt<TSubject> = subject.sendImage(image)
@JvmSynthetic
suspend inline fun sendImage(image: URL): MessageReceipt<TSubject> = subject.sendImage(image)
@JvmSynthetic
suspend inline fun sendImage(image: Input): MessageReceipt<TSubject> = subject.sendImage(image)
@JvmSynthetic
suspend inline fun sendImage(image: InputStream): MessageReceipt<TSubject> = subject.sendImage(image)
@JvmSynthetic
suspend inline fun sendImage(image: File): MessageReceipt<TSubject> = subject.sendImage(image)
// endregion
@ -71,12 +70,16 @@ actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual c
// region 上传图片 (扩展)
@JvmSynthetic
suspend inline fun BufferedImage.upload(): Image = upload(subject)
@JvmSynthetic
suspend inline fun URL.uploadAsImage(): Image = uploadAsImage(subject)
@JvmSynthetic
suspend inline fun Input.uploadAsImage(): Image = uploadAsImage(subject)
@JvmSynthetic
suspend inline fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
@JvmSynthetic
suspend inline fun File.uploadAsImage(): Image = uploadAsImage(subject)
// endregion 上传图片 (扩展)
@ -84,12 +87,16 @@ actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual c
// region 发送图片 (扩展)
@JvmSynthetic
suspend inline fun BufferedImage.send(): MessageReceipt<TSubject> = sendTo(subject)
@JvmSynthetic
suspend inline fun URL.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
@JvmSynthetic
suspend inline fun Input.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
@JvmSynthetic
suspend inline fun InputStream.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
@JvmSynthetic
suspend inline fun File.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
// endregion 发送图片 (扩展)