Introduce ContactMessage to replace MessagePacket<*, *>

This commit is contained in:
Him188 2020-04-02 20:27:16 +08:00
parent 3773548983
commit 32553fad2b
22 changed files with 268 additions and 91 deletions

View File

@ -2,6 +2,13 @@
开发版本. 频繁更新, 不保证高稳定性
## `0.32.0`
- 将部分 internal API 从 `mirai-core` 移至 `mirai-core-qqandroid`
- 将部分意外 public 的 API 改为 internal.
- 使用 Kotlin 1.3.71, 兼容原使用 Kotlin 1.4-M1 编译的代码.
- 优化 `BotConfiguration`, 去掉 DSL 操作, 使用 `fileBasedDeviceInfo(filename)` 等函数替代. (兼容原操作方式, 计划于 `0.34.0` 删除)
- 调整长消息判定权重, 具体为: Chinese char=4, English char=1, Quote=700, Image=800, 其他消息类型转换为字符串后判断长度.
## `0.31.4` 2020/3/31
- 修复 At 在手机上显示错误的问题

View File

@ -75,7 +75,7 @@ internal class QQImpl(
@JvmSynthetic
@Suppress("DuplicatedCode")
override suspend fun sendMessage(message: Message): MessageReceipt<out QQ> {
override suspend fun sendMessage(message: Message): MessageReceipt<QQ> {
val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast()
if (event.isCancelled) {
throw EventCancelledException("cancelled by FriendMessageSendEvent")

View File

@ -66,7 +66,7 @@ actual abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI() {
* @return 消息回执. [引用回复][MessageReceipt.quote]仅群聊 [撤回][MessageReceipt.recall] 这条消息.
*/
@JvmSynthetic
actual abstract suspend fun sendMessage(message: Message): MessageReceipt<out Contact>
actual abstract suspend fun sendMessage(message: Message): MessageReceipt<Contact>
/**
* 上传一个图片以备发送.

View File

@ -62,14 +62,14 @@ actual abstract class ContactJavaFriendlyAPI {
*/
@Throws(EventCancelledException::class, IllegalStateException::class)
@JvmName("sendMessage")
open fun __sendMessageBlockingForJava__(message: Message): MessageReceipt<out Contact> {
open fun __sendMessageBlockingForJava__(message: Message): MessageReceipt<Contact> {
return runBlocking {
sendMessage(message)
}
}
@JvmName("sendMessage")
open fun __sendMessageBlockingForJava__(message: String): MessageReceipt<out Contact> {
open fun __sendMessageBlockingForJava__(message: String): MessageReceipt<Contact> {
return runBlocking { sendMessage(message) }
}
@ -143,7 +143,7 @@ actual abstract class ContactJavaFriendlyAPI {
* @see Contact.sendMessage
*/
@JvmName("sendMessageAsync")
open fun __sendMessageAsyncForJava__(message: Message): Future<MessageReceipt<out Contact>> {
open fun __sendMessageAsyncForJava__(message: Message): Future<MessageReceipt<Contact>> {
return future { sendMessage(message) }
}
@ -152,7 +152,7 @@ actual abstract class ContactJavaFriendlyAPI {
* @see Contact.sendMessage
*/
@JvmName("sendMessageAsync")
open fun __sendMessageAsyncForJava__(message: String): Future<MessageReceipt<out Contact>> {
open fun __sendMessageAsyncForJava__(message: String): Future<MessageReceipt<Contact>> {
return future { sendMessage(message) }
}

View File

@ -90,7 +90,7 @@ actual abstract class QQ : Contact(), CoroutineScope {
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
*/
@JvmSynthetic
actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<out QQ>
actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ>
/**
* 上传一个图片以备发送.

View File

@ -9,36 +9,48 @@
package net.mamoe.mirai.message
import android.graphics.Bitmap
import kotlinx.io.core.Input
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.utils.MiraiInternalAPI
import java.io.File
import java.io.InputStream
import java.net.URL
/**
* 平台相关扩展
*/
@Suppress("DEPRECATION")
@Deprecated(
message = "use ContactMessage",
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
@OptIn(MiraiInternalAPI::class)
actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual constructor() : MessagePacketBase<TSender, TSubject>() {
// suspend inline fun uploadImage(image: Bitmap): Image = subject.uploadImage(image)
//suspend inline fun uploadImage(image: URL): Image = subject.uploadImage(image)
//suspend inline fun uploadImage(image: Input): Image = subject.uploadImage(image)
//suspend inline fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
//suspend inline fun uploadImage(image: File): Image = subject.uploadImage(image)
actual sealed class MessagePacket<TSender : QQ, TSubject : Contact> actual constructor() :
MessagePacketBase<TSender, TSubject>() {
suspend inline fun uploadImage(image: Bitmap): Image = subject.uploadImage(image)
suspend inline fun uploadImage(image: URL): Image = subject.uploadImage(image)
suspend inline fun uploadImage(image: Input): Image = subject.uploadImage(image)
suspend inline fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
suspend inline fun uploadImage(image: File): Image = subject.uploadImage(image)
//// suspend inline fun sendImage(image: Bitmap) = subject.sendImage(image)
//suspend inline fun sendImage(image: URL) = subject.sendImage(image)
//suspend inline fun sendImage(image: Input) = subject.sendImage(image)
//suspend inline fun sendImage(image: InputStream) = subject.sendImage(image)
//suspend inline fun sendImage(image: File) = subject.sendImage(image)
suspend inline fun sendImage(image: Bitmap): MessageReceipt<TSubject> = subject.sendImage(image)
suspend inline fun sendImage(image: URL): MessageReceipt<TSubject> = subject.sendImage(image)
suspend inline fun sendImage(image: Input): MessageReceipt<TSubject> = subject.sendImage(image)
suspend inline fun sendImage(image: InputStream): MessageReceipt<TSubject> = subject.sendImage(image)
suspend inline fun sendImage(image: File): MessageReceipt<TSubject> = subject.sendImage(image)
//// suspend inline fun Bitmap.upload(): Image = upload(subject)
//suspend inline fun URL.uploadAsImage(): Image = uploadAsImage(subject)
//suspend inline fun Input.uploadAsImage(): Image = uploadAsImage(subject)
//suspend inline fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
//suspend inline fun File.uploadAsImage(): Image = uploadAsImage(subject)
suspend inline fun Bitmap.upload(): Image = upload(subject)
suspend inline fun URL.uploadAsImage(): Image = uploadAsImage(subject)
suspend inline fun Input.uploadAsImage(): Image = uploadAsImage(subject)
suspend inline fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
suspend inline fun File.uploadAsImage(): Image = uploadAsImage(subject)
//// suspend inline fun Bitmap.send() = sendTo(subject)
//suspend inline fun URL.sendAsImage() = sendAsImageTo(subject)
//suspend inline fun Input.sendAsImage() = sendAsImageTo(subject)
//suspend inline fun InputStream.sendAsImage() = sendAsImageTo(subject)
//suspend inline fun File.sendAsImage() = sendAsImageTo(subject)
suspend inline fun Bitmap.send(): MessageReceipt<TSubject> = sendTo(subject)
suspend inline fun URL.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
suspend inline fun Input.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
suspend inline fun InputStream.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
suspend inline fun File.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
}

View File

@ -29,7 +29,7 @@ import net.mamoe.mirai.utils.unsafeWeakRef
*/
@Suppress("FunctionName")
@OptIn(MiraiInternalAPI::class)
actual open class MessageReceipt<C : Contact> @OptIn(ExperimentalMessageSource::class)
actual open class MessageReceipt<out C : Contact> @OptIn(ExperimentalMessageSource::class)
actual constructor(
actual val source: MessageSource,
target: C,

View File

@ -69,7 +69,7 @@ expect abstract class Contact() : CoroutineScope, ContactJavaFriendlyAPI {
* @return 消息回执. [引用回复][MessageReceipt.quote]仅群聊 [撤回][MessageReceipt.recall] 这条消息.
*/
@JvmSynthetic
abstract suspend fun sendMessage(message: Message): MessageReceipt<out Contact>
abstract suspend fun sendMessage(message: Message): MessageReceipt<Contact>
/**
* 上传一个图片以备发送.

View File

@ -99,7 +99,7 @@ expect abstract class QQ() : Contact, CoroutineScope {
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
*/
@JvmSynthetic
abstract override suspend fun sendMessage(message: Message): MessageReceipt<out QQ>
abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ>
/**
* 上传一个图片以备发送.

View File

@ -10,7 +10,7 @@
package net.mamoe.mirai.event
import kotlinx.coroutines.*
import net.mamoe.mirai.message.MessagePacket
import net.mamoe.mirai.message.ContactMessage
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.isContextIdenticalWith
@ -59,7 +59,7 @@ import kotlin.jvm.JvmSynthetic
*/
@SinceMirai("0.29.0")
@Suppress("unused")
suspend inline fun <reified T : MessagePacket<*, *>> T.whileSelectMessages(
suspend inline fun <reified T : ContactMessage> T.whileSelectMessages(
timeoutMillis: Long = -1,
crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, Boolean>.() -> Unit
) = whileSelectMessagesImpl(timeoutMillis, selectBuilder)
@ -71,7 +71,7 @@ suspend inline fun <reified T : MessagePacket<*, *>> T.whileSelectMessages(
@MiraiExperimentalAPI
@SinceMirai("0.29.0")
@JvmName("selectMessages1")
suspend inline fun <reified T : MessagePacket<*, *>> T.selectMessagesUnit(
suspend inline fun <reified T : ContactMessage> T.selectMessagesUnit(
timeoutMillis: Long = -1,
crossinline selectBuilder: @MessageDsl MessageSelectBuilderUnit<T, Unit>.() -> Unit
) = selectMessagesImpl(timeoutMillis, true, selectBuilder)
@ -100,7 +100,7 @@ suspend inline fun <reified T : MessagePacket<*, *>> T.selectMessagesUnit(
@SinceMirai("0.29.0")
@Suppress("unused") // false positive
// @BuilderInference // https://youtrack.jetbrains.com/issue/KT-37716
suspend inline fun <reified T : MessagePacket<*, *>, R> T.selectMessages(
suspend inline fun <reified T : ContactMessage, R> T.selectMessages(
timeoutMillis: Long = -1,
// @BuilderInference
crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, R>.() -> Unit
@ -114,7 +114,7 @@ suspend inline fun <reified T : MessagePacket<*, *>, R> T.selectMessages(
* @see MessageSelectBuilderUnit 查看上层 API
*/
@SinceMirai("0.29.0")
abstract class MessageSelectBuilder<M : MessagePacket<*, *>, R> @PublishedApi internal constructor(
abstract class MessageSelectBuilder<M : ContactMessage, R> @PublishedApi internal constructor(
ownerMessagePacket: M,
stub: Any?,
subscriber: (M.(String) -> Boolean, MessageListener<M, Any?>) -> Unit
@ -194,7 +194,7 @@ abstract class MessageSelectBuilder<M : MessagePacket<*, *>, R> @PublishedApi in
* @see MessageSubscribersBuilder 查看上层 API
*/
@SinceMirai("0.29.0")
abstract class MessageSelectBuilderUnit<M : MessagePacket<*, *>, R> @PublishedApi internal constructor(
abstract class MessageSelectBuilderUnit<M : ContactMessage, R> @PublishedApi internal constructor(
private val ownerMessagePacket: M,
stub: Any?,
subscriber: (M.(String) -> Boolean, MessageListener<M, Any?>) -> Unit
@ -373,7 +373,7 @@ internal val SELECT_MESSAGE_STUB = Any()
@PublishedApi
@BuilderInference
@OptIn(ExperimentalTypeInference::class)
internal suspend inline fun <reified T : MessagePacket<*, *>, R> T.selectMessagesImpl(
internal suspend inline fun <reified T : ContactMessage, R> T.selectMessagesImpl(
timeoutMillis: Long = -1,
isUnit: Boolean,
@BuilderInference
@ -465,7 +465,7 @@ internal suspend inline fun <reified T : MessagePacket<*, *>, R> T.selectMessage
@Suppress("unused")
@PublishedApi
internal suspend inline fun <reified T : MessagePacket<*, *>> T.whileSelectMessagesImpl(
internal suspend inline fun <reified T : ContactMessage> T.whileSelectMessagesImpl(
timeoutMillis: Long = -1,
crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, Boolean>.() -> Unit
) {

View File

@ -20,9 +20,9 @@ import net.mamoe.mirai.contact.isAdministrator
import net.mamoe.mirai.contact.isOperator
import net.mamoe.mirai.contact.isOwner
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.MessagePacket
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.first
import net.mamoe.mirai.utils.SinceMirai
@ -34,7 +34,7 @@ import kotlin.coroutines.EmptyCoroutineContext
import kotlin.js.JsName
import kotlin.jvm.JvmName
typealias MessagePacketSubscribersBuilder = MessageSubscribersBuilder<MessagePacket<*, *>, Listener<MessagePacket<*, *>>, Unit, Unit>
typealias MessagePacketSubscribersBuilder = MessageSubscribersBuilder<ContactMessage, Listener<ContactMessage>, Unit, Unit>
/**
* 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
@ -53,7 +53,7 @@ fun <R> CoroutineScope.subscribeMessages(
}
return MessagePacketSubscribersBuilder(Unit)
{ filter, messageListener: MessageListener<MessagePacket<*, *>, Unit> ->
{ filter, messageListener: MessageListener<ContactMessage, Unit> ->
// subscribeAlways 即注册一个监听器. 这个监听器收到消息后就传递给 [messageListener]
// messageListener 即为 DSL 里 `contains(...) { }`, `startsWith(...) { }` 的代码块.
subscribeAlways(coroutineContext, concurrencyKind) {
@ -245,7 +245,7 @@ inline fun <reified E : BotEvent> Bot.incoming(
* 消息事件的处理器.
*
* :
* 接受者 T [MessagePacket]
* 接受者 T [ContactMessage]
* 参数 String 转为字符串了的消息 ([Message.toString])
*/
typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
@ -262,7 +262,7 @@ typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
*/
@Suppress("unused", "DSL_SCOPE_VIOLATION_WARNING")
@MessageDsl
open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, RR>(
open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
val stub: RR,
/**
* invoke 这个 lambda , 它将会把 [消息事件的处理器][MessageListener] 注册给事件, 并返回注册完成返回的监听器.

View File

@ -19,7 +19,7 @@ import net.mamoe.mirai.utils.unsafeWeakRef
class FriendMessage(
sender: QQ,
override val message: MessageChain
) : MessagePacket<QQ, QQ>(), BroadcastControllable {
) : ContactMessage(), BroadcastControllable {
override val sender: QQ by sender.unsafeWeakRef()
override val bot: Bot get() = sender.bot
override val subject: QQ get() = sender

View File

@ -27,7 +27,7 @@ class GroupMessage(
val permission: MemberPermission,
sender: Member,
override val message: MessageChain
) : MessagePacket<Member, Group>(), Event {
) : ContactMessage(), Event {
override val sender: Member by sender.unsafeWeakRef()
val group: Group get() = sender.group
override val bot: Bot get() = sender.bot

View File

@ -7,7 +7,14 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE", "unused")
@file:Suppress(
"EXPERIMENTAL_UNSIGNED_LITERALS",
"EXPERIMENTAL_API_USAGE",
"unused",
"INVISIBLE_REFERENCE",
"INVISIBLE_MEMBER"
)
@file:OptIn(MiraiInternalAPI::class)
package net.mamoe.mirai.message
@ -33,20 +40,42 @@ 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]
*
* @see isContextIdenticalWith 判断语境是否相同
*/
@Suppress("DEPRECATION")
@SinceMirai("0.32.0")
abstract class ContactMessage : MessagePacket<QQ, Contact>(), BotEvent
/**
* 一条从服务器接收到的消息事件.
* 请查看各平台的 `actual` 实现的说明.
*/
@OptIn(MiraiInternalAPI::class)
expect abstract class MessagePacket<TSender : QQ, TSubject : Contact>() : MessagePacketBase<TSender, TSubject>
@Suppress("DEPRECATION")
@Deprecated(
message = "use ContactMessage",
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
expect sealed class MessagePacket<TSender : QQ, TSubject : Contact>() : MessagePacketBase<TSender, TSubject>
/**
* 仅内部使用, 请使用 [MessagePacket]
* 仅内部使用, 请使用 [ContactMessage]
*/ // Tips: 在 IntelliJ 中 (左侧边栏) 打开 `Structure`, 可查看类结构
@Deprecated(
message = "use ContactMessage",
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
@MiraiInternalAPI
abstract class MessagePacketBase<TSender : QQ, TSubject : Contact> : Packet, BotEvent {
abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Packet, BotEvent {
/**
* 接受到这条消息的
*/
@ -91,11 +120,6 @@ abstract class MessagePacketBase<TSender : QQ, TSubject : Contact> : Packet, Bot
suspend inline fun reply(plain: String): MessageReceipt<TSubject> =
subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject>
@JvmName("reply1")
suspend inline fun String.reply(): MessageReceipt<TSubject> = reply(this)
@JvmName("reply1")
suspend inline fun Message.reply(): MessageReceipt<TSubject> = reply(this)
// endregion
// region 撤回
@ -131,6 +155,7 @@ abstract class MessagePacketBase<TSender : QQ, TSubject : Contact> : Packet, Bot
// endregion
// region 引用回复
/**
* 给这个消息事件的主体发送引用回复消息
* 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
@ -151,12 +176,11 @@ abstract class MessagePacketBase<TSender : QQ, TSubject : Contact> : Packet, Bot
@JvmName("reply2")
suspend inline fun MessageChain.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
/**
* 引用这个消息
*/
@ExperimentalMessageSource
inline fun MessageChain.quote(): QuoteReplyToSend = this.quote(sender)
// endregion
operator fun <M : Message> get(at: Message.Key<M>): M {
return this.message[at]
}
@ -189,13 +213,22 @@ abstract class MessagePacketBase<TSender : QQ, TSubject : Contact> : Packet, Bot
*/
suspend inline fun Image.channel(): ByteReadChannel = bot.openChannel(this)
// endregion
@Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)"))
@JvmName("reply1")
suspend inline fun String.reply(): MessageReceipt<TSubject> = reply(this)
@Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)"))
@JvmName("reply1")
suspend inline fun Message.reply(): MessageReceipt<TSubject> = reply(this)
}
/**
* 判断两个 [MessagePacket] [MessagePacket.sender] [MessagePacket.subject] 是否相同
*/
@SinceMirai("0.29.0")
fun MessagePacket<*, *>.isContextIdenticalWith(another: MessagePacket<*, *>): Boolean {
fun ContactMessage.isContextIdenticalWith(another: ContactMessage): Boolean {
return this.sender == another.sender && this.subject == another.subject && this.bot == another.bot
}
@ -209,7 +242,8 @@ fun MessagePacket<*, *>.isContextIdenticalWith(another: MessagePacket<*, *>): Bo
*
* @see subscribingGet
*/
suspend inline fun <reified P : MessagePacket<*, *>> P.nextMessage(
@JvmSynthetic
suspend inline fun <reified P : ContactMessage> P.nextMessage(
timeoutMillis: Long = -1,
crossinline filter: suspend P.(P) -> Boolean
): MessageChain {
@ -229,7 +263,8 @@ suspend inline fun <reified P : MessagePacket<*, *>> P.nextMessage(
*
* @see subscribingGetOrNull
*/
suspend inline fun <reified P : MessagePacket<*, *>> P.nextMessageOrNull(
@JvmSynthetic
suspend inline fun <reified P : ContactMessage> P.nextMessageOrNull(
timeoutMillis: Long = -1,
crossinline filter: suspend P.(P) -> Boolean
): MessageChain? {
@ -247,7 +282,8 @@ suspend inline fun <reified P : MessagePacket<*, *>> P.nextMessageOrNull(
*
* @see subscribingGet
*/
suspend inline fun <reified P : MessagePacket<*, *>> P.nextMessage(
@JvmSynthetic
suspend inline fun <reified P : ContactMessage> P.nextMessage(
timeoutMillis: Long = -1
): MessageChain {
return subscribingGet<P, P>(timeoutMillis) {
@ -259,7 +295,8 @@ suspend inline fun <reified P : MessagePacket<*, *>> P.nextMessage(
* @see nextMessage
* @throws TimeoutCancellationException
*/
inline fun <reified P : MessagePacket<*, *>> P.nextMessageAsync(
@JvmSynthetic
inline fun <reified P : ContactMessage> P.nextMessageAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<MessageChain> {
@ -273,7 +310,8 @@ inline fun <reified P : MessagePacket<*, *>> P.nextMessageAsync(
/**
* @see nextMessage
*/
inline fun <reified P : MessagePacket<*, *>> P.nextMessageAsync(
@JvmSynthetic
inline fun <reified P : ContactMessage> P.nextMessageAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext,
crossinline filter: suspend P.(P) -> Boolean
@ -296,7 +334,8 @@ inline fun <reified P : MessagePacket<*, *>> P.nextMessageAsync(
*
* @see subscribingGetOrNull
*/
suspend inline fun <reified P : MessagePacket<*, *>> P.nextMessageOrNull(
@JvmSynthetic
suspend inline fun <reified P : ContactMessage> P.nextMessageOrNull(
timeoutMillis: Long = -1
): MessageChain? {
return subscribingGetOrNull<P, P>(timeoutMillis) {
@ -307,7 +346,8 @@ suspend inline fun <reified P : MessagePacket<*, *>> P.nextMessageOrNull(
/**
* @see nextMessageOrNull
*/
inline fun <reified P : MessagePacket<*, *>> P.nextMessageOrNullAsync(
@JvmSynthetic
inline fun <reified P : ContactMessage> P.nextMessageOrNullAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<MessageChain?> {
@ -329,21 +369,23 @@ inline fun <reified P : MessagePacket<*, *>> P.nextMessageOrNullAsync(
* @see whileSelectMessages
* @see selectMessages
*/
suspend inline fun <reified M : Message> MessagePacket<*, *>.nextMessageContaining(
@JvmSynthetic
suspend inline fun <reified M : Message> ContactMessage.nextMessageContaining(
timeoutMillis: Long = -1
): M {
return subscribingGet<MessagePacket<*, *>, MessagePacket<*, *>>(timeoutMillis) {
return subscribingGet<ContactMessage, ContactMessage>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContaining) }
}.message.first()
}
inline fun <reified M : Message> MessagePacket<*, *>.nextMessageContainingAsync(
@JvmSynthetic
inline fun <reified M : Message> ContactMessage.nextMessageContainingAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<M> {
return this.bot.async(coroutineContext) {
@Suppress("RemoveExplicitTypeArguments")
subscribingGet<MessagePacket<*, *>, MessagePacket<*, *>>(timeoutMillis) {
subscribingGet<ContactMessage, ContactMessage>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingAsync) }
}.message.first<M>()
}
@ -359,21 +401,30 @@ inline fun <reified M : Message> MessagePacket<*, *>.nextMessageContainingAsync(
*
* @see subscribingGetOrNull
*/
suspend inline fun <reified M : Message> MessagePacket<*, *>.nextMessageContainingOrNull(
@JvmSynthetic
suspend inline fun <reified M : Message> ContactMessage.nextMessageContainingOrNull(
timeoutMillis: Long = -1
): M? {
return subscribingGetOrNull<MessagePacket<*, *>, MessagePacket<*, *>>(timeoutMillis) {
return subscribingGetOrNull<ContactMessage, ContactMessage>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNull) }
}?.message?.first()
}
inline fun <reified M : Message> MessagePacket<*, *>.nextMessageContainingOrNullAsync(
@JvmSynthetic
inline fun <reified M : Message> ContactMessage.nextMessageContainingOrNullAsync(
timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext
): Deferred<M?> {
return this.bot.async(coroutineContext) {
subscribingGetOrNull<MessagePacket<*, *>, MessagePacket<*, *>>(timeoutMillis) {
subscribingGetOrNull<ContactMessage, ContactMessage>(timeoutMillis) {
takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNullAsync) }
}?.message?.first<M>()
}
}
@Suppress("DEPRECATION")
@Deprecated(level = DeprecationLevel.HIDDEN, message = "for binary compatibility")
fun MessagePacket<*, *>.isContextIdenticalWith(another: MessagePacket<*, *>): Boolean {
return (this as ContactMessage).isContextIdenticalWith(another as ContactMessage)
}

View File

@ -32,7 +32,7 @@ import kotlin.jvm.JvmSynthetic
* @see MessageReceipt.sourceSequenceId 源序列号
* @see MessageReceipt.sourceTime 源时间
*/
expect open class MessageReceipt<C : Contact> @OptIn(ExperimentalMessageSource::class) constructor(
expect open class MessageReceipt<out C : Contact> @OptIn(ExperimentalMessageSource::class) constructor(
source: MessageSource,
target: C,
botAsMember: Member?
@ -100,7 +100,8 @@ expect open class MessageReceipt<C : Contact> @OptIn(ExperimentalMessageSource::
*/
@get:JvmSynthetic
@ExperimentalMessageSource
inline val MessageReceipt<*>.sourceId: Long get() = this.source.id
inline val MessageReceipt<*>.sourceId: Long
get() = this.source.id
/**
* 获取源消息 [MessageSource.sequenceId]
@ -109,7 +110,8 @@ inline val MessageReceipt<*>.sourceId: Long get() = this.source.id
*/
@get:JvmSynthetic
@ExperimentalMessageSource
inline val MessageReceipt<*>.sourceSequenceId: Int get() = this.source.sequenceId
inline val MessageReceipt<*>.sourceSequenceId: Int
get() = this.source.sequenceId
/**
* 获取源消息 [MessageSource.time]
@ -118,13 +120,14 @@ inline val MessageReceipt<*>.sourceSequenceId: Int get() = this.source.sequenceI
*/
@get:JvmSynthetic
@ExperimentalMessageSource
inline val MessageReceipt<*>.sourceTime: Long get() = this.source.time
inline val MessageReceipt<*>.sourceTime: Long
get() = this.source.time
suspend inline fun MessageReceipt<out Contact>.quoteReply(message: Message) {
suspend inline fun MessageReceipt<*>.quoteReply(message: Message) {
return this.quoteReply(message.asMessageChain())
}
suspend inline fun MessageReceipt<out Contact>.quoteReply(message: String) {
suspend inline fun MessageReceipt<*>.quoteReply(message: String) {
return this.quoteReply(message.toMessage().asMessageChain())
}

View File

@ -14,4 +14,4 @@ package net.mamoe.mirai.utils
*/
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.BINARY)
annotation class LazyProperty
internal annotation class LazyProperty

View File

@ -0,0 +1,99 @@
/*
* 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("unused", "NOTHING_TO_INLINE")
package net.mamoe.mirai.utils
/*
/**
* SoftRef that `getValue` for delegation throws an [IllegalStateException] if the referent is released by GC. Therefore it returns notnull value only
*/
class UnsafeSoftRef<T>(private val softRef: SoftRef<T>) {
fun get(): T = softRef.get() ?: error("SoftRef is released")
fun clear() = softRef.clear()
}
/**
* Provides delegate value.
*
* ```kotlin
* val bot: Bot by param.unsafeSoftRef()
* ```
*/
@JvmSynthetic
inline operator fun <T> UnsafeSoftRef<T>.getValue(thisRef: Any?, property: KProperty<*>): T = get()
/**
* Soft Reference.
* On JVM, it is implemented as a typealias referring to `SoftReference` from JDK.
*
* Details:
* On JVM, instances of objects are stored in the JVM Heap and are accessed via references.
* GC(garbage collection) can automatically collect and release the memory used by objects that are not directly referred by any other.
* [SoftRef] will keep the reference until JVM run out of memory.
*
* @see softRef provides a SoftRef
* @see unsafeSoftRef provides a UnsafeSoftRef
*/
@SinceMirai("0.32.0")
expect class SoftRef<T>(referent: T) {
fun get(): T?
fun clear()
}
/**
* Indicates that the property is delegated by a [SoftRef]
*
* @see softRef
*/
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
annotation class SoftRefProperty
/**
* Provides a soft reference to [this]
* The `getValue` for delegation returns [this] when [this] is not released by GC
*/
@JvmSynthetic
inline fun <T> T.softRef(): SoftRef<T> = SoftRef(this)
/**
* Constructs an unsafe inline delegate for [this]
*/
@JvmSynthetic
inline fun <T> SoftRef<T>.unsafe(): UnsafeSoftRef<T> = UnsafeSoftRef(this)
/**
* Provides a soft reference to [this].
* The `getValue` for delegation throws an [IllegalStateException] if the referent is released by GC. Therefore it returns notnull value only
*
* **UNSTABLE API**: It is strongly suggested not to use this api
*/
@JvmSynthetic
inline fun <T> T.unsafeSoftRef(): UnsafeSoftRef<T> = UnsafeSoftRef(this.softRef())
/**
* Provides delegate value.
*
* ```kotlin
* val bot: Bot? by param.softRef()
* ```
*/
@JvmSynthetic
inline operator fun <T> SoftRef<T>.getValue(thisRef: Any?, property: KProperty<*>): T? = this.get()
/**
* Call the block if the referent is absent
*/
@JvmSynthetic
inline fun <T, R> SoftRef<T>.ifAbsent(block: (T) -> R): R? = this.get()?.let(block)
*/

View File

@ -65,7 +65,7 @@ actual abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI() {
* @return 消息回执. [引用回复][MessageReceipt.quote]仅群聊 [撤回][MessageReceipt.recall] 这条消息.
*/
@JvmSynthetic //
actual abstract suspend fun sendMessage(message: Message): MessageReceipt<out Contact>
actual abstract suspend fun sendMessage(message: Message): MessageReceipt<Contact>
/**
* 上传一个图片以备发送.

View File

@ -61,12 +61,12 @@ actual abstract class ContactJavaFriendlyAPI {
*/
@Throws(EventCancelledException::class, IllegalStateException::class)
@JvmName("sendMessage")
open fun __sendMessageBlockingForJava__(message: Message): MessageReceipt<out Contact> {
open fun __sendMessageBlockingForJava__(message: Message): MessageReceipt<Contact> {
return runBlocking { sendMessage(message) }
}
@JvmName("sendMessage")
open fun __sendMessageBlockingForJava__(message: String): MessageReceipt<out Contact> {
open fun __sendMessageBlockingForJava__(message: String): MessageReceipt<Contact> {
return runBlocking { sendMessage(message) }
}
@ -140,7 +140,7 @@ actual abstract class ContactJavaFriendlyAPI {
* @see Contact.sendMessage
*/
@JvmName("sendMessageAsync")
open fun __sendMessageAsyncForJava__(message: Message): Future<MessageReceipt<out Contact>> {
open fun __sendMessageAsyncForJava__(message: Message): Future<MessageReceipt<Contact>> {
return future { sendMessage(message) }
}
@ -149,7 +149,7 @@ actual abstract class ContactJavaFriendlyAPI {
* @see Contact.sendMessage
*/
@JvmName("sendMessageAsync")
open fun __sendMessageAsyncForJava__(message: String): Future<MessageReceipt<out Contact>> {
open fun __sendMessageAsyncForJava__(message: String): Future<MessageReceipt<Contact>> {
return future { sendMessage(message) }
}

View File

@ -90,7 +90,7 @@ actual abstract class QQ : Contact(), CoroutineScope {
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
*/
@JvmSynthetic
actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<out QQ>
actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ>
/**
* 上传一个图片以备发送.

View File

@ -32,8 +32,13 @@ import java.net.URL
* 一条从服务器接收到的消息事件.
* JVM 平台相关扩展
*/
@Suppress("DEPRECATION")
@Deprecated(
message = "use ContactMessage",
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual constructor() : MessagePacketBase<TSender, TSubject>() {
actual sealed class MessagePacket<TSender : QQ, TSubject : Contact> actual constructor() : MessagePacketBase<TSender, TSubject>() {
// region 上传图片
suspend inline fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)

View File

@ -29,7 +29,7 @@ import net.mamoe.mirai.utils.unsafeWeakRef
*/
@Suppress("FunctionName")
@OptIn(MiraiInternalAPI::class)
actual open class MessageReceipt<C : Contact> @OptIn(ExperimentalMessageSource::class)
actual open class MessageReceipt<out C : Contact> @OptIn(ExperimentalMessageSource::class)
actual constructor(
actual val source: MessageSource,
target: C,