mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-05 07:30:09 +08:00
Improve KDoc
This commit is contained in:
parent
c8cd03ac02
commit
1542b73fa9
@ -74,6 +74,8 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), Identified {
|
||||
/**
|
||||
* 上传一个图片以备发送.
|
||||
*
|
||||
* @see Image 查看更多信息
|
||||
*
|
||||
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
|
||||
* @see ImageUploadEvent 图片发送完成事件
|
||||
*
|
||||
|
@ -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() {
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user