Implement MessagePreSendEvent and MessagePostSendEvent.

Deprecate `MessageSendEvent`
Fix #339
This commit is contained in:
Him188 2020-06-14 18:43:21 +08:00
parent 4c79d4cd18
commit 6f0853e6e4
11 changed files with 623 additions and 271 deletions

View File

@ -33,6 +33,7 @@ import net.mamoe.mirai.event.events.ImageUploadEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.OfflineFriendImage
import net.mamoe.mirai.message.data.isContentNotEmpty
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.highway.postImage
@ -87,7 +88,11 @@ internal class FriendImpl(
@Suppress("DuplicatedCode")
override suspend fun sendMessage(message: Message): MessageReceipt<Friend> {
require(message.isContentNotEmpty()) { "message is empty" }
return sendMessageImpl(this, message).also {
return sendMessageImpl(
message,
friendReceiptConstructor = { MessageReceipt(it, this, null) },
tReceiptConstructor = { MessageReceipt(it, this, null) }
).also {
logMessageSent(message)
}
}
@ -101,64 +106,63 @@ internal class FriendImpl(
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
}
bot.network.run {
val response = LongConn.OffPicUp(
val response = bot.network.run {
LongConn.OffPicUp(
bot.client, Cmd0x352.TryUpImgReq(
srcUin = bot.id.toInt(),
dstUin = id.toInt(),
fileId = 0,
fileMd5 = @Suppress("INVISIBLE_MEMBER") image.md5,
fileSize = @Suppress("INVISIBLE_MEMBER")
image.input.size.toInt(),
fileName = @Suppress("INVISIBLE_MEMBER") image.md5.toUHexString("") + "." + ExternalImage.defaultFormatName,
fileMd5 = image.md5,
fileSize = image.input.size.toInt(),
fileName = image.md5.toUHexString("") + "." + ExternalImage.defaultFormatName,
imgOriginal = 1
)
).sendAndExpect<LongConn.OffPicUp.Response>()
}
@Suppress("UNCHECKED_CAST", "DEPRECATION", "INVISIBLE_MEMBER")
return when (response) {
is LongConn.OffPicUp.Response.FileExists -> net.mamoe.mirai.message.data.OfflineFriendImage(response.resourceId)
.also {
ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
}
is LongConn.OffPicUp.Response.RequireUpload -> {
bot.network.logger.verbose {
"[Http] Uploading friend image, size=${image.input.size.sizeToString()}"
}
val time = measureTime {
MiraiPlatformUtils.Http.postImage(
"0x6ff0070",
bot.id,
null,
imageInput = image.input,
uKeyHex = response.uKey.toUHexString("")
)
}
bot.network.logger.verbose {
"[Http] Uploading friend image: succeed at ${(image.input.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s"
}
/*
HighwayHelper.uploadImageToServers(
bot,
response.serverIp.zip(response.serverPort),
response.uKey,
image,
kind = "friend",
commandId = 1
)*/
// 为什么不能 ??
return net.mamoe.mirai.message.data.OfflineFriendImage(response.resourceId).also {
ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
}
@Suppress("UNCHECKED_CAST", "DEPRECATION")
when (response) {
is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage(response.resourceId)
.also {
ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
}
is LongConn.OffPicUp.Response.Failed -> {
ImageUploadEvent.Failed(this@FriendImpl, image, -1, response.message).broadcast()
error(response.message)
is LongConn.OffPicUp.Response.RequireUpload -> {
bot.network.logger.verbose {
"[Http] Uploading friend image, size=${image.input.size.sizeToString()}"
}
val time = measureTime {
MiraiPlatformUtils.Http.postImage(
"0x6ff0070",
bot.id,
null,
imageInput = image.input,
uKeyHex = response.uKey.toUHexString("")
)
}
bot.network.logger.verbose {
"[Http] Uploading friend image: succeed at ${(image.input.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s"
}
/*
HighwayHelper.uploadImageToServers(
bot,
response.serverIp.zip(response.serverPort),
response.uKey,
image,
kind = "friend",
commandId = 1
)*/
// 为什么不能 ??
OfflineFriendImage(response.resourceId).also {
ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
}
}
is LongConn.OffPicUp.Response.Failed -> {
ImageUploadEvent.Failed(this@FriendImpl, image, -1, response.message).broadcast()
error(response.message)
}
}
} finally {

View File

@ -22,7 +22,6 @@ import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.QQAndroidBot
@ -33,6 +32,7 @@ import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.createToGroup
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService
import net.mamoe.mirai.qqandroid.utils.estimateLength
import net.mamoe.mirai.utils.*
@ -304,94 +304,92 @@ internal class GroupImpl(
check(message.nodeList.size < 200) {
throw MessageTooLargeException(
this, message, message,
"ForwardMessage allows up to 200 nodes, but found ${message.nodeList.size}")
"ForwardMessage allows up to 200 nodes, but found ${message.nodeList.size}"
)
}
return bot.lowLevelSendGroupLongOrForwardMessage(this.id, message.nodeList, false, message)
}
val msg: MessageChain
val msg: MessageChain = if (message !is LongMessage && message !is ForwardMessageInternal) {
val chain = kotlin.runCatching {
GroupMessagePreSendEvent(this, message).broadcast()
}.onSuccess {
check(!it.isCancelled) {
throw EventCancelledException("cancelled by GroupMessagePreSendEvent")
}
}.getOrElse {
throw EventCancelledException("exception thrown when broadcasting GroupMessagePreSendEvent", it)
}.message.asMessageChain()
if (message !is LongMessage && message !is ForwardMessageInternal) {
val event = GroupMessageSendEvent(this, message.asMessageChain()).broadcast()
if (event.isCancelled) {
throw EventCancelledException("cancelled by GroupMessageSendEvent")
}
val length = event.message.estimateLength(703) // 阈值为700左右限制到3的倍数
val length = chain.estimateLength(703) // 阈值为700左右限制到3的倍数
var imageCnt = 0 // 通过下方逻辑短路延迟计算
if (length > 5000 || event.message.count { it is Image }.apply { imageCnt = this } > 50) {
if (length > 5000 || chain.count { it is Image }.apply { imageCnt = this } > 50) {
throw MessageTooLargeException(
this,
message,
event.message,
"message(${event.message.joinToString(
"",
limit = 10
)}) is too large. Allow up to 50 images or 5000 chars"
this, message, chain,
"message(${chain.joinToString("", limit = 10)}) is too large. Allow up to 50 images or 5000 chars"
)
}
if (length > 702 || imageCnt > 2) {
return bot.lowLevelSendGroupLongOrForwardMessage(this.id,
listOf(ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds.toInt(),
message = event.message,
senderName = bot.nick)
return bot.lowLevelSendGroupLongOrForwardMessage(
this.id,
listOf(
ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds.toInt(),
message = chain,
senderName = bot.nick
)
),
true, null)
true, null
)
}
chain
} else message.asMessageChain()
msg = event.message
} else msg = message.asMessageChain()
msg.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
lateinit var source: MessageSourceToGroupImpl
bot.network.run {
val response: MessageSvcPbSendMsg.Response = MessageSvcPbSendMsg.createToGroup(
val result = bot.network.runCatching {
val source: MessageSourceToGroupImpl
MessageSvcPbSendMsg.createToGroup(
bot.client,
this@GroupImpl,
msg,
isForward
) {
source = it
}.sendAndExpect()
if (response is MessageSvcPbSendMsg.Response.Failed) {
when (response.resultType) {
120 -> throw BotIsBeingMutedException(this@GroupImpl)
34 -> {
kotlin.runCatching { // allow retry once
return bot.lowLevelSendGroupLongOrForwardMessage(
id, listOf(
ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds.toInt(),
message = msg,
senderName = bot.nick
)
), true, null)
}.getOrElse {
throw IllegalStateException("internal error: send message failed(34)", it)
}
}
else -> error("send message failed: $response")
}.sendAndExpect<MessageSvcPbSendMsg.Response>().let {
check(it is MessageSvcPbSendMsg.Response.SUCCESS) {
"Send temp message failed: $it"
}
}
}
try {
source.ensureSequenceIdAvailable()
} catch (e: Exception) {
bot.network.logger.warning {
"Timeout awaiting sequenceId for group message(${message.contentToString()
.take(10)}). Some features may not work properly"
try {
source.ensureSequenceIdAvailable()
} catch (e: Exception) {
bot.network.logger.warning {
"Timeout awaiting sequenceId for group message(${message.contentToString()
.take(10)}). Some features may not work properly"
}
bot.network.logger.warning(e)
}
bot.network.logger.warning(e)
MessageReceipt(source, this@GroupImpl, botAsMember)
}
return MessageReceipt(source, this, botAsMember)
result.fold(
onSuccess = {
GroupMessagePostSendEvent(this, msg, null, it)
},
onFailure = {
GroupMessagePostSendEvent(this, msg, it, null)
}
).broadcast()
return result.getOrThrow()
}
@Suppress("DEPRECATION")

View File

@ -28,6 +28,7 @@ import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.createToTemp
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.currentTimeSeconds
import net.mamoe.mirai.utils.getValue
@ -54,34 +55,59 @@ internal class MemberImpl constructor(
override val id: Long = qq.id
override val nick: String = qq.nick
@Suppress("UNCHECKED_CAST")
@JvmSynthetic
override suspend fun sendMessage(message: Message): MessageReceipt<Member> {
require(message.isContentNotEmpty()) { "message is empty" }
return (this.asFriendOrNull()?.sendMessageImpl(this, message) ?: sendMessageImpl(message))
.also { logMessageSent(message) }
val asFriend = this.asFriendOrNull()
return (asFriend?.sendMessageImpl(
message,
friendReceiptConstructor = { MessageReceipt(it, asFriend, null) },
tReceiptConstructor = { MessageReceipt(it, this, null) }
) ?: sendMessageImpl(message)).also { logMessageSent(message) }
}
private suspend fun sendMessageImpl(message: Message): MessageReceipt<Member> {
val event = MessageSendEvent.TempMessageSendEvent(this, message.asMessageChain()).broadcast()
if (event.isCancelled) {
throw EventCancelledException("cancelled by TempMessageSendEvent")
}
event.message.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
val chain = kotlin.runCatching {
TempMessagePreSendEvent(this, message).broadcast()
}.onSuccess {
check(!it.isCancelled) {
throw EventCancelledException("cancelled by TempMessagePreSendEvent")
}
}.getOrElse {
throw EventCancelledException("exception thrown when broadcasting TempMessagePreSendEvent", it)
}.message.asMessageChain()
lateinit var source: MessageSourceToTempImpl
bot.network.run {
check(
MessageSvcPbSendMsg.createToTemp(
bot.client,
this@MemberImpl,
message.asMessageChain()
) {
source = it
}.sendAndExpect<MessageSvcPbSendMsg.Response>() is MessageSvcPbSendMsg.Response.SUCCESS
) { "send message failed" }
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
val result = bot.network.runCatching {
val source: MessageSourceToTempImpl
MessageSvcPbSendMsg.createToTemp(
bot.client,
this@MemberImpl,
chain
) {
source = it
}.sendAndExpect<MessageSvcPbSendMsg.Response>().let {
check(it is MessageSvcPbSendMsg.Response.SUCCESS) {
"Send temp message failed: $it"
}
}
MessageReceipt(source, this@MemberImpl, null)
}
return MessageReceipt(source, this, null)
result.fold(
onSuccess = {
TempMessagePostSendEvent(this, chain, null, it)
},
onFailure = {
TempMessagePostSendEvent(this, chain, it, null)
}
).broadcast()
return result.getOrThrow()
}
@JvmSynthetic

View File

@ -12,40 +12,72 @@ package net.mamoe.mirai.qqandroid.contact
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.MessageSendEvent
import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
import net.mamoe.mirai.message.*
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.QuoteReply
import net.mamoe.mirai.message.data.asMessageChain
import net.mamoe.mirai.message.data.firstIsInstanceOrNull
import net.mamoe.mirai.qqandroid.asQQAndroidBot
import net.mamoe.mirai.qqandroid.message.MessageSourceToFriendImpl
import net.mamoe.mirai.qqandroid.message.ensureSequenceIdAvailable
import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.createToFriend
import net.mamoe.mirai.utils.verbose
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
internal suspend fun <T : Contact> Friend.sendMessageImpl(generic: T, message: Message): MessageReceipt<T> {
val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast()
if (event.isCancelled) {
throw EventCancelledException("cancelled by FriendMessageSendEvent")
}
event.message.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
lateinit var source: MessageSourceToFriendImpl
internal suspend fun <T : User> Friend.sendMessageImpl(
message: Message,
friendReceiptConstructor: (MessageSourceToFriendImpl) -> MessageReceipt<Friend>,
tReceiptConstructor: (MessageSourceToFriendImpl) -> MessageReceipt<T>
): MessageReceipt<T> {
contract { callsInPlace(friendReceiptConstructor, InvocationKind.EXACTLY_ONCE) }
val bot = bot.asQQAndroidBot()
bot.network.run {
check(
MessageSvcPbSendMsg.createToFriend(
bot.asQQAndroidBot().client,
this@sendMessageImpl,
event.message
) {
source = it
}.sendAndExpect<MessageSvcPbSendMsg.Response>() is MessageSvcPbSendMsg.Response.SUCCESS
) { "send message failed" }
val chain = kotlin.runCatching {
FriendMessagePreSendEvent(this, message).broadcast()
}.onSuccess {
check(!it.isCancelled) {
throw EventCancelledException("cancelled by FriendMessagePreSendEvent")
}
}.getOrElse {
throw EventCancelledException("exception thrown when broadcasting FriendMessagePreSendEvent", it)
}.message.asMessageChain()
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
lateinit var source: MessageSourceToFriendImpl
val result = bot.network.runCatching {
MessageSvcPbSendMsg.createToFriend(
bot.client,
this@sendMessageImpl,
chain
) {
source = it
}.sendAndExpect<MessageSvcPbSendMsg.Response>().let {
check(it is MessageSvcPbSendMsg.Response.SUCCESS) {
"Send temp message failed: $it"
}
}
friendReceiptConstructor(source)
}
return MessageReceipt(source, generic, null)
result.fold(
onSuccess = {
FriendMessagePostSendEvent(this, chain, null, it)
},
onFailure = {
FriendMessagePostSendEvent(this, chain, it, null)
}
).broadcast()
result.getOrThrow()
return tReceiptConstructor(source)
}
internal fun Contact.logMessageSent(message: Message) {

View File

@ -34,6 +34,8 @@ import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
import net.mamoe.mirai.utils.currentTimeSeconds
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.math.absoluteValue
import kotlin.random.Random
@ -52,34 +54,11 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
}
}
inline fun createToFriend(
client: QQAndroidClient,
qq: Friend,
message: MessageChain,
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
): OutgoingPacket {
val rand = Random.nextInt().absoluteValue
val source = MessageSourceToFriendImpl(
internalId = rand,
sender = client.bot,
target = qq,
time = currentTimeSeconds.toInt(),
sequenceId = client.nextFriendSeq(),
originalMessage = message
)
sourceCallback(source)
return createToFriend(
client,
qq.id,
message,
source)
}
/**
* 发送好友消息
*/
@Suppress("FunctionName")
private fun createToFriend(
internal fun createToFriendImpl(
client: QQAndroidClient,
toUin: Long,
message: MessageChain,
@ -106,33 +85,10 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
}
inline fun createToTemp(
client: QQAndroidClient,
member: Member,
message: MessageChain,
sourceCallback: (MessageSourceToTempImpl) -> Unit
): OutgoingPacket {
val source = MessageSourceToTempImpl(
internalId = Random.nextInt().absoluteValue,
sender = client.bot,
target = member,
time = currentTimeSeconds.toInt(),
sequenceId = client.atomicNextMessageSequenceId(),
originalMessage = message
)
sourceCallback(source)
return createToTemp(
client,
(member.group as GroupImpl).uin,
member.id,
message,
source)
}
/**
* 发送临时消息
*/
private fun createToTemp(
internal fun createToTempImpl(
client: QQAndroidClient,
groupUin: Long,
toUin: Long,
@ -158,37 +114,11 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
}
inline fun createToGroup(
client: QQAndroidClient,
group: Group,
message: MessageChain,
isForward: Boolean,
sourceCallback: (MessageSourceToGroupImpl) -> Unit
): OutgoingPacket {
val source = MessageSourceToGroupImpl(
group,
internalId = Random.nextInt().absoluteValue,
sender = client.bot,
target = group,
time = currentTimeSeconds.toInt(),
originalMessage = message//,
// sourceMessage = message
)
sourceCallback(source)
return createToGroup(
client,
group.id,
message,
isForward,
source)
}
/**
* 发送群消息
*/
@Suppress("FunctionName")
private fun createToGroup(
internal fun createToGroupImpl(
client: QQAndroidClient,
groupCode: Long,
message: MessageChain,
@ -231,7 +161,91 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
Response.Failed(
response.result,
response.errtype,
response.errmsg)
response.errmsg
)
}
}
}
internal inline fun MessageSvcPbSendMsg.createToTemp(
client: QQAndroidClient,
member: Member,
message: MessageChain,
crossinline sourceCallback: (MessageSourceToTempImpl) -> Unit
): OutgoingPacket {
contract {
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
}
val source = MessageSourceToTempImpl(
internalId = Random.nextInt().absoluteValue,
sender = client.bot,
target = member,
time = currentTimeSeconds.toInt(),
sequenceId = client.atomicNextMessageSequenceId(),
originalMessage = message
)
sourceCallback(source)
return createToTempImpl(
client,
(member.group as GroupImpl).uin,
member.id,
message,
source
)
}
internal inline fun MessageSvcPbSendMsg.createToFriend(
client: QQAndroidClient,
qq: Friend,
message: MessageChain,
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
): OutgoingPacket {
contract {
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
}
val rand = Random.nextInt().absoluteValue
val source = MessageSourceToFriendImpl(
internalId = rand,
sender = client.bot,
target = qq,
time = currentTimeSeconds.toInt(),
sequenceId = client.nextFriendSeq(),
originalMessage = message
)
sourceCallback(source)
return createToFriendImpl(
client,
qq.id,
message,
source
)
}
internal inline fun MessageSvcPbSendMsg.createToGroup(
client: QQAndroidClient,
group: Group,
message: MessageChain,
isForward: Boolean,
crossinline sourceCallback: (MessageSourceToGroupImpl) -> Unit
): OutgoingPacket {
contract {
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
}
val source = MessageSourceToGroupImpl(
group,
internalId = Random.nextInt().absoluteValue,
sender = client.bot,
target = group,
time = currentTimeSeconds.toInt(),
originalMessage = message//,
// sourceMessage = message
)
sourceCallback(source)
return createToGroupImpl(
client,
group.id,
message,
isForward,
source
)
}

View File

@ -16,13 +16,11 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import net.mamoe.mirai.Bot
import net.mamoe.mirai.JavaFriendlyAPI
import net.mamoe.mirai.event.events.BeforeImageUploadEvent
import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.ImageUploadEvent
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.quote
import net.mamoe.mirai.message.recall
import net.mamoe.mirai.recall
import net.mamoe.mirai.recallIn
import net.mamoe.mirai.utils.ExternalImage
@ -57,8 +55,8 @@ abstract class Contact : ContactOrBot, CoroutineScope, ContactJavaFriendlyAPI {
*
* 单条消息最大可发送 4500 字符或 50 张图片.
*
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送消息事件. cancellable
* @see MessagePreSendEvent 发送消息前事件
* @see MessagePostSendEvent 发送消息事件
*
* @throws EventCancelledException 当发送消息事件被取消时抛出
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
@ -71,7 +69,7 @@ abstract class Contact : ContactOrBot, CoroutineScope, ContactJavaFriendlyAPI {
abstract suspend fun sendMessage(message: Message): MessageReceipt<Contact>
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@kotlin.internal.InlineOnly
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<Contact> {
return sendMessage(message.toMessage())

View File

@ -14,12 +14,14 @@ package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope
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.event.events.FriendMessagePostSendEvent
import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
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.isContentEmpty
import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.message.recall
import kotlin.jvm.JvmSynthetic
/**
@ -55,8 +57,8 @@ abstract class Friend : User(), CoroutineScope {
*
* 单条消息最大可发送 4500 字符或 50 张图片.
*
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送群消息事件. cancellable
* @see FriendMessagePreSendEvent 发送消息前事件
* @see FriendMessagePostSendEvent 发送消息后事件
*
* @throws EventCancelledException 当发送消息事件被取消时抛出
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
@ -69,7 +71,7 @@ abstract class Friend : User(), CoroutineScope {
abstract override suspend fun sendMessage(message: Message): MessageReceipt<Friend>
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@kotlin.internal.InlineOnly
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<Friend> {
return sendMessage(message.toMessage())

View File

@ -17,12 +17,12 @@ import net.mamoe.mirai.JavaFriendlyAPI
import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.isContentEmpty
import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.message.recall
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.internal.runBlocking
import kotlin.jvm.JvmName
@ -137,8 +137,8 @@ abstract class Group : Contact(), CoroutineScope {
*
* 单条消息最大可发送 4500 字符或 50 张图片.
*
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送消息事件. cancellable
* @see GroupMessagePreSendEvent 发送消息前事件
* @see GroupMessagePostSendEvent 发送消息事件
*
* @throws EventCancelledException 当发送消息事件被取消时抛出
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
@ -154,7 +154,7 @@ abstract class Group : Contact(), CoroutineScope {
* @see sendMessage
*/
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@kotlin.internal.InlineOnly
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<Group> {
return sendMessage(message.toMessage())

View File

@ -17,7 +17,12 @@ import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.getFriendOrNull
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.isContentEmpty
import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.message.recall
import net.mamoe.mirai.utils.hoursToSeconds
import net.mamoe.mirai.utils.daysToSeconds
import net.mamoe.mirai.utils.minutesToSeconds
import net.mamoe.mirai.utils.WeakRefProperty
import kotlin.jvm.JvmSynthetic
import kotlin.time.Duration
@ -139,8 +144,11 @@ abstract class Member : MemberJavaFriendlyAPI, User() {
*
* 单条消息最大可发送 4500 字符或 50 张图片.
*
* @see MessageSendEvent.FriendMessageSendEvent 发送好友信息事件, cancellable
* @see MessageSendEvent.GroupMessageSendEvent 发送群消息事件. cancellable
* @see FriendMessagePreSendEvent 当此成员是好友时发送消息前事件
* @see FriendMessagePostSendEvent 当此成员是好友时发送消息后事件
*
* @see TempMessagePreSendEvent 当此成员不是好友时发送消息前事件
* @see TempMessagePostSendEvent 当此成员不是好友时发送消息后事件
*
* @throws EventCancelledException 当发送消息事件被取消时抛出
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
@ -156,7 +164,7 @@ abstract class Member : MemberJavaFriendlyAPI, User() {
* @see sendMessage
*/
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@kotlin.internal.InlineOnly
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<Member> {
return sendMessage(message.toMessage())

View File

@ -13,11 +13,7 @@ package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.events.BeforeImageUploadEvent
import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.ImageUploadEvent
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.Message
@ -58,8 +54,8 @@ abstract class User : Contact(), CoroutineScope {
*
* 单条消息最大可发送 4500 字符或 50 张图片.
*
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送群消息事件. cancellable
* @see UserMessagePreSendEvent 发送消息前事件
* @see UserMessagePostSendEvent 发送消息后事件
*
* @throws EventCancelledException 当发送消息事件被取消时抛出
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
@ -75,7 +71,7 @@ abstract class User : Contact(), CoroutineScope {
* @see sendMessage
*/
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@kotlin.internal.InlineOnly
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<User> {
return sendMessage(message.toMessage())

View File

@ -9,7 +9,7 @@
@file:JvmMultifileClass
@file:JvmName("BotEventsKt")
@file:Suppress("unused")
@file:Suppress("unused", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
package net.mamoe.mirai.event.events
@ -19,41 +19,253 @@ import net.mamoe.mirai.event.AbstractEvent
import net.mamoe.mirai.event.CancellableEvent
import net.mamoe.mirai.event.events.ImageUploadEvent.Failed
import net.mamoe.mirai.event.events.ImageUploadEvent.Succeed
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.PlannedRemoval
import net.mamoe.mirai.utils.SinceMirai
import kotlin.internal.InlineOnly
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
// region MessagePreSendEvent
/**
* 主动发送消息
* 在发送消息前广播的事件. 可被 [取消][CancellableEvent.cancel].
*
* 此事件总是在 [MessagePostSendEvent] 之前广播.
*
* [MessagePreSendEvent] [取消][CancellableEvent.cancel] :
* - [MessagePostSendEvent] 不会广播
* - 消息不会发送.
* - [Contact.sendMessage] 会抛出异常 [EventCancelledException]
*
* @see Contact.sendMessage 发送消息. 为广播这个事件的唯一途径
*/
sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractEvent() {
@SinceMirai("1.1.0")
sealed class MessagePreSendEvent : BotEvent, BotActiveEvent, AbstractEvent(), CancellableEvent {
/** 发信目标. */
abstract val target: Contact
final override val bot: Bot
get() = target.bot
final override val bot: Bot get() = target.bot
data class GroupMessageSendEvent internal constructor(
override val target: Group,
var message: MessageChain
) : MessageSendEvent(), CancellableEvent
data class FriendMessageSendEvent internal constructor(
override val target: Friend,
var message: MessageChain
) : MessageSendEvent(), CancellableEvent
data class TempMessageSendEvent internal constructor(
override val target: Member,
var message: MessageChain
) : MessageSendEvent(), CancellableEvent
/** 待发送的消息. 修改后将会同时应用于发送. */
abstract var message: Message
}
/**
* 在发送群消息前广播的事件.
* @see MessagePreSendEvent
*/
@SinceMirai("1.1.0")
data class GroupMessagePreSendEvent internal constructor(
/** 发信目标. */
override val target: Group,
/** 待发送的消息. 修改后将会同时应用于发送. */
override var message: Message
) : MessagePreSendEvent()
/**
* 在发送好友或群临时会话消息前广播的事件.
* @see MessagePreSendEvent
*/
@SinceMirai("1.1.0")
sealed class UserMessagePreSendEvent : MessagePreSendEvent() {
/** 发信目标. */
abstract override val target: User
}
/**
* 在发送好友消息前广播的事件.
* @see MessagePreSendEvent
*/
@SinceMirai("1.1.0")
data class FriendMessagePreSendEvent internal constructor(
/** 发信目标. */
override val target: Friend,
/** 待发送的消息. 修改后将会同时应用于发送. */
override var message: Message
) : UserMessagePreSendEvent()
/**
* 在发送群临时会话消息前广播的事件.
* @see MessagePreSendEvent
*/
@SinceMirai("1.1.0")
data class TempMessagePreSendEvent internal constructor(
/** 发信目标. */
override val target: Member,
/** 待发送的消息. 修改后将会同时应用于发送. */
override var message: Message
) : UserMessagePreSendEvent() {
val group get() = target.group
}
// endregion
// region MessagePostSendEvent
/**
* 在发送消息后广播的事件, 总是在 [MessagePreSendEvent] 之后广播.
*
* 只要 [MessagePreSendEvent] 未被 [取消][CancellableEvent.cancel], [MessagePostSendEvent] 就一定会被广播, 并携带 [发送时产生的异常][MessagePostSendEvent.exception] (如果有).
*
* 在此事件广播前, 消息一定已经发送成功, 或产生一个异常.
*
* @see Contact.sendMessage 发送消息. 为广播这个事件的唯一途径
* @see MessagePreSendEvent
*/
@SinceMirai("1.1.0")
sealed class MessagePostSendEvent<C : Contact> : BotEvent, BotActiveEvent, AbstractEvent() {
/** 发信目标. */
abstract val target: C
final override val bot: Bot get() = target.bot
/** 待发送的消息. 此为 [MessagePreSendEvent.message] 的最终值. */
abstract val message: MessageChain
/**
* 发送消息时抛出的异常. `null` 表示消息成功发送.
* @see result
*/
abstract val exception: Throwable?
/**
* 发送消息成功时的回执. `null` 表示消息发送失败.
* @see result
*/
abstract val receipt: MessageReceipt<C>?
}
/**
* 获取指代这条已经发送的消息的 [MessageSource]. 若消息发送失败, 返回 `null`
* @see MessagePostSendEvent.sourceResult
*/
@get:JvmSynthetic
@SinceMirai("1.1.0")
inline val MessagePostSendEvent<*>.source: MessageSource?
get() = receipt?.source
/**
* 获取指代这条已经发送的消息的 [MessageSource], 并包装为 [kotlin.Result]
* @see MessagePostSendEvent.result
*/
@get:JvmSynthetic
@SinceMirai("1.1.0")
inline val MessagePostSendEvent<*>.sourceResult: Result<MessageSource>
get() = result.map { it.source }
/**
* 在此消息发送成功时返回 `true`.
* @see MessagePostSendEvent.exception
* @see MessagePostSendEvent.result
*/
@get:JvmSynthetic
@SinceMirai("1.1.0")
inline val MessagePostSendEvent<*>.isSuccess: Boolean
get() = exception == null
/**
* 在此消息发送失败时返回 `true`.
* @see MessagePostSendEvent.exception
* @see MessagePostSendEvent.result
*/
@get:JvmSynthetic
@SinceMirai("1.1.0")
inline val MessagePostSendEvent<*>.isFailure: Boolean
get() = exception != null
/**
* [MessagePostSendEvent.exception] [MessagePostSendEvent.receipt] 表示为 [Result]
*/
@InlineOnly
@SinceMirai("1.1.0")
inline val <C : Contact> MessagePostSendEvent<C>.result: Result<MessageReceipt<C>>
get() = exception.let { exception -> if (exception != null) Result.failure(exception) else Result.success(receipt!!) }
/**
* 在群消息发送后广播的事件.
* @see MessagePostSendEvent
*/
@SinceMirai("1.1.0")
data class GroupMessagePostSendEvent internal constructor(
/** 发信目标. */
override val target: Group,
/** 待发送的消息. 此为 [MessagePreSendEvent.message] 的最终值. */
override val message: MessageChain,
/**
* 发送消息时抛出的异常. `null` 表示消息成功发送.
* @see result
*/
override val exception: Throwable?,
/**
* 发送消息成功时的回执. `null` 表示消息发送失败.
* @see result
*/
override val receipt: MessageReceipt<Group>?
) : MessagePostSendEvent<Group>()
/**
* 在好友或群临时会话消息发送后广播的事件.
* @see MessagePostSendEvent
*/
@SinceMirai("1.1.0")
sealed class UserMessagePostSendEvent<C : User> : MessagePostSendEvent<C>()
/**
* 在好友消息发送后广播的事件.
* @see MessagePostSendEvent
*/
@SinceMirai("1.1.0")
data class FriendMessagePostSendEvent internal constructor(
/** 发信目标. */
override val target: Friend,
/** 待发送的消息. 此为 [MessagePreSendEvent.message] 的最终值. */
override val message: MessageChain,
/**
* 发送消息时抛出的异常. `null` 表示消息成功发送.
* @see result
*/
override val exception: Throwable?,
/**
* 发送消息成功时的回执. `null` 表示消息发送失败.
* @see result
*/
override val receipt: MessageReceipt<Friend>?
) : UserMessagePostSendEvent<Friend>()
/**
* 在群临时会话消息发送后广播的事件.
* @see MessagePostSendEvent
*/
@SinceMirai("1.1.0")
data class TempMessagePostSendEvent internal constructor(
/** 发信目标. */
override val target: Member,
/** 待发送的消息. 此为 [MessagePreSendEvent.message] 的最终值. */
override val message: MessageChain,
/**
* 发送消息时抛出的异常. `null` 表示消息成功发送.
* @see result
*/
override val exception: Throwable?,
/**
* 发送消息成功时的回执. `null` 表示消息发送失败.
* @see result
*/
override val receipt: MessageReceipt<Member>?
) : UserMessagePostSendEvent<Member>() {
val group get() = target.group
}
// endregion
/**
* 消息撤回事件. 可是任意消息被任意人撤回.
*
@ -177,3 +389,65 @@ sealed class ImageUploadEvent : BotEvent, BotActiveEvent, AbstractEvent() {
) : ImageUploadEvent()
}
// region deprecated
/**
* 主动发送消息
*
* @see Contact.sendMessage 发送消息. 为广播这个事件的唯一途径
*/
@Suppress("DEPRECATION")
@PlannedRemoval("1.3.0") // arise deprecation level to ERROR in 1.2.0.
@Deprecated(
message = """
MessagePreSendEvent MessagePostSendEvent 替换.
""",
replaceWith = ReplaceWith("MessagePreSendEvent", "net.mamoe.mirai.event.events.MessagePreSendEvent"),
level = DeprecationLevel.WARNING
)
sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractEvent() {
abstract val target: Contact
final override val bot: Bot
get() = target.bot
@Deprecated(
message = """
GroupMessagePreSendEvent GroupMessagePostSendEvent 替换.
""",
replaceWith = ReplaceWith("GroupMessagePreSendEvent", "net.mamoe.mirai.event.events.GroupMessagePreSendEvent"),
level = DeprecationLevel.WARNING
)
data class GroupMessageSendEvent internal constructor(
override val target: Group,
var message: MessageChain
) : MessageSendEvent(), CancellableEvent
@Deprecated(
message = """
FriendMessagePreSendEvent FriendMessagePostSendEvent 替换.
""",
replaceWith = ReplaceWith(
"FriendMessagePreSendEvent",
"net.mamoe.mirai.event.events.FriendMessagePreSendEvent"
),
level = DeprecationLevel.WARNING
)
data class FriendMessageSendEvent internal constructor(
override val target: Friend,
var message: MessageChain
) : MessageSendEvent(), CancellableEvent
@Deprecated(
message = """
TempMessagePreSendEvent TempMessagePostSendEvent 替换.
""",
replaceWith = ReplaceWith("TempMessagePreSendEvent", "net.mamoe.mirai.event.events.TempMessagePreSendEvent"),
level = DeprecationLevel.WARNING
)
data class TempMessageSendEvent internal constructor(
override val target: Member,
var message: MessageChain
) : MessageSendEvent(), CancellableEvent
}
// endregion