Improve KDoc

This commit is contained in:
Him188 2020-04-20 16:55:44 +08:00
parent c8cd03ac02
commit 1542b73fa9
7 changed files with 187 additions and 100 deletions

View File

@ -74,6 +74,8 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), Identified {
/**
* 上传一个图片以备发送.
*
* @see Image 查看更多信息
*
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
* @see ImageUploadEvent 图片发送完成事件
*

View File

@ -11,6 +11,7 @@
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.MiraiInternalAPI
import net.mamoe.mirai.utils.SinceMirai
@ -40,15 +41,18 @@ sealed class HummerMessage : MessageContent {
/**
* 戳一戳. 可以发送给好友或群.
*
* @see Types 使用伴生对象中的常量
*/
@SinceMirai("0.31.0")
@OptIn(MiraiInternalAPI::class)
data class PokeMessage @MiraiInternalAPI(message = "使用伴生对象中的常量") constructor(
@MiraiExperimentalAPI
data class PokeMessage internal constructor(
@MiraiExperimentalAPI("may change in future")
val type: Int,
@MiraiExperimentalAPI
@MiraiExperimentalAPI("may change in future")
val id: Int
) : HummerMessage() {
@Suppress("DEPRECATION_ERROR", "DEPRECATION", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
companion object Types : Message.Key<PokeMessage> {
override val typeName: String
get() = "PokeMessage"
@ -117,6 +121,8 @@ data class PokeMessage @MiraiInternalAPI(message = "使用伴生对象中的常
* 闪照
*
* @see Image.flash 转换普通图片为闪照
*
* @see Image 查看图片相关信息
*/
@SinceMirai("0.33.0")
sealed class FlashImage : MessageContent, HummerMessage() {

View File

@ -29,13 +29,14 @@ import kotlin.jvm.JvmSynthetic
/**
* 自定义表情 (收藏的表情), 图片
*
* 查看平台 actual 定义以获取更多说明.
*
* @see FlashImage 闪照
* @see Image.flash 转换普通图片为闪照
*/
interface Image : Message, MessageContent {
expect interface Image : Message, MessageContent {
companion object Key : Message.Key<Image> {
override val typeName: String
get() = "Image"
}
/**
@ -64,7 +65,7 @@ interface Image : Message, MessageContent {
fun Image(imageId: String): Image = when {
imageId.startsWith('/') -> OfflineFriendImage(imageId) // /f8f1ab55-bf8e-4236-b55e-955848d7069f
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")
@ -286,82 +287,4 @@ data class OfflineFriendImage(
*/
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

View File

@ -113,7 +113,7 @@ interface Message {
* [GroupImage]: "[mirai:image:{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png]"
* [FriendImage]: "[mirai:image:/f8f1ab55-bf8e-4236-b55e-955848d7069f]"
* [PokeMessage]: "[mirai:poke:1,-1]"
* [MessageChain]: 直接无间隔地连接所有元素 (`joinToString("")`)
* [MessageChain]: 无间隔地连接所有元素 (`joinToString("")`)
*
* @see contentToString
*/
@ -122,10 +122,13 @@ interface Message {
/**
* 转为最接近官方格式的字符串. `At(member) + "test"` 将转为 `"@群名片 test"`.
*
* 对于 [NullMessageChain], 这个函数返回空字符串 "";
* 对于其他 [MessageChain], 这个函数返回值同 [toString].
* 在使用消息相关 DSL 和扩展时, 一些内容比较的实现均使用 [contentToString] 而不是 [toString]
*
* 在使用消息相关 DSL 和扩展时, 一些内容比较的实现均使用的是 [contentToString] 而不是 [toString]
* 各个 [SingleMessage] 的转换示例:
* [PlainText]: "Hello"
* [Image]: "\[图片\]"
* [PokeMessage]: "\[戳一戳\]"
* [MessageChain]: 无间隔地连接所有元素 (`joinToString("", transformer=Message::contentToString)`)
*/
@SinceMirai("0.34.0")
fun contentToString(): String
@ -234,25 +237,39 @@ inline operator fun Message.times(count: Int): MessageChain = this.repeat(count)
@Suppress("OverridingDeprecatedMember")
interface SingleMessage : Message, CharSequence, Comparable<String> {
/**
* `sub in this.contentToString()`
*/
@PlannedRemoval("1.0.0")
@JvmSynthetic
@Deprecated(
"有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"),
DeprecationLevel.ERROR
)
/* final */ override operator fun contains(sub: String): Boolean = sub in this.contentToString()
/**
* 比较两个消息的 [contentToString]
*/
@PlannedRemoval("1.0.0")
@JvmSynthetic
@Deprecated(
"有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"),
DeprecationLevel.ERROR
)
/* final */ override infix fun eq(other: Message): Boolean = this.contentToString() == other.contentToString()
/**
* [contentToString] [other] 比较
*/
@PlannedRemoval("1.0.0")
@JvmSynthetic
@Deprecated(
"有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"),
DeprecationLevel.ERROR
)
/* final */ override infix fun eq(other: String): Boolean = this.contentToString() == other
}
/**
* 消息元数据, 即不含内容的元素.
* 包括: [MessageSource]
*
* @see ConstrainSingle 约束一个 [MessageChain] 中只存在这一种类型的元素
*/
interface MessageMetadata : SingleMessage {
override val length: Int get() = 0

View File

@ -262,3 +262,80 @@ internal class SingleMessageChainImpl constructor(
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

View File

@ -133,7 +133,7 @@ class ExternalImage private constructor(
* PNG: 1001
* WEBP: 1002
* BMP: 1005
* GIG: 2000
* GIG: 2000 // TODO gig? gif?
* APNG: 2001
* SHARPP: 1004
*/
@ -148,7 +148,7 @@ class ExternalImage private constructor(
}
/**
* 将图片发送给指定联系人
* 将图片作为单独的消息发送给指定联系人
*/
@JvmSynthetic
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
}
/**
* 将图片发送给 [this]
* 将图片作为单独的消息发送给 [this]
*/
@JvmSynthetic
suspend inline fun <C : Contact> C.sendImage(image: ExternalImage): MessageReceipt<C> = image.sendTo(this)

View File

@ -0,0 +1,62 @@
/*
* 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
}