Unify image id

This commit is contained in:
Him188 2021-01-05 23:28:34 +08:00
parent b7232182ae
commit 1d24a810fe
8 changed files with 154 additions and 177 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* Copyright 2019-2021 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.
@ -36,13 +36,15 @@ import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Contact.Companion.sendImage
import net.mamoe.mirai.contact.Contact.Companion.uploadImage
import net.mamoe.mirai.message.code.CodableMessage
import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_2
import net.mamoe.mirai.message.data.Image.Key.GROUP_IMAGE_ID_REGEX
import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_2
import net.mamoe.mirai.message.data.Image.Key.queryUrl
import net.mamoe.mirai.utils.*
import java.io.File
import java.io.InputStream
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo
import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage
import net.mamoe.mirai.utils.MiraiInternalApi
import net.mamoe.mirai.utils.safeCast
import kotlin.LazyThreadSafetyMode.NONE
/**
@ -56,15 +58,12 @@ import kotlin.LazyThreadSafetyMode.NONE
* - [Image.fromId]. Kotlin, 更推荐使用顶层函数 `val image = Image("id")`
*
* ### 上传和发送图片
* - [Contact.uploadImage] 上传 [图片文件][ExternalImage] 并得到 [Image] 消息
* - [Contact.sendImage] 上传 [图片文件][ExternalImage] 并发送返回的 [Image] 作为一条消息
* - [Image.sendTo] 上传 [图片文件][ExternalImage] 并得到 [Image] 消息
* - [Contact.uploadImage] 上传 [资源文件][ExternalResource] 并得到 [Image] 消息
* - [Contact.sendImage] 上传 [资源文件][ExternalResource] 并发送返回的 [Image] 作为一条消息
*
* - [File.uploadAsImage]
* - [InputStream.uploadAsImage]
*
* - [File.sendAsImageTo]
* - [InputStream.sendAsImageTo]
* - [ExternalResource.uploadAsImage]
* - [ExternalResource.sendAsImageTo]
* - [Contact.sendImage]
*
* ### 下载图片
* - [Image.queryUrl] 扩展函数. 查询图片下载链接
@ -85,12 +84,7 @@ public interface Image : Message, MessageContent, CodableMessage {
* 图片 id 不一定会长时间保存, 也可能在将来改变格式, 因此不建议使用 id 发送图片.
*
* ### 格式
* 群图片:
* - [GROUP_IMAGE_ID_REGEX], 示例: `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.ext` (ext 为文件后缀, png)
*
* 好友图片:
* - [FRIEND_IMAGE_ID_REGEX_1], 示例: `/f8f1ab55-bf8e-4236-b55e-955848d7069f`
* - [FRIEND_IMAGE_ID_REGEX_2], 示例: `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206`
* 所有图片的 id 都满足正则表达式 [IMAGE_ID_REGEX]. 示例: `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.ext` (ext 为文件后缀, png)
*
* @see Image 使用 id 构造图片
*/
@ -147,52 +141,40 @@ public interface Image : Message, MessageContent, CodableMessage {
/**
* 好友图片 ID 正则表达式
*
* `/f8f1ab55-bf8e-4236-b55e-955848d7069f`
* @see FRIEND_IMAGE_ID_REGEX_2
*/
// Java: MessageUtils.FRIEND_IMAGE_ID_REGEX_1
@JvmStatic
@MiraiExperimentalApi
@get:JvmName("getFriendImageIdRegex1")
public val FRIEND_IMAGE_ID_REGEX_1: Regex = Regex("""/[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}""")
/**
* 好友图片 ID 正则表达式 2
*
* `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206`
* @see FRIEND_IMAGE_ID_REGEX_1
*/
// Java: MessageUtils.FRIEND_IMAGE_ID_REGEX_2
@JvmStatic
@MiraiExperimentalApi
@get:JvmName("getFriendImageIdRegex2")
public val FRIEND_IMAGE_ID_REGEX_2: Regex = Regex("""/[0-9]*-[0-9]*-[0-9a-fA-F]{32}""")
/**
* 群图片 ID 正则表达式
* 统一 ID 正则表达式
*
* `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.ext`
*/
@Suppress("RegExpRedundantEscape") // This is required on Android
// Java: MessageUtils.GROUP_IMAGE_ID_REGEX
@JvmStatic
@MiraiExperimentalApi
@get:JvmName("getGroupImageIdRegex")
public val GROUP_IMAGE_ID_REGEX: Regex =
@get:JvmName("getImageIdRegex")
public val IMAGE_ID_REGEX: Regex =
Regex("""\{[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\}\..{3,5}""")
/**
* 图片资源 ID 正则表达式 1. mirai 内部使用.
*
* `/f8f1ab55-bf8e-4236-b55e-955848d7069f`
* @see IMAGE_RESOURCE_ID_REGEX_2
*/
@JvmStatic
@MiraiInternalApi
@get:JvmName("getImageResourceIdRegex1")
public val IMAGE_RESOURCE_ID_REGEX_1: Regex = Regex("""/[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}""")
/**
* 图片资源 ID 正则表达式 2. mirai 内部使用.
*
* `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206`
* @see IMAGE_RESOURCE_ID_REGEX_1
*/
@JvmStatic
@MiraiInternalApi
@get:JvmName("getImageResourceIdRegex2")
public val IMAGE_RESOURCE_ID_REGEX_2: Regex = Regex("""/[0-9]*-[0-9]*-[0-9a-fA-F]{32}""")
}
}
@MiraiExperimentalApi
public val Image.isGroupImage: Boolean
get() = GROUP_IMAGE_ID_REGEX matches imageId
@MiraiExperimentalApi
public val Image.isFriendImage: Boolean
get() = FRIEND_IMAGE_ID_REGEX_1 matches imageId || FRIEND_IMAGE_ID_REGEX_2 matches imageId
/**
* 通过 [Image.imageId] 构造一个 [Image] 以便发送.
* 这个图片必须是服务器已经存在的图片.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* Copyright 2019-2021 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.
@ -14,9 +14,9 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.Serializable
import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_2
import net.mamoe.mirai.message.data.Image.Key.GROUP_IMAGE_ID_REGEX
import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_2
import net.mamoe.mirai.utils.MiraiExperimentalApi
import kotlin.native.concurrent.SharedImmutable
@ -240,9 +240,9 @@ internal fun String.imageIdToMd5(offset: Int): ByteArray {
internal fun calculateImageMd5ByImageId(imageId: String): ByteArray {
@Suppress("DEPRECATION")
return when {
imageId matches FRIEND_IMAGE_ID_REGEX_2 -> imageId.imageIdToMd5(imageId.skipToSecondHyphen() + 1)
imageId matches FRIEND_IMAGE_ID_REGEX_1 -> imageId.imageIdToMd5(1)
imageId matches GROUP_IMAGE_ID_REGEX -> imageId.imageIdToMd5(1)
imageId matches IMAGE_ID_REGEX -> imageId.imageIdToMd5(1)
imageId matches IMAGE_RESOURCE_ID_REGEX_2 -> imageId.imageIdToMd5(imageId.skipToSecondHyphen() + 1)
imageId matches IMAGE_RESOURCE_ID_REGEX_1 -> imageId.imageIdToMd5(1)
else -> error(
"illegal imageId: $imageId. $ILLEGAL_IMAGE_ID_EXCEPTION_MESSAGE"
@ -251,8 +251,8 @@ internal fun calculateImageMd5ByImageId(imageId: String): ByteArray {
}
internal val ILLEGAL_IMAGE_ID_EXCEPTION_MESSAGE: String =
"ImageId must match Regex `${FRIEND_IMAGE_ID_REGEX_1.pattern}`, " +
"`${FRIEND_IMAGE_ID_REGEX_2.pattern}` or " +
"`${GROUP_IMAGE_ID_REGEX.pattern}`"
"ImageId must match Regex `${IMAGE_RESOURCE_ID_REGEX_1.pattern}`, " +
"`${IMAGE_RESOURCE_ID_REGEX_2.pattern}` or " +
"`${IMAGE_ID_REGEX.pattern}`"
// endregion

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* Copyright 2019-2021 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.
@ -25,10 +25,27 @@ public fun generateImageId(md5: ByteArray, format: String = "mirai"): String {
return """{${generateUUID(md5)}}.$format"""
}
@JvmOverloads
public fun generateImageIdFromResourceId(resourceId: String, format: String = "mirai"): String? {
// friend image id: /1040400290-3666252994-EFF4427CE3D27DB6B1D9A8AB72E7A29C
// friend image id: /1040400290-3666252994-EFF4427C E3D2 7DB6 B1D9 A8AB72E7A29C
// group image id: {EF42A82D-8DB6-5D0F-4F11-68961D8DA5CB}.png
val md5String = resourceId.substringAfterLast("-").substringAfter("/").takeIf { it.length == 32 } ?: return null
return "{${generateUUID(md5String)}}.$format"
}
public fun generateUUID(md5: ByteArray): String {
return "${md5[0, 3]}-${md5[4, 5]}-${md5[6, 7]}-${md5[8, 9]}-${md5[10, 15]}"
}
public fun generateUUID(md5String: String): String {
with(md5String) {
check(length == 32) { "it should md5String.length == 32" }
return "${substring(0, 8)}-${substring(8, 12)}-${substring(12, 16)}-${substring(16, 20)}-${substring(20, 32)}"
}
}
@JvmSynthetic
public operator fun ByteArray.get(rangeStart: Int, rangeEnd: Int): String = buildString {
for (it in rangeStart..rangeEnd) {

View File

@ -36,9 +36,9 @@ import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.action.Nudge
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_2
import net.mamoe.mirai.message.data.Image.Key.GROUP_IMAGE_ID_REGEX
import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_2
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
import kotlin.math.absoluteValue
@ -815,9 +815,9 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
override fun createImage(imageId: String): Image {
return when {
imageId matches FRIEND_IMAGE_ID_REGEX_1 -> OfflineFriendImage(imageId)
imageId matches FRIEND_IMAGE_ID_REGEX_2 -> OfflineFriendImage(imageId)
imageId matches GROUP_IMAGE_ID_REGEX -> OfflineGroupImage(imageId)
imageId matches IMAGE_ID_REGEX -> OfflineGroupImage(imageId)
imageId matches IMAGE_RESOURCE_ID_REGEX_1 -> OfflineFriendImage(imageId)
imageId matches IMAGE_RESOURCE_ID_REGEX_2 -> OfflineFriendImage(imageId)
else ->
@Suppress("INVISIBLE_MEMBER")
throw IllegalArgumentException("Illegal imageId: $imageId. $ILLEGAL_IMAGE_ID_EXCEPTION_MESSAGE")

View File

@ -21,12 +21,14 @@ 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.internal.message.OfflineFriendImage
import net.mamoe.mirai.internal.message.getImageType
import net.mamoe.mirai.internal.network.highway.postImage
import net.mamoe.mirai.internal.network.highway.sizeToString
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x352
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.generateImageIdFromResourceId
import net.mamoe.mirai.utils.toUHexString
import net.mamoe.mirai.utils.verbose
import kotlin.coroutines.CoroutineContext
@ -71,10 +73,16 @@ internal abstract class AbstractUser(
else -> "unknown"
}
return when (response) {
is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage(response.resourceId)
.also {
is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage(
imageId = generateImageIdFromResourceId(
resourceId = response.resourceId,
format = getImageType(response.imageInfo.fileType).takeIf { it != ExternalResource.DEFAULT_FORMAT_NAME }
?: resource.formatName
) ?: response.resourceId
).also {
ImageUploadEvent.Succeed(this, resource, it).broadcast()
}
is LongConn.OffPicUp.Response.RequireUpload -> {
bot.network.logger.verbose {
"[Http] Uploading $kind image, size=${resource.size.sizeToString()}"
@ -105,7 +113,9 @@ internal abstract class AbstractUser(
)*/
// 为什么不能 ??
OfflineFriendImage(response.resourceId).also {
OfflineFriendImage(
generateImageIdFromResourceId(response.resourceId, resource.formatName) ?: response.resourceId
).also {
ImageUploadEvent.Succeed(this, resource, it).broadcast()
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright 2019-2021 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("DEPRECATION_ERROR", "UnusedImport")
package net.mamoe.mirai.internal.message
import net.mamoe.mirai.internal.network.protocol.data.proto.HummerCommelem
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.data.FlashImage
import net.mamoe.mirai.message.data.isFriendImage
import net.mamoe.mirai.message.data.isGroupImage
import net.mamoe.mirai.message.data.md5
internal fun FlashImage.toJceData(): ImMsgBody.Elem {
return when {
image.isFriendImage -> ImMsgBody.Elem(
commonElem = ImMsgBody.CommonElem(
serviceType = 3,
businessType = 0,
pbElem = HummerCommelem.MsgElemInfoServtype3(
flashC2cPic = ImMsgBody.NotOnlineImage(
filePath = image.imageId,
resId = image.imageId,
picMd5 = image.md5,
oldPicMd5 = false,
pbReserve = byteArrayOf(0x78, 0x06)
)
).toByteArray(HummerCommelem.MsgElemInfoServtype3.serializer())
)
)
image.isGroupImage -> ImMsgBody.Elem(
commonElem = ImMsgBody.CommonElem(
serviceType = 3,
businessType = 0,
pbElem = HummerCommelem.MsgElemInfoServtype3(
flashTroopPic = ImMsgBody.CustomFace(
filePath = image.imageId,
picMd5 = image.md5,
pbReserve = byteArrayOf(0x78, 0x06)
)
).toByteArray(HummerCommelem.MsgElemInfoServtype3.serializer())
)
)
else -> error("Internal error: an image is neither group image nor friend image.")
}
}

View File

@ -177,8 +177,10 @@ internal fun MessageChain.toRichTextElems(
}
is FlashImage -> elements.add(currentMessage.toJceData())
is FlashImage -> elements.add(currentMessage.toJceData(messageTarget))
.also { transformOneMessage(UNSUPPORTED_FLASH_MESSAGE_PLAIN) }
is AtAll -> elements.add(atAllData)
is Face -> elements.add(
if (currentMessage.id >= 260) {

View File

@ -16,17 +16,19 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.IMirai
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Contact.Companion.uploadImage
import net.mamoe.mirai.contact.ContactOrBot
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.internal.network.protocol.data.proto.HummerCommelem
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.message.data.FriendImage
import net.mamoe.mirai.message.data.GroupImage
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_2
import net.mamoe.mirai.message.data.Image.Key.GROUP_IMAGE_ID_REGEX
import net.mamoe.mirai.message.data.md5
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX
import net.mamoe.mirai.utils.ExternalResource.Companion.DEFAULT_FORMAT_NAME
import net.mamoe.mirai.utils.generateImageId
import net.mamoe.mirai.utils.generateImageIdFromResourceId
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
internal class OnlineGroupImageImpl(
internal val delegate: ImMsgBody.CustomFace
@ -36,7 +38,7 @@ OnlineGroupImage() {
delegate.picMd5,
delegate.filePath.substringAfterLast('.')
).takeIf {
GROUP_IMAGE_ID_REGEX.matches(it)
IMAGE_ID_REGEX.matches(it)
} ?: generateImageId(delegate.picMd5)
override val originUrl: String
@ -60,7 +62,8 @@ internal class OnlineFriendImageImpl(
internal val delegate: ImMsgBody.NotOnlineImage
) : @Suppress("DEPRECATION")
OnlineFriendImage() {
override val imageId: String get() = delegate.resId
override val imageId: String =
generateImageIdFromResourceId(delegate.resId, getImageType(delegate.imgType)) ?: delegate.resId
override val originUrl: String
get() = if (delegate.origUrl.isNotBlank()) {
"http://c2cpicdw.qpic.cn" + this.delegate.origUrl
@ -96,7 +99,7 @@ internal fun getImageType(id: Int): String {
1002 -> "webp"
1005 -> "bmp"
2000 -> "gif"
2001 -> "png"
2001, 3 -> "png"
else -> DEFAULT_FORMAT_NAME
}
}
@ -116,6 +119,7 @@ internal fun ImMsgBody.NotOnlineImage.toCustomFace(): ImMsgBody.CustomFace {
)
}
// aka friend image id
internal fun ImMsgBody.NotOnlineImageOrCustomFace.calculateResId(): String {
val url = origUrl.takeIf { it.isNotBlank() }
?: thumbUrl.takeIf { it.isNotBlank() }
@ -165,12 +169,13 @@ private val oldData: ByteArray =
@Suppress("DEPRECATION")
internal fun OfflineFriendImage.toJceData(): ImMsgBody.NotOnlineImage {
val friendImageId = this.friendImageId
return ImMsgBody.NotOnlineImage(
filePath = this.imageId,
resId = this.imageId,
filePath = friendImageId,
resId = friendImageId,
oldPicMd5 = false,
picMd5 = this.md5,
downloadPath = this.imageId,
downloadPath = friendImageId,
original = 1,
pbReserve = byteArrayOf(0x78, 0x02)
)
@ -207,21 +212,6 @@ internal interface SuspendDeferredOriginUrlAware : Image {
suspend fun getUrl(bot: Bot): String
}
/**
* [ExternalImage] 委托的 [Image] 类型.
*/
@MiraiExperimentalApi("Will be renamed to OfflineImage on 1.2.0")
@Suppress("DEPRECATION_ERROR")
internal class ExperimentalDeferredImage internal constructor(
@Suppress("CanBeParameter") private val externalImage: ExternalResource // for future use
) : AbstractImage(), SuspendDeferredOriginUrlAware {
override suspend fun getUrl(bot: Bot): String {
TODO()
}
override val imageId: String = externalImage.calculateResourceId()
}
@Suppress("EXPOSED_SUPER_INTERFACE")
internal interface OnlineImage : Image, ConstOriginUrlAware {
override val originUrl: String
@ -236,8 +226,6 @@ internal interface OnlineImage : Image, ConstOriginUrlAware {
internal interface OfflineImage : Image
/**
* 通过 [Group.uploadImage] 上传得到的 [GroupImage]. 它的链接需要查询 [IMirai.queryImageUrl]
*
* @param imageId 参考 [Image.imageId]
*/
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@ -254,7 +242,7 @@ internal data class OfflineGroupImage(
init {
@Suppress("DEPRECATION")
require(imageId matches GROUP_IMAGE_ID_REGEX) {
require(imageId matches IMAGE_ID_REGEX) {
"Illegal imageId. It must matches GROUP_IMAGE_ID_REGEX"
}
}
@ -266,6 +254,12 @@ internal data class OfflineGroupImage(
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
internal abstract class OnlineGroupImage : GroupImage(), OnlineImage
internal val Image.friendImageId: String
get() {
// /1234567890-3666252994-EFF4427CE3D27DB6B1D9A8AB72E7A29C
return "/000000000-000000000-${md5.toUHexString("")}"
}
/**
* 通过 [Group.uploadImage] 上传得到的 [GroupImage]. 它的链接需要查询 [IMirai.queryImageUrl]
*
@ -276,13 +270,7 @@ internal data class OfflineFriendImage(
override val imageId: String
) : FriendImage(), OfflineImage, DeferredOriginUrlAware {
override fun getUrl(bot: Bot): String {
return "http://c2cpicdw.qpic.cn/offpic_new/${bot.id}/${this.imageId}/0?term=2"
}
init {
require(imageId matches FRIEND_IMAGE_ID_REGEX_1 || imageId matches FRIEND_IMAGE_ID_REGEX_2) {
"Illegal imageId. It must matches either FRIEND_IMAGE_ID_REGEX_1 or FRIEND_IMAGE_ID_REGEX_2"
}
return "http://c2cpicdw.qpic.cn/offpic_new/${bot.id}${this.friendImageId}/0?term=2"
}
}
@ -292,3 +280,38 @@ internal data class OfflineFriendImage(
internal abstract class OnlineFriendImage : FriendImage(), OnlineImage
// endregion
internal fun FlashImage.toJceData(messageTarget: ContactOrBot?): ImMsgBody.Elem {
return if (messageTarget is User) {
ImMsgBody.Elem(
commonElem = ImMsgBody.CommonElem(
serviceType = 3,
businessType = 0,
pbElem = HummerCommelem.MsgElemInfoServtype3(
flashC2cPic = ImMsgBody.NotOnlineImage(
filePath = image.friendImageId,
resId = image.friendImageId,
picMd5 = image.md5,
oldPicMd5 = false,
pbReserve = byteArrayOf(0x78, 0x06)
)
).toByteArray(HummerCommelem.MsgElemInfoServtype3.serializer())
)
)
} else {
ImMsgBody.Elem(
commonElem = ImMsgBody.CommonElem(
serviceType = 3,
businessType = 0,
pbElem = HummerCommelem.MsgElemInfoServtype3(
flashTroopPic = ImMsgBody.CustomFace(
filePath = image.imageId,
picMd5 = image.md5,
pbReserve = byteArrayOf(0x78, 0x06)
)
).toByteArray(HummerCommelem.MsgElemInfoServtype3.serializer())
)
)
}
}