diff --git a/konabot/common/nb/is_admin.py b/konabot/common/nb/is_admin.py new file mode 100644 index 0000000..ac2d3e8 --- /dev/null +++ b/konabot/common/nb/is_admin.py @@ -0,0 +1,34 @@ +from nonebot import get_plugin_config +import nonebot +import nonebot.adapters +import nonebot.adapters.console +import nonebot.adapters.discord +import nonebot.adapters.onebot +from pydantic import BaseModel + + +class IsAdminConfig(BaseModel): + admin_qq_group: list[int] = [] + admin_qq_account: list[int] = [] + admin_discord_channel: list[int] = [] + admin_discord_account: list[int] = [] + +cfg = get_plugin_config(IsAdminConfig) + + +def is_admin(event: nonebot.adapters.Event): + if isinstance(event, nonebot.adapters.onebot.v11.MessageEvent): + if event.user_id in cfg.admin_qq_account: + return True + if isinstance(event, nonebot.adapters.onebot.v11.GroupMessageEvent): + if event.group_id in cfg.admin_qq_group: + return True + if isinstance(event, nonebot.adapters.discord.event.MessageEvent): + if event.channel_id in cfg.admin_discord_channel: + return True + if event.user_id in cfg.admin_discord_account: + return True + if isinstance(event, nonebot.adapters.console.event.Event): + return True + + return False diff --git a/konabot/common/path.py b/konabot/common/path.py index 0795b0a..813a82b 100644 --- a/konabot/common/path.py +++ b/konabot/common/path.py @@ -2,3 +2,11 @@ from pathlib import Path ASSETS_PATH = Path(__file__).resolve().parent.parent.parent / "assets" FONTS_PATH = ASSETS_PATH / "fonts" + +SRC_PATH = Path(__file__).resolve().parent.parent + +DOCS_PATH = SRC_PATH / "docs" +DOCS_PATH_MAN1 = DOCS_PATH / "user" +DOCS_PATH_MAN3 = DOCS_PATH / "lib" +DOCS_PATH_MAN7 = DOCS_PATH / "concepts" +DOCS_PATH_MAN8 = DOCS_PATH / "sys" diff --git a/konabot/docs/README.md b/konabot/docs/README.md new file mode 100644 index 0000000..175261d --- /dev/null +++ b/konabot/docs/README.md @@ -0,0 +1,40 @@ +# 此方 Bot 的文档系统 + +此方 Bot 使用类 Linux 的 `man` 指令来管理文档。文档一般建议使用纯文本书写,带有相对良好的格式。 + +## 文件夹摆放规则 + +`docs` 目录下,有若干文档可以拿来阅读和输出。每个子文件夹里,文档文件使用名字不含空格的 txt 文件书写,其他后缀名的文件将会被忽略。所以,如果你希望有些文件只在代码库中可阅读,你可以使用 `.md` 格式。 + +### 1 - user + +`docs/user` 目录下的文档是直接会给用户进行检索的文档,在直接使用 `man` 指令时,会搜索该文件夹的全部文件,以知晓所有有文档的指令。 + +### 3 - lib + +`docs/lib` 目录下的文档主要给该项目的维护者进行阅读和使用,讲述的是本项目内置的一些函数的功能讲解(一般以便利为主要目的)以及一些项目安排上的要求。一般不会列举,除非用户指定要求列举该范围。 + +### 7 - concepts + +`docs/concepts` 用来摆放任何的概念。任何的。一般不会列举,除非用户指定要求列举该范围。 + +### 8 - sys + +`docs/sys` 用于摆放仅 MTTU 群可以使用的文档集合。在 MTTU 群内,该目录下的文档也会被索引,否则文档将不可阅读。 + +## 书写规范 + +无特殊要求,因为当用户进行 `man` 的时候,会将文档内的内容原封不动地展示出来。但是,你仍然可以模仿 Linux 下的 `man` 指令的格式进行书写。 + +``` +指令介绍 + man - 用于展示此方 BOT 使用手册的指令 + +格式 + man [文档类型] <指令> + +示例 + `man` 查看所有有文档的指令清单 + `man 喵` 查看指令「喵」的使用说明 +…… +``` diff --git a/konabot/docs/concepts/占位.md b/konabot/docs/concepts/占位.md new file mode 100644 index 0000000..e69de29 diff --git a/konabot/docs/lib/占位.md b/konabot/docs/lib/占位.md new file mode 100644 index 0000000..e69de29 diff --git a/konabot/docs/sys/out.txt b/konabot/docs/sys/out.txt new file mode 100644 index 0000000..ac168e6 --- /dev/null +++ b/konabot/docs/sys/out.txt @@ -0,0 +1 @@ +MAN what can I say! \ No newline at end of file diff --git a/konabot/docs/user/man.txt b/konabot/docs/user/man.txt new file mode 100644 index 0000000..99034e5 --- /dev/null +++ b/konabot/docs/user/man.txt @@ -0,0 +1,24 @@ +指令介绍 + man - 用于展示此方 BOT 使用手册的指令 + +格式 + man 文档类型 [文档类型选项] + man [文档类型] <指令> + +示例 + `man` 查看所有有文档的指令清单 + `man 3` 列举所有可读文档的库函数清单 + `man 喵` 查看指令「喵」的使用说明 + `man 8 out` 查看管理员指令「out」的使用说明 + +文档类型 + 文档类型用来区分同一指令在不同场景下的情景。你可以使用数字编号进行筛选。分为这些种类: + + - 1 用户态指令,用于日常使用的指令 + - 3 库函数指令,用于 Bot 开发用的函数查询 + - 7 概念指令,用于概念解释 + - 8 系统指令,仅管理员可用 + +文档类型选项 + -p + 指定要列举的文档清单的页 diff --git a/konabot/plugins/man/__init__.py b/konabot/plugins/man/__init__.py new file mode 100644 index 0000000..a0af217 --- /dev/null +++ b/konabot/plugins/man/__init__.py @@ -0,0 +1,104 @@ +from curses.ascii import isdigit +from pathlib import Path + +import nonebot +import nonebot.adapters +import nonebot.adapters.discord +import nonebot.rule +from nonebot import on_command +from nonebot_plugin_alconna import Alconna, Args, UniMessage, on_alconna + +from konabot.common.nb.is_admin import is_admin +from konabot.common.path import DOCS_PATH_MAN1, DOCS_PATH_MAN3, DOCS_PATH_MAN7, DOCS_PATH_MAN8 + +def search_man(section: int) -> dict[tuple[int, str], Path]: + base_path = { + 1: DOCS_PATH_MAN1, + 3: DOCS_PATH_MAN3, + 7: DOCS_PATH_MAN7, + 8: DOCS_PATH_MAN8, + }.get(section, DOCS_PATH_MAN1) + + res: dict[tuple[int, str], Path] = {} + for fp in base_path.iterdir(): + if fp.suffix != '.txt': + continue + name = fp.name.lower().removesuffix('.txt') + res[(section, name)] = fp + return res + + +man = on_alconna(Alconna( + 'man', + Args['section', int | None], + Args['doc', str | None], +)) + +@man.handle() +async def _( + section: int | None, + doc: str | None, + event: nonebot.adapters.Event, +): + if doc is not None and section is None and all(isdigit(c) for c in doc): + section = int(doc) + doc = None + + if section is not None and section not in {1, 3, 7, 8}: + await man.send( + UniMessage().text(f"你所指定的文档类型 {section} 不在可用范围内") + ) + return + + if doc is None: + # 检索模式 + if section is None: + section_set = {1} + else: + section_set = {section} + if 1 in section_set and is_admin(event): + section_set.add(8) + mans: list[str] = [] + for section in section_set: + mans += [f"{n}({s})" for s, n in search_man(section).keys()] + mans.sort() + + await man.send(UniMessage().text( + ( + "★此方 BOT 使用帮助★\n" + "使用 man <指令名> 查询某个指令的名字\n\n" + "可供查询的指令清单:" + ) + + ", ".join(mans) + + "\n\n例如,使用 man man 来查询 man 指令的使用方法" + )) + else: + # 查阅模式 + if section is None: + section_set = {1} + else: + section_set = {section} + if 1 in section_set and is_admin(event): + section_set.add(8) + if 8 in section_set and not is_admin(event): + await man.send(UniMessage().text("你没有查看该指令类型的权限")) + return + mans_dict: dict[tuple[int, str], Path] = {} + for section in section_set: + mans_dict: dict[tuple[int, str], Path] = {**mans_dict, **search_man(section)} + mans_dict_2 = {key[1]: val for key, val in mans_dict.items()} + mans_fp = mans_dict_2.get(doc.lower()) + if mans_fp is None: + await man.send(UniMessage().text("你所检索的指令不存在")) + return + mans_msg = mans_fp.read_text('utf-8', 'replace') + if isinstance(event, nonebot.adapters.discord.event.MessageEvent): + mans_msg = f'```\n{mans_msg}\n```' + await man.send(UniMessage().text(mans_msg)) + + +help_deprecated = on_command('help', rule=nonebot.rule.to_me()) + +@help_deprecated.handle() +async def _(): + await help_deprecated.send('请使用 man 指令来查询此方 bot 的帮助文档哦')