Commit 1542b73f authored by Him188's avatar Him188

Improve KDoc

parent c8cd03ac
...@@ -74,6 +74,8 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), Identified { ...@@ -74,6 +74,8 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), Identified {
/** /**
* 上传一个图片以备发送. * 上传一个图片以备发送.
* *
* @see Image 查看更多信息
*
* @see BeforeImageUploadEvent 图片发送前事件, cancellable * @see BeforeImageUploadEvent 图片发送前事件, cancellable
* @see ImageUploadEvent 图片发送完成事件 * @see ImageUploadEvent 图片发送完成事件
* *
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
package net.mamoe.mirai.message.data package net.mamoe.mirai.message.data
import net.mamoe.mirai.message.data.PokeMessage.Types
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.SinceMirai import net.mamoe.mirai.utils.SinceMirai
...@@ -40,15 +41,18 @@ sealed class HummerMessage : MessageContent { ...@@ -40,15 +41,18 @@ sealed class HummerMessage : MessageContent {
/** /**
* 戳一戳. 可以发送给好友或群. * 戳一戳. 可以发送给好友或群.
*
* @see Types 使用伴生对象中的常量
*/ */
@SinceMirai("0.31.0") @SinceMirai("0.31.0")
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
data class PokeMessage @MiraiInternalAPI(message = "使用伴生对象中的常量") constructor( data class PokeMessage internal constructor(
@MiraiExperimentalAPI @MiraiExperimentalAPI("may change in future")
val type: Int, val type: Int,
@MiraiExperimentalAPI @MiraiExperimentalAPI("may change in future")
val id: Int val id: Int
) : HummerMessage() { ) : HummerMessage() {
@Suppress("DEPRECATION_ERROR", "DEPRECATION", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
companion object Types : Message.Key<PokeMessage> { companion object Types : Message.Key<PokeMessage> {
override val typeName: String override val typeName: String
get() = "PokeMessage" get() = "PokeMessage"
...@@ -117,6 +121,8 @@ data class PokeMessage @MiraiInternalAPI(message = "使用伴生对象中的常 ...@@ -117,6 +121,8 @@ data class PokeMessage @MiraiInternalAPI(message = "使用伴生对象中的常
* 闪照 * 闪照
* *
* @see Image.flash 转换普通图片为闪照 * @see Image.flash 转换普通图片为闪照
*
* @see Image 查看图片相关信息
*/ */
@SinceMirai("0.33.0") @SinceMirai("0.33.0")
sealed class FlashImage : MessageContent, HummerMessage() { sealed class FlashImage : MessageContent, HummerMessage() {
......
...@@ -29,13 +29,14 @@ import kotlin.jvm.JvmSynthetic ...@@ -29,13 +29,14 @@ import kotlin.jvm.JvmSynthetic
/** /**
* 自定义表情 (收藏的表情), 图片 * 自定义表情 (收藏的表情), 图片
* *
* 查看平台 actual 定义以获取更多说明.
*
* @see FlashImage 闪照 * @see FlashImage 闪照
* @see Image.flash 转换普通图片为闪照 * @see Image.flash 转换普通图片为闪照
*/ */
interface Image : Message, MessageContent { expect interface Image : Message, MessageContent {
companion object Key : Message.Key<Image> { companion object Key : Message.Key<Image> {
override val typeName: String override val typeName: String
get() = "Image"
} }
/** /**
...@@ -64,7 +65,7 @@ interface Image : Message, MessageContent { ...@@ -64,7 +65,7 @@ interface Image : Message, MessageContent {
fun Image(imageId: String): Image = when { fun Image(imageId: String): Image = when {
imageId.startsWith('/') -> OfflineFriendImage(imageId) // /f8f1ab55-bf8e-4236-b55e-955848d7069f imageId.startsWith('/') -> OfflineFriendImage(imageId) // /f8f1ab55-bf8e-4236-b55e-955848d7069f
imageId.length == 42 || imageId.startsWith('{') && imageId.endsWith('}') -> OfflineGroupImage(imageId) // {01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png imageId.length == 42 || imageId.startsWith('{') && imageId.endsWith('}') -> OfflineGroupImage(imageId) // {01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png
else -> throw IllegalArgumentException("Bad imageId, expecting length=37 or 42, got ${imageId.length}") else -> throw IllegalArgumentException("illegal imageId: $imageId. $ILLEGAL_IMAGE_ID_EXCEPTION_MESSAGE")
} }
@MiraiInternalAPI("使用 Image") @MiraiInternalAPI("使用 Image")
...@@ -286,82 +287,4 @@ data class OfflineFriendImage( ...@@ -286,82 +287,4 @@ data class OfflineFriendImage(
*/ */
abstract class OnlineFriendImage : FriendImage(), OnlineImage abstract class OnlineFriendImage : FriendImage(), OnlineImage
// endregion
//////////////////////
// region internal
//////////////////////
private val EMPTY_BYTE_ARRAY = ByteArray(0)
// /000000000-3814297509-BFB7027B9354B8F899A062061D74E206
private val FRIEND_IMAGE_ID_REGEX_1 = Regex("""/[0-9]*-[0-9]*-[0-9a-zA-Z]{32}""")
// /f8f1ab55-bf8e-4236-b55e-955848d7069f
private val FRIEND_IMAGE_ID_REGEX_2 = Regex("""/.{8}-(.{4}-){3}.{12}""")
// {01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png
private val GROUP_IMAGE_ID_REGEX = Regex("""\{.{8}-(.{4}-){3}.{12}}\..*""")
@Suppress("NOTHING_TO_INLINE") // no stack waste
internal inline fun Char.hexDigitToByte(): Int {
return when (this) {
in '0'..'9' -> this - '0'
in 'A'..'F' -> 10 + (this - 'A')
in 'a'..'f' -> 10 + (this - 'a')
else -> throw IllegalArgumentException("illegal hex digit: $this")
}
}
internal fun String.skipToSecondHyphen(): Int {
var count = 0
this.forEachIndexed { index, c ->
if (c == '-' && ++count == 2) return index
}
error("cannot find two hyphens")
}
internal fun String.imageIdToMd5(offset: Int): ByteArray {
val result = ByteArray(16)
var cur = 0
var hasCurrent = false
var lastChar: Char = 0.toChar()
for (index in offset..this.lastIndex) {
val char = this[index]
if (char == '-') continue
if (hasCurrent) {
result[cur++] = (lastChar.hexDigitToByte().shl(4) or char.hexDigitToByte()).toByte()
if (cur == 16) return result
hasCurrent = false
} else {
lastChar = char
hasCurrent = true
}
}
error("No enough chars")
}
@OptIn(ExperimentalStdlibApi::class)
internal fun calculateImageMd5ByImageId(imageId: String): ByteArray {
return when {
imageId.matches(FRIEND_IMAGE_ID_REGEX_1) -> imageId.imageIdToMd5(imageId.skipToSecondHyphen() + 1)
imageId.matches(FRIEND_IMAGE_ID_REGEX_2) ->
imageId.imageIdToMd5(1)
imageId.matches(GROUP_IMAGE_ID_REGEX) -> {
imageId.imageIdToMd5(1)
}
else -> error(
"illegal imageId: $imageId. " +
"ImageId must matches Regex `${FRIEND_IMAGE_ID_REGEX_1.pattern}`, " +
"`${FRIEND_IMAGE_ID_REGEX_2.pattern}` or " +
"`${GROUP_IMAGE_ID_REGEX.pattern}`"
)
}
}
// endregion // endregion
\ No newline at end of file
...@@ -113,7 +113,7 @@ interface Message { ...@@ -113,7 +113,7 @@ interface Message {
* [GroupImage]: "[mirai:image:{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png]" * [GroupImage]: "[mirai:image:{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png]"
* [FriendImage]: "[mirai:image:/f8f1ab55-bf8e-4236-b55e-955848d7069f]" * [FriendImage]: "[mirai:image:/f8f1ab55-bf8e-4236-b55e-955848d7069f]"
* [PokeMessage]: "[mirai:poke:1,-1]" * [PokeMessage]: "[mirai:poke:1,-1]"
* [MessageChain]: 直接无间隔地连接所有元素 (`joinToString("")`) * [MessageChain]: 无间隔地连接所有元素 (`joinToString("")`)
* *
* @see contentToString * @see contentToString
*/ */
...@@ -122,10 +122,13 @@ interface Message { ...@@ -122,10 +122,13 @@ interface Message {
/** /**
* 转为最接近官方格式的字符串. 如 `At(member) + "test"` 将转为 `"@群名片 test"`. * 转为最接近官方格式的字符串. 如 `At(member) + "test"` 将转为 `"@群名片 test"`.
* *
* 对于 [NullMessageChain], 这个函数返回空字符串 ""; * 在使用消息相关 DSL 和扩展时, 一些内容比较的实现均使用 [contentToString] 而不是 [toString]
* 对于其他 [MessageChain], 这个函数返回值同 [toString].
* *
* 在使用消息相关 DSL 和扩展时, 一些内容比较的实现均使用的是 [contentToString] 而不是 [toString] * 各个 [SingleMessage] 的转换示例:
* [PlainText]: "Hello"
* [Image]: "\[图片\]"
* [PokeMessage]: "\[戳一戳\]"
* [MessageChain]: 无间隔地连接所有元素 (`joinToString("", transformer=Message::contentToString)`)
*/ */
@SinceMirai("0.34.0") @SinceMirai("0.34.0")
fun contentToString(): String fun contentToString(): String
...@@ -234,25 +237,39 @@ inline operator fun Message.times(count: Int): MessageChain = this.repeat(count) ...@@ -234,25 +237,39 @@ inline operator fun Message.times(count: Int): MessageChain = this.repeat(count)
@Suppress("OverridingDeprecatedMember") @Suppress("OverridingDeprecatedMember")
interface SingleMessage : Message, CharSequence, Comparable<String> { interface SingleMessage : Message, CharSequence, Comparable<String> {
/** @PlannedRemoval("1.0.0")
* 即 `sub in this.contentToString()` @JvmSynthetic
*/ @Deprecated(
"有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"),
DeprecationLevel.ERROR
)
/* final */ override operator fun contains(sub: String): Boolean = sub in this.contentToString() /* final */ override operator fun contains(sub: String): Boolean = sub in this.contentToString()
/** @PlannedRemoval("1.0.0")
* 比较两个消息的 [contentToString] @JvmSynthetic
*/ @Deprecated(
"有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"),
DeprecationLevel.ERROR
)
/* final */ override infix fun eq(other: Message): Boolean = this.contentToString() == other.contentToString() /* final */ override infix fun eq(other: Message): Boolean = this.contentToString() == other.contentToString()
/** @PlannedRemoval("1.0.0")
* 将 [contentToString] 与 [other] 比较 @JvmSynthetic
*/ @Deprecated(
"有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"),
DeprecationLevel.ERROR
)
/* final */ override infix fun eq(other: String): Boolean = this.contentToString() == other /* final */ override infix fun eq(other: String): Boolean = this.contentToString() == other
} }
/** /**
* 消息元数据, 即不含内容的元素. * 消息元数据, 即不含内容的元素.
* 包括: [MessageSource] * 包括: [MessageSource]
*
* @see ConstrainSingle 约束一个 [MessageChain] 中只存在这一种类型的元素
*/ */
interface MessageMetadata : SingleMessage { interface MessageMetadata : SingleMessage {
override val length: Int get() = 0 override val length: Int get() = 0
......
...@@ -262,3 +262,80 @@ internal class SingleMessageChainImpl constructor( ...@@ -262,3 +262,80 @@ internal class SingleMessageChainImpl constructor(
override fun iterator(): Iterator<SingleMessage> = iterator { yield(delegate) } override fun iterator(): Iterator<SingleMessage> = iterator { yield(delegate) }
} }
//////////////////////
// region Image impl
//////////////////////
internal val EMPTY_BYTE_ARRAY = ByteArray(0)
// /000000000-3814297509-BFB7027B9354B8F899A062061D74E206
private val FRIEND_IMAGE_ID_REGEX_1 = Regex("""/[0-9]*-[0-9]*-[0-9a-zA-Z]{32}""")
// /f8f1ab55-bf8e-4236-b55e-955848d7069f
private val FRIEND_IMAGE_ID_REGEX_2 = Regex("""/.{8}-(.{4}-){3}.{12}""")
// {01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png
private val GROUP_IMAGE_ID_REGEX = Regex("""\{.{8}-(.{4}-){3}.{12}}\..*""")
@Suppress("NOTHING_TO_INLINE") // no stack waste
internal inline fun Char.hexDigitToByte(): Int {
return when (this) {
in '0'..'9' -> this - '0'
in 'A'..'F' -> 10 + (this - 'A')
in 'a'..'f' -> 10 + (this - 'a')
else -> throw IllegalArgumentException("illegal hex digit: $this")
}
}
internal fun String.skipToSecondHyphen(): Int {
var count = 0
this.forEachIndexed { index, c ->
if (c == '-' && ++count == 2) return index
}
error("cannot find two hyphens")
}
internal fun String.imageIdToMd5(offset: Int): ByteArray {
val result = ByteArray(16)
var cur = 0
var hasCurrent = false
var lastChar: Char = 0.toChar()
for (index in offset..this.lastIndex) {
val char = this[index]
if (char == '-') continue
if (hasCurrent) {
result[cur++] = (lastChar.hexDigitToByte().shl(4) or char.hexDigitToByte()).toByte()
if (cur == 16) return result
hasCurrent = false
} else {
lastChar = char
hasCurrent = true
}
}
error("No enough chars")
}
@OptIn(ExperimentalStdlibApi::class)
internal fun calculateImageMd5ByImageId(imageId: String): ByteArray {
return when {
imageId.matches(FRIEND_IMAGE_ID_REGEX_1) -> imageId.imageIdToMd5(imageId.skipToSecondHyphen() + 1)
imageId.matches(FRIEND_IMAGE_ID_REGEX_2) ->
imageId.imageIdToMd5(1)
imageId.matches(GROUP_IMAGE_ID_REGEX) -> {
imageId.imageIdToMd5(1)
}
else -> error(
"illegal imageId: $imageId. $ILLEGAL_IMAGE_ID_EXCEPTION_MESSAGE"
)
}
}
internal val ILLEGAL_IMAGE_ID_EXCEPTION_MESSAGE =
"ImageId must matches Regex `${FRIEND_IMAGE_ID_REGEX_1.pattern}`, " +
"`${FRIEND_IMAGE_ID_REGEX_2.pattern}` or " +
"`${GROUP_IMAGE_ID_REGEX.pattern}`"
// endregion
\ No newline at end of file
...@@ -133,7 +133,7 @@ class ExternalImage private constructor( ...@@ -133,7 +133,7 @@ class ExternalImage private constructor(
* PNG: 1001 * PNG: 1001
* WEBP: 1002 * WEBP: 1002
* BMP: 1005 * BMP: 1005
* GIG: 2000 * GIG: 2000 // TODO gig? gif?
* APNG: 2001 * APNG: 2001
* SHARPP: 1004 * SHARPP: 1004
*/ */
...@@ -148,7 +148,7 @@ class ExternalImage private constructor( ...@@ -148,7 +148,7 @@ class ExternalImage private constructor(
} }
/** /**
* 将图片发送给指定联系人 * 将图片作为单独的消息发送给指定联系人
*/ */
@JvmSynthetic @JvmSynthetic
suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> = when (contact) { suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> = when (contact) {
...@@ -171,7 +171,7 @@ suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact ...@@ -171,7 +171,7 @@ suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact
} }
/** /**
* 将图片发送给 [this] * 将图片作为单独的消息发送给 [this]
*/ */
@JvmSynthetic @JvmSynthetic
suspend inline fun <C : Contact> C.sendImage(image: ExternalImage): MessageReceipt<C> = image.sendTo(this) suspend inline fun <C : Contact> C.sendImage(image: ExternalImage): MessageReceipt<C> = image.sendTo(this)
......
/*
* Copyright 2020 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:JvmMultifileClass
@file:JvmName("MessageUtils")
package net.mamoe.mirai.message.data
import kotlinx.io.core.Input
import net.mamoe.mirai.contact.Contact
import java.io.File
import java.io.InputStream
import java.net.URL
/**
* 自定义表情 (收藏的表情) 和普通图片.
*
* ### 上传和发送图片
* @see Contact.uploadImage 上传图片并得到 [Image] 消息
* @see Contact.sendImage 上传并发送单个图片作为一条消息
* @see Image.sendTo 上传图片并得到 [Image] 消息
*
* @see File.uploadAsImage
* @see InputStream.uploadAsImage
* @see Input.uploadAsImage
* @see URL.uploadAsImage
*
* @see File.sendAsImageTo
* @see InputStream.sendAsImageTo
* @see Input.sendAsImageTo
* @see URL.sendAsImageTo
*
*
*
* @see FlashImage 闪照
* @see Image.flash 转换普通图片为闪照
*/
actual interface Image : Message, MessageContent {
actual companion object Key : Message.Key<Image> {
actual override val typeName: String
get() = "Image"
}
/**
* 图片的 id.
* 图片 id 不一定会长时间保存, 因此不建议使用 id 发送图片.
* 图片 id 主要根据图片文件 md5 计算得到.
*
* 示例:
* 好友图片的 id: `/f8f1ab55-bf8e-4236-b55e-955848d7069f` 或 `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206`
* 群图片的 id: `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png`
*
* @see Image 使用 id 构造图片
*/
actual val imageId: String
}
\ 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