mirror of
https://github.com/mamoe/mirai.git
synced 2025-05-06 05:45:19 +08:00
Unified Image types: Online/Offline Image, Group/Friend Image
This commit is contained in:
parent
3c25c3df65
commit
12ebfa1db5
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid
mirai-core/src
androidMain/kotlin/net/mamoe/mirai/contact
commonMain/kotlin/net.mamoe.mirai
jvmMain/kotlin/net/mamoe/mirai/contact
@ -20,7 +20,10 @@ 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.*
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.message.data.OfflineFriendImage
|
||||
import net.mamoe.mirai.message.data.OfflineGroupImage
|
||||
import net.mamoe.mirai.qqandroid.message.MessageSourceFromSendGroup
|
||||
import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
|
||||
import net.mamoe.mirai.qqandroid.network.highway.postImage
|
||||
@ -73,7 +76,7 @@ internal class QQImpl(
|
||||
return MessageReceipt(source, this, null)
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(image: ExternalImage): Image = try {
|
||||
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try {
|
||||
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
|
||||
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
|
||||
}
|
||||
@ -93,8 +96,9 @@ internal class QQImpl(
|
||||
)
|
||||
).sendAndExpect<LongConn.OffPicUp.Response>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST") // bug
|
||||
return when (response) {
|
||||
is LongConn.OffPicUp.Response.FileExists -> NotOnlineImageFromFile(
|
||||
is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage(
|
||||
filepath = response.resourceId,
|
||||
md5 = response.imageInfo.fileMd5,
|
||||
fileLength = response.imageInfo.fileSize.toInt(),
|
||||
@ -125,7 +129,7 @@ internal class QQImpl(
|
||||
//)
|
||||
// 为什么不能 ??
|
||||
|
||||
return NotOnlineImageFromFile(
|
||||
return OfflineFriendImage(
|
||||
filepath = response.resourceId,
|
||||
md5 = image.md5,
|
||||
fileLength = image.inputSize.toInt(),
|
||||
@ -192,6 +196,7 @@ internal class MemberImpl(
|
||||
// region QQ delegate
|
||||
override val id: Long = qq.id
|
||||
override val nick: String = qq.nick
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override suspend fun queryProfile(): Profile = qq.queryProfile()
|
||||
|
||||
@ -202,12 +207,14 @@ internal class MemberImpl(
|
||||
override suspend fun queryRemark(): FriendNameRemark = qq.queryRemark()
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain): MessageReceipt<QQ> = qq.sendMessage(message)
|
||||
override suspend fun uploadImage(image: ExternalImage): Image = qq.uploadImage(image)
|
||||
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image)
|
||||
// endregion
|
||||
|
||||
override var permission: MemberPermission = memberInfo.permission
|
||||
|
||||
@Suppress("PropertyName")
|
||||
internal var _nameCard: String = memberInfo.nameCard
|
||||
|
||||
@Suppress("PropertyName")
|
||||
internal var _specialTitle: String = memberInfo.specialTitle
|
||||
|
||||
@ -589,7 +596,7 @@ internal class GroupImpl(
|
||||
return MessageReceipt(source, this, botAsMember)
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(image: ExternalImage): Image = try {
|
||||
override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage = try {
|
||||
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
|
||||
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
|
||||
}
|
||||
@ -606,6 +613,7 @@ internal class GroupImpl(
|
||||
filename = image.filename
|
||||
).sendAndExpect()
|
||||
|
||||
@Suppress("UNCHECKED_CAST") // bug
|
||||
when (response) {
|
||||
is ImgStore.GroupPicUp.Response.Failed -> {
|
||||
ImageUploadEvent.Failed(this@GroupImpl, image, response.resultCode, response.message).broadcast()
|
||||
@ -625,7 +633,7 @@ internal class GroupImpl(
|
||||
// fileId = response.fileId.toInt()
|
||||
// )
|
||||
// println("NMSL")
|
||||
return CustomFaceFromFile(
|
||||
return OfflineGroupImage(
|
||||
md5 = image.md5,
|
||||
filepath = resourceId
|
||||
).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
|
||||
@ -655,7 +663,7 @@ internal class GroupImpl(
|
||||
// imageType = image.imageType,
|
||||
// fileId = response.fileId.toInt()
|
||||
// )
|
||||
return CustomFaceFromFile(
|
||||
return OfflineGroupImage(
|
||||
md5 = image.md5,
|
||||
filepath = resourceId
|
||||
).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
|
||||
|
@ -24,8 +24,8 @@ import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.MessageRecallEvent
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.qqandroid.message.CustomFaceFromServer
|
||||
import net.mamoe.mirai.qqandroid.message.NotOnlineImageFromServer
|
||||
import net.mamoe.mirai.qqandroid.message.OnlineFriendImageImpl
|
||||
import net.mamoe.mirai.qqandroid.message.OnlineGroupImageImpl
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
||||
@ -58,6 +58,7 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
)
|
||||
internal var firstLoginSucceed: Boolean = false
|
||||
override val uin: Long get() = client.uin
|
||||
|
||||
@Deprecated(
|
||||
"use friends instead",
|
||||
level = DeprecationLevel.ERROR,
|
||||
@ -208,14 +209,14 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun queryImageUrl(image: Image): String = "http://gchat.qpic.cn" + when (image) {
|
||||
is NotOnlineImageFromServer -> image.delegate.origUrl
|
||||
is CustomFaceFromServer -> image.delegate.origUrl
|
||||
is CustomFaceFromFile -> {
|
||||
TODO()
|
||||
override suspend fun queryImageUrl(image: Image): String = when (image) {
|
||||
is OnlineFriendImageImpl -> image.originUrl
|
||||
is OnlineGroupImageImpl -> image.originUrl
|
||||
is OfflineGroupImage -> {
|
||||
TODO("暂不支持获取离线图片链接")
|
||||
}
|
||||
is NotOnlineImageFromFile -> {
|
||||
TODO()
|
||||
is OfflineFriendImage -> {
|
||||
TODO("暂不支持获取离线图片链接")
|
||||
}
|
||||
else -> error("unsupported image class: ${image::class.simpleName}")
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ internal fun At.toJceData(): ImMsgBody.Text {
|
||||
)
|
||||
}
|
||||
|
||||
internal fun NotOnlineImageFromFile.toJceData(): ImMsgBody.NotOnlineImage {
|
||||
internal fun OfflineFriendImage.toJceData(): ImMsgBody.NotOnlineImage {
|
||||
return ImMsgBody.NotOnlineImage(
|
||||
filePath = this.filepath,
|
||||
resId = this.resourceId,
|
||||
@ -104,7 +104,7 @@ internal fun Face.toJceData(): ImMsgBody.Face {
|
||||
)
|
||||
}
|
||||
|
||||
internal fun CustomFaceFromFile.toJceData(): ImMsgBody.CustomFace {
|
||||
internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace {
|
||||
return ImMsgBody.CustomFace(
|
||||
filePath = this.filepath,
|
||||
fileId = this.fileId,
|
||||
@ -240,10 +240,10 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
|
||||
elements.add(ImMsgBody.Elem(text = it.toJceData()))
|
||||
elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " ")))
|
||||
}
|
||||
is CustomFaceFromFile -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
|
||||
is CustomFaceFromServer -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
|
||||
is NotOnlineImageFromServer -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
|
||||
is NotOnlineImageFromFile -> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData()))
|
||||
is OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
|
||||
is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
|
||||
is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
|
||||
is OfflineFriendImage -> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData()))
|
||||
is AtAll -> elements.add(atAllData)
|
||||
is Face -> elements.add(ImMsgBody.Elem(face = it.toJceData()))
|
||||
is QuoteReplyToSend -> {
|
||||
@ -273,9 +273,9 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
|
||||
return elements
|
||||
}
|
||||
|
||||
internal class CustomFaceFromServer(
|
||||
internal class OnlineGroupImageImpl(
|
||||
internal val delegate: ImMsgBody.CustomFace
|
||||
) : CustomFace() {
|
||||
) : OnlineGroupImage() {
|
||||
override val filepath: String = delegate.filePath
|
||||
override val fileId: Int get() = delegate.fileId
|
||||
override val serverIp: Int get() = delegate.serverIp
|
||||
@ -293,9 +293,11 @@ internal class CustomFaceFromServer(
|
||||
override val original: Int get() = delegate.origin
|
||||
override val pbReserve: ByteArray get() = delegate.pbReserve
|
||||
override val imageId: String = ExternalImage.generateImageId(delegate.md5, imageType)
|
||||
override val originUrl: String
|
||||
get() = "http://gchat.qpic.cn" + delegate.origUrl
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is CustomFaceFromServer && other.filepath == this.filepath && other.md5.contentEquals(this.md5)
|
||||
return other is OnlineGroupImageImpl && other.filepath == this.filepath && other.md5.contentEquals(this.md5)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@ -303,9 +305,9 @@ internal class CustomFaceFromServer(
|
||||
}
|
||||
}
|
||||
|
||||
internal class NotOnlineImageFromServer(
|
||||
internal class OnlineFriendImageImpl(
|
||||
internal val delegate: ImMsgBody.NotOnlineImage
|
||||
) : NotOnlineImage() {
|
||||
) : OnlineFriendImage() {
|
||||
override val resourceId: String get() = delegate.resId
|
||||
override val md5: ByteArray get() = delegate.picMd5
|
||||
override val filepath: String get() = delegate.filePath
|
||||
@ -317,9 +319,12 @@ internal class NotOnlineImageFromServer(
|
||||
override val downloadPath: String get() = delegate.downloadPath
|
||||
override val fileId: Int get() = delegate.fileId
|
||||
override val original: Int get() = delegate.original
|
||||
override val originUrl: String
|
||||
get() = "http://c2cpicdw.qpic.cn" + this.delegate.origUrl
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is NotOnlineImageFromServer && other.resourceId == this.resourceId && other.md5.contentEquals(this.md5)
|
||||
return other is OnlineFriendImageImpl && other.resourceId == this.resourceId && other.md5
|
||||
.contentEquals(this.md5)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@ -368,8 +373,8 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilde
|
||||
this.forEach {
|
||||
when {
|
||||
it.srcMsg != null -> message.add(QuoteReply(MessageSourceFromServer(it.srcMsg)))
|
||||
it.notOnlineImage != null -> message.add(NotOnlineImageFromServer(it.notOnlineImage))
|
||||
it.customFace != null -> message.add(CustomFaceFromServer(it.customFace))
|
||||
it.notOnlineImage != null -> message.add(OnlineFriendImageImpl(it.notOnlineImage))
|
||||
it.customFace != null -> message.add(OnlineGroupImageImpl(it.customFace))
|
||||
it.face != null -> message.add(Face(it.face.index))
|
||||
it.text != null -> {
|
||||
if (it.text.attr6Buf.isEmpty()) {
|
||||
|
@ -18,8 +18,8 @@ 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.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.OfflineImage
|
||||
import net.mamoe.mirai.message.data.id
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
@ -32,6 +32,7 @@ import net.mamoe.mirai.utils.WeakRefProperty
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
@UseExperimental(MiraiInternalAPI::class, JavaHappyAPI::class)
|
||||
actual abstract class Contact : CoroutineScope, ContactJavaHappyAPI() {
|
||||
/**
|
||||
@ -61,6 +62,8 @@ actual abstract class Contact : CoroutineScope, ContactJavaHappyAPI() {
|
||||
*
|
||||
* @return 消息回执. 可 [引用回复][MessageReceipt.quote](仅群聊)或 [撤回][MessageReceipt.recall] 这条消息.
|
||||
*/
|
||||
@JvmName("sendMessgaeSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract suspend fun sendMessage(message: MessageChain): MessageReceipt<out Contact>
|
||||
|
||||
/**
|
||||
@ -72,7 +75,9 @@ actual abstract class Contact : CoroutineScope, ContactJavaHappyAPI() {
|
||||
* @throws EventCancelledException 当发送消息事件被取消
|
||||
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
|
||||
*/
|
||||
actual abstract suspend fun uploadImage(image: ExternalImage): Image
|
||||
@JvmName("uploadImageSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract suspend fun uploadImage(image: ExternalImage): OfflineImage
|
||||
|
||||
/**
|
||||
* 判断 `this` 和 [other] 是否是相同的类型, 并且 [id] 相同.
|
||||
|
@ -17,11 +17,15 @@ 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.MessageChain
|
||||
import net.mamoe.mirai.message.data.OfflineGroupImage
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
|
||||
/**
|
||||
* 群. 在 QQ Android 中叫做 "Troop"
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
actual abstract class Group : Contact(), CoroutineScope {
|
||||
/**
|
||||
* 群名称.
|
||||
@ -33,6 +37,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var name: String
|
||||
|
||||
/**
|
||||
* 入群公告, 没有时为空字符串.
|
||||
*
|
||||
@ -42,6 +47,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var entranceAnnouncement: String
|
||||
|
||||
/**
|
||||
* 全体禁言状态. `true` 为开启.
|
||||
*
|
||||
@ -51,6 +57,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var isMuteAll: Boolean
|
||||
|
||||
/**
|
||||
* 坦白说状态. `true` 为允许.
|
||||
*
|
||||
@ -60,6 +67,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var isConfessTalkEnabled: Boolean
|
||||
|
||||
/**
|
||||
* 允许群员邀请好友入群的状态. `true` 为允许
|
||||
*
|
||||
@ -69,29 +77,35 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var isAllowMemberInvite: Boolean
|
||||
|
||||
/**
|
||||
* 自动加群审批
|
||||
*/
|
||||
actual abstract val isAutoApproveEnabled: Boolean
|
||||
|
||||
/**
|
||||
* 匿名聊天
|
||||
*/
|
||||
actual abstract val isAnonymousChatEnabled: Boolean
|
||||
|
||||
/**
|
||||
* 同为 groupCode, 用户看到的群号码.
|
||||
*/
|
||||
actual abstract override val id: Long
|
||||
|
||||
/**
|
||||
* 群主.
|
||||
*
|
||||
* @return 若机器人是群主, 返回 [botAsMember]. 否则返回相应的成员
|
||||
*/
|
||||
actual abstract val owner: Member
|
||||
|
||||
/**
|
||||
* [Bot] 在群内的 [Member] 实例
|
||||
*/
|
||||
@MiraiExperimentalAPI
|
||||
actual abstract val botAsMember: Member
|
||||
|
||||
/**
|
||||
* 机器人被禁言还剩余多少秒
|
||||
*
|
||||
@ -99,6 +113,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @see isBotMuted 判断机器人是否正在被禁言
|
||||
*/
|
||||
actual abstract val botMuteRemaining: Int
|
||||
|
||||
/**
|
||||
* 机器人在这个群里的权限
|
||||
*
|
||||
@ -108,6 +123,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @see BotGroupPermissionChangeEvent 机器人群员修改
|
||||
*/
|
||||
actual abstract val botPermission: MemberPermission
|
||||
|
||||
/**
|
||||
* 群头像下载链接.
|
||||
*/
|
||||
@ -161,8 +177,23 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
*
|
||||
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
|
||||
*/
|
||||
@JvmName("sendMessageSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract override suspend fun sendMessage(message: MessageChain): MessageReceipt<Group>
|
||||
|
||||
/**
|
||||
* 上传一个图片以备发送.
|
||||
*
|
||||
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
|
||||
* @see ImageUploadEvent 图片发送完成事件
|
||||
*
|
||||
* @throws EventCancelledException 当发送消息事件被取消
|
||||
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
|
||||
*/
|
||||
@JvmName("uploadImageSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage
|
||||
|
||||
actual companion object {
|
||||
/**
|
||||
* by @kar98k
|
||||
|
@ -7,12 +7,17 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.data.FriendNameRemark
|
||||
import net.mamoe.mirai.data.PreviousNameList
|
||||
import net.mamoe.mirai.data.Profile
|
||||
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.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.OfflineFriendImage
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
|
||||
/**
|
||||
* QQ 对象.
|
||||
@ -84,4 +89,17 @@ actual abstract class QQ : Contact(), CoroutineScope {
|
||||
@JvmName("sendMessageSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract override suspend fun sendMessage(message: MessageChain): MessageReceipt<QQ>
|
||||
|
||||
/**
|
||||
* 上传一个图片以备发送.
|
||||
*
|
||||
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
|
||||
* @see ImageUploadEvent 图片发送完成事件
|
||||
*
|
||||
* @throws EventCancelledException 当发送消息事件被取消
|
||||
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
|
||||
*/
|
||||
@JvmName("uploadImageSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage
|
||||
}
|
@ -82,7 +82,7 @@ expect abstract class Contact() : CoroutineScope, ContactJavaHappyAPI {
|
||||
*/
|
||||
@JvmName("uploadImageSuspend")
|
||||
@JvmSynthetic
|
||||
abstract suspend fun uploadImage(image: ExternalImage): Image
|
||||
abstract suspend fun uploadImage(image: ExternalImage): OfflineImage
|
||||
|
||||
/**
|
||||
* 判断 `this` 和 [other] 是否是相同的类型, 并且 [id] 相同.
|
||||
|
@ -19,7 +19,10 @@ 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.MessageChain
|
||||
import net.mamoe.mirai.message.data.OfflineGroupImage
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
|
||||
@ -177,6 +180,19 @@ expect abstract class Group() : Contact, CoroutineScope {
|
||||
@JvmSynthetic
|
||||
abstract override suspend fun sendMessage(message: MessageChain): MessageReceipt<Group>
|
||||
|
||||
/**
|
||||
* 上传一个图片以备发送.
|
||||
*
|
||||
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
|
||||
* @see ImageUploadEvent 图片发送完成事件
|
||||
*
|
||||
* @throws EventCancelledException 当发送消息事件被取消
|
||||
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
|
||||
*/
|
||||
@JvmName("uploadImageSuspend")
|
||||
@JvmSynthetic
|
||||
abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage
|
||||
|
||||
companion object {
|
||||
// don't @JvmStatic: JDK 1.8 required
|
||||
fun calculateGroupUinByGroupCode(groupCode: Long): Long
|
||||
|
@ -16,12 +16,17 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.data.FriendNameRemark
|
||||
import net.mamoe.mirai.data.PreviousNameList
|
||||
import net.mamoe.mirai.data.Profile
|
||||
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.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.OfflineFriendImage
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
|
||||
@ -95,5 +100,16 @@ expect abstract class QQ() : Contact, CoroutineScope {
|
||||
@JvmName("sendMessageSuspend")
|
||||
abstract override suspend fun sendMessage(message: MessageChain): MessageReceipt<QQ>
|
||||
|
||||
|
||||
/**
|
||||
* 上传一个图片以备发送.
|
||||
*
|
||||
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
|
||||
* @see ImageUploadEvent 图片发送完成事件
|
||||
*
|
||||
* @throws EventCancelledException 当发送消息事件被取消
|
||||
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
|
||||
*/
|
||||
@JvmName("uploadImageSuspend")
|
||||
@JvmSynthetic
|
||||
abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage
|
||||
}
|
@ -16,26 +16,21 @@ package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.BotImpl
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.chunkedHexToBytes
|
||||
import kotlin.js.JsName
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
* 自定义表情 (收藏的表情), 图片
|
||||
*/
|
||||
sealed class Image : Message, MessageContent {
|
||||
companion object Key : Message.Key<Image> {
|
||||
@JvmStatic
|
||||
@JsName("fromId")
|
||||
@JvmName("fromId")
|
||||
operator fun invoke(imageId: String): Image = when (imageId.length) {
|
||||
37 -> NotOnlineImageFromFile(imageId) // /f8f1ab55-bf8e-4236-b55e-955848d7069f
|
||||
42 -> CustomFaceFromFile(imageId) // {01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png
|
||||
else -> throw IllegalArgumentException("Bad imageId, expecting length=37 or 42, got ${imageId.length}")
|
||||
}
|
||||
}
|
||||
interface Image : Message, MessageContent {
|
||||
companion object Key : Message.Key<Image>
|
||||
|
||||
/**
|
||||
* 图片的 id. 只需要有这个 id 即可发送图片.
|
||||
@ -44,14 +39,84 @@ sealed class Image : Message, MessageContent {
|
||||
* 好友图片的 id: `/f8f1ab55-bf8e-4236-b55e-955848d7069f`
|
||||
* 群图片的 id: `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png`
|
||||
*/
|
||||
abstract val imageId: String
|
||||
val imageId: String
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@JsName("newImage")
|
||||
@JvmName("newImage")
|
||||
fun Image(imageId: String): Image = when (imageId.length) {
|
||||
37 -> OfflineFriendImage(imageId) // /f8f1ab55-bf8e-4236-b55e-955848d7069f
|
||||
42 -> OfflineGroupImage(imageId) // {01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png
|
||||
else -> throw IllegalArgumentException("Bad imageId, expecting length=37 or 42, got ${imageId.length}")
|
||||
}
|
||||
|
||||
@MiraiInternalAPI("使用 Image")
|
||||
abstract class AbstractImage internal constructor() : Image {
|
||||
final override fun toString(): String {
|
||||
return "[mirai:$imageId]"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CustomFace : Image() {
|
||||
// region 在线图片
|
||||
|
||||
/**
|
||||
* 在服务器上的图片. 它可以直接获取下载链接.
|
||||
*
|
||||
* 一般由 [Contact.uploadImage] 得到
|
||||
*/
|
||||
interface OnlineImage : Image {
|
||||
/**
|
||||
* 原图下载链接. 包含域名
|
||||
*/
|
||||
val originUrl: String
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询原图下载链接.
|
||||
*/
|
||||
suspend fun Image.queryUrl(): String {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
return when (this) {
|
||||
is OnlineImage -> this.originUrl
|
||||
else -> BotImpl.instances.peekFirst().get()?.queryImageUrl(this)
|
||||
?: error("No Bot available to query image url")
|
||||
}
|
||||
}
|
||||
|
||||
// endregion 在线图片
|
||||
|
||||
|
||||
// region 离线图片
|
||||
|
||||
/**
|
||||
* 离线的图片, 即为客户端主动上传到服务器而获得的 [Image] 实例.
|
||||
* 不能直接获取它在服务器上的链接. 需要通过 [Bot.queryImageUrl] 查询
|
||||
*
|
||||
* 一般由 [Contact.uploadImage] 得到
|
||||
* @see queryOriginUrl
|
||||
*/
|
||||
interface OfflineImage : Image
|
||||
|
||||
/**
|
||||
* 原图下载链接. 包含域名
|
||||
*/
|
||||
suspend fun OfflineImage.queryOriginUrl(): String {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
return BotImpl.instances.peekFirst().get()?.queryImageUrl(this) ?: error("No Bot available to query image url")
|
||||
}
|
||||
|
||||
// endregion 离线图片
|
||||
|
||||
// region 群图片
|
||||
|
||||
|
||||
/**
|
||||
* 群图片
|
||||
*/
|
||||
// CustomFace
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
sealed class GroupImage : AbstractImage() {
|
||||
abstract val filepath: String
|
||||
abstract val fileId: Int
|
||||
abstract val serverIp: Int
|
||||
@ -70,30 +135,14 @@ abstract class CustomFace : Image() {
|
||||
abstract val original: Int
|
||||
}
|
||||
|
||||
private val EMPTY_BYTE_ARRAY = ByteArray(0)
|
||||
|
||||
private fun calculateImageMd5ByImageId(imageId: String): ByteArray {
|
||||
return if (imageId.startsWith('/')) {
|
||||
imageId
|
||||
.drop(1)
|
||||
.replace("-", "")
|
||||
.take(16 * 2)
|
||||
.chunkedHexToBytes()
|
||||
} else {
|
||||
imageId
|
||||
.substringAfter('{')
|
||||
.substringBefore('}')
|
||||
.replace("-", "")
|
||||
.take(16 * 2)
|
||||
.chunkedHexToBytes()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 [Group.uploadImage] 上传得到的 [GroupImage]. 它的链接需要查询 [Bot.queryImageUrl]
|
||||
*/
|
||||
@Serializable
|
||||
data class CustomFaceFromFile(
|
||||
data class OfflineGroupImage(
|
||||
override val filepath: String, // {01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png
|
||||
override val md5: ByteArray
|
||||
) : CustomFace() {
|
||||
) : GroupImage(), OfflineImage {
|
||||
constructor(imageId: String) : this(filepath = imageId, md5 = calculateImageMd5ByImageId(imageId))
|
||||
|
||||
override val fileId: Int get() = 0
|
||||
@ -117,14 +166,26 @@ data class CustomFaceFromFile(
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is CustomFaceFromFile && other.md5.contentEquals(this.md5) && other.filepath == this.filepath
|
||||
return other is OfflineGroupImage && other.md5.contentEquals(this.md5) && other.filepath == this.filepath
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 电脑可能看不到这个消息.
|
||||
* 接收消息时获取到的 [GroupImage]. 它可以直接获取下载链接 [originUrl]
|
||||
*/
|
||||
abstract class NotOnlineImage : Image() {
|
||||
abstract class OnlineGroupImage : GroupImage(), OnlineImage
|
||||
|
||||
|
||||
// endregion 群图片
|
||||
|
||||
// region 好友图片
|
||||
|
||||
|
||||
/**
|
||||
* 好友图片
|
||||
*/ // NotOnlineImage
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
sealed class FriendImage : AbstractImage() {
|
||||
abstract val resourceId: String
|
||||
abstract val md5: ByteArray
|
||||
abstract val filepath: String
|
||||
@ -140,7 +201,10 @@ abstract class NotOnlineImage : Image() {
|
||||
override val imageId: String get() = resourceId
|
||||
}
|
||||
|
||||
data class NotOnlineImageFromFile(
|
||||
/**
|
||||
* 通过 [Group.uploadImage] 上传得到的 [GroupImage]. 它的链接需要查询 [Bot.queryImageUrl]
|
||||
*/
|
||||
data class OfflineFriendImage(
|
||||
override val resourceId: String,
|
||||
override val md5: ByteArray,
|
||||
@Transient override val filepath: String = resourceId,
|
||||
@ -151,7 +215,7 @@ data class NotOnlineImageFromFile(
|
||||
@Transient override val imageType: Int = 1000,
|
||||
@Transient override val downloadPath: String = resourceId,
|
||||
@Transient override val fileId: Int = 0
|
||||
) : NotOnlineImage() {
|
||||
) : FriendImage(), OfflineImage {
|
||||
constructor(imageId: String) : this(resourceId = imageId, md5 = calculateImageMd5ByImageId(imageId))
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@ -159,6 +223,39 @@ data class NotOnlineImageFromFile(
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is NotOnlineImageFromFile && other.md5.contentEquals(this.md5) && other.resourceId == this.resourceId
|
||||
return other is OfflineFriendImage && other.md5
|
||||
.contentEquals(this.md5) && other.resourceId == this.resourceId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收消息时获取到的 [FriendImage]. 它可以直接获取下载链接 [originUrl]
|
||||
*/
|
||||
abstract class OnlineFriendImage : FriendImage(), OnlineImage
|
||||
|
||||
|
||||
// endregion
|
||||
|
||||
|
||||
// region internal
|
||||
|
||||
private val EMPTY_BYTE_ARRAY = ByteArray(0)
|
||||
|
||||
private fun calculateImageMd5ByImageId(imageId: String): ByteArray {
|
||||
return if (imageId.startsWith('/')) {
|
||||
imageId
|
||||
.drop(1)
|
||||
.replace("-", "")
|
||||
.take(16 * 2)
|
||||
.chunkedHexToBytes()
|
||||
} else {
|
||||
imageId
|
||||
.substringAfter('{')
|
||||
.substringBefore('}')
|
||||
.replace("-", "")
|
||||
.take(16 * 2)
|
||||
.chunkedHexToBytes()
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
@ -18,8 +18,8 @@ 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.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.OfflineImage
|
||||
import net.mamoe.mirai.message.data.id
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
@ -76,7 +76,7 @@ actual abstract class Contact : CoroutineScope, ContactJavaHappyAPI() {
|
||||
*/
|
||||
@JvmName("uploadImageSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract suspend fun uploadImage(image: ExternalImage): Image
|
||||
actual abstract suspend fun uploadImage(image: ExternalImage): OfflineImage
|
||||
|
||||
/**
|
||||
* 判断 `this` 和 [other] 是否是相同的类型, 并且 [id] 相同.
|
||||
|
@ -17,11 +17,15 @@ 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.MessageChain
|
||||
import net.mamoe.mirai.message.data.OfflineGroupImage
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
|
||||
/**
|
||||
* 群. 在 QQ Android 中叫做 "Troop"
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
actual abstract class Group : Contact(), CoroutineScope {
|
||||
/**
|
||||
* 群名称.
|
||||
@ -33,6 +37,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var name: String
|
||||
|
||||
/**
|
||||
* 入群公告, 没有时为空字符串.
|
||||
*
|
||||
@ -42,6 +47,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var entranceAnnouncement: String
|
||||
|
||||
/**
|
||||
* 全体禁言状态. `true` 为开启.
|
||||
*
|
||||
@ -51,6 +57,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var isMuteAll: Boolean
|
||||
|
||||
/**
|
||||
* 坦白说状态. `true` 为允许.
|
||||
*
|
||||
@ -60,6 +67,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var isConfessTalkEnabled: Boolean
|
||||
|
||||
/**
|
||||
* 允许群员邀请好友入群的状态. `true` 为允许
|
||||
*
|
||||
@ -69,29 +77,35 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @throws PermissionDeniedException 无权限修改时将会抛出异常
|
||||
*/
|
||||
actual abstract var isAllowMemberInvite: Boolean
|
||||
|
||||
/**
|
||||
* 自动加群审批
|
||||
*/
|
||||
actual abstract val isAutoApproveEnabled: Boolean
|
||||
|
||||
/**
|
||||
* 匿名聊天
|
||||
*/
|
||||
actual abstract val isAnonymousChatEnabled: Boolean
|
||||
|
||||
/**
|
||||
* 同为 groupCode, 用户看到的群号码.
|
||||
*/
|
||||
actual abstract override val id: Long
|
||||
|
||||
/**
|
||||
* 群主.
|
||||
*
|
||||
* @return 若机器人是群主, 返回 [botAsMember]. 否则返回相应的成员
|
||||
*/
|
||||
actual abstract val owner: Member
|
||||
|
||||
/**
|
||||
* [Bot] 在群内的 [Member] 实例
|
||||
*/
|
||||
@MiraiExperimentalAPI
|
||||
actual abstract val botAsMember: Member
|
||||
|
||||
/**
|
||||
* 机器人被禁言还剩余多少秒
|
||||
*
|
||||
@ -99,6 +113,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @see isBotMuted 判断机器人是否正在被禁言
|
||||
*/
|
||||
actual abstract val botMuteRemaining: Int
|
||||
|
||||
/**
|
||||
* 机器人在这个群里的权限
|
||||
*
|
||||
@ -108,6 +123,7 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
* @see BotGroupPermissionChangeEvent 机器人群员修改
|
||||
*/
|
||||
actual abstract val botPermission: MemberPermission
|
||||
|
||||
/**
|
||||
* 群头像下载链接.
|
||||
*/
|
||||
@ -161,9 +177,23 @@ actual abstract class Group : Contact(), CoroutineScope {
|
||||
*
|
||||
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
|
||||
*/
|
||||
@JvmName("sendMessageSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract override suspend fun sendMessage(message: MessageChain): MessageReceipt<Group>
|
||||
|
||||
/**
|
||||
* 上传一个图片以备发送.
|
||||
*
|
||||
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
|
||||
* @see ImageUploadEvent 图片发送完成事件
|
||||
*
|
||||
* @throws EventCancelledException 当发送消息事件被取消
|
||||
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
|
||||
*/
|
||||
@JvmName("uploadImageSuspend")
|
||||
@JvmSynthetic
|
||||
actual abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage
|
||||
|
||||
actual companion object {
|
||||
/**
|
||||
* by @kar98k
|
||||
|
@ -7,12 +7,17 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.data.FriendNameRemark
|
||||
import net.mamoe.mirai.data.PreviousNameList
|
||||
import net.mamoe.mirai.data.Profile
|
||||
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.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.OfflineFriendImage
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
|
||||
/**
|
||||
* QQ 对象.
|
||||
@ -85,4 +90,16 @@ actual abstract class QQ : Contact(), CoroutineScope {
|
||||
@JvmSynthetic
|
||||
actual abstract override suspend fun sendMessage(message: MessageChain): MessageReceipt<QQ>
|
||||
|
||||
/**
|
||||
* 上传一个图片以备发送.
|
||||
*
|
||||
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
|
||||
* @see ImageUploadEvent 图片发送完成事件
|
||||
*
|
||||
* @throws EventCancelledException 当发送消息事件被取消
|
||||
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@JvmName("uploadImageSuspend")
|
||||
actual abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage
|
||||
}
|
Loading…
Reference in New Issue
Block a user