Skip to content

Commit d6df064

Browse files
committed
feat: ProjectSekaiStickers
1 parent 61fbfd9 commit d6df064

File tree

3 files changed

+103
-22
lines changed

3 files changed

+103
-22
lines changed

src/main/kotlin/ProjectSekaiStickers.kt

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,25 @@ import org.jetbrains.skia.*
77
import org.jetbrains.skia.paragraph.*
88
import xyz.cssxsh.skia.FontUtils
99
import java.io.Closeable
10+
import java.io.File
1011
import java.io.FileNotFoundException
1112
import java.io.InputStream
1213
import java.util.zip.ZipFile
1314
import kotlin.math.*
1415

1516
/**
1617
* [The Original Ayaka](https://github.com/TheOriginalAyaka/sekai-stickers)
17-
*/
18+
*/
1819
public class ProjectSekaiStickers private constructor(private val source: ZipFile) : Closeable {
1920
public constructor(path: String) : this(source = ZipFile(path))
21+
public constructor(file: File) : this(source = ZipFile(file))
2022

2123
private fun ZipFile.getInputStream(name: String): InputStream {
2224
val entry = getEntry(name) ?: throw FileNotFoundException(name)
2325
return getInputStream(entry)
2426
}
2527

26-
private val characters: List<Character> by lazy {
28+
internal val characters: List<Character> by lazy {
2729
source.getInputStream("sekai-stickers-main/src/characters.json").use { input ->
2830
@OptIn(ExperimentalSerializationApi::class)
2931
Json.decodeFromStream<List<Character>>(input)
@@ -48,7 +50,7 @@ public class ProjectSekaiStickers private constructor(private val source: ZipFil
4850
return Image.makeFromEncoded(bytes)
4951
}
5052

51-
public fun create(name: String, block: Content.() -> Unit): Image {
53+
public fun create(name: String, block: Content.() -> Unit): Surface {
5254
val character = characters.find { it.name == name } ?: throw NoSuchElementException(name)
5355

5456
val surface = Surface.makeRasterN32Premul(296, 256)
@@ -85,36 +87,40 @@ public class ProjectSekaiStickers private constructor(private val source: ZipFil
8587
}
8688

8789
ParagraphBuilder(style, fonts)
88-
.pushStyle(TextStyle()
89-
.setFontSize(content.size)
90-
.setForeground(Paint().apply {
91-
strokeCap = PaintStrokeCap.ROUND
92-
strokeJoin = PaintStrokeJoin.ROUND
93-
strokeWidth = 10F
94-
color = Color.WHITE
95-
mode = PaintMode.STROKE
96-
})
97-
.setFontFamilies(arrayOf("FOT-Yuruka Std UB", "YurukaStd", "SSFangTangTi")))
90+
.pushStyle(
91+
TextStyle()
92+
.setFontSize(content.size)
93+
.setForeground(Paint().apply {
94+
strokeCap = PaintStrokeCap.ROUND
95+
strokeJoin = PaintStrokeJoin.ROUND
96+
strokeWidth = 10F
97+
color = Color.WHITE
98+
mode = PaintMode.STROKE
99+
})
100+
.setFontFamilies(arrayOf("FOT-Yuruka Std UB", "YurukaStd", "SSFangTangTi"))
101+
)
98102
.addText(content.text)
99103
.build()
100104
.layout(surface.width.toFloat())
101105
.paint(canvas, -6F, 6F)
102106

103107

104108
ParagraphBuilder(style, fonts)
105-
.pushStyle(TextStyle()
106-
.setFontSize(content.size)
107-
.setForeground(Paint().apply {
108-
color = character.color.replace("#", "FF").toLong(16).toInt()
109-
mode = PaintMode.FILL
110-
})
111-
.setFontFamilies(arrayOf("FOT-Yuruka Std UB", "YurukaStd", "SSFangTangTi")))
109+
.pushStyle(
110+
TextStyle()
111+
.setFontSize(content.size)
112+
.setForeground(Paint().apply {
113+
color = character.color.replace("#", "FF").toLong(16).toInt()
114+
mode = PaintMode.FILL
115+
})
116+
.setFontFamilies(arrayOf("FOT-Yuruka Std UB", "YurukaStd", "SSFangTangTi"))
117+
)
112118
.addText(content.text)
113119
.build()
114120
.layout(surface.width.toFloat())
115121
.paint(canvas, -6F, 6F)
116122

117-
return surface.makeImageSnapshot()
123+
return surface
118124
}
119125

120126
override fun close(): Unit = source.close()
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package xyz.cssxsh.mirai.meme.impl
2+
3+
import kotlinx.coroutines.*
4+
import net.mamoe.mirai.console.permission.*
5+
import net.mamoe.mirai.event.events.*
6+
import net.mamoe.mirai.message.data.*
7+
import xyz.cssxsh.mirai.meme.*
8+
import xyz.cssxsh.mirai.meme.service.*
9+
import xyz.cssxsh.mirai.skia.*
10+
import java.io.*
11+
import java.util.*
12+
import kotlin.collections.*
13+
14+
/**
15+
* [Project Sekai Stickers](https://st.ayaka.one/)
16+
*/
17+
public class MemeProjectSekaiStickers : MemeService {
18+
override val name: String = "Project Sekai Stickers"
19+
override val id: String = "sekai-stickers"
20+
override val description: String = "Stickers 合成"
21+
override val loaded: Boolean = true
22+
override lateinit var regex: Regex
23+
override val properties: Properties = Properties()
24+
override lateinit var permission: Permission
25+
private lateinit var tool: ProjectSekaiStickers
26+
private var folder: File = File(System.getProperty("user.dir", ".")).resolve(".sekai-stickers")
27+
private lateinit var loadJob: Job
28+
29+
override fun load(folder: File) {
30+
this.folder = folder
31+
loadJob = MemeService.launch(CoroutineName(name)) {
32+
folder.mkdirs()
33+
34+
val archive = folder.resolve("sekai-stickers-main.zip")
35+
if (archive.exists().not()) {
36+
try {
37+
download(
38+
urlString = "https://mirror.ghproxy.com/https://github.com/TheOriginalAyaka/sekai-stickers/archive/main.zip",
39+
folder = folder
40+
)
41+
} catch (_: Exception) {
42+
archive.delete()
43+
download(
44+
urlString = "https://github.com/TheOriginalAyaka/sekai-stickers/archive/main.zip",
45+
folder = folder
46+
)
47+
}.renameTo(archive)
48+
}
49+
tool = ProjectSekaiStickers(file = archive)
50+
regex = tool.characters
51+
.joinToString(prefix = "#(", separator = "|", postfix = ")\\s*(.*)") { it.name }
52+
.toRegex()
53+
}
54+
}
55+
56+
override fun enable(permission: Permission) {
57+
this.permission = permission
58+
runBlocking {
59+
loadJob.join()
60+
}
61+
}
62+
63+
override fun disable() {}
64+
65+
override suspend fun MessageEvent.replier(match: MatchResult): Message {
66+
val (name, text) = match.destructured
67+
val image = tool.create(name = name) {
68+
if (text.isNotBlank()) this.text = text
69+
}
70+
71+
return image.makeSnapshotResource()
72+
.use { resource -> subject.uploadImage(resource = resource) }
73+
}
74+
}

src/main/resources/META-INF/services/xyz.cssxsh.mirai.meme.service.MemeService

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ xyz.cssxsh.mirai.meme.impl.MemeTank
99
xyz.cssxsh.mirai.meme.impl.MemeWeiboEmoticon
1010
xyz.cssxsh.mirai.meme.impl.MemeYgo
1111
xyz.cssxsh.mirai.meme.impl.MemeZZKIA
12-
xyz.cssxsh.mirai.meme.impl.MemeSchool
12+
xyz.cssxsh.mirai.meme.impl.MemeSchool
13+
xyz.cssxsh.mirai.meme.impl.MemeProjectSekaiStickers

0 commit comments

Comments
 (0)