MiraiCode of MusicShare (#1044)

* Improve MiraiCode parsing

* MiraiCode of MusicShare
This commit is contained in:
Karlatemp 2021-02-23 23:00:32 +08:00 committed by GitHub
parent 69cceaf695
commit 9bb3ae20ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 114 additions and 13 deletions

View File

@ -4706,12 +4706,13 @@ public final class net/mamoe/mirai/message/data/MusicKind : java/lang/Enum {
public static fun values ()[Lnet/mamoe/mirai/message/data/MusicKind;
}
public final class net/mamoe/mirai/message/data/MusicShare : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent {
public final class net/mamoe/mirai/message/data/MusicShare : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent {
public static final field Key Lnet/mamoe/mirai/message/data/MusicShare$Key;
public static final field SERIAL_NAME Ljava/lang/String;
public synthetic fun <init> (ILnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Lnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public fun <init> (Lnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public fun appendMiraiCodeTo (Ljava/lang/StringBuilder;)V
public final fun component1 ()Lnet/mamoe/mirai/message/data/MusicKind;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;

View File

@ -25,13 +25,7 @@ internal fun String.parseMiraiCodeImpl(contact: Contact?): MessageChain = buildM
add(PlainText(origin.decodeMiraiCode()))
return@forEachMiraiCode
}
parser.argsRegex.matchEntire(args)
?.destructured
?.let {
parser.runCatching {
contact.mapper(it)
}.getOrNull()
}
parser.parse(contact, args)
?.let(::add)
?: add(PlainText(origin.decodeMiraiCode()))
}
@ -128,12 +122,76 @@ private object MiraiCodeParsers : Map<String, MiraiCodeParser> by mapOf(
"dice" to MiraiCodeParser(Regex("""([1-6])""")) { (value) ->
Dice(value.toInt())
},
"musicshare" to MiraiCodeParser.DynamicParser(7) { args ->
val (kind, title, summary, jumpUrl, pictureUrl) = args
val musicUrl = args[5]
val brief = args[6]
MusicShare(MusicKind.valueOf(kind), title, summary, jumpUrl, pictureUrl, musicUrl, brief)
},
)
private class MiraiCodeParser(
val argsRegex: Regex,
val mapper: Contact?.(MatchResult.Destructured) -> Message?
)
// Visitable for test
internal sealed class MiraiCodeParser {
abstract fun parse(contact: Contact?, args: String): Message?
class RegexParser(
private val argsRegex: Regex,
private val mapper: Contact?.(MatchResult.Destructured) -> Message?
) : MiraiCodeParser() {
override fun parse(contact: Contact?, args: String): Message? =
argsRegex.matchEntire(args)
?.destructured
?.let {
runCatching {
contact.mapper(it)
}.getOrNull()
}
}
class DynamicParser(
private val minArgs: Int,
private val maxArgs: Int = minArgs,
private val parser: (Contact?.(args: Array<String>) -> Message?),
) : MiraiCodeParser() {
override fun parse(contact: Contact?, args: String): Message? {
val ranges = mutableListOf<IntRange>()
if (args.isNotEmpty()) {
var begin = 0
var pos = 0
val len = args.length
while (pos < len) {
when (args[pos]) {
'\\' -> pos += 2
',' -> {
ranges.add(begin..pos)
pos++
begin = pos
}
else -> pos++
}
}
ranges.add(begin..len)
}
if (ranges.size < minArgs) return null
if (ranges.size > maxArgs) return null
@Suppress("RemoveExplicitTypeArguments")
val args0 = Array<String>(ranges.size) { index ->
val range = ranges[index]
args.substring(range.first, range.last).decodeMiraiCode()
}
runCatching {
return parser(contact, args0)
}
return null
}
}
}
private fun MiraiCodeParser(
argsRegex: Regex,
mapper: Contact?.(MatchResult.Destructured) -> Message?
): MiraiCodeParser = MiraiCodeParser.RegexParser(argsRegex, mapper)
internal fun StringBuilder.appendStringAsMiraiCode(value: String): StringBuilder = apply {
value.forEach { char ->

View File

@ -13,6 +13,8 @@ package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.message.code.CodableMessage
import net.mamoe.mirai.message.code.internal.appendStringAsMiraiCode
import net.mamoe.mirai.utils.MiraiExperimentalApi
import net.mamoe.mirai.utils.MiraiInternalApi
import net.mamoe.mirai.utils.safeCast
@ -53,7 +55,7 @@ public data class MusicShare(
* 在消息列表显示
*/
public val brief: String,
) : MessageContent, ConstrainSingle {
) : MessageContent, ConstrainSingle, CodableMessage {
public constructor(
/**
@ -88,6 +90,19 @@ public data class MusicShare(
override fun contentToString(): String =
brief.takeIf { it.isNotBlank() } ?: "[分享]$title" // empty content is not accepted by `sendMessage`
override fun appendMiraiCodeTo(builder: StringBuilder) {
builder.append("[mirai:musicshare:")
.append(kind.name)
.append(',').appendStringAsMiraiCode(title)
.append(',').appendStringAsMiraiCode(summary)
.append(',').appendStringAsMiraiCode(jumpUrl)
.append(',').appendStringAsMiraiCode(pictureUrl)
.append(',').appendStringAsMiraiCode(musicUrl)
.append(',').appendStringAsMiraiCode(brief)
.append(']')
}
// MusicShare(type=NeteaseCloudMusic, title='ファッション', summary='rinahamu/Yunomi', brief='', url='http://music.163.com/song/1338728297/?userid=324076307', pictureUrl='http://p2.music.126.net/y19E5SadGUmSR8SZxkrNtw==/109951163785855539.jpg', musicUrl='http://music.163.com/song/media/outer/url?id=1338728297&userid=324076307')
/**

View File

@ -10,11 +10,27 @@
package net.mamoe.mirai.message.code
import net.mamoe.mirai.message.code.MiraiCode.deserializeMiraiCode
import net.mamoe.mirai.message.code.internal.MiraiCodeParser
import net.mamoe.mirai.message.data.*
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class TestMiraiCode {
@Test
fun testDynamicMiraiCodeParser() {
fun runTest(args: Int, code: String, parse: (args: Array<String>) -> Unit) {
val response = MiraiCodeParser.DynamicParser(args) { args0 -> parse(args0); AtAll }.parse(null, code)
assertNotNull(response, "Parser not invoked")
}
runTest(3, "test,\\,test,\\,\\,test") { (arg1, arg2, arg3) ->
assertEquals("test", arg1)
assertEquals(",test", arg2)
assertEquals(",,test", arg3)
}
runTest(2, ",") {}
}
@Test
fun testCodes() {
assertEquals(AtAll.toMessageChain(), "[mirai:atall]".deserializeMiraiCode())
@ -46,5 +62,16 @@ class TestMiraiCode {
assertEquals(buildMessageChain {
+Dice(1)
}, "[mirai:dice:1]".deserializeMiraiCode())
val musicShare = MusicShare(
kind = MusicKind.NeteaseCloudMusic,
title = "ファッション",
summary = "rinahamu/Yunomi",
jumpUrl = "http://music.163.com/song/1338728297/?userid=324076307",
pictureUrl = "http://p2.music.126.net/y19E5SadGUmSR8SZxkrNtw==/109951163785855539.jpg",
musicUrl = "http://music.163.com/song/media/outer/url?id=1338728297&userid=324076307",
brief = "",
)
assertEquals(musicShare.toMessageChain(), musicShare.serializeToMiraiCode().deserializeMiraiCode())
}
}