Commit 12ebfa1d authored by Him188's avatar Him188

Unified Image types: Online/Offline Image, Group/Friend Image

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