Simplify Image structure: deprecate online and offline classification.

This commit is contained in:
Him188 2020-05-01 21:51:09 +08:00
parent 20c2f6fb05
commit 1a4c3ba602
16 changed files with 160 additions and 110 deletions

View File

@ -720,6 +720,7 @@ internal abstract class QQAndroidBotBase constructor(
} }
} }
@Suppress("DEPRECATION")
override suspend fun queryImageUrl(image: Image): String = when (image) { override suspend fun queryImageUrl(image: Image): String = when (image) {
is OnlineFriendImageImpl -> image.originUrl is OnlineFriendImageImpl -> image.originUrl
is OnlineGroupImageImpl -> image.originUrl is OnlineGroupImageImpl -> image.originUrl

View File

@ -25,8 +25,8 @@ import net.mamoe.mirai.event.events.BeforeImageUploadEvent
import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.ImageUploadEvent import net.mamoe.mirai.event.events.ImageUploadEvent
import net.mamoe.mirai.message.MessageReceipt 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.Message
import net.mamoe.mirai.message.data.OfflineFriendImage
import net.mamoe.mirai.message.data.isContentNotEmpty import net.mamoe.mirai.message.data.isContentNotEmpty
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.highway.postImage import net.mamoe.mirai.qqandroid.network.highway.postImage
@ -86,7 +86,7 @@ internal class FriendImpl(
@JvmSynthetic @JvmSynthetic
@OptIn(MiraiInternalAPI::class, ExperimentalStdlibApi::class, ExperimentalTime::class) @OptIn(MiraiInternalAPI::class, ExperimentalStdlibApi::class, ExperimentalTime::class)
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try { override suspend fun uploadImage(image: ExternalImage): Image = try {
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) { if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup") throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
} }
@ -103,11 +103,12 @@ internal class FriendImpl(
) )
).sendAndExpect<LongConn.OffPicUp.Response>() ).sendAndExpect<LongConn.OffPicUp.Response>()
@Suppress("UNCHECKED_CAST") // bug @Suppress("UNCHECKED_CAST", "DEPRECATION") // bug
return when (response) { return when (response) {
is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage(response.resourceId).also { is LongConn.OffPicUp.Response.FileExists -> net.mamoe.mirai.message.data.OfflineFriendImage(response.resourceId)
ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast() .also {
} ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
}
is LongConn.OffPicUp.Response.RequireUpload -> { is LongConn.OffPicUp.Response.RequireUpload -> {
bot.network.logger.verbose { bot.network.logger.verbose {
"[Http] Uploading friend image, size=${image.inputSize.sizeToString()}" "[Http] Uploading friend image, size=${image.inputSize.sizeToString()}"
@ -139,7 +140,7 @@ internal class FriendImpl(
)*/ )*/
// 为什么不能 ?? // 为什么不能 ??
return OfflineFriendImage(response.resourceId).also { return net.mamoe.mirai.message.data.OfflineFriendImage(response.resourceId).also {
ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast() ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
} }
} }

View File

@ -399,6 +399,7 @@ internal class GroupImpl(
return MessageReceipt(source, this, botAsMember) return MessageReceipt(source, this, botAsMember)
} }
@Suppress("DEPRECATION")
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
@JvmSynthetic @JvmSynthetic
override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage = try { override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage = try {

View File

@ -24,8 +24,8 @@ import net.mamoe.mirai.event.events.MemberCardChangeEvent
import net.mamoe.mirai.event.events.MemberLeaveEvent import net.mamoe.mirai.event.events.MemberLeaveEvent
import net.mamoe.mirai.event.events.MemberSpecialTitleChangeEvent import net.mamoe.mirai.event.events.MemberSpecialTitleChangeEvent
import net.mamoe.mirai.message.MessageReceipt 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.Message
import net.mamoe.mirai.message.data.OfflineFriendImage
import net.mamoe.mirai.message.data.asMessageChain import net.mamoe.mirai.message.data.asMessageChain
import net.mamoe.mirai.message.data.isContentNotEmpty import net.mamoe.mirai.message.data.isContentNotEmpty
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
@ -81,7 +81,7 @@ internal class MemberImpl constructor(
} }
@JvmSynthetic @JvmSynthetic
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image) override suspend fun uploadImage(image: ExternalImage): Image = qq.uploadImage(image)
override var permission: MemberPermission = memberInfo.permission override var permission: MemberPermission = memberInfo.permission

View File

@ -129,10 +129,14 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
) )
transformOneMessage(UNSUPPORTED_POKE_MESSAGE_PLAIN) transformOneMessage(UNSUPPORTED_POKE_MESSAGE_PLAIN)
} }
is OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData())) is @Suppress("DEPRECATION")
OfflineGroupImage
-> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate)) is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
is OfflineFriendImage -> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData())) is @Suppress("DEPRECATION")
OfflineFriendImage
-> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData()))
is GroupFlashImage -> elements.add(it.toJceData()) is GroupFlashImage -> elements.add(it.toJceData())
.also { transformOneMessage(UNSUPPORTED_FLASH_MESSAGE_PLAIN) } .also { transformOneMessage(UNSUPPORTED_FLASH_MESSAGE_PLAIN) }
is FriendFlashImage -> elements.add(it.toJceData()) is FriendFlashImage -> elements.add(it.toJceData())

View File

@ -16,7 +16,8 @@ import net.mamoe.mirai.utils.ExternalImage
internal class OnlineGroupImageImpl( internal class OnlineGroupImageImpl(
internal val delegate: ImMsgBody.CustomFace internal val delegate: ImMsgBody.CustomFace
) : OnlineGroupImage() { ) : @Suppress("DEPRECATION")
OnlineGroupImage() {
override val imageId: String = ExternalImage.generateImageId(delegate.md5) override val imageId: String = ExternalImage.generateImageId(delegate.md5)
override val originUrl: String override val originUrl: String
get() = "http://gchat.qpic.cn" + delegate.origUrl get() = "http://gchat.qpic.cn" + delegate.origUrl
@ -32,7 +33,8 @@ internal class OnlineGroupImageImpl(
internal class OnlineFriendImageImpl( internal class OnlineFriendImageImpl(
internal val delegate: ImMsgBody.NotOnlineImage internal val delegate: ImMsgBody.NotOnlineImage
) : OnlineFriendImage() { ) : @Suppress("DEPRECATION")
OnlineFriendImage() {
override val imageId: String get() = delegate.resId override val imageId: String get() = delegate.resId
override val originUrl: String override val originUrl: String
get() = if (delegate.origUrl.isNotEmpty()) { get() = if (delegate.origUrl.isNotEmpty()) {
@ -51,6 +53,7 @@ internal class OnlineFriendImageImpl(
} }
} }
@Suppress("DEPRECATION")
internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace { internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace {
return ImMsgBody.CustomFace( return ImMsgBody.CustomFace(
filePath = this.imageId, filePath = this.imageId,
@ -67,6 +70,7 @@ private val oldData: ByteArray =
"15 36 20 39 32 6B 41 31 00 38 37 32 66 30 36 36 30 33 61 65 31 30 33 62 37 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 30 31 45 39 34 35 31 42 2D 37 30 45 44 2D 45 41 45 33 2D 42 33 37 43 2D 31 30 31 46 31 45 45 42 46 35 42 35 7D 2E 70 6E 67 41".hexToBytes() "15 36 20 39 32 6B 41 31 00 38 37 32 66 30 36 36 30 33 61 65 31 30 33 62 37 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 30 31 45 39 34 35 31 42 2D 37 30 45 44 2D 45 41 45 33 2D 42 33 37 43 2D 31 30 31 46 31 45 45 42 46 35 42 35 7D 2E 70 6E 67 41".hexToBytes()
@Suppress("DEPRECATION")
internal fun OfflineFriendImage.toJceData(): ImMsgBody.NotOnlineImage { internal fun OfflineFriendImage.toJceData(): ImMsgBody.NotOnlineImage {
return ImMsgBody.NotOnlineImage( return ImMsgBody.NotOnlineImage(
filePath = this.imageId, filePath = this.imageId,

View File

@ -146,7 +146,7 @@ internal class MessageSourceToGroupImpl(
type = 0, type = 0,
time = time, time = time,
pbReserve = SourceMsg.ResvAttr( pbReserve = SourceMsg.ResvAttr(
origUids = id.toLong() and 0xffFFffFF // id is actually messageRandom origUids = internalId.toLong() and 0xffFFffFF // id is actually messageRandom
).toByteArray(SourceMsg.ResvAttr.serializer()), ).toByteArray(SourceMsg.ResvAttr.serializer()),
srcMsg = MsgComm.Msg( srcMsg = MsgComm.Msg(
msgHead = MsgComm.MsgHead( msgHead = MsgComm.MsgHead(
@ -156,7 +156,7 @@ internal class MessageSourceToGroupImpl(
c2cCmd = 1, c2cCmd = 1,
msgSeq = sequenceId, msgSeq = sequenceId,
msgTime = time, msgTime = time,
msgUid = id.toLong() and 0xffFFffFF, // ok msgUid = internalId.toLong() and 0xffFFffFF, // ok
groupInfo = MsgComm.GroupInfo(groupCode = targetId), groupInfo = MsgComm.GroupInfo(groupCode = targetId),
isSrcMsg = true isSrcMsg = true
), ),

View File

@ -89,7 +89,7 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时抛出. (最大大小约为 20 MB, mirai 限制的大小为 30 MB) * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时抛出. (最大大小约为 20 MB, mirai 限制的大小为 30 MB)
*/ */
@JvmSynthetic @JvmSynthetic
abstract suspend fun uploadImage(image: ExternalImage): OfflineImage abstract suspend fun uploadImage(image: ExternalImage): Image
final override fun equals(other: Any?): Boolean = super.equals(other) final override fun equals(other: Any?): Boolean = super.equals(other)
final override fun hashCode(): Int = super.hashCode() final override fun hashCode(): Int = super.hashCode()

View File

@ -20,8 +20,8 @@ import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.message.MessageReceipt 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.Message
import net.mamoe.mirai.message.data.OfflineGroupImage
import net.mamoe.mirai.message.data.toMessage import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.internal.runBlocking import net.mamoe.mirai.utils.internal.runBlocking
@ -172,7 +172,7 @@ abstract class Group : Contact(), CoroutineScope {
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB) * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
*/ */
@JvmSynthetic @JvmSynthetic
abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage abstract override suspend fun uploadImage(image: ExternalImage): Image
companion object { companion object {
/** /**

View File

@ -19,8 +19,8 @@ import net.mamoe.mirai.event.events.ImageUploadEvent
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.message.MessageReceipt 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.Message
import net.mamoe.mirai.message.data.OfflineFriendImage
import net.mamoe.mirai.message.data.toMessage import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.OverFileSizeMaxException import net.mamoe.mirai.utils.OverFileSizeMaxException
@ -91,7 +91,7 @@ abstract class User : Contact(), CoroutineScope {
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB) * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
*/ */
@JvmSynthetic @JvmSynthetic
abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage abstract override suspend fun uploadImage(image: ExternalImage): Image
abstract override fun toString(): String abstract override fun toString(): String
} }

View File

@ -10,7 +10,7 @@
@file:JvmMultifileClass @file:JvmMultifileClass
@file:JvmName("MessageUtils") @file:JvmName("MessageUtils")
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused") @file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "WRONG_MODIFIER_CONTAINING_DECLARATION", "DEPRECATION")
package net.mamoe.mirai.message.data package net.mamoe.mirai.message.data
@ -81,9 +81,50 @@ expect interface Image : Message, MessageContent {
@Deprecated(""" @Deprecated("""
不要自行实现 Image, 它必须由协议模块实现, 否则会无法发送也无法解析. 不要自行实现 Image, 它必须由协议模块实现, 否则会无法发送也无法解析.
""", level = DeprecationLevel.HIDDEN) """, level = DeprecationLevel.HIDDEN)
@Suppress("PropertyName", "DeprecatedCallableAddReplaceWith") @Suppress("PropertyName")
@get:JvmSynthetic @get:JvmSynthetic
val DoNotImplementThisClass: Nothing? internal val DoNotImplementThisClass: Nothing?
}
/**
* 群图片.
*
* 群拖
*
* @property imageId 形如 `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.mirai` (后缀一定为 `".mirai"`)
* @see Image 查看更多说明
*/
@Suppress("DEPRECATION_ERROR")
// CustomFace
@OptIn(MiraiInternalAPI::class)
sealed class GroupImage : AbstractImage() {
companion object Key : Message.Key<GroupImage> {
override val typeName: String get() = "GroupImage"
}
}
/**
* 计算图片的 md5 校验值.
*
* Java 使用: `MessageUtils.calculateImageMd5(image)`
*/
@get:JvmName("calculateImageMd5")
@SinceMirai("0.39.0")
val Image.md5: ByteArray
get() = calculateImageMd5ByImageId(imageId)
/**
* 好友图片
*
* [imageId] 形如 `/f8f1ab55-bf8e-4236-b55e-955848d7069f` (37 长度) `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206` (54 长度)
*/ // NotOnlineImage
@Suppress("DEPRECATION_ERROR")
@OptIn(MiraiInternalAPI::class)
sealed class FriendImage : AbstractImage() {
companion object Key : Message.Key<FriendImage> {
override val typeName: String get() = "FriendImage"
}
} }
/** /**
@ -148,29 +189,18 @@ fun Image(imageId: String): OfflineImage = when {
else -> throw IllegalArgumentException("Illegal imageId: $imageId. $ILLEGAL_IMAGE_ID_EXCEPTION_MESSAGE") else -> throw IllegalArgumentException("Illegal imageId: $imageId. $ILLEGAL_IMAGE_ID_EXCEPTION_MESSAGE")
} }
// region 在线图片
/**
* 在服务器上的图片. 它可以直接获取下载链接.
*
* 一般由 [Contact.uploadImage] 得到
*/
interface OnlineImage : Image {
companion object Key : Message.Key<OnlineImage> {
override val typeName: String get() = "OnlineImage"
}
/**
* 原图下载链接. 包含域名
*/
val originUrl: String
}
/** /**
* 查询原图下载链接. * 查询原图下载链接.
*
* 当图片为从服务器接收的消息中的图片时, 可以直接获取下载链接, 本函数不会挂起协程.
* 其他情况下协程可能会挂起并向服务器查询下载链接, 或不挂起并拼接一个链接.
*
* @return 原图 HTTP 下载链接 ( HTTPS)
* @throws IllegalStateException 当无任何 [Bot] 在线时抛出 (因为无法获取相关协议)
*/ */
@JvmSynthetic @JvmSynthetic
suspend fun Image.queryUrl(): String { suspend fun Image.queryUrl(): String {
@Suppress("DEPRECATION")
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
return when (this) { return when (this) {
is OnlineImage -> this.originUrl is OnlineImage -> this.originUrl
@ -179,10 +209,32 @@ suspend fun Image.queryUrl(): String {
} }
} }
// endregion 在线图片
/////////////////////////
///// 以下 API 已弃用 /////
/////////////////////////
// region 离线图片 // region 已启用
internal const val ONLINE_OFFLINE_DEPRECATION_MESSAGE = """
1.0.0 , mirai 已经能正常处理离线图片和在线图片的下载链接等功能.
使用者无需考虑一个图片为在线图片还是离线图片, 只需使用 Image 类型.
"""
@PlannedRemoval("1.2.0") // 改为 internal
@Deprecated(ONLINE_OFFLINE_DEPRECATION_MESSAGE,
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("Image", "net.mamoe.mirai.message.data.Image")
)
interface OnlineImage : Image {
companion object Key : Message.Key<OnlineImage> {
override val typeName: String get() = "OnlineImage"
}
val originUrl: String
}
/** /**
* 离线的图片, 即为客户端主动上传到服务器而获得的 [Image] 实例. * 离线的图片, 即为客户端主动上传到服务器而获得的 [Image] 实例.
@ -190,45 +242,37 @@ suspend fun Image.queryUrl(): String {
* *
* 一般由 [Contact.uploadImage] 得到 * 一般由 [Contact.uploadImage] 得到
*/ */
@PlannedRemoval("1.2.0") // 改为 internal
@Deprecated(ONLINE_OFFLINE_DEPRECATION_MESSAGE,
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("Image", "net.mamoe.mirai.message.data.Image")
)
interface OfflineImage : Image { interface OfflineImage : Image {
companion object Key : Message.Key<OfflineImage> { companion object Key : Message.Key<OfflineImage> {
override val typeName: String get() = "OfflineImage" override val typeName: String get() = "OfflineImage"
} }
} }
/** @PlannedRemoval("1.2.0") // 删除
* 原图下载链接. 包含域名 @Deprecated(ONLINE_OFFLINE_DEPRECATION_MESSAGE,
*/ level = DeprecationLevel.HIDDEN
)
@JvmSynthetic @JvmSynthetic
suspend fun OfflineImage.queryUrl(): String { suspend fun OfflineImage.queryUrl(): String {
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
return BotImpl.instances.peekFirst().get()?.queryImageUrl(this) ?: error("No Bot available to query image url") return BotImpl.instances.peekFirst().get()?.queryImageUrl(this) ?: error("No Bot available to query image url")
} }
// endregion 离线图片
// region 群图片
/**
* 群图片.
*
* [imageId] 形如 `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.mirai` (45 长度)
*/
@Suppress("DEPRECATION_ERROR")
// CustomFace
@OptIn(MiraiInternalAPI::class)
sealed class GroupImage : AbstractImage() {
companion object Key : Message.Key<GroupImage> {
override val typeName: String get() = "GroupImage"
}
}
/** /**
* 通过 [Group.uploadImage] 上传得到的 [GroupImage]. 它的链接需要查询 [Bot.queryImageUrl] * 通过 [Group.uploadImage] 上传得到的 [GroupImage]. 它的链接需要查询 [Bot.queryImageUrl]
* *
* @param imageId 参考 [Image.imageId] * @param imageId 参考 [Image.imageId]
*/ */
@PlannedRemoval("1.2.0") // 改为 internal
@Deprecated(ONLINE_OFFLINE_DEPRECATION_MESSAGE,
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("Image", "net.mamoe.mirai.message.data.Image")
)
@Serializable @Serializable
data class OfflineGroupImage( data class OfflineGroupImage(
override val imageId: String override val imageId: String
@ -241,40 +285,26 @@ data class OfflineGroupImage(
} }
} }
@get:JvmName("calculateImageMd5")
@SinceMirai("0.39.0")
val Image.md5: ByteArray
get() = calculateImageMd5ByImageId(imageId)
/** /**
* 接收消息时获取到的 [GroupImage]. 它可以直接获取下载链接 [originUrl] * 接收消息时获取到的 [GroupImage]. 它可以直接获取下载链接 [originUrl]
*/ */
@PlannedRemoval("1.2.0") // 改为 internal
@Deprecated(ONLINE_OFFLINE_DEPRECATION_MESSAGE,
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("Image", "net.mamoe.mirai.message.data.Image")
)
abstract class OnlineGroupImage : GroupImage(), OnlineImage abstract class OnlineGroupImage : GroupImage(), OnlineImage
// endregion 群图片
// region 好友图片
/**
* 好友图片
*
* [imageId] 形如 `/f8f1ab55-bf8e-4236-b55e-955848d7069f` (37 长度) `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206` (54 长度)
*/ // NotOnlineImage
@Suppress("DEPRECATION_ERROR")
@OptIn(MiraiInternalAPI::class)
sealed class FriendImage : AbstractImage() {
companion object Key : Message.Key<FriendImage> {
override val typeName: String get() = "FriendImage"
}
}
/** /**
* 通过 [Group.uploadImage] 上传得到的 [GroupImage]. 它的链接需要查询 [Bot.queryImageUrl] * 通过 [Group.uploadImage] 上传得到的 [GroupImage]. 它的链接需要查询 [Bot.queryImageUrl]
* *
* @param imageId 参考 [Image.imageId] * @param imageId 参考 [Image.imageId]
*/ */
@PlannedRemoval("1.2.0") // 改为 internal
@Deprecated(ONLINE_OFFLINE_DEPRECATION_MESSAGE,
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("Image", "net.mamoe.mirai.message.data.Image")
)
@Serializable @Serializable
data class OfflineFriendImage( data class OfflineFriendImage(
override val imageId: String override val imageId: String
@ -289,6 +319,11 @@ data class OfflineFriendImage(
/** /**
* 接收消息时获取到的 [FriendImage]. 它可以直接获取下载链接 [originUrl] * 接收消息时获取到的 [FriendImage]. 它可以直接获取下载链接 [originUrl]
*/ */
@PlannedRemoval("1.2.0") // 改为 internal
@Deprecated(ONLINE_OFFLINE_DEPRECATION_MESSAGE,
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("Image", "net.mamoe.mirai.message.data.Image")
)
abstract class OnlineFriendImage : FriendImage(), OnlineImage abstract class OnlineFriendImage : FriendImage(), OnlineImage
// endregion // endregion
@ -326,6 +361,7 @@ sealed class AbstractImage : Image {
field = "[mirai:image:$imageId]" field = "[mirai:image:$imageId]"
field field
} }
final override fun toString(): String = _stringValue!! final override fun toString(): String = _stringValue!!
final override fun contentToString(): String = "[图片]" final override fun contentToString(): String = "[图片]"
} }

View File

@ -25,6 +25,7 @@ import kotlin.jvm.JvmSynthetic
/** /**
* 可发送的或从服务器接收的消息. * 可发送的或从服务器接收的消息.
*
* 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 [纯文本][PlainText], [图片][Image] . * 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 [纯文本][PlainText], [图片][Image] .
* *
* [消息][Message] 分为 * [消息][Message] 分为
@ -37,20 +38,20 @@ import kotlin.jvm.JvmSynthetic
* 这与使用 [String] 的使用非常类似. * 这与使用 [String] 的使用非常类似.
* *
* 比较 [SingleMessage] [String]: * 比较 [SingleMessage] [String]:
* `if(message.contentToString() == "你好") qq.sendMessage(event)` * `if(message.contentToString() == "你好") friend.sendMessage(event)`
* *
* 连接 [Message] [Message], [String], (使用 operator [Message.plus]): * 连接 [Message] [Message], [String], (使用操作符 [Message.plus]):
* ```kotlin * ```
* text = PlainText("Hello ") * text = PlainText("Hello ")
* qq.sendMessage(text + "world") * qq.sendMessage(text + "world")
* ``` * ```
* *
* `Message1 + Message2 + Message3`, 类似 [String] 的连接: * `Message1 + Message2 + Message3`, 类似 [String] 的连接:
* * ```
* +----------+ plus +----------+ plus +----------+ * +----------+ plus +----------+ plus +----------+
* | Message1 | <------ | Message2 | <------ | Message3 | * | Message1 | <------ | Message2 | <------ | Message3 |
* +----------+ +----------+ +----------+ * +----------+ +----------+ +----------+
* * ```
* *
* 但注意: 不能 `String + Message`. 只能 `Message + String` * 但注意: 不能 `String + Message`. 只能 `Message + String`
* *

View File

@ -198,7 +198,7 @@ internal inline fun <T> List<T>.indexOfFirst(offset: Int, predicate: (T) -> Bool
@OptIn(MiraiExperimentalAPI::class) @OptIn(MiraiExperimentalAPI::class)
@JvmSynthetic @JvmSynthetic
@Suppress("UNCHECKED_CAST", "DEPRECATION_ERROR") @Suppress("UNCHECKED_CAST", "DEPRECATION_ERROR", "DEPRECATION")
internal fun <M : Message> MessageChain.firstOrNullImpl(key: Message.Key<M>): M? = when (key) { internal fun <M : Message> MessageChain.firstOrNullImpl(key: Message.Key<M>): M? = when (key) {
At -> firstIsInstanceOrNull<At>() At -> firstIsInstanceOrNull<At>()
AtAll -> firstIsInstanceOrNull<AtAll>() AtAll -> firstIsInstanceOrNull<AtAll>()

View File

@ -21,7 +21,6 @@ import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.User import net.mamoe.mirai.contact.User
import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.OfflineImage
import net.mamoe.mirai.message.data.sendTo import net.mamoe.mirai.message.data.sendTo
import net.mamoe.mirai.message.data.toLongUnsigned import net.mamoe.mirai.message.data.toLongUnsigned
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
@ -117,7 +116,7 @@ suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> =
* @see contact 图片上传对象. 由于好友图片与群图片不通用, 上传时必须提供目标联系人 * @see contact 图片上传对象. 由于好友图片与群图片不通用, 上传时必须提供目标联系人
*/ */
@JvmSynthetic @JvmSynthetic
suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact) { suspend fun ExternalImage.upload(contact: Contact): Image = when (contact) {
is Group -> contact.uploadImage(this) is Group -> contact.uploadImage(this)
is User -> contact.uploadImage(this) is User -> contact.uploadImage(this)
else -> error("unreachable") else -> error("unreachable")
@ -129,6 +128,8 @@ suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact
@JvmSynthetic @JvmSynthetic
suspend inline fun <C : Contact> C.sendImage(image: ExternalImage): MessageReceipt<C> = image.sendTo(this) suspend inline fun <C : Contact> C.sendImage(image: ExternalImage): MessageReceipt<C> = image.sendTo(this)
@JvmSynthetic
internal operator fun ByteArray.get(rangeStart: Int, rangeEnd: Int): String = buildString { internal operator fun ByteArray.get(rangeStart: Int, rangeEnd: Int): String = buildString {
for (it in rangeStart..rangeEnd) { for (it in rangeStart..rangeEnd) {
append(this@get[it].fixToString()) append(this@get[it].fixToString())

View File

@ -16,7 +16,6 @@ import kotlinx.coroutines.withContext
import kotlinx.io.core.Input import kotlinx.io.core.Input
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.OfflineImage
import net.mamoe.mirai.utils.OverFileSizeMaxException import net.mamoe.mirai.utils.OverFileSizeMaxException
import net.mamoe.mirai.utils.sendTo import net.mamoe.mirai.utils.sendTo
import net.mamoe.mirai.utils.toExternalImage import net.mamoe.mirai.utils.toExternalImage
@ -82,8 +81,9 @@ suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
* [Dispatchers.IO] 中将图片上传后构造 [Image]. 不会创建临时文件 * [Dispatchers.IO] 中将图片上传后构造 [Image]. 不会创建临时文件
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@JvmSynthetic
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend fun BufferedImage.upload(contact: Contact): OfflineImage = suspend fun BufferedImage.upload(contact: Contact): Image =
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
/** /**
@ -91,7 +91,7 @@ suspend fun BufferedImage.upload(contact: Contact): OfflineImage =
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend fun URL.uploadAsImage(contact: Contact): OfflineImage = suspend fun URL.uploadAsImage(contact: Contact): Image =
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
/** /**
@ -99,7 +99,7 @@ suspend fun URL.uploadAsImage(contact: Contact): OfflineImage =
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend fun Input.uploadAsImage(contact: Contact): OfflineImage = suspend fun Input.uploadAsImage(contact: Contact): Image =
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
/** /**
@ -107,7 +107,7 @@ suspend fun Input.uploadAsImage(contact: Contact): OfflineImage =
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend fun InputStream.uploadAsImage(contact: Contact): OfflineImage = suspend fun InputStream.uploadAsImage(contact: Contact): Image =
withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
/** /**
@ -115,7 +115,7 @@ suspend fun InputStream.uploadAsImage(contact: Contact): OfflineImage =
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend fun File.uploadAsImage(contact: Contact): OfflineImage { suspend fun File.uploadAsImage(contact: Contact): Image {
require(this.isFile && this.exists() && this.canRead()) { "file ${this.path} is not readable" } require(this.isFile && this.exists() && this.canRead()) { "file ${this.path} is not readable" }
return withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) return withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
} }
@ -170,34 +170,34 @@ suspend inline fun <C : Contact> C.sendImage(file: File): MessageReceipt<C> = fi
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend inline fun Contact.uploadImage(bufferedImage: BufferedImage): OfflineImage = bufferedImage.upload(this) suspend inline fun Contact.uploadImage(bufferedImage: BufferedImage): Image = bufferedImage.upload(this)
/** /**
* [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传, 但不发送 * [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传, 但不发送
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend inline fun Contact.uploadImage(imageUrl: URL): OfflineImage = imageUrl.uploadAsImage(this) suspend inline fun Contact.uploadImage(imageUrl: URL): Image = imageUrl.uploadAsImage(this)
/** /**
* [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传, 但不发送 * [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传, 但不发送
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend inline fun Contact.uploadImage(imageInput: Input): OfflineImage = imageInput.uploadAsImage(this) suspend inline fun Contact.uploadImage(imageInput: Input): Image = imageInput.uploadAsImage(this)
/** /**
* [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传, 但不发送 * [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传, 但不发送
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend inline fun Contact.uploadImage(imageStream: InputStream): OfflineImage = imageStream.uploadAsImage(this) suspend inline fun Contact.uploadImage(imageStream: InputStream): Image = imageStream.uploadAsImage(this)
/** /**
* [Dispatchers.IO] 中将文件作为图片上传, 但不发送 * [Dispatchers.IO] 中将文件作为图片上传, 但不发送
* @throws OverFileSizeMaxException * @throws OverFileSizeMaxException
*/ */
@Throws(OverFileSizeMaxException::class) @Throws(OverFileSizeMaxException::class)
suspend inline fun Contact.uploadImage(file: File): OfflineImage = file.uploadAsImage(this) suspend inline fun Contact.uploadImage(file: File): Image = file.uploadAsImage(this)
// endregion // endregion

View File

@ -9,6 +9,7 @@
@file:JvmMultifileClass @file:JvmMultifileClass
@file:JvmName("MessageUtils") @file:JvmName("MessageUtils")
@file:Suppress("WRONG_MODIFIER_CONTAINING_DECLARATION")
package net.mamoe.mirai.message.data package net.mamoe.mirai.message.data
@ -74,7 +75,7 @@ actual interface Image : Message, MessageContent {
@Deprecated(""" @Deprecated("""
不要自行实现 OnlineGroupImage, 它必须由协议模块实现, 否则会无法发送也无法解析. 不要自行实现 OnlineGroupImage, 它必须由协议模块实现, 否则会无法发送也无法解析.
""", level = DeprecationLevel.HIDDEN) """, level = DeprecationLevel.HIDDEN)
@Suppress("PropertyName", "DeprecatedCallableAddReplaceWith") @Suppress( "PropertyName", "unused")
@get:JvmSynthetic @get:JvmSynthetic
actual val DoNotImplementThisClass: Nothing? internal actual val DoNotImplementThisClass: Nothing?
} }