Commit 094bd72e authored by Him188's avatar Him188

Add `String.parserMiraiCode`, close #264

parent 0af6639e
......@@ -68,8 +68,9 @@ subprojects {
afterEvaluate {
apply(plugin = "com.github.johnrengelman.shadow")
val kotlin =
(this as ExtensionAware).extensions.getByName("kotlin") as? org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
?: return@afterEvaluate
runCatching {
(this as ExtensionAware).extensions.getByName("kotlin") as? org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
}.getOrNull() ?: return@afterEvaluate
val shadowJvmJar by tasks.creating(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) {
group = "mirai"
......
@file:Suppress("UNUSED_VARIABLE")
plugins {
kotlin("multiplatform")
id("kotlinx-atomicfu")
kotlin("plugin.serialization")
id("signing")
`maven-publish`
id("com.jfrog.bintray") version Versions.Publishing.bintray
}
description = "Mirai serialization module"
val isAndroidSDKAvailable: Boolean by project
kotlin {
if (isAndroidSDKAvailable) {
apply(from = rootProject.file("gradle/android.gradle"))
android("android") {
publishAllLibraryVariants()
}
} else {
println(
"""Android SDK 可能未安装.
$name 的 Android 目标编译将不会进行.
这不会影响 Android 以外的平台的编译.
""".trimIndent()
)
println(
"""Android SDK might not be installed.
Android target of $name will not be compiled.
It does no influence on the compilation of other platforms.
""".trimIndent()
)
}
jvm()
sourceSets {
all {
languageSettings.enableLanguageFeature("InlineClasses")
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI")
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.LowLevelAPI")
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
languageSettings.useExperimentalAnnotation("kotlin.time.ExperimentalTime")
languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
languageSettings.progressiveMode = true
}
val commonMain by getting {
dependencies {
api(kotlin("stdlib"))
api(project(":mirai-core"))
}
}
val commonTest by getting {
dependencies {
api(kotlin("test-annotations-common"))
api(kotlin("test-common"))
}
}
if (isAndroidSDKAvailable) {
val androidMain by getting {
dependencies {
}
}
val androidTest by getting {
dependencies {
}
}
}
val jvmMain by getting {
dependencies {
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
}
}
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(kotlin("test-junit"))
runtimeOnly(files("build/classes/kotlin/jvm/test")) // classpath is not properly set by IDE
}
}
}
}
apply(from = rootProject.file("gradle/publish.gradle"))
/*
* 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("MiraiCode")
@file:Suppress("INVISIBLE_MEMBER")
package net.mamoe.mirai.message.code
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.SinceMirai
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
@Suppress("RegExpRedundantEscape") // required on android
internal val codeRegex = Regex("""(?:\[mirai:([^\]]*)?:(.*?)?\])|(?:\[mirai:(.*?)\])""")
/**
* 解析形如 "[mirai:]" 的 mirai 码, 即 [Message.toString] 返回的内容.
*/
@SinceMirai("1.1.0")
fun String.parseMiraiCode(): MessageChain = buildMessageChain {
forEachMiraiCode { origin, name, args ->
if (name == null) {
add(origin.toMessage())
return@forEachMiraiCode
}
val parser = MiraiCodeParsers[name] ?: kotlin.run {
add(origin.toMessage())
return@forEachMiraiCode
}
parser.argsRegex.matchEntire(args)
?.destructured
?.let {
parser.runCatching {
mapper(it)
}.getOrNull()
}
?.let(::add)
?: add(origin.toMessage())
}
}
private inline fun String.forEachMiraiCode(crossinline block: (origin: String, name: String?, args: String) -> Unit) {
var lastIndex = 0
for (result in codeRegex.findAll(this)) {
if (result.range.first != lastIndex) {
// skipped string
block(result.value, null, substring(lastIndex, result.range.first))
}
lastIndex = result.range.last + 1
if (result.groups[3] != null) {
// no param
block(result.value, result.groups[3]!!.value, "")
} else block(result.value, result.groups[1]!!.value, result.groups[2]?.value ?: "")
}
}
internal object MiraiCodeParsers : Map<String, MiraiCodeParser> by mapOf(
"at" to MiraiCodeParser(Regex("""(\d*),(.*)""")) { (target, display) ->
At._lowLevelConstructAtInstance(target.toLong(), display)
},
"atall" to MiraiCodeParser(Regex("")) {
AtAll
},
"poke" to MiraiCodeParser(Regex("(.*)?,(\\d*),(-?\\d*)")) { (name, type, id) ->
PokeMessage(name, type.toInt(), id.toInt())
},
"vipface" to MiraiCodeParser(Regex("""\((\d*),(.*)\),(\d*)""")) { (id, name, count) ->
VipFace(VipFace.Kind(id.toInt(), name), count.toInt())
},
"face" to MiraiCodeParser(Regex("""(\d*)""")) { (id) ->
Face(id.toInt())
},
"image" to MiraiCodeParser(Regex("""(.*)""")) { (id) ->
Image(id)
},
"flash" to MiraiCodeParser(Regex("""(.*)""")) { (id) ->
Image(id).flash()
}
)
/*
internal object MiraiCodeParsers2 : Map<String, (args: String) -> Message?> by mapOf(
"at" to l@{ args ->
val group = args.split(',')
if (group.size != 2) return@l null
val target = group[0].toLongOrNull() ?: return@l null
@Suppress("INVISIBLE_MEMBER")
At(target, group[1])
},
"atall" to l@{
AtAll
},
"poke" to l@{ args ->
val group = args.split(',')
if (group.size != 2) return@l null
val type = group[1].toIntOrNull() ?: return@l null
val id = group[2].toIntOrNull() ?: return@l null
@Suppress("INVISIBLE_MEMBER")
PokeMessage(group[0], type, id)
},
"vipface" to l@{ args ->
val group = args.split(',')
if (group.size != 2) return@l null
val type = group[1].toIntOrNull() ?: return@l null
val id = group[2].toIntOrNull() ?: return@l null
@Suppress("INVISIBLE_MEMBER")
PokeMessage(group[0], type, id)
}
)*/
internal class MiraiCodeParser(
val argsRegex: Regex,
val mapper: MiraiCodeParser.(MatchResult.Destructured) -> Message?
)
\ No newline at end of file
/*
* 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:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.message.code
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.VipFace.Companion.AiXin
import kotlin.test.Test
import kotlin.test.assertEquals
internal class MiraiCodeParserTest {
@Test
fun testSplit() {
val str = "sadvass][ [mirai:at:1,test]]vdsavs [mirai:atall]"
assertEquals(str, str.parseMiraiCode().toString())
}
@Test
fun at() {
val str = "[mirai:at:1,test]"
assertEquals(At._lowLevelConstructAtInstance(1, "test"), str.parseMiraiCode()[0])
fun testPlain(str: String) {
assertEquals(str, (str.parseMiraiCode()[0] as PlainText).content)
}
testPlain("[mirai:at:bad,test]")
testPlain("[mirai:at:bad]")
testPlain("[mirai:at:]")
testPlain("[mirai:at]")
}
@Test
fun atAll() {
val str = "[mirai:atall]"
assertEquals(AtAll, str.parseMiraiCode()[0])
}
@Test
fun poke() {
assertEquals(PokeMessage.Poke, PokeMessage.Poke.toString().parseMiraiCode()[0])
}
@Test
fun vipFace() {
val instance = VipFace(AiXin, 1)
assertEquals(instance, instance.toString().parseMiraiCode()[0])
}
@Test
fun image() {
val instance = Image("{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.mirai")
assertEquals(instance, instance.toString().parseMiraiCode()[0])
}
@Test
fun flash() {
val instance = Image("{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.mirai").flash()
assertEquals(instance, instance.toString().parseMiraiCode()[0])
}
}
\ No newline at end of file
......@@ -23,6 +23,7 @@ rootProject.name = 'mirai'
include(':mirai-core')
include(':mirai-core-qqandroid')
include(':mirai-serialization')
//include(':compatibility-validator') // THIS WILL CAUSE A DEPENDENCY RESOLUTION BUG
//include(':java-compatibility-validator')
......
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