mirror of
https://github.com/jie65535/mirai-console-jcr-plugin.git
synced 2025-06-02 17:39:15 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
6a21e074c7 | |||
d0d4fdca32 | |||
f734a96a56 | |||
c340f971d1 | |||
89d6038c9f | |||
c25cebc838 |
BIN
.github/WhereIsTheGreenButton.png
vendored
BIN
.github/WhereIsTheGreenButton.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 95 KiB |
45
.github/workflows/Gradle CI.yml
vendored
45
.github/workflows/Gradle CI.yml
vendored
@ -1,45 +0,0 @@
|
|||||||
name: Gradle CI
|
|
||||||
|
|
||||||
# Controls when the action will run.
|
|
||||||
on:
|
|
||||||
# Triggers the workflow on push and pull request events but only for the master branch
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
# Allows to run this workflow manually from the Actions tab
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
|
|
||||||
name: Gradle-Build
|
|
||||||
|
|
||||||
# The type of runner that the job will run on
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
||||||
steps:
|
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# Setup JDK
|
|
||||||
- name: Setup Java JDK
|
|
||||||
uses: actions/setup-java@v1.4.3
|
|
||||||
with:
|
|
||||||
java-version: 11
|
|
||||||
|
|
||||||
# Validate Gradle Wrapper
|
|
||||||
- name: Gradle Wrapper Validation
|
|
||||||
uses: gradle/wrapper-validation-action@v1.0.3
|
|
||||||
|
|
||||||
# Build
|
|
||||||
- name: Make gradlew executable
|
|
||||||
run: chmod +x ./gradlew
|
|
||||||
- name: Build with Gradle
|
|
||||||
run: ./gradlew build
|
|
14
README.md
14
README.md
@ -1,18 +1,22 @@
|
|||||||
# mirai-console-jcr-plugin
|
# mirai-console-jcr-plugin
|
||||||
## J Cpp Reference Plugin
|
## J Cpp Reference Plugin
|
||||||
|
|
||||||
在QQ群内搜索 [CppReference](https://en.cppreference.com/w/) 的文档
|
在QQ群内搜索 `C/C++` 文档的 [mirai](https://github.com/mamoe/mirai) 插件
|
||||||
|
|
||||||
# 用法
|
# 用法
|
||||||
```shell
|
```shell
|
||||||
# 消息检测
|
# 消息检测
|
||||||
c <keyword> # 搜索C文档
|
c <keyword> # 查询C标准库
|
||||||
cpp <keyword> # 搜索CPP文档
|
cpp <keyword> # 查询C++标准库
|
||||||
|
qt <keyword> # 查询Qt类库
|
||||||
|
gcc <keyword> # 查询GCC命令行选项
|
||||||
|
|
||||||
# 控制台命令
|
# 控制台命令
|
||||||
/jcr update # 更新CPP索引
|
/jcr help # 获取帮助
|
||||||
```
|
```
|
||||||
|
|
||||||
# 索引来源
|
# 索引来源
|
||||||
- c 索引来自官网离线文档,手动解析生成
|
- c 索引来自官网离线文档,手动解析生成
|
||||||
- cpp 索引来自 https://github.com/Guyutongxue/cppreference-index 感谢 @Guyutongxue
|
- cpp 索引来自 https://github.com/Guyutongxue/cppreference-index 感谢 @Guyutongxue
|
||||||
|
- qt 索引来自 Qt 官网,版本 `Qt 5.13.0`
|
||||||
|
- gcc 命令行选项索引来自[GNU](https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Option-Index.html)官网,版本 `GCC 12.2.0`
|
@ -1,13 +1,13 @@
|
|||||||
plugins {
|
plugins {
|
||||||
val kotlinVersion = "1.6.10"
|
val kotlinVersion = "1.7.10"
|
||||||
kotlin("jvm") version kotlinVersion
|
kotlin("jvm") version kotlinVersion
|
||||||
kotlin("plugin.serialization") version kotlinVersion
|
kotlin("plugin.serialization") version kotlinVersion
|
||||||
|
|
||||||
id("net.mamoe.mirai-console") version "2.12.2"
|
id("net.mamoe.mirai-console") version "2.14.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "top.jie65535"
|
group = "top.jie65535"
|
||||||
version = "0.2.0"
|
version = "0.4.0"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven("https://maven.aliyun.com/repository/public") // 阿里云国内代理仓库
|
maven("https://maven.aliyun.com/repository/public") // 阿里云国内代理仓库
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
@ -1,10 +1,7 @@
|
|||||||
package top.jie65535.jcr
|
package top.jie65535.jcr
|
||||||
|
|
||||||
import kotlinx.coroutines.runInterruptible
|
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
|
|
||||||
@OptIn(kotlinx.serialization.ExperimentalSerializationApi::class)
|
@OptIn(kotlinx.serialization.ExperimentalSerializationApi::class)
|
||||||
object Data {
|
object Data {
|
||||||
@ -20,6 +17,7 @@ object Data {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化数据
|
* 初始化数据
|
||||||
|
* 更新索引:https://cdn.jsdelivr.net/npm/@gytx/cppreference-index/dist/generated.json
|
||||||
*/
|
*/
|
||||||
fun initData() {
|
fun initData() {
|
||||||
val linkMap = JCppReferencePlugin.resolveDataFile("linkmap.json")
|
val linkMap = JCppReferencePlugin.resolveDataFile("linkmap.json")
|
||||||
@ -36,31 +34,6 @@ object Data {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val httpClient by lazy { OkHttpClient() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新数据
|
|
||||||
*/
|
|
||||||
suspend fun updateData() {
|
|
||||||
val call = httpClient.newCall(Request.Builder()
|
|
||||||
.url("https://cdn.jsdelivr.net/npm/@gytx/cppreference-index/dist/generated.json")
|
|
||||||
.build())
|
|
||||||
JCppReferencePlugin.logger.info("正在下载索引")
|
|
||||||
runInterruptible {
|
|
||||||
val response = call.execute()
|
|
||||||
if (response.isSuccessful) {
|
|
||||||
val json = response.body!!.string()
|
|
||||||
indexes = Json.decodeFromString(json)
|
|
||||||
// 保存到文件
|
|
||||||
JCppReferencePlugin.resolveDataFile("linkmap.json")
|
|
||||||
.writeText(json)
|
|
||||||
JCppReferencePlugin.logger.info("索引更新完成")
|
|
||||||
} else {
|
|
||||||
JCppReferencePlugin.logger.error("下载失败 HTTP Code: ${response.code}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取索引
|
* 获取索引
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,7 @@ import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
|
|||||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||||
import net.mamoe.mirai.event.GlobalEventChannel
|
import net.mamoe.mirai.event.GlobalEventChannel
|
||||||
|
import net.mamoe.mirai.event.events.MessageEvent
|
||||||
import net.mamoe.mirai.event.subscribeMessages
|
import net.mamoe.mirai.event.subscribeMessages
|
||||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||||
import net.mamoe.mirai.utils.info
|
import net.mamoe.mirai.utils.info
|
||||||
@ -14,26 +15,64 @@ object JCppReferencePlugin : KotlinPlugin(
|
|||||||
JvmPluginDescription(
|
JvmPluginDescription(
|
||||||
id = "top.jie65535.mirai-console-jcr-plugin",
|
id = "top.jie65535.mirai-console-jcr-plugin",
|
||||||
name = "J Cpp Reference Plugin",
|
name = "J Cpp Reference Plugin",
|
||||||
version = "0.2.0"
|
version = "0.4.0"
|
||||||
) {
|
) {
|
||||||
author("jie65535")
|
author("jie65535")
|
||||||
info("cppreference.com 帮助插件")
|
info("C/C++ 参考搜索插件")
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
private const val cppreferencePrefix = "https://zh.cppreference.com/w/"
|
private const val cppreferencePrefix = "https://zh.cppreference.com/w/"
|
||||||
|
private const val qtDocPrefix = "https://doc.qt.io/qt-5/"
|
||||||
|
private const val gccDocPrefix = "https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/"
|
||||||
|
|
||||||
private fun loadMap(path: String): Map<String, String> {
|
/**
|
||||||
|
* C索引
|
||||||
|
*/
|
||||||
|
private val indexC by lazy { loadMap("/devhelp-index-c.txt") }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Qt文档索引,按::分割
|
||||||
|
*/
|
||||||
|
private val indexQt by lazy {
|
||||||
|
val map = loadMap("/qt-5.13.0.txt")
|
||||||
|
splitKeys(map as MutableMap<String, String>)
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GCC编译选项索引,不能忽略大小写
|
||||||
|
*/
|
||||||
|
private val indexGcc by lazy { loadMap("/gcc-12-opt-index.txt", false) }
|
||||||
|
|
||||||
|
private fun loadMap(path: String, ignoreCase: Boolean = true): Map<String, String> {
|
||||||
val map = mutableMapOf<String, String>()
|
val map = mutableMapOf<String, String>()
|
||||||
val stream = this::class.java.getResourceAsStream(path)
|
val stream = this::class.java.getResourceAsStream(path)
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
logger.error("资源文件为空")
|
logger.error("无法找到指定资源:$path")
|
||||||
} else {
|
} else {
|
||||||
val br = BufferedReader(InputStreamReader(stream))
|
stream.use {
|
||||||
while (br.ready()) {
|
val br = BufferedReader(InputStreamReader(stream))
|
||||||
val line = br.readLine()
|
while (br.ready()) {
|
||||||
val s = line.indexOf('\t')
|
val line = br.readLine()
|
||||||
if (s > 0) {
|
val s = line.indexOf('\t')
|
||||||
map[line.substring(0, s)] = line.substring(s+1)
|
if (s > 0) {
|
||||||
|
val key = line.substring(0, s)
|
||||||
|
val url = line.substring(s+1)
|
||||||
|
map.putIfAbsent(key, url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果忽略大小写,则将key的小写形式也加入索引
|
||||||
|
if (ignoreCase) {
|
||||||
|
// 使用临时的map来存储小写版本
|
||||||
|
val lowerMap = mutableMapOf<String, String>()
|
||||||
|
for ((key, value) in map.entries) {
|
||||||
|
lowerMap.putIfAbsent(key.lowercase(), value)
|
||||||
|
}
|
||||||
|
// 插入回返回索引
|
||||||
|
for ((key, value) in lowerMap.entries) {
|
||||||
|
map.putIfAbsent(key.lowercase(), value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,8 +80,27 @@ object JCppReferencePlugin : KotlinPlugin(
|
|||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
private val indexC by lazy { loadMap("/devhelp-index-c.txt") }
|
/**
|
||||||
// private val indexCpp by lazy { loadMap("/devhelp-index-cpp.txt") }
|
* 按照 `::` 分割关键字
|
||||||
|
*/
|
||||||
|
private fun splitKeys(map: MutableMap<String, String>) {
|
||||||
|
val subMap = mutableMapOf<String, String>()
|
||||||
|
for (item in map) {
|
||||||
|
val s = item.key.indexOf("::")
|
||||||
|
if (s >= 0) {
|
||||||
|
val sub = item.key.substring(s+2)
|
||||||
|
if (!map.containsKey(sub)) {
|
||||||
|
subMap[sub] = item.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subMap.isNotEmpty()) {
|
||||||
|
splitKeys(subMap)
|
||||||
|
for (item in subMap) {
|
||||||
|
map.putIfAbsent(item.key, item.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
logger.info { "Plugin loaded" }
|
logger.info { "Plugin loaded" }
|
||||||
@ -51,23 +109,91 @@ object JCppReferencePlugin : KotlinPlugin(
|
|||||||
|
|
||||||
val eventChannel = GlobalEventChannel.parentScope(this)
|
val eventChannel = GlobalEventChannel.parentScope(this)
|
||||||
eventChannel.subscribeMessages {
|
eventChannel.subscribeMessages {
|
||||||
startsWith("c ") { keyword ->
|
startsWith("c ") { checkC(it) }
|
||||||
if (keyword.isEmpty()) return@startsWith
|
startsWith("C ") { checkC(it) }
|
||||||
logger.info("check c \"$keyword\"")
|
startsWith("cpp ") { checkCpp(it) }
|
||||||
val link = indexC[keyword]
|
startsWith("CPP ") { checkCpp(it) }
|
||||||
if (link != null) {
|
startsWith("c++ ") { checkCpp(it) }
|
||||||
subject.sendMessage(message.quote() + cppreferencePrefix + link)
|
startsWith("C++ ") { checkCpp(it) }
|
||||||
}
|
startsWith("qt ") { checkQt(it) }
|
||||||
}
|
startsWith("Qt ") { checkQt(it) }
|
||||||
|
startsWith("QT ") { checkQt(it) }
|
||||||
|
startsWith("gcc ") { checkGcc(it) }
|
||||||
|
startsWith("GCC ") { checkGcc(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
startsWith("cpp ") { keyword ->
|
/**
|
||||||
if (keyword.isEmpty()) return@startsWith
|
* 查找C文档
|
||||||
logger.info("check cpp \"$keyword\"")
|
*/
|
||||||
val index = Data.getIndex(keyword)
|
private suspend fun MessageEvent.checkC(keyword: String) {
|
||||||
if (index != null) {
|
if (keyword.isEmpty()) return
|
||||||
subject.sendMessage(message.quote() + cppreferencePrefix + index.link)
|
logger.info("check c \"$keyword\"")
|
||||||
}
|
findAndReply(indexC, keyword, cppreferencePrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找C++文档
|
||||||
|
*/
|
||||||
|
private suspend fun MessageEvent.checkCpp(keyword: String) {
|
||||||
|
if (keyword.isEmpty()) return
|
||||||
|
logger.info("check cpp \"$keyword\"")
|
||||||
|
val index = Data.getIndex(keyword)
|
||||||
|
if (index != null) {
|
||||||
|
subject.sendMessage(message.quote() + cppreferencePrefix + index.link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找Qt文档
|
||||||
|
*/
|
||||||
|
private suspend fun MessageEvent.checkQt(keyword: String) {
|
||||||
|
if (keyword.isEmpty()) return
|
||||||
|
logger.info("check qt \"$keyword\"")
|
||||||
|
findAndReply(indexQt, keyword, qtDocPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找GCC命令行文档
|
||||||
|
*/
|
||||||
|
private suspend fun MessageEvent.checkGcc(keyword: String) {
|
||||||
|
if (keyword.isEmpty()) return
|
||||||
|
logger.info("check gcc \"$keyword\"")
|
||||||
|
if (!findAndReply(indexGcc, keyword, gccDocPrefix, false) && keyword.startsWith('-')) {
|
||||||
|
findAndReply(indexGcc, keyword.substring(1), gccDocPrefix, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从索引中查找并拼接前缀回复发送者
|
||||||
|
* @param index 索引
|
||||||
|
* @param keyword 关键字
|
||||||
|
* @param prefix 前缀
|
||||||
|
* @param ignoreCase 忽略大小写 默认true
|
||||||
|
* @return 返回是否回复
|
||||||
|
*/
|
||||||
|
private suspend fun MessageEvent.findAndReply(index: Map<String, String>, keyword: String, prefix: String, ignoreCase: Boolean = true): Boolean {
|
||||||
|
if (keyword.isEmpty()) return false
|
||||||
|
val url = find(index, keyword, prefix) ?: if (ignoreCase) find(index, keyword.lowercase(), prefix) else null
|
||||||
|
return if (url != null) {
|
||||||
|
subject.sendMessage(message.quote() + url)
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从索引中查找并拼接前缀返回
|
||||||
|
* @param index 索引
|
||||||
|
* @param keyword 关键字
|
||||||
|
* @param prefix 前缀
|
||||||
|
* @return 返回查找结果,未找到返回null
|
||||||
|
*/
|
||||||
|
private fun find(index: Map<String, String>, keyword: String, prefix: String): String? {
|
||||||
|
val value = index[keyword]
|
||||||
|
return if (value != null) {
|
||||||
|
prefix + value
|
||||||
|
} else {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,13 @@ object PluginCommands : CompositeCommand(
|
|||||||
description = "J CppReference Commands"
|
description = "J CppReference Commands"
|
||||||
) {
|
) {
|
||||||
@SubCommand
|
@SubCommand
|
||||||
@Description("更新索引")
|
@Description("查询帮助")
|
||||||
suspend fun CommandSender.update() {
|
suspend fun CommandSender.help() {
|
||||||
Data.updateData()
|
sendMessage("""
|
||||||
sendMessage("OK")
|
c <keyword> # 查询C标准库
|
||||||
|
cpp <keyword> # 查询C++标准库
|
||||||
|
qt <keyword> # 查询Qt类库
|
||||||
|
gcc <keyword> # 查询GCC命令行选项
|
||||||
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
}
|
}
|
3413
src/main/resources/gcc-12-opt-index.txt
Normal file
3413
src/main/resources/gcc-12-opt-index.txt
Normal file
File diff suppressed because it is too large
Load Diff
220053
src/main/resources/qt-5.13.0.txt
Normal file
220053
src/main/resources/qt-5.13.0.txt
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user