├── .github
└── workflows
│ ├── docs-build.yml
│ ├── pre-commit.yml
│ └── pypi-publish.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .pre-commit
└── check_filename.py
├── CNAME
├── LICENSE-LSO
├── LICENSE-MIT
├── LICENSE-MULAN
├── README.md
├── README_DEV.md
├── README_EN.md
├── README_TOOLS.md
├── README_TOOLS_EN.md
├── build-docs.sh
├── docs
├── .vitepress
│ ├── config
│ │ ├── common.ts
│ │ ├── en.ts
│ │ ├── index.ts
│ │ ├── ja.ts
│ │ └── zh.ts
│ └── theme
│ │ ├── LICENSE
│ │ ├── Layout.vue
│ │ ├── index.ts
│ │ └── style.css
├── components
│ └── ContributorsBar.vue
├── en
│ ├── dev
│ │ ├── api
│ │ │ ├── azure.md
│ │ │ ├── azure_onebot.md
│ │ │ ├── config.md
│ │ │ ├── constants.md
│ │ │ ├── deal_latex.md
│ │ │ ├── hunyuan.md
│ │ │ ├── index.md
│ │ │ ├── metadata.md
│ │ │ ├── models.md
│ │ │ ├── plugin
│ │ │ │ ├── index.md
│ │ │ │ ├── load.md
│ │ │ │ ├── models.md
│ │ │ │ ├── register.md
│ │ │ │ ├── typing.md
│ │ │ │ └── utils.md
│ │ │ ├── plugins
│ │ │ │ ├── marshoai_bangumi
│ │ │ │ │ └── index.md
│ │ │ │ └── marshoai_basic
│ │ │ │ │ └── index.md
│ │ │ ├── tools
│ │ │ │ ├── marshoai_bangumi
│ │ │ │ │ └── index.md
│ │ │ │ ├── marshoai_basic
│ │ │ │ │ └── index.md
│ │ │ │ ├── marshoai_megakits
│ │ │ │ │ ├── index.md
│ │ │ │ │ ├── mk_common.md
│ │ │ │ │ ├── mk_info.md
│ │ │ │ │ ├── mk_morse_code.md
│ │ │ │ │ └── mk_nya_code.md
│ │ │ │ └── marshoai_meogirl
│ │ │ │ │ ├── index.md
│ │ │ │ │ ├── mg_info.md
│ │ │ │ │ ├── mg_introduce.md
│ │ │ │ │ └── mg_search.md
│ │ │ ├── tools_wip
│ │ │ │ └── marshoai_memory
│ │ │ │ │ └── index.md
│ │ │ ├── util.md
│ │ │ └── util_hunyuan.md
│ │ └── index.md
│ ├── index.md
│ └── start
│ │ ├── index.md
│ │ └── install.md
├── ja
│ └── index.md
├── public
│ ├── favicon.ico
│ └── marsho-full.svg
└── zh
│ ├── dev
│ ├── api
│ │ ├── azure.md
│ │ ├── azure_onebot.md
│ │ ├── config.md
│ │ ├── constants.md
│ │ ├── deal_latex.md
│ │ ├── hunyuan.md
│ │ ├── index.md
│ │ ├── metadata.md
│ │ ├── models.md
│ │ ├── plugin
│ │ │ ├── index.md
│ │ │ ├── load.md
│ │ │ ├── models.md
│ │ │ ├── register.md
│ │ │ ├── typing.md
│ │ │ └── utils.md
│ │ ├── plugins
│ │ │ ├── marshoai_bangumi
│ │ │ │ └── index.md
│ │ │ └── marshoai_basic
│ │ │ │ └── index.md
│ │ ├── tools
│ │ │ ├── marshoai_bangumi
│ │ │ │ └── index.md
│ │ │ ├── marshoai_basic
│ │ │ │ └── index.md
│ │ │ ├── marshoai_megakits
│ │ │ │ ├── index.md
│ │ │ │ ├── mk_common.md
│ │ │ │ ├── mk_info.md
│ │ │ │ ├── mk_morse_code.md
│ │ │ │ └── mk_nya_code.md
│ │ │ └── marshoai_meogirl
│ │ │ │ ├── index.md
│ │ │ │ ├── mg_info.md
│ │ │ │ ├── mg_introduce.md
│ │ │ │ └── mg_search.md
│ │ ├── tools_wip
│ │ │ └── marshoai_memory
│ │ │ │ └── index.md
│ │ ├── util.md
│ │ └── util_hunyuan.md
│ ├── extension.md
│ ├── index.md
│ └── project.md
│ ├── index.md
│ └── start
│ ├── index.md
│ ├── install-old.md
│ ├── install.md
│ └── use.md
├── main.py
├── nonebot_plugin_marshoai
├── __init__.py
├── _types.py
├── cache
│ └── decos.py
├── config.py
├── constants.py
├── deal_latex.py
├── dev.py
├── handler.py
├── hooks.py
├── hunyuan.py
├── instances.py
├── marsho.py
├── marsho_onebot.py
├── metadata.py
├── models.py
├── observer.py
├── plugin
│ ├── __init__.py
│ ├── func_call
│ │ ├── __init__.py
│ │ ├── caller.py
│ │ ├── models.py
│ │ ├── params.py
│ │ └── utils.py
│ ├── load.py
│ ├── models.py
│ ├── typing.py
│ └── utils.py
├── plugins
│ ├── builtin_tools
│ │ ├── __init__.py
│ │ ├── chat.py
│ │ ├── file_io.py
│ │ ├── liteyuki.py
│ │ ├── manager.py
│ │ ├── network.py
│ │ └── utils.py
│ ├── marshoai_bangumi
│ │ ├── __init__.py
│ │ └── tools.json
│ ├── twisuki_megakits
│ │ ├── __init__.py
│ │ ├── mk_morse_code.py
│ │ └── mk_nya_code.py
│ └── twisuki_petcat
│ │ ├── __init__.py
│ │ ├── pc_cat.py
│ │ ├── pc_info.py
│ │ ├── pc_shop.py
│ │ └── pc_token.py
├── plugins_test
│ ├── marshoai_basic
│ │ ├── __init__.py
│ │ ├── tools.json
│ │ └── tools_test.json
│ ├── marshoai_memory
│ │ ├── __init__.py
│ │ ├── command.py
│ │ ├── config.py
│ │ └── tools.json
│ ├── random_number_generator.py
│ ├── snowykami_testplugin
│ │ └── __init__.py
│ └── weather_demo.py
├── tools
│ ├── marshoai_bangumi
│ │ ├── __init__.py
│ │ └── tools.json
│ ├── marshoai_basic
│ │ ├── __init__.py
│ │ ├── tools.json
│ │ └── tools_test.json
│ ├── marshoai_megakits
│ │ ├── __init__.py
│ │ ├── mk_common.py
│ │ ├── mk_info.py
│ │ ├── mk_morse_code.py
│ │ ├── mk_nya_code.py
│ │ └── tools.json
│ ├── marshoai_memory
│ │ ├── __init__.py
│ │ └── tools.json
│ └── marshoai_meogirl
│ │ ├── __init__.py
│ │ ├── mg_info.py
│ │ ├── mg_introduce.py
│ │ ├── mg_search.py
│ │ └── tools.json
├── util.py
├── util_hunyuan.py
└── utils
│ └── processor.py
├── package.json
├── pdm.lock
├── pnpm-lock.yaml
├── pyproject.toml
├── resources
├── README.md
├── bg.png
├── catface.svg
├── marsho-640x360.png
├── marsho-bg-1x1.png
├── marsho-bg.png
├── marsho-icon.png
├── marsho-icon.svg
├── marsho-new.png
├── marsho-new.svg
├── marsho-no-paw.png
├── marsho-paw.png
├── marsho.png
└── marsho.svg
└── tests
└── test_example.py
/.github/workflows/docs-build.yml:
--------------------------------------------------------------------------------
1 | name: Deploy VitePress site to Liteyuki PaaS
2 |
3 | on: ["push", "pull_request_target"]
4 |
5 | permissions:
6 | contents: write
7 | statuses: write
8 |
9 | concurrency:
10 | group: pages
11 | cancel-in-progress: false
12 |
13 | env:
14 | MELI_SITE: f31e3b17-c4ea-4d9d-bdce-9417d67fd30e
15 |
16 | jobs:
17 | # 构建工作
18 | build:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v4
23 | with:
24 | fetch-depth: 0 # 如果未启用 lastUpdated,则不需要
25 | - name: Setup Python
26 | uses: actions/setup-python@v2
27 | with:
28 | python-version: "3.11"
29 |
30 | - name: Setup API markdown
31 | run: |-
32 | python -m pip install litedoc
33 | chmod +x build-docs.sh
34 | ./build-docs.sh
35 |
36 | - name: 安装 pnpm
37 | uses: pnpm/action-setup@v2
38 | with:
39 | run_install: true
40 | version: 8
41 |
42 | - name: 设置 Node.js
43 | run: |-
44 | pnpm install
45 |
46 | - name: 构建文档
47 | env:
48 | NODE_OPTIONS: --max_old_space_size=8192
49 | run: |-
50 | pnpm run docs:build
51 |
52 | - name: "发布"
53 | run: |
54 | npx -p "@getmeli/cli" meli upload docs/.vitepress/dist \
55 | --url "https://dash.apage.dev" \
56 | --site "$MELI_SITE" \
57 | --token "$MELI_TOKEN" \
58 | --release "$GITHUB_SHA"
59 | env:
60 | MELI_TOKEN: ${{ secrets.MELI_TOKEN }}
61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62 |
--------------------------------------------------------------------------------
/.github/workflows/pre-commit.yml:
--------------------------------------------------------------------------------
1 | name: Pre-commit checks
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | pre-commit:
7 | runs-on: ubuntu-latest
8 | strategy:
9 | matrix:
10 | python-version: ['3.10', '3.11', '3.12', '3.13'] # 添加你想要测试的 Python 版本
11 |
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v3
15 |
16 | - name: Set up Python
17 | uses: actions/setup-python@v3
18 | with:
19 | python-version: ${{ matrix.python-version }} # 使用矩阵中的 Python 版本
20 |
21 | - name: Install dependencies
22 | run: |
23 | python -m pip install pdm
24 | python -m pip install pre-commit
25 | pdm config python.use_venv false
26 | pdm install --no-lock
27 | pre-commit install
28 |
29 | - name: Run pre-commit
30 | run: pre-commit run --all-files
31 |
--------------------------------------------------------------------------------
/.github/workflows/pypi-publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | release:
5 | types:
6 | - published
7 | workflow_dispatch:
8 |
9 | jobs:
10 | pypi-publish:
11 | name: Upload release to PyPI
12 | runs-on: ubuntu-latest
13 | environment: release
14 | permissions:
15 | id-token: write
16 | steps:
17 | - uses: actions/checkout@master
18 | - name: Set up Python
19 | uses: actions/setup-python@v1
20 | with:
21 | python-version: "3.x"
22 | - name: Install pypa/build
23 | run: >-
24 | python -m
25 | pip install
26 | build
27 | --user
28 | - name: Build a binary wheel and a source tarball
29 | run: >-
30 | python -m
31 | build
32 | --sdist
33 | --wheel
34 | --outdir dist/
35 | .
36 | - name: Publish distribution to PyPI
37 | uses: pypa/gh-action-pypi-publish@release/v1
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | fail_fast: true
2 | repos:
3 | - repo: local
4 | hooks:
5 | - id: check-filenames
6 | name: Check Python Filenames
7 | entry: python ./.pre-commit/check_filename.py
8 | language: python
9 | files: \.py$
10 |
11 | - repo: https://github.com/psf/black
12 | rev: 25.1.0
13 | hooks:
14 | - id: black
15 | args: [--config=./pyproject.toml]
16 |
17 | - repo: https://github.com/PyCQA/isort
18 | rev: 6.0.1
19 | hooks:
20 | - id: isort
21 | args: ["--profile", "black"]
22 |
23 | - repo: https://github.com/pre-commit/mirrors-mypy
24 | rev: v1.15.0
25 | hooks:
26 | - id: mypy
27 |
28 | # - repo: https://github.com/pre-commit/pre-commit-hooks
29 | # rev: v4.0.1
30 | # hooks:
31 | # - id: trailing-whitespace
32 | # - id: end-of-file-fixer
33 | # - id: check-yaml
34 | # - id: check-added-large-files
35 |
--------------------------------------------------------------------------------
/.pre-commit/check_filename.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import re
5 | import sys
6 |
7 |
8 | def is_valid_filename(filename: str) -> bool:
9 | """文件名完整相对路径
10 |
11 | Args:
12 | filename (str): _description_
13 |
14 | Returns:
15 | bool: _description_
16 | """
17 | # 检查文件名是否仅包含小写字母,数字,下划线
18 | # 啊?文件名还不能有大写啊……
19 | if not re.match(r"^[a-z0-9_]+\.py$", filename):
20 | return False
21 | else:
22 | return True
23 |
24 |
25 | def main():
26 | invalid_files = []
27 | for root, _, files in os.walk("nonebot_plugin_marshoai"):
28 | for file in files:
29 | if file.endswith(".py"):
30 | if not is_valid_filename(file):
31 | invalid_files.append(os.path.join(root, file))
32 |
33 | if invalid_files:
34 | print("以下文件名不符合命名规则:")
35 | for file in invalid_files:
36 | print(file)
37 | sys.exit(1)
38 |
39 |
40 | if __name__ == "__main__":
41 | main()
42 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | marshoai-docs.pages.liteyuki.icu
2 |
--------------------------------------------------------------------------------
/LICENSE-LSO:
--------------------------------------------------------------------------------
1 | LSO license
2 | LiteyukiStudio Opensource license
3 |
4 | ---
5 |
6 | Copyright © 2025 Asankilp & LiteyukiStudio
7 |
8 | ---
9 |
10 | Free to grant the same license-based rights to any person or organization who obtains a copy
11 |
12 | including but not limited to using, copying, modifying, merging, publishing, distributing, sublicenseing, and/or selling copies of the software
13 |
14 | This software and related documentation files (hereinafter referred to as "this software") are licensed in the same way as the base, and are released in the form of open source on the Internet or other media platforms
15 |
16 | Everyone has the right to obtain a copy and obtain permission to distribute and/or use it in the above manner
17 |
18 | In the event of a conflict with other open source or non-open source licenses,
19 | the conflicting portions, unless otherwise stated, shall remain subject to the terms of this open source license.
20 |
21 | During the process of distribution and dissemination,
22 | it is necessary to preserve the existence of this license and distribute and redistribute it in the same manner.
23 |
24 | In the reprocessing of software or software copies for profit purposes
25 | If using this license, individuals and organizations to which the reprocessing software belongs may change, add, or delete non essential license regulations at their own discretion
26 |
27 | The necessary licensing regulations include:
28 | 1. Distribution of Rights and Its Scope of Application
29 | 2. Disclaimer Regulations and Their Interpretation
30 |
31 | However, when obtaining a copy, it is still necessary to pay attention to the following:
32 |
33 | - The above copyright notice and this permission notice shall be included in a copy of the Software
34 | - When using this software and its copies, it is still necessary to maintain the same form as the original
35 |
36 | - When using this software, you still need to disclose the copy of this software under the same license:
37 | - Do not profit from copies of this software in a non-original license without the permission of the original author
38 |
39 | ---
40 |
41 | The software is provided as a "copy as is" without any warranty of any kind, either express or implied:
42 | including but not limited to the warranty of merchantability, non-infringement for specific purposes
43 |
44 | In any case, the author or copyright owner shall not be liable for any claims, damages, or other liabilities arising from the use of the software by the author or copyright owner, whether in contract litigation, infringement litigation, or other litigation. The author and its copyright owner have the right to refuse compensation for any losses caused by the user for personal reasons
45 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Asankilp & LiteyukiStudio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE-MULAN:
--------------------------------------------------------------------------------
1 | Copyright (c) 2025 EillesWan
2 | nonebot-plugin-latex & other specified codes is licensed under Mulan PSL v2.
3 | You can use this software according to the terms and conditions of the Mulan PSL v2.
4 | You may obtain a copy of Mulan PSL v2 at:
5 | http://license.coscl.org.cn/MulanPSL2
6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
7 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
8 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9 | See the Mulan PSL v2 for more details.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | # nonebot-plugin-marshoai
10 |
11 | _✨ 使用 OpenAI 标准格式 API 的聊天机器人插件 ✨_
12 |
13 | [](https://qm.qq.com/q/a13iwP5kAw)
14 | [](https://registry.nonebot.dev/plugin/nonebot-plugin-marshoai:nonebot_plugin_marshoai)
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | ## 📖 介绍
28 |
29 | 通过调用 OpenAI 标准格式 API(例如 GitHub Models API),来实现聊天的插件。
30 | 插件内置了猫娘小棉(Marsho,マルショ)的人物设定,可以进行可爱的聊天!
31 | _谁不喜欢回复消息快又可爱的猫娘呢?_
32 | **对 OneBot 以外的适配器与非 GitHub Models API 的支持未完全经过验证。**
33 | [Melobot 实现](https://github.com/LiteyukiStudio/marshoai-melo)
34 |
35 | ## 🐱 设定
36 |
37 | #### 基本信息
38 |
39 | - 名字:小棉(Marsho,マルショ)
40 | - 生日:9 月 6 日
41 |
42 | #### 喜好
43 |
44 | - 🌞 晒太阳晒到融化
45 | - 🤱 撒娇啊~谁不喜欢呢~
46 | - 🍫 吃零食!肉肉好吃!
47 | - 🐾 玩!我喜欢和朋友们一起玩!
48 |
49 | ## 😼 使用
50 |
51 | 请查看[使用文档](https://marsho.liteyuki.org/start/use.html)
52 |
53 | ## ❤ 鸣谢&版权说明
54 |
55 | > Copyright (c) 2025 Asankilp & LiteyukiStudio
56 |
57 | 本项目使用了以下项目的代码:
58 |
59 | - [nonebot-plugin-latex](https://github.com/EillesWan/nonebot-plugin-latex)
60 | - [nonebot-plugin-deepseek](https://github.com/KomoriDev/nonebot-plugin-deepseek)
61 |
62 | "Marsho" logo 由 [@Asankilp](https://github.com/Asankilp) 绘制,基于 [CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 许可下提供。
63 | "nonebot-plugin-marshoai" 基于 [MIT](./LICENSE-MIT) 许可下提供。
64 | 部分指定的代码基于 [Mulan PSL v2](./LICENSE-MULAN) 许可下提供。
65 |
66 |
71 |
72 | 感谢所有的贡献者!
73 |
74 | ## 开发
75 |
76 | - 请阅读[开发规范](./README_DEV.md)
77 |
--------------------------------------------------------------------------------
/README_DEV.md:
--------------------------------------------------------------------------------
1 | # 开发指北
2 |
3 | ## 规范化
4 |
5 | - PEP8
6 | - mypy 类型检查
7 | - black 格式化
8 |
9 | ## 开发依赖
10 |
11 | - pre-commit,确保代码质量合格才可以提交
12 |
13 | ```bash
14 | pre-commit install
15 | ```
16 |
17 | ## 提交及拉取请求
18 |
19 | - 提交后请静待workflows运行结果,若pre-commit通不过请不要PR到主仓库,自行解决掉问题后再次提交
20 |
21 | ## 其他提示
22 |
23 | - 在西文大小写不敏感的文件系统或操作系统中开发时请注意文件名的西文大小写情况,点名批评 APFS 文件系统和视窗操作系统
24 | - 请在提交的文件中尽可能使用相对路径
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | # nonebot-plugin-marshoai
10 |
11 | _✨ A chat bot plugin which use OpenAI standard API ✨_
12 |
13 | [](https://registry.nonebot.dev/plugin/nonebot-plugin-marshoai:nonebot_plugin_marshoai)
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ## 📖 Indroduction
25 |
26 | A plugin made by call OpenAI standard API(Such as GitHub Models API)
27 |
28 | Plugin internally installed the catgirl character of Marsho, is able to have a cute conversation!
29 |
30 | *Who don't like a cute catgirl with fast answer speed?*
31 |
32 | **Support for adapters other than OneBot and non-Github Models APIs is not fully verified.**
33 |
34 | [Melobot implementation](https://github.com/LiteyukiStudio/marshoai-melo)
35 |
36 | ## 🐱 Character setting
37 |
38 | #### Basic information
39 |
40 | - Name : Marsho
41 | - Birthday : September 6th
42 |
43 | #### Hobbies
44 |
45 | - 🌞 Melt in sunshine
46 | - 🤱 Coquetry~ who don't like that~
47 | - 🍫 Eating snacks! Meat is yummy!
48 | - 🐾 Play! I like play with friends!
49 |
50 | ## 😼 Usage
51 | Please read [Documentation](https://marsho.liteyuki.org/start/use.html)
52 |
53 | ## ❤ Thanks&Copyright
54 | This project uses the following code from other projects:
55 | - [nonebot-plugin-latex](https://github.com/EillesWan/nonebot-plugin-latex)
56 | - [nonebot-plugin-deepseek](https://github.com/KomoriDev/nonebot-plugin-deepseek)
57 |
58 | "Marsho" logo contributed by [@Asankilp](https://github.com/Asankilp),licensed under [CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) lisense.
59 |
60 | "nonebot-plugin-marshoai" is licensed under [MIT](./LICENSE-MIT) license.
61 | Some of the code is licensed under [Mulan PSL v2](./LICENSE-MULAN) license.
62 |
63 |
68 |
69 | Thanks to all the contributors!
70 |
--------------------------------------------------------------------------------
/README_TOOLS.md:
--------------------------------------------------------------------------------
1 | # 🛠️小棉工具
2 | 小棉工具(MarshoTools)是一个简单的模块加载器,允许从插件数据目录下的`tools`目录内加载数个工具包与其中定义的函数,以供 AI 模型调用。
3 | 有关 Function Call 的更多信息,请参阅[OpenAI 官方文档](https://platform.openai.com/docs/guides/function-calling)。
4 |
5 | ## ✍️ 编写工具
6 | ### 📁 目录结构
7 | 插件数据目录下的`tools`目录被称作**工具集**,其中可包含数个**工具包**,工具包与 Python 的**包**结构类似,需要在其中包含`__init__.py`文件与`tools.json`定义文件,这些文件将被用于存放以及定义编写的函数。
8 |
9 | 一个工具包的目录结构类似于:
10 | ```
11 | tools/ # 工具集目录
12 | └── marshoai-example/ # 工具包目录,以包名命名
13 | └── __init__.py # 工具模块
14 | └── tools.json # 函数定义文件
15 | ```
16 | 在这个目录树中:
17 | - **工具包目录**是以`marshoai-xxxxx`命名的目录,目录名即为工具包的包名。编写工具时,应尽量采取此命名标准。
18 | - **工具模块**可包含数个可调用的**异步**函数,可以接受入参,也可以不接受。它们的返回值数据类型应为 AI 模型受支持的类型,一般情况下`str`被大部分模型所支持。
19 | - **函数定义文件**是让 AI 模型知道如何调用这些函数的关键。
20 | ### 编写函数
21 | 来编写一个简单的函数吧,例如一个获取天气的函数和一个获取时间的函数:
22 | ###### **\_\_init\_\_.py**
23 | ```python
24 | from datetime import datetime
25 |
26 | async def get_weather(location: str):
27 | return f"{location}的温度是114514℃。" #模拟天气返回信息
28 |
29 | async def get_current_time():
30 | current_time = datetime.now().strftime("%Y.%m.%d %H:%M:%S")
31 | time_prompt = f"现在的时间是{current_time}。"
32 | return time_prompt
33 | ```
34 | 在这个示例代码中,定义了`get_weather`和`get_current_time`两个函数,其中一个接受`str`类型的地点入参。要让 AI 模型知道这两个函数的存在以及调用的条件和方法,需要编写**函数定义文件**。
35 | ###### **tools.json**
36 | ```json
37 | [
38 | {
39 | "type": "function",
40 | "function": {
41 | "name": "marshoai-example__get_weather", # 函数调用名称
42 | "description": "查询指定地点的天气。", # 对该函数的描述,需要清楚描述该函数的用途
43 | "parameters": { # 定义函数的入参
44 | "type": "object",
45 | "properties": {
46 | "location": { # 此处'location'即为__init__.py定义的入参名
47 | "type": "string", # 该入参的数据类型
48 | "description": "城市或县区,比如北京市、杭州市、余杭区等。" # 对该入参的描述,需要清楚描述该入参应传入什么样子的内容
49 | }
50 | }
51 | },
52 | "required": [ # 定义该函数的必需入参
53 | "location"
54 | ]
55 | }
56 | },
57 | {
58 | "type": "function",
59 | "function": {
60 | "name": "marshoai-example__get_current_time",
61 | "description": "获取现在的时间。",
62 | "parameters": {} # 该函数不需要入参,故此处为空
63 | }
64 | }
65 | ]
66 | ```
67 | 在这个文件中定义了两个已经编写好的函数,该定义文件将被输入到 AI 模型中,来让 AI 模型知道这些函数的存在与调用方法。
68 | **函数调用名称**的命名方式比较特别。以获取天气的函数为例,它的函数调用名称`marshoai-example__get_weather`包含三个信息:
69 | - 前面的**marshoai-example**即为该函数所在工具包的**包名**。
70 | - 后面的**get_weather**是这个函数在代码里的名称。
71 | - 中间的两个下划线是用于分割这两个信息的分隔符。
72 |
73 | 使用这种命名方式,是为了兼容更多的 OpenAI 标准格式 API。因此,在给工具包和函数取名时,不要使用带有两个下划线的名称。
74 | ### 测试函数
75 | 在编写完工具后,启动 Bot,Nonebot 的日志应当会输出工具包的加载信息。
76 | 以下是测试示例:
77 | ```
78 | > marsho 深圳天气怎么样
79 | 深圳的天气显示温度是114514°C,真是不可思议呢!这一定是个误报吧~(≧▽≦) 希望你那里有个好天气哦!
80 | > marsho 分别告诉我下北泽,杭州,苏州的天气
81 | 下北泽、杭州和苏州的天气都显示温度为114514°C呢!这么奇怪的温度,一定是个误报吧~(≧▽≦)
82 |
83 | 如果要查看真实的天气情况,建议查看专业天气预报哦~
84 | > marsho 现在几点了
85 | 现在的时间是2024年11月23日,21点05分哦~(*^ω^) 你准备做些什么呢?
86 | ```
87 |
--------------------------------------------------------------------------------
/build-docs.sh:
--------------------------------------------------------------------------------
1 | litedoc nonebot_plugin_marshoai -o docs/zh/dev/api -l zh-Hans -cd class -fd func -md func -vd var -f title=%filetitle%,order=100 -bu https://github.com/LiteyukiStudio/nonebot-plugin-marshoai/tree/main/nonebot_plugin_marshoai/
2 | litedoc nonebot_plugin_marshoai -o docs/en/dev/api -l en -cd class -fd func -md func -vd var -f title=%filetitle%,order=100 -bu https://github.com/LiteyukiStudio/nonebot-plugin-marshoai/tree/main/nonebot_plugin_marshoai/
--------------------------------------------------------------------------------
/docs/.vitepress/config/common.ts:
--------------------------------------------------------------------------------
1 | import { VitePressSidebarOptions } from "vitepress-sidebar/types";
2 |
3 | export const gitea = {
4 | svg: ' ',
5 | };
6 |
7 | export const defaultLang = "zh";
8 |
9 | const commonSidebarOptions: VitePressSidebarOptions = {
10 | collapsed: true,
11 | convertSameNameSubFileToGroupIndexPage: true,
12 | useTitleFromFrontmatter: true,
13 | useFolderTitleFromIndexFile: false,
14 | useFolderLinkFromIndexFile: true,
15 | useTitleFromFileHeading: true,
16 | rootGroupText: "MARSHOAI",
17 | includeFolderIndexFile: true,
18 | sortMenusByFrontmatterOrder: true,
19 | };
20 |
21 | export function generateSidebarConfig(): VitePressSidebarOptions[] {
22 | let sections = ["dev", "start"];
23 | let languages = ["zh", "en"];
24 | let ret: VitePressSidebarOptions[] = [];
25 | for (let language of languages) {
26 | for (let section of sections) {
27 | if (language === defaultLang) {
28 | ret.push({
29 | basePath: `/${section}/`,
30 | scanStartPath: `docs/${language}/${section}`,
31 | resolvePath: `/${section}/`,
32 | ...commonSidebarOptions,
33 | });
34 | } else {
35 | ret.push({
36 | basePath: `/${language}/${section}/`,
37 | scanStartPath: `docs/${language}/${section}`,
38 | resolvePath: `/${language}/${section}/`,
39 | ...commonSidebarOptions,
40 | });
41 | }
42 | }
43 | }
44 | return ret;
45 | }
46 |
47 | export const ThemeConfig = {
48 | getEditLink: (
49 | editPageText: string
50 | ): { pattern: (params: { filePath: string }) => string; text: string } => {
51 | return {
52 | pattern: ({ filePath }: { filePath: string }): string => {
53 | if (!filePath) {
54 | throw new Error("filePath is undefined");
55 | }
56 | const regex = /^(dev\/api|[^\/]+\/dev\/api)/;
57 | if (regex.test(filePath)) {
58 | filePath = filePath
59 | .replace(regex, "")
60 | .replace("index.md", "__init__.py")
61 | .replace(".md", ".py");
62 | const fileName = filePath.split("/").pop();
63 | const parentFolder = filePath.split("/").slice(-2, -1)[0];
64 | if (
65 | fileName &&
66 | parentFolder &&
67 | fileName.split(".")[0] === parentFolder
68 | ) {
69 | filePath =
70 | filePath.split("/").slice(0, -1).join("/") + "/__init__.py";
71 | }
72 | return `https://github.com/LiteyukiStudio/nonebot-plugin-marshoai/tree/main/nonebot_plugin_marshoai/${filePath}`;
73 | } else {
74 | return `https://github.com/LiteyukiStudio/nonebot-plugin-marshoai/tree/main/docs/${filePath}`;
75 | }
76 | },
77 | text: editPageText,
78 | };
79 | },
80 |
81 | getOutLine: (label: string): { label: string; level: [number, number] } => {
82 | return {
83 | label: label,
84 | level: [2, 6],
85 | };
86 | },
87 | };
88 |
--------------------------------------------------------------------------------
/docs/.vitepress/config/en.ts:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vitepress'
2 | import { ThemeConfig } from './common'
3 |
4 | export const en = defineConfig({
5 | lang: "en-US",
6 | title: "Marsho AI",
7 | description: "Kawaii, Intelligent and Easy to Extend",
8 | themeConfig: {
9 | docFooter: {
10 | prev: 'Prev',
11 | next: 'Next'
12 | },
13 | nav: [
14 | {text: 'Home', link: '/en'},
15 | {text: 'Usage', link: '/en/start/install'},
16 | {text: 'Develop', link: '/en/dev/extension'},
17 | ],
18 | editLink: ThemeConfig.getEditLink('Edit this page'),
19 | langMenuLabel: 'Language',
20 | returnToTopLabel: 'To top',
21 | sidebarMenuLabel: 'Option',
22 | darkModeSwitchLabel: 'Theme',
23 | lightModeSwitchTitle: 'Light',
24 | darkModeSwitchTitle: 'Dark',
25 | footer: {
26 | message: "The document is being improved. Suggestions are welcome. Webpage is deployed at Liteyuki Meli and accelerated by Liteyukiflare .",
27 | copyright: '© 2024 Liteyuki Studio ',
28 | }
29 | },
30 |
31 | })
--------------------------------------------------------------------------------
/docs/.vitepress/config/index.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitepress'
2 | import { zh } from './zh'
3 | import { en } from './en'
4 | import { ja } from './ja'
5 | import { defaultLang, generateSidebarConfig, gitea } from './common'
6 | import { generateSidebar } from 'vitepress-sidebar'
7 |
8 | // https://vitepress.dev/reference/site-config
9 | export default defineConfig({
10 | head: [
11 | ["script", { src: "https://cdn.liteyuki.icu/js/liteyuki_footer.js" }],
12 | ['link', { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
13 | ],
14 | rewrites: {
15 | [`${defaultLang}/:rest*`]: ":rest*",
16 | },
17 | cleanUrls: false,
18 | themeConfig: {
19 | // https://vitepress.dev/reference/default-theme-config
20 | logo: {
21 | light: '/marsho-full.svg',
22 | dark: '/marsho-full.svg',
23 | alt: 'Marsho Logo'
24 | },
25 |
26 | sidebar: generateSidebar(
27 | [...generateSidebarConfig(),]
28 | ),
29 |
30 | socialLinks: [
31 | { icon: 'github', link: 'https://github.com/LiteyukiStudio/nonebot-plugin-marshoai' },
32 | { icon: gitea, link: 'https://git.liteyuki.icu/LiteyukiStudio/nonebot-plugin-marshoai' }
33 | ]
34 | },
35 | locales: {
36 | root: { label: "简体中文", ...zh },
37 | en: { label: "English", ...en },
38 | ja: { label: "日本語", ...ja },
39 | },
40 | lastUpdated: true,
41 | })
42 |
--------------------------------------------------------------------------------
/docs/.vitepress/config/ja.ts:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vitepress'
2 | import { ThemeConfig } from './common'
3 |
4 | export const ja = defineConfig({
5 | lang: "ja-JP",
6 | title: "Marsho AI",
7 | description: "かわいくて、賢くて、拡張しやすい",
8 | themeConfig: {
9 | docFooter: {
10 | prev: '前へ',
11 | next: '次へ'
12 | },
13 | nav: [
14 | {text: 'ホーム', link: '/ja'},
15 | {text: '使用方法', link: '/ja/start/install'},
16 | {text: '開発', link: '/ja/dev/extension'},
17 | ],
18 | editLink: ThemeConfig.getEditLink('このページを編集'),
19 | langMenuLabel: '言語',
20 | returnToTopLabel: 'トップへ戻る',
21 | sidebarMenuLabel: 'オプション',
22 | darkModeSwitchLabel: 'テーマ',
23 | lightModeSwitchTitle: 'ライト',
24 | darkModeSwitchTitle: 'ダーク',
25 | footer: {
26 | message: "ドキュメントは改善中です。ご意見をお待ちしております。 ウェブサイトは Liteyuki Meli によってデプロイされ、Liteyukiflare によって加速されています。",
27 | copyright: '© 2024 Liteyuki Studio ',
28 | }
29 | },
30 | })
--------------------------------------------------------------------------------
/docs/.vitepress/config/zh.ts:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vitepress'
2 | import { ThemeConfig } from './common'
3 |
4 | export const zh = defineConfig({
5 | lang: "zh-Hans",
6 | title: "小棉智能",
7 | description: "可爱,智能且易扩展",
8 | themeConfig: {
9 | docFooter: {
10 | prev: '上一页',
11 | next: '下一页'
12 | },
13 | nav: [
14 | {text: '家', link: '/'},
15 | {text: '使用', link: '/start/use'},
16 | {text: '开发', link: '/dev/extension'},
17 | ],
18 | editLink: ThemeConfig.getEditLink('编辑此页面'),
19 | langMenuLabel: '语言',
20 | returnToTopLabel: '返回顶部',
21 | sidebarMenuLabel: '菜单',
22 | darkModeSwitchLabel: '主题',
23 | lightModeSwitchTitle: '轻色模式',
24 | darkModeSwitchTitle: '深色模式',
25 | footer: {
26 | message: "文档完善中,欢迎提出建议或帮助我们完善。 网站部署在 Liteyuki Meli 由 Liteyukiflare 提供加速服务。",
27 | copyright: '© 2024 Liteyuki Studio ',
28 | }
29 | },
30 | })
--------------------------------------------------------------------------------
/docs/.vitepress/theme/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 NapCat
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/docs/.vitepress/theme/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 |
2 | import DefaultTheme from 'vitepress/theme'
3 | import './style.css'
4 | import Layout from './Layout.vue'
5 |
6 | export default {
7 | extends: DefaultTheme,
8 | // 使用注入插槽的包装组件覆盖 Layout
9 | Layout: Layout
10 | }
--------------------------------------------------------------------------------
/docs/components/ContributorsBar.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/docs/en/dev/api/azure_onebot.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: azure_onebot
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.azure_onebot`
5 |
6 |
--------------------------------------------------------------------------------
/docs/en/dev/api/constants.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: constants
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.constants`
5 |
6 |
--------------------------------------------------------------------------------
/docs/en/dev/api/hunyuan.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: hunyuan
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.hunyuan`
5 |
6 | ---
7 | `@genimage_cmd.handle()`
8 | ### ***async func*** `genimage(event: Event, prompt = None)`
9 |
10 |
11 |
12 | Source code or View on GitHub
13 |
14 | ```python
15 | @genimage_cmd.handle()
16 | async def genimage(event: Event, prompt=None):
17 | if not prompt:
18 | await genimage_cmd.finish('无提示词')
19 | try:
20 | result = generate_image(prompt)
21 | url = json.loads(result)['ResultImage']
22 | await UniMessage.image(url=url).send()
23 | except Exception as e:
24 | traceback.print_exc()
25 | ```
26 |
27 |
28 |
--------------------------------------------------------------------------------
/docs/en/dev/api/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai`
6 |
7 |
--------------------------------------------------------------------------------
/docs/en/dev/api/metadata.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: metadata
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.metadata`
5 |
6 |
--------------------------------------------------------------------------------
/docs/en/dev/api/plugin/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai.plugin`
6 |
7 | 该功能目前正在开发中,暂时不可用,受影响的文件夹 `plugin`, `plugins`
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/en/dev/api/plugin/models.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: models
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.plugin.models`
5 |
6 | ### ***class*** `PluginMetadata(BaseModel)`
7 | #### ***attr*** `name: str = NO_DEFAULT`
8 |
9 | #### ***attr*** `description: str = ''`
10 |
11 | #### ***attr*** `usage: str = ''`
12 |
13 | #### ***attr*** `author: str = ''`
14 |
15 | #### ***attr*** `homepage: str = ''`
16 |
17 | #### ***attr*** `extra: dict[str, Any] = {}`
18 |
19 | ### ***class*** `Plugin(BaseModel)`
20 | ---
21 | #### ***func*** `hash self => int`
22 |
23 |
24 |
25 | Source code or View on GitHub
26 |
27 | ```python
28 | def __hash__(self) -> int:
29 | return hash(self.name)
30 | ```
31 |
32 |
33 | ---
34 | #### ***func*** `self == other: Any => bool`
35 |
36 |
37 |
38 | Source code or View on GitHub
39 |
40 | ```python
41 | def __eq__(self, other: Any) -> bool:
42 | return self.name == other.name
43 | ```
44 |
45 |
46 | #### ***attr*** `name: str = NO_DEFAULT`
47 |
48 | #### ***attr*** `module: ModuleType = NO_DEFAULT`
49 |
50 | #### ***attr*** `module_name: str = NO_DEFAULT`
51 |
52 | #### ***attr*** `metadata: PluginMetadata | None = None`
53 |
54 | ### ***class*** `FunctionCallArgument(BaseModel)`
55 | ---
56 | #### ***func*** `data(self) -> dict[str, Any]`
57 |
58 |
59 |
60 | Source code or View on GitHub
61 |
62 | ```python
63 | def data(self) -> dict[str, Any]:
64 | return {'type': self.type_, 'description': self.description}
65 | ```
66 |
67 |
68 | #### ***attr*** `type_: str = NO_DEFAULT`
69 |
70 | #### ***attr*** `description: str = NO_DEFAULT`
71 |
72 | #### ***attr*** `default: Any = None`
73 |
74 | ### ***class*** `FunctionCall(BaseModel)`
75 | ---
76 | #### ***func*** `hash self => int`
77 |
78 |
79 |
80 | Source code or View on GitHub
81 |
82 | ```python
83 | def __hash__(self) -> int:
84 | return hash(self.name)
85 | ```
86 |
87 |
88 | ---
89 | #### ***func*** `data(self) -> dict[str, Any]`
90 |
91 | **Description**: 生成函数描述信息
92 |
93 |
94 | **Return**: dict[str, Any]: 函数描述信息 字典
95 |
96 |
97 |
98 | Source code or View on GitHub
99 |
100 | ```python
101 | def data(self) -> dict[str, Any]:
102 | return {'type': 'function', 'function': {'name': self.name, 'description': self.description, 'parameters': {'type': 'object', 'properties': {k: v.data() for k, v in self.arguments.items()}}, 'required': [k for k, v in self.arguments.items() if v.default is None]}}
103 | ```
104 |
105 |
106 | #### ***attr*** `name: str = NO_DEFAULT`
107 |
108 | #### ***attr*** `description: str = NO_DEFAULT`
109 |
110 | #### ***attr*** `arguments: dict[str, FunctionCallArgument] = NO_DEFAULT`
111 |
112 | #### ***attr*** `function: ASYNC_FUNCTION_CALL_FUNC = NO_DEFAULT`
113 |
114 |
--------------------------------------------------------------------------------
/docs/en/dev/api/plugin/register.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: register
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.plugin.register`
5 |
6 | 此模块用于获取function call中函数定义信息以及注册函数
7 |
8 |
9 | ---
10 | ### ***func*** `async_wrapper(func: SYNC_FUNCTION_CALL_FUNC) -> ASYNC_FUNCTION_CALL_FUNC`
11 |
12 | **Description**: 将同步函数包装为异步函数,但是不会真正异步执行,仅用于统一调用及函数签名
13 |
14 |
15 | **Arguments**:
16 | > - func: 同步函数
17 |
18 | **Return**: ASYNC_FUNCTION_CALL: 异步函数
19 |
20 |
21 |
22 | Source code or View on GitHub
23 |
24 | ```python
25 | def async_wrapper(func: SYNC_FUNCTION_CALL_FUNC) -> ASYNC_FUNCTION_CALL_FUNC:
26 |
27 | async def wrapper(*args, **kwargs) -> str:
28 | return func(*args, **kwargs)
29 | return wrapper
30 | ```
31 |
32 |
33 | ---
34 | ### ***func*** `function_call(*funcs: FUNCTION_CALL_FUNC) -> None`
35 |
36 |
37 | **Arguments**:
38 | > - func: 函数对象,要有完整的 Google Style Docstring
39 |
40 | **Return**: str: 函数定义信息
41 |
42 |
43 |
44 | Source code or View on GitHub
45 |
46 | ```python
47 | def function_call(*funcs: FUNCTION_CALL_FUNC) -> None:
48 | for func in funcs:
49 | function_call = get_function_info(func)
50 | ```
51 |
52 |
53 | ---
54 | ### ***func*** `get_function_info(func: FUNCTION_CALL_FUNC)`
55 |
56 | **Description**: 获取函数信息
57 |
58 |
59 | **Arguments**:
60 | > - func: 函数对象
61 |
62 | **Return**: FunctionCall: 函数信息对象模型
63 |
64 |
65 |
66 | Source code or View on GitHub
67 |
68 | ```python
69 | def get_function_info(func: FUNCTION_CALL_FUNC):
70 | name = func.__name__
71 | description = func.__doc__
72 | logger.info(f'注册函数: {name} {description}')
73 | ```
74 |
75 |
76 |
--------------------------------------------------------------------------------
/docs/en/dev/api/plugin/typing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: typing
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.plugin.typing`
5 |
6 |
--------------------------------------------------------------------------------
/docs/en/dev/api/plugin/utils.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: utils
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.plugin.utils`
5 |
6 | ---
7 | ### ***func*** `path_to_module_name(path: Path) -> str`
8 |
9 | **Description**: 转换路径为模块名
10 |
11 | **Arguments**:
12 | > - path: 路径a/b/c/d -> a.b.c.d
13 |
14 | **Return**: str: 模块名
15 |
16 |
17 |
18 | Source code or View on GitHub
19 |
20 | ```python
21 | def path_to_module_name(path: Path) -> str:
22 | rel_path = path.resolve().relative_to(Path.cwd().resolve())
23 | if rel_path.stem == '__init__':
24 | return '.'.join(rel_path.parts[:-1])
25 | else:
26 | return '.'.join(rel_path.parts[:-1] + (rel_path.stem,))
27 | ```
28 |
29 |
30 | ---
31 | ### ***func*** `is_coroutine_callable(call: Callable[..., Any]) -> bool`
32 |
33 | **Description**: 判断是否为async def 函数
34 |
35 | **Arguments**:
36 | > - call: 可调用对象
37 |
38 | **Return**: bool: 是否为协程可调用对象
39 |
40 |
41 |
42 | Source code or View on GitHub
43 |
44 | ```python
45 | def is_coroutine_callable(call: Callable[..., Any]) -> bool:
46 | if inspect.isroutine(call):
47 | return inspect.iscoroutinefunction(call)
48 | if inspect.isclass(call):
49 | return False
50 | func_ = getattr(call, '__call__', None)
51 | return inspect.iscoroutinefunction(func_)
52 | ```
53 |
54 |
55 |
--------------------------------------------------------------------------------
/docs/en/dev/api/plugins/marshoai_bangumi/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai.plugins.marshoai_bangumi`
6 |
7 | ---
8 | ### ***async func*** `fetch_calendar()`
9 |
10 |
11 |
12 | Source code or View on GitHub
13 |
14 | ```python
15 | async def fetch_calendar():
16 | url = 'https://api.bgm.tv/calendar'
17 | headers = {'User-Agent': 'LiteyukiStudio/nonebot-plugin-marshoai (https://github.com/LiteyukiStudio/nonebot-plugin-marshoai)'}
18 | async with httpx.AsyncClient() as client:
19 | response = await client.get(url, headers=headers)
20 | return response.json()
21 | ```
22 |
23 |
24 | ---
25 | `@function_call`
26 | ### ***async func*** `get_bangumi_news() -> str`
27 |
28 | **Description**: 获取今天的新番(动漫)列表,在调用之前,你需要知道今天星期几。
29 |
30 |
31 | **Return**: _type_: _description_
32 |
33 |
34 |
35 | Source code or View on GitHub
36 |
37 | ```python
38 | @function_call
39 | async def get_bangumi_news() -> str:
40 | result = await fetch_calendar()
41 | info = ''
42 | try:
43 | for i in result:
44 | weekday = i['weekday']['cn']
45 | info += f'{weekday}:'
46 | items = i['items']
47 | for item in items:
48 | name = item['name_cn']
49 | info += f'《{name}》'
50 | info += '\n'
51 | return info
52 | except Exception as e:
53 | traceback.print_exc()
54 | return ''
55 | ```
56 |
57 |
58 | ---
59 | `@function_call`
60 | ### ***func*** `test_sync() -> str`
61 |
62 |
63 |
64 | Source code or View on GitHub
65 |
66 | ```python
67 | @function_call
68 | def test_sync() -> str:
69 | return 'sync'
70 | ```
71 |
72 |
73 |
--------------------------------------------------------------------------------
/docs/en/dev/api/plugins/marshoai_basic/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai.plugins.marshoai_basic`
6 |
7 | ---
8 | ### ***async func*** `get_weather(location: str)`
9 |
10 |
11 |
12 | Source code or View on GitHub
13 |
14 | ```python
15 | async def get_weather(location: str):
16 | return f'{location}的温度是114514℃。'
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `get_current_env()`
22 |
23 |
24 |
25 | Source code or View on GitHub
26 |
27 | ```python
28 | async def get_current_env():
29 | ver = os.popen('uname -a').read()
30 | return str(ver)
31 | ```
32 |
33 |
34 | ---
35 | ### ***async func*** `get_current_time()`
36 |
37 |
38 |
39 | Source code or View on GitHub
40 |
41 | ```python
42 | async def get_current_time():
43 | current_time = DateTime.now().strftime('%Y.%m.%d %H:%M:%S')
44 | current_weekday = DateTime.now().weekday()
45 | weekdays = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
46 | current_weekday_name = weekdays[current_weekday]
47 | current_lunar_date = DateTime.now().to_lunar().date_hanzify()[5:]
48 | time_prompt = f'现在的时间是{current_time},{current_weekday_name},农历{current_lunar_date}。'
49 | return time_prompt
50 | ```
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_bangumi/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_bangumi`
6 |
7 | ---
8 | ### ***async func*** `fetch_calendar()`
9 |
10 |
11 |
12 | Source code or View on GitHub
13 |
14 | ```python
15 | async def fetch_calendar():
16 | url = 'https://api.bgm.tv/calendar'
17 | headers = {'User-Agent': 'LiteyukiStudio/nonebot-plugin-marshoai (https://github.com/LiteyukiStudio/nonebot-plugin-marshoai)'}
18 | async with httpx.AsyncClient() as client:
19 | response = await client.get(url, headers=headers)
20 | return response.json()
21 | ```
22 |
23 |
24 | ---
25 | ### ***async func*** `get_bangumi_news()`
26 |
27 |
28 |
29 | Source code or View on GitHub
30 |
31 | ```python
32 | async def get_bangumi_news():
33 | result = await fetch_calendar()
34 | info = ''
35 | try:
36 | for i in result:
37 | weekday = i['weekday']['cn']
38 | info += f'{weekday}:'
39 | items = i['items']
40 | for item in items:
41 | name = item['name_cn']
42 | info += f'《{name}》'
43 | info += '\n'
44 | return info
45 | except Exception as e:
46 | traceback.print_exc()
47 | return ''
48 | ```
49 |
50 |
51 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_basic/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_basic`
6 |
7 | ---
8 | ### ***async func*** `get_weather(location: str)`
9 |
10 |
11 |
12 | Source code or View on GitHub
13 |
14 | ```python
15 | async def get_weather(location: str):
16 | return f'{location}的温度是114514℃。'
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `get_current_env()`
22 |
23 |
24 |
25 | Source code or View on GitHub
26 |
27 | ```python
28 | async def get_current_env():
29 | ver = os.popen('uname -a').read()
30 | return str(ver)
31 | ```
32 |
33 |
34 | ---
35 | ### ***async func*** `get_current_time()`
36 |
37 |
38 |
39 | Source code or View on GitHub
40 |
41 | ```python
42 | async def get_current_time():
43 | current_time = DateTime.now().strftime('%Y.%m.%d %H:%M:%S')
44 | current_weekday = DateTime.now().weekday()
45 | weekdays = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
46 | current_weekday_name = weekdays[current_weekday]
47 | current_lunar_date = DateTime.now().to_lunar().date_hanzify()[5:]
48 | time_prompt = f'现在的时间是{current_time},{current_weekday_name},农历{current_lunar_date}。'
49 | return time_prompt
50 | ```
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_megakits/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_megakits`
6 |
7 | ---
8 | ### ***async func*** `twisuki()`
9 |
10 |
11 |
12 | Source code or View on GitHub
13 |
14 | ```python
15 | async def twisuki():
16 | return str(await mk_info.twisuki())
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `megakits()`
22 |
23 |
24 |
25 | Source code or View on GitHub
26 |
27 | ```python
28 | async def megakits():
29 | return str(await mk_info.megakits())
30 | ```
31 |
32 |
33 | ---
34 | ### ***async func*** `random_turntable(upper: int, lower: int = 0)`
35 |
36 |
37 |
38 | Source code or View on GitHub
39 |
40 | ```python
41 | async def random_turntable(upper: int, lower: int=0):
42 | return str(await mk_common.random_turntable(upper, lower))
43 | ```
44 |
45 |
46 | ---
47 | ### ***async func*** `number_calc(a: str, b: str, op: str)`
48 |
49 |
50 |
51 | Source code or View on GitHub
52 |
53 | ```python
54 | async def number_calc(a: str, b: str, op: str):
55 | return str(await mk_common.number_calc(a, b, op))
56 | ```
57 |
58 |
59 | ---
60 | ### ***async func*** `morse_encrypt(msg: str)`
61 |
62 |
63 |
64 | Source code or View on GitHub
65 |
66 | ```python
67 | async def morse_encrypt(msg: str):
68 | return str(await mk_morse_code.morse_encrypt(msg))
69 | ```
70 |
71 |
72 | ---
73 | ### ***async func*** `morse_decrypt(msg: str)`
74 |
75 |
76 |
77 | Source code or View on GitHub
78 |
79 | ```python
80 | async def morse_decrypt(msg: str):
81 | return str(await mk_morse_code.morse_decrypt(msg))
82 | ```
83 |
84 |
85 | ---
86 | ### ***async func*** `nya_encode(msg: str)`
87 |
88 |
89 |
90 | Source code or View on GitHub
91 |
92 | ```python
93 | async def nya_encode(msg: str):
94 | return str(await mk_nya_code.nya_encode(msg))
95 | ```
96 |
97 |
98 | ---
99 | ### ***async func*** `nya_decode(msg: str)`
100 |
101 |
102 |
103 | Source code or View on GitHub
104 |
105 | ```python
106 | async def nya_decode(msg: str):
107 | return str(await mk_nya_code.nya_decode(msg))
108 | ```
109 |
110 |
111 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_megakits/mk_common.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mk_common
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_megakits.mk_common`
5 |
6 | ---
7 | ### ***async func*** `random_turntable(upper: int, lower: int)`
8 |
9 | **Description**: Random Turntable
10 |
11 |
12 | **Arguments**:
13 | > - upper (int): _description_
14 | > - lower (int): _description_
15 |
16 | **Return**: _type_: _description_
17 |
18 |
19 |
20 | Source code or View on GitHub
21 |
22 | ```python
23 | async def random_turntable(upper: int, lower: int):
24 | return random.randint(lower, upper)
25 | ```
26 |
27 |
28 | ---
29 | ### ***async func*** `number_calc(a: str, b: str, op: str) -> str`
30 |
31 | **Description**: Number Calc
32 |
33 |
34 | **Arguments**:
35 | > - a (str): _description_
36 | > - b (str): _description_
37 | > - op (str): _description_
38 |
39 | **Return**: str: _description_
40 |
41 |
42 |
43 | Source code or View on GitHub
44 |
45 | ```python
46 | async def number_calc(a: str, b: str, op: str) -> str:
47 | a, b = (float(a), float(b))
48 | match op:
49 | case '+':
50 | return str(a + b)
51 | case '-':
52 | return str(a - b)
53 | case '*':
54 | return str(a * b)
55 | case '/':
56 | return str(a / b)
57 | case '**':
58 | return str(a ** b)
59 | case '%':
60 | return str(a % b)
61 | case _:
62 | return '未知运算符'
63 | ```
64 |
65 |
66 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_megakits/mk_info.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mk_info
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_megakits.mk_info`
5 |
6 | ---
7 | ### ***async func*** `twisuki()`
8 |
9 |
10 |
11 | Source code or View on GitHub
12 |
13 | ```python
14 | async def twisuki():
15 | return 'Twiuski(苏阳)是megakits插件作者, Github : "https://github.com/Twisuki"'
16 | ```
17 |
18 |
19 | ---
20 | ### ***async func*** `megakits()`
21 |
22 |
23 |
24 | Source code or View on GitHub
25 |
26 | ```python
27 | async def megakits():
28 | return 'MegaKits插件是一个功能混杂的MarshoAI插件, 由Twisuki(Github : "https://github.com/Twisuki")开发, 插件仓库 : "https://github.com/LiteyukiStudio/marsho-toolsets/tree/main/Twisuki/marshoai-megakits"'
29 | ```
30 |
31 |
32 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_megakits/mk_morse_code.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mk_morse_code
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_megakits.mk_morse_code`
5 |
6 | ---
7 | ### ***async func*** `morse_encrypt(msg: str)`
8 |
9 |
10 |
11 | Source code or View on GitHub
12 |
13 | ```python
14 | async def morse_encrypt(msg: str):
15 | result = ''
16 | msg = msg.upper()
17 | for char in msg:
18 | if char in MorseEncode:
19 | result += MorseEncode[char]
20 | else:
21 | result += '..--..'
22 | result += ' '
23 | return result
24 | ```
25 |
26 |
27 | ---
28 | ### ***async func*** `morse_decrypt(msg: str)`
29 |
30 |
31 |
32 | Source code or View on GitHub
33 |
34 | ```python
35 | async def morse_decrypt(msg: str):
36 | result = ''
37 | msg_arr = msg.split()
38 | for char in msg_arr:
39 | if char in MorseDecode:
40 | result += MorseDecode[char]
41 | else:
42 | result += '?'
43 | return result
44 | ```
45 |
46 |
47 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_megakits/mk_nya_code.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mk_nya_code
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_megakits.mk_nya_code`
5 |
6 | ---
7 | ### ***async func*** `nya_encode(msg: str)`
8 |
9 |
10 |
11 | Source code or View on GitHub
12 |
13 | ```python
14 | async def nya_encode(msg: str):
15 | msg_b64str = base64.b64encode(msg.encode()).decode().replace('=', '')
16 | msg_nyastr = ''.join((NyaCodeEncode[base64_char] for base64_char in msg_b64str))
17 | result = ''
18 | for char in msg_nyastr:
19 | if char == '呜' and random.random() < 0.5:
20 | result += '!'
21 | if random.random() < 0.25:
22 | result += random.choice(NyaCodeSpecialCharset) + char
23 | else:
24 | result += char
25 | return result
26 | ```
27 |
28 |
29 | ---
30 | ### ***async func*** `nya_decode(msg: str)`
31 |
32 |
33 |
34 | Source code or View on GitHub
35 |
36 | ```python
37 | async def nya_decode(msg: str):
38 | msg = msg.replace('唔', '').replace('!', '').replace('.', '')
39 | msg_nyastr = []
40 | i = 0
41 | if len(msg) % 3 != 0:
42 | return '这句话不是正确的猫语'
43 | while i < len(msg):
44 | nyachar = msg[i:i + 3]
45 | try:
46 | if all((char in NyaCodeCharset for char in nyachar)):
47 | msg_nyastr.append(nyachar)
48 | i += 3
49 | except Exception:
50 | return '这句话不是正确的猫语'
51 | msg_b64str = ''.join((NyaCodeDecode[nya_char] for nya_char in msg_nyastr))
52 | msg_b64str += '=' * (4 - len(msg_b64str) % 4)
53 | try:
54 | result = base64.b64decode(msg_b64str.encode()).decode()
55 | except Exception:
56 | return '翻译失败'
57 | return result
58 | ```
59 |
60 |
61 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_meogirl/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_meogirl`
6 |
7 | ---
8 | ### ***async func*** `meogirl()`
9 |
10 |
11 |
12 | Source code or View on GitHub
13 |
14 | ```python
15 | async def meogirl():
16 | return mg_info.meogirl()
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `search(msg: str, num: int = 3)`
22 |
23 |
24 |
25 | Source code or View on GitHub
26 |
27 | ```python
28 | async def search(msg: str, num: int=3):
29 | return str(await mg_search.search(msg, num))
30 | ```
31 |
32 |
33 | ---
34 | ### ***async func*** `introduce(msg: str)`
35 |
36 |
37 |
38 | Source code or View on GitHub
39 |
40 | ```python
41 | async def introduce(msg: str):
42 | return str(await mg_introduce.introduce(msg))
43 | ```
44 |
45 |
46 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_meogirl/mg_info.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mg_info
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_meogirl.mg_info`
5 |
6 | ---
7 | ### ***func*** `meogirl()`
8 |
9 |
10 |
11 | Source code or View on GitHub
12 |
13 | ```python
14 | def meogirl():
15 | return 'Meogirl指的是"萌娘百科"(https://zh.moegirl.org.cn/ , 简称"萌百"), 是一个"万物皆可萌的百科全书!"; 同时, MarshoTools也配有"Meogirl"插件, 可调用萌百的api'
16 | ```
17 |
18 |
19 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_meogirl/mg_introduce.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mg_introduce
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_meogirl.mg_introduce`
5 |
6 | ---
7 | ### ***async func*** `get_async_data(url)`
8 |
9 |
10 |
11 | Source code or View on GitHub
12 |
13 | ```python
14 | async def get_async_data(url):
15 | async with httpx.AsyncClient(timeout=None) as client:
16 | return await client.get(url, headers=headers)
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `introduce(msg: str)`
22 |
23 |
24 |
25 | Source code or View on GitHub
26 |
27 | ```python
28 | async def introduce(msg: str):
29 | logger.info(f'介绍 : "{msg}" ...')
30 | result = ''
31 | url = 'https://mzh.moegirl.org.cn/' + urllib.parse.quote_plus(msg)
32 | response = await get_async_data(url)
33 | logger.success(f'连接"{url}"完成, 状态码 : {response.status_code}')
34 | soup = BeautifulSoup(response.text, 'html.parser')
35 | if response.status_code == 200:
36 | '\n 萌娘百科页面结构\n div#mw-content-text\n └── div#404search # 空白页面出现\n └── div.mw-parser-output # 正常页面\n └── div, p, table ... # 大量的解释项\n '
37 | result += msg + '\n'
38 | img = soup.find('img', class_='infobox-image')
39 | if img:
40 | result += f' \n'
41 | div = soup.find('div', class_='mw-parser-output')
42 | if div:
43 | p_tags = div.find_all('p')
44 | num = 0
45 | for p_tag in p_tags:
46 | p = str(p_tag)
47 | p = re.sub('|', '', p, flags=re.DOTALL)
48 | p = re.sub('<.*?>', '', p, flags=re.DOTALL)
49 | p = re.sub('\\[.*?]', '', p, flags=re.DOTALL)
50 | if p != '':
51 | result += str(p)
52 | num += 1
53 | if num >= 20:
54 | break
55 | return result
56 | elif response.status_code == 404:
57 | logger.info(f'未找到"{msg}", 进行搜索')
58 | from . import mg_search
59 | context = await mg_search.search(msg, 1)
60 | keyword = re.search('.*?\\n', context, flags=re.DOTALL).group()[:-1]
61 | logger.success(f'搜索完成, 打开"{keyword}"')
62 | return await introduce(keyword)
63 | elif response.status_code == 301:
64 | return f'未找到{msg}'
65 | else:
66 | logger.error(f'网络错误, 状态码 : {response.status_code}')
67 | return f'网络错误, 状态码 : {response.status_code}'
68 | ```
69 |
70 |
71 | ### var `keyword`
72 |
73 | - **Description**: type: ignore
74 |
75 | - **Default**: `re.search('.*?\\n', context, flags=re.DOTALL).group()[:-1]`
76 |
77 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools/marshoai_meogirl/mg_search.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mg_search
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.tools.marshoai_meogirl.mg_search`
5 |
6 | ---
7 | ### ***async func*** `get_async_data(url)`
8 |
9 |
10 |
11 | Source code or View on GitHub
12 |
13 | ```python
14 | async def get_async_data(url):
15 | async with httpx.AsyncClient(timeout=None) as client:
16 | return await client.get(url, headers=headers)
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `search(msg: str, num: int)`
22 |
23 |
24 |
25 | Source code or View on GitHub
26 |
27 | ```python
28 | async def search(msg: str, num: int):
29 | logger.info(f'搜索 : "{msg}" ...')
30 | result = ''
31 | url = 'https://mzh.moegirl.org.cn/index.php?search=' + urllib.parse.quote_plus(msg)
32 | response = await get_async_data(url)
33 | logger.success(f'连接"{url}"完成, 状态码 : {response.status_code}')
34 | if response.status_code == 200:
35 | '\n 萌娘百科搜索页面结构\n div.searchresults\n └── p ...\n └── ul.mw-search-results # 若无, 证明无搜索结果\n └── li # 一个搜索结果\n └── div.mw-search-result-heading > a # 标题\n └── div.mw-searchresult # 内容\n └── div.mw-search-result-data\n └── li ...\n └── li ...\n '
36 | soup = BeautifulSoup(response.text, 'html.parser')
37 | ul_tag = soup.find('ul', class_='mw-search-results')
38 | if ul_tag:
39 | li_tags = ul_tag.find_all('li')
40 | for li_tag in li_tags:
41 | div_heading = li_tag.find('div', class_='mw-search-result-heading')
42 | if div_heading:
43 | a_tag = div_heading.find('a')
44 | result += a_tag['title'] + '\n'
45 | logger.info(f'搜索到 : "{a_tag['title']}"')
46 | div_result = li_tag.find('div', class_='searchresult')
47 | if div_result:
48 | content = str(div_result).replace('', '').replace('
', '')
49 | content = content.replace('', '').replace(' ', '')
50 | result += content + '\n'
51 | num -= 1
52 | if num == 0:
53 | break
54 | return result
55 | else:
56 | logger.info('无结果')
57 | return '无结果'
58 | elif response.status_code == 302:
59 | logger.info(f'"{msg}"已被重定向至"{response.headers.get('location')}"')
60 | from . import mg_introduce
61 | return await mg_introduce.introduce(msg)
62 | else:
63 | logger.error(f'网络错误, 状态码 : {response.status_code}')
64 | return f'网络错误, 状态码 : {response.status_code}'
65 | ```
66 |
67 |
68 | ### var `soup`
69 |
70 | - **Description**:
71 |
72 | - **Default**: `BeautifulSoup(response.text, 'html.parser')`
73 |
74 |
--------------------------------------------------------------------------------
/docs/en/dev/api/tools_wip/marshoai_memory/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **Module** `nonebot_plugin_marshoai.tools_wip.marshoai_memory`
6 |
7 | ---
8 | ### ***async func*** `write_memory(memory: str)`
9 |
10 |
11 |
12 | Source code or View on GitHub
13 |
14 | ```python
15 | async def write_memory(memory: str):
16 | return ''
17 | ```
18 |
19 |
20 |
--------------------------------------------------------------------------------
/docs/en/dev/api/util_hunyuan.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: util_hunyuan
3 | ---
4 | # **Module** `nonebot_plugin_marshoai.util_hunyuan`
5 |
6 | ---
7 | ### ***func*** `generate_image(prompt: str)`
8 |
9 |
10 |
11 | Source code or View on GitHub
12 |
13 | ```python
14 | def generate_image(prompt: str):
15 | cred = credential.Credential(config.marshoai_tencent_secretid, config.marshoai_tencent_secretkey)
16 | httpProfile = HttpProfile()
17 | httpProfile.endpoint = 'hunyuan.tencentcloudapi.com'
18 | clientProfile = ClientProfile()
19 | clientProfile.httpProfile = httpProfile
20 | client = hunyuan_client.HunyuanClient(cred, 'ap-guangzhou', clientProfile)
21 | req = models.TextToImageLiteRequest()
22 | params = {'Prompt': prompt, 'RspImgType': 'url', 'Resolution': '1080:1920'}
23 | req.from_json_string(json.dumps(params))
24 | resp = client.TextToImageLite(req)
25 | return resp.to_json_string()
26 | ```
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/en/dev/index.md:
--------------------------------------------------------------------------------
1 | # DEV
--------------------------------------------------------------------------------
/docs/en/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | # https://vitepress.dev/reference/default-theme-home-page
3 | layout: home
4 |
5 | hero:
6 | name: "MarshoAI"
7 | text: "A kawaii cat"
8 | tagline: Kawaii, intelligent and extensible AI service plugin
9 | actions:
10 | - theme: brand
11 | text: Start
12 | link: /en/start/install/
13 | - theme: alt
14 | text: Develop & Extened
15 | link: /en/dev/extension/
16 | image:
17 | light: /marsho-full.svg
18 | dark: /marsho-full.svg
19 | alt: Marsho Logo
20 |
21 | features:
22 | - title: Powerful Driver
23 | icon: 🚀
24 | details: Based on NoneBot2, it can be quickly installed on existing NoneBot2 or Liteyuki instances
25 |
26 | - title: Interface Specification
27 | icon: 💻
28 | details: Any interface that follows the OpenAI standard can interact with MarshoAI
29 |
30 | - title: Easy to Extend
31 | icon: 🧩
32 | details: Use Python writing tools and plugins to achieve function calls, and easily extend the functionality of MarshoAI
33 |
34 | - title: Self-Bootstrapping
35 | icon: 🤖
36 | details: Use AI to automatically write code for the robot, achieve self-learning and self-optimization
37 | ---
38 |
39 |
--------------------------------------------------------------------------------
/docs/en/start/index.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/docs/en/start/index.md
--------------------------------------------------------------------------------
/docs/ja/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | # https://vitepress.dev/reference/default-theme-home-page
3 | layout: home
4 |
5 | hero:
6 | name: "小綿智能"
7 | text: "猫娘ロボット"
8 | tagline: かわいくて、賢くて、拡張可能なAIサービスプラグイン
9 | actions:
10 | - theme: brand
11 | text: 始める
12 | link: /ja/start/install/
13 | - theme: alt
14 | text: 開発と拡張
15 | link: /ja/dev/extension/
16 | image:
17 | light: /marsho-full.svg
18 | dark: /marsho-full.svg
19 | alt: Marshoロゴ
20 |
21 | features:
22 | - title: 強力なドライバー
23 | icon: 🚀
24 | details: NoneBot2に基づいており、既存のNoneBot2またはLiteyukiインスタンスに迅速にインストールできます
25 |
26 | - title: インターフェース規格
27 | icon: 💻
28 | details: どんなオープンAI標準に従うインターフェースでも小綿智能と対話できます
29 |
30 | - title: 簡単に拡張
31 | icon: 🧩
32 | details: Pythonでツールやプラグインを作成し、関数呼び出しを実現し、小綿智能の機能を簡単に拡張できます
33 |
34 | - title: 自己起動
35 | icon: 🤖
36 | details: AIを使用してロボットのためのコードを自動的に書き、自己学習と自己最適化を実現します
37 | ---
--------------------------------------------------------------------------------
/docs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/docs/public/favicon.ico
--------------------------------------------------------------------------------
/docs/zh/dev/api/azure_onebot.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: azure_onebot
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.azure_onebot`
5 |
6 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/constants.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: constants
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.constants`
5 |
6 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/hunyuan.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: hunyuan
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.hunyuan`
5 |
6 | ---
7 | `@genimage_cmd.handle()`
8 | ### ***async func*** `genimage(event: Event, prompt = None)`
9 |
10 |
11 |
12 | 源代码 或 在GitHub上查看
13 |
14 | ```python
15 | @genimage_cmd.handle()
16 | async def genimage(event: Event, prompt=None):
17 | if not prompt:
18 | await genimage_cmd.finish('无提示词')
19 | try:
20 | result = generate_image(prompt)
21 | url = json.loads(result)['ResultImage']
22 | await UniMessage.image(url=url).send()
23 | except Exception as e:
24 | traceback.print_exc()
25 | ```
26 |
27 |
28 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 100
3 | title: index
4 | collapsed: true
5 | ---
6 | # **模块** `nonebot_plugin_marshoai`
7 |
8 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/metadata.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: metadata
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.metadata`
5 |
6 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/plugin/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **模块** `nonebot_plugin_marshoai.plugin`
6 |
7 | 该功能目前正在开发中,暂时不可用,受影响的文件夹 `plugin`, `plugins`
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/plugin/models.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: models
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.plugin.models`
5 |
6 | ### ***class*** `PluginMetadata(BaseModel)`
7 | #### ***attr*** `name: str = NO_DEFAULT`
8 |
9 | #### ***attr*** `description: str = ''`
10 |
11 | #### ***attr*** `usage: str = ''`
12 |
13 | #### ***attr*** `author: str = ''`
14 |
15 | #### ***attr*** `homepage: str = ''`
16 |
17 | #### ***attr*** `extra: dict[str, Any] = {}`
18 |
19 | ### ***class*** `Plugin(BaseModel)`
20 | ---
21 | #### ***func*** `hash self => int`
22 |
23 |
24 |
25 | 源代码 或 在GitHub上查看
26 |
27 | ```python
28 | def __hash__(self) -> int:
29 | return hash(self.name)
30 | ```
31 |
32 |
33 | ---
34 | #### ***func*** `self == other: Any => bool`
35 |
36 |
37 |
38 | 源代码 或 在GitHub上查看
39 |
40 | ```python
41 | def __eq__(self, other: Any) -> bool:
42 | return self.name == other.name
43 | ```
44 |
45 |
46 | #### ***attr*** `name: str = NO_DEFAULT`
47 |
48 | #### ***attr*** `module: ModuleType = NO_DEFAULT`
49 |
50 | #### ***attr*** `module_name: str = NO_DEFAULT`
51 |
52 | #### ***attr*** `metadata: PluginMetadata | None = None`
53 |
54 | ### ***class*** `FunctionCallArgument(BaseModel)`
55 | ---
56 | #### ***func*** `data(self) -> dict[str, Any]`
57 |
58 |
59 |
60 | 源代码 或 在GitHub上查看
61 |
62 | ```python
63 | def data(self) -> dict[str, Any]:
64 | return {'type': self.type_, 'description': self.description}
65 | ```
66 |
67 |
68 | #### ***attr*** `type_: str = NO_DEFAULT`
69 |
70 | #### ***attr*** `description: str = NO_DEFAULT`
71 |
72 | #### ***attr*** `default: Any = None`
73 |
74 | ### ***class*** `FunctionCall(BaseModel)`
75 | ---
76 | #### ***func*** `hash self => int`
77 |
78 |
79 |
80 | 源代码 或 在GitHub上查看
81 |
82 | ```python
83 | def __hash__(self) -> int:
84 | return hash(self.name)
85 | ```
86 |
87 |
88 | ---
89 | #### ***func*** `data(self) -> dict[str, Any]`
90 |
91 | **说明**: 生成函数描述信息
92 |
93 |
94 | **返回**: dict[str, Any]: 函数描述信息 字典
95 |
96 |
97 |
98 | 源代码 或 在GitHub上查看
99 |
100 | ```python
101 | def data(self) -> dict[str, Any]:
102 | return {'type': 'function', 'function': {'name': self.name, 'description': self.description, 'parameters': {'type': 'object', 'properties': {k: v.data() for k, v in self.arguments.items()}}, 'required': [k for k, v in self.arguments.items() if v.default is None]}}
103 | ```
104 |
105 |
106 | #### ***attr*** `name: str = NO_DEFAULT`
107 |
108 | #### ***attr*** `description: str = NO_DEFAULT`
109 |
110 | #### ***attr*** `arguments: dict[str, FunctionCallArgument] = NO_DEFAULT`
111 |
112 | #### ***attr*** `function: ASYNC_FUNCTION_CALL_FUNC = NO_DEFAULT`
113 |
114 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/plugin/register.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: register
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.plugin.register`
5 |
6 | 此模块用于获取function call中函数定义信息以及注册函数
7 |
8 |
9 | ---
10 | ### ***func*** `async_wrapper(func: SYNC_FUNCTION_CALL_FUNC) -> ASYNC_FUNCTION_CALL_FUNC`
11 |
12 | **说明**: 将同步函数包装为异步函数,但是不会真正异步执行,仅用于统一调用及函数签名
13 |
14 |
15 | **参数**:
16 | > - func: 同步函数
17 |
18 | **返回**: ASYNC_FUNCTION_CALL: 异步函数
19 |
20 |
21 |
22 | 源代码 或 在GitHub上查看
23 |
24 | ```python
25 | def async_wrapper(func: SYNC_FUNCTION_CALL_FUNC) -> ASYNC_FUNCTION_CALL_FUNC:
26 |
27 | async def wrapper(*args, **kwargs) -> str:
28 | return func(*args, **kwargs)
29 | return wrapper
30 | ```
31 |
32 |
33 | ---
34 | ### ***func*** `function_call(*funcs: FUNCTION_CALL_FUNC) -> None`
35 |
36 |
37 | **参数**:
38 | > - func: 函数对象,要有完整的 Google Style Docstring
39 |
40 | **返回**: str: 函数定义信息
41 |
42 |
43 |
44 | 源代码 或 在GitHub上查看
45 |
46 | ```python
47 | def function_call(*funcs: FUNCTION_CALL_FUNC) -> None:
48 | for func in funcs:
49 | function_call = get_function_info(func)
50 | ```
51 |
52 |
53 | ---
54 | ### ***func*** `get_function_info(func: FUNCTION_CALL_FUNC)`
55 |
56 | **说明**: 获取函数信息
57 |
58 |
59 | **参数**:
60 | > - func: 函数对象
61 |
62 | **返回**: FunctionCall: 函数信息对象模型
63 |
64 |
65 |
66 | 源代码 或 在GitHub上查看
67 |
68 | ```python
69 | def get_function_info(func: FUNCTION_CALL_FUNC):
70 | name = func.__name__
71 | description = func.__doc__
72 | logger.info(f'注册函数: {name} {description}')
73 | ```
74 |
75 |
76 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/plugin/typing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: typing
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.plugin.typing`
5 |
6 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/plugin/utils.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: utils
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.plugin.utils`
5 |
6 | ---
7 | ### ***func*** `path_to_module_name(path: Path) -> str`
8 |
9 | **说明**: 转换路径为模块名
10 |
11 | **参数**:
12 | > - path: 路径a/b/c/d -> a.b.c.d
13 |
14 | **返回**: str: 模块名
15 |
16 |
17 |
18 | 源代码 或 在GitHub上查看
19 |
20 | ```python
21 | def path_to_module_name(path: Path) -> str:
22 | rel_path = path.resolve().relative_to(Path.cwd().resolve())
23 | if rel_path.stem == '__init__':
24 | return '.'.join(rel_path.parts[:-1])
25 | else:
26 | return '.'.join(rel_path.parts[:-1] + (rel_path.stem,))
27 | ```
28 |
29 |
30 | ---
31 | ### ***func*** `is_coroutine_callable(call: Callable[..., Any]) -> bool`
32 |
33 | **说明**: 判断是否为async def 函数
34 |
35 | **参数**:
36 | > - call: 可调用对象
37 |
38 | **返回**: bool: 是否为协程可调用对象
39 |
40 |
41 |
42 | 源代码 或 在GitHub上查看
43 |
44 | ```python
45 | def is_coroutine_callable(call: Callable[..., Any]) -> bool:
46 | if inspect.isroutine(call):
47 | return inspect.iscoroutinefunction(call)
48 | if inspect.isclass(call):
49 | return False
50 | func_ = getattr(call, '__call__', None)
51 | return inspect.iscoroutinefunction(func_)
52 | ```
53 |
54 |
55 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/plugins/marshoai_bangumi/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **模块** `nonebot_plugin_marshoai.plugins.marshoai_bangumi`
6 |
7 | ---
8 | ### ***async func*** `fetch_calendar()`
9 |
10 |
11 |
12 | 源代码 或 在GitHub上查看
13 |
14 | ```python
15 | async def fetch_calendar():
16 | url = 'https://api.bgm.tv/calendar'
17 | headers = {'User-Agent': 'LiteyukiStudio/nonebot-plugin-marshoai (https://github.com/LiteyukiStudio/nonebot-plugin-marshoai)'}
18 | async with httpx.AsyncClient() as client:
19 | response = await client.get(url, headers=headers)
20 | return response.json()
21 | ```
22 |
23 |
24 | ---
25 | `@function_call`
26 | ### ***async func*** `get_bangumi_news() -> str`
27 |
28 | **说明**: 获取今天的新番(动漫)列表,在调用之前,你需要知道今天星期几。
29 |
30 |
31 | **返回**: _type_: _description_
32 |
33 |
34 |
35 | 源代码 或 在GitHub上查看
36 |
37 | ```python
38 | @function_call
39 | async def get_bangumi_news() -> str:
40 | result = await fetch_calendar()
41 | info = ''
42 | try:
43 | for i in result:
44 | weekday = i['weekday']['cn']
45 | info += f'{weekday}:'
46 | items = i['items']
47 | for item in items:
48 | name = item['name_cn']
49 | info += f'《{name}》'
50 | info += '\n'
51 | return info
52 | except Exception as e:
53 | traceback.print_exc()
54 | return ''
55 | ```
56 |
57 |
58 | ---
59 | `@function_call`
60 | ### ***func*** `test_sync() -> str`
61 |
62 |
63 |
64 | 源代码 或 在GitHub上查看
65 |
66 | ```python
67 | @function_call
68 | def test_sync() -> str:
69 | return 'sync'
70 | ```
71 |
72 |
73 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/plugins/marshoai_basic/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **模块** `nonebot_plugin_marshoai.plugins.marshoai_basic`
6 |
7 | ---
8 | ### ***async func*** `get_weather(location: str)`
9 |
10 |
11 |
12 | 源代码 或 在GitHub上查看
13 |
14 | ```python
15 | async def get_weather(location: str):
16 | return f'{location}的温度是114514℃。'
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `get_current_env()`
22 |
23 |
24 |
25 | 源代码 或 在GitHub上查看
26 |
27 | ```python
28 | async def get_current_env():
29 | ver = os.popen('uname -a').read()
30 | return str(ver)
31 | ```
32 |
33 |
34 | ---
35 | ### ***async func*** `get_current_time()`
36 |
37 |
38 |
39 | 源代码 或 在GitHub上查看
40 |
41 | ```python
42 | async def get_current_time():
43 | current_time = DateTime.now().strftime('%Y.%m.%d %H:%M:%S')
44 | current_weekday = DateTime.now().weekday()
45 | weekdays = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
46 | current_weekday_name = weekdays[current_weekday]
47 | current_lunar_date = DateTime.now().to_lunar().date_hanzify()[5:]
48 | time_prompt = f'现在的时间是{current_time},{current_weekday_name},农历{current_lunar_date}。'
49 | return time_prompt
50 | ```
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_bangumi/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_bangumi`
6 |
7 | ---
8 | ### ***async func*** `fetch_calendar()`
9 |
10 |
11 |
12 | 源代码 或 在GitHub上查看
13 |
14 | ```python
15 | async def fetch_calendar():
16 | url = 'https://api.bgm.tv/calendar'
17 | headers = {'User-Agent': 'LiteyukiStudio/nonebot-plugin-marshoai (https://github.com/LiteyukiStudio/nonebot-plugin-marshoai)'}
18 | async with httpx.AsyncClient() as client:
19 | response = await client.get(url, headers=headers)
20 | return response.json()
21 | ```
22 |
23 |
24 | ---
25 | ### ***async func*** `get_bangumi_news()`
26 |
27 |
28 |
29 | 源代码 或 在GitHub上查看
30 |
31 | ```python
32 | async def get_bangumi_news():
33 | result = await fetch_calendar()
34 | info = ''
35 | try:
36 | for i in result:
37 | weekday = i['weekday']['cn']
38 | info += f'{weekday}:'
39 | items = i['items']
40 | for item in items:
41 | name = item['name_cn']
42 | info += f'《{name}》'
43 | info += '\n'
44 | return info
45 | except Exception as e:
46 | traceback.print_exc()
47 | return ''
48 | ```
49 |
50 |
51 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_basic/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_basic`
6 |
7 | ---
8 | ### ***async func*** `get_weather(location: str)`
9 |
10 |
11 |
12 | 源代码 或 在GitHub上查看
13 |
14 | ```python
15 | async def get_weather(location: str):
16 | return f'{location}的温度是114514℃。'
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `get_current_env()`
22 |
23 |
24 |
25 | 源代码 或 在GitHub上查看
26 |
27 | ```python
28 | async def get_current_env():
29 | ver = os.popen('uname -a').read()
30 | return str(ver)
31 | ```
32 |
33 |
34 | ---
35 | ### ***async func*** `get_current_time()`
36 |
37 |
38 |
39 | 源代码 或 在GitHub上查看
40 |
41 | ```python
42 | async def get_current_time():
43 | current_time = DateTime.now().strftime('%Y.%m.%d %H:%M:%S')
44 | current_weekday = DateTime.now().weekday()
45 | weekdays = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
46 | current_weekday_name = weekdays[current_weekday]
47 | current_lunar_date = DateTime.now().to_lunar().date_hanzify()[5:]
48 | time_prompt = f'现在的时间是{current_time},{current_weekday_name},农历{current_lunar_date}。'
49 | return time_prompt
50 | ```
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_megakits/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_megakits`
6 |
7 | ---
8 | ### ***async func*** `twisuki()`
9 |
10 |
11 |
12 | 源代码 或 在GitHub上查看
13 |
14 | ```python
15 | async def twisuki():
16 | return str(await mk_info.twisuki())
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `megakits()`
22 |
23 |
24 |
25 | 源代码 或 在GitHub上查看
26 |
27 | ```python
28 | async def megakits():
29 | return str(await mk_info.megakits())
30 | ```
31 |
32 |
33 | ---
34 | ### ***async func*** `random_turntable(upper: int, lower: int = 0)`
35 |
36 |
37 |
38 | 源代码 或 在GitHub上查看
39 |
40 | ```python
41 | async def random_turntable(upper: int, lower: int=0):
42 | return str(await mk_common.random_turntable(upper, lower))
43 | ```
44 |
45 |
46 | ---
47 | ### ***async func*** `number_calc(a: str, b: str, op: str)`
48 |
49 |
50 |
51 | 源代码 或 在GitHub上查看
52 |
53 | ```python
54 | async def number_calc(a: str, b: str, op: str):
55 | return str(await mk_common.number_calc(a, b, op))
56 | ```
57 |
58 |
59 | ---
60 | ### ***async func*** `morse_encrypt(msg: str)`
61 |
62 |
63 |
64 | 源代码 或 在GitHub上查看
65 |
66 | ```python
67 | async def morse_encrypt(msg: str):
68 | return str(await mk_morse_code.morse_encrypt(msg))
69 | ```
70 |
71 |
72 | ---
73 | ### ***async func*** `morse_decrypt(msg: str)`
74 |
75 |
76 |
77 | 源代码 或 在GitHub上查看
78 |
79 | ```python
80 | async def morse_decrypt(msg: str):
81 | return str(await mk_morse_code.morse_decrypt(msg))
82 | ```
83 |
84 |
85 | ---
86 | ### ***async func*** `nya_encode(msg: str)`
87 |
88 |
89 |
90 | 源代码 或 在GitHub上查看
91 |
92 | ```python
93 | async def nya_encode(msg: str):
94 | return str(await mk_nya_code.nya_encode(msg))
95 | ```
96 |
97 |
98 | ---
99 | ### ***async func*** `nya_decode(msg: str)`
100 |
101 |
102 |
103 | 源代码 或 在GitHub上查看
104 |
105 | ```python
106 | async def nya_decode(msg: str):
107 | return str(await mk_nya_code.nya_decode(msg))
108 | ```
109 |
110 |
111 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_megakits/mk_common.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mk_common
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_megakits.mk_common`
5 |
6 | ---
7 | ### ***async func*** `random_turntable(upper: int, lower: int)`
8 |
9 | **说明**: Random Turntable
10 |
11 |
12 | **参数**:
13 | > - upper (int): _description_
14 | > - lower (int): _description_
15 |
16 | **返回**: _type_: _description_
17 |
18 |
19 |
20 | 源代码 或 在GitHub上查看
21 |
22 | ```python
23 | async def random_turntable(upper: int, lower: int):
24 | return random.randint(lower, upper)
25 | ```
26 |
27 |
28 | ---
29 | ### ***async func*** `number_calc(a: str, b: str, op: str) -> str`
30 |
31 | **说明**: Number Calc
32 |
33 |
34 | **参数**:
35 | > - a (str): _description_
36 | > - b (str): _description_
37 | > - op (str): _description_
38 |
39 | **返回**: str: _description_
40 |
41 |
42 |
43 | 源代码 或 在GitHub上查看
44 |
45 | ```python
46 | async def number_calc(a: str, b: str, op: str) -> str:
47 | a, b = (float(a), float(b))
48 | match op:
49 | case '+':
50 | return str(a + b)
51 | case '-':
52 | return str(a - b)
53 | case '*':
54 | return str(a * b)
55 | case '/':
56 | return str(a / b)
57 | case '**':
58 | return str(a ** b)
59 | case '%':
60 | return str(a % b)
61 | case _:
62 | return '未知运算符'
63 | ```
64 |
65 |
66 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_megakits/mk_info.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mk_info
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_megakits.mk_info`
5 |
6 | ---
7 | ### ***async func*** `twisuki()`
8 |
9 |
10 |
11 | 源代码 或 在GitHub上查看
12 |
13 | ```python
14 | async def twisuki():
15 | return 'Twiuski(苏阳)是megakits插件作者, Github : "https://github.com/Twisuki"'
16 | ```
17 |
18 |
19 | ---
20 | ### ***async func*** `megakits()`
21 |
22 |
23 |
24 | 源代码 或 在GitHub上查看
25 |
26 | ```python
27 | async def megakits():
28 | return 'MegaKits插件是一个功能混杂的MarshoAI插件, 由Twisuki(Github : "https://github.com/Twisuki")开发, 插件仓库 : "https://github.com/LiteyukiStudio/marsho-toolsets/tree/main/Twisuki/marshoai-megakits"'
29 | ```
30 |
31 |
32 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_megakits/mk_morse_code.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mk_morse_code
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_megakits.mk_morse_code`
5 |
6 | ---
7 | ### ***async func*** `morse_encrypt(msg: str)`
8 |
9 |
10 |
11 | 源代码 或 在GitHub上查看
12 |
13 | ```python
14 | async def morse_encrypt(msg: str):
15 | result = ''
16 | msg = msg.upper()
17 | for char in msg:
18 | if char in MorseEncode:
19 | result += MorseEncode[char]
20 | else:
21 | result += '..--..'
22 | result += ' '
23 | return result
24 | ```
25 |
26 |
27 | ---
28 | ### ***async func*** `morse_decrypt(msg: str)`
29 |
30 |
31 |
32 | 源代码 或 在GitHub上查看
33 |
34 | ```python
35 | async def morse_decrypt(msg: str):
36 | result = ''
37 | msg_arr = msg.split()
38 | for char in msg_arr:
39 | if char in MorseDecode:
40 | result += MorseDecode[char]
41 | else:
42 | result += '?'
43 | return result
44 | ```
45 |
46 |
47 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_megakits/mk_nya_code.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mk_nya_code
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_megakits.mk_nya_code`
5 |
6 | ---
7 | ### ***async func*** `nya_encode(msg: str)`
8 |
9 |
10 |
11 | 源代码 或 在GitHub上查看
12 |
13 | ```python
14 | async def nya_encode(msg: str):
15 | msg_b64str = base64.b64encode(msg.encode()).decode().replace('=', '')
16 | msg_nyastr = ''.join((NyaCodeEncode[base64_char] for base64_char in msg_b64str))
17 | result = ''
18 | for char in msg_nyastr:
19 | if char == '呜' and random.random() < 0.5:
20 | result += '!'
21 | if random.random() < 0.25:
22 | result += random.choice(NyaCodeSpecialCharset) + char
23 | else:
24 | result += char
25 | return result
26 | ```
27 |
28 |
29 | ---
30 | ### ***async func*** `nya_decode(msg: str)`
31 |
32 |
33 |
34 | 源代码 或 在GitHub上查看
35 |
36 | ```python
37 | async def nya_decode(msg: str):
38 | msg = msg.replace('唔', '').replace('!', '').replace('.', '')
39 | msg_nyastr = []
40 | i = 0
41 | if len(msg) % 3 != 0:
42 | return '这句话不是正确的猫语'
43 | while i < len(msg):
44 | nyachar = msg[i:i + 3]
45 | try:
46 | if all((char in NyaCodeCharset for char in nyachar)):
47 | msg_nyastr.append(nyachar)
48 | i += 3
49 | except Exception:
50 | return '这句话不是正确的猫语'
51 | msg_b64str = ''.join((NyaCodeDecode[nya_char] for nya_char in msg_nyastr))
52 | msg_b64str += '=' * (4 - len(msg_b64str) % 4)
53 | try:
54 | result = base64.b64decode(msg_b64str.encode()).decode()
55 | except Exception:
56 | return '翻译失败'
57 | return result
58 | ```
59 |
60 |
61 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_meogirl/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_meogirl`
6 |
7 | ---
8 | ### ***async func*** `meogirl()`
9 |
10 |
11 |
12 | 源代码 或 在GitHub上查看
13 |
14 | ```python
15 | async def meogirl():
16 | return mg_info.meogirl()
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `search(msg: str, num: int = 3)`
22 |
23 |
24 |
25 | 源代码 或 在GitHub上查看
26 |
27 | ```python
28 | async def search(msg: str, num: int=3):
29 | return str(await mg_search.search(msg, num))
30 | ```
31 |
32 |
33 | ---
34 | ### ***async func*** `introduce(msg: str)`
35 |
36 |
37 |
38 | 源代码 或 在GitHub上查看
39 |
40 | ```python
41 | async def introduce(msg: str):
42 | return str(await mg_introduce.introduce(msg))
43 | ```
44 |
45 |
46 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_meogirl/mg_info.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mg_info
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_meogirl.mg_info`
5 |
6 | ---
7 | ### ***func*** `meogirl()`
8 |
9 |
10 |
11 | 源代码 或 在GitHub上查看
12 |
13 | ```python
14 | def meogirl():
15 | return 'Meogirl指的是"萌娘百科"(https://zh.moegirl.org.cn/ , 简称"萌百"), 是一个"万物皆可萌的百科全书!"; 同时, MarshoTools也配有"Meogirl"插件, 可调用萌百的api'
16 | ```
17 |
18 |
19 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_meogirl/mg_introduce.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mg_introduce
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_meogirl.mg_introduce`
5 |
6 | ---
7 | ### ***async func*** `get_async_data(url)`
8 |
9 |
10 |
11 | 源代码 或 在GitHub上查看
12 |
13 | ```python
14 | async def get_async_data(url):
15 | async with httpx.AsyncClient(timeout=None) as client:
16 | return await client.get(url, headers=headers)
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `introduce(msg: str)`
22 |
23 |
24 |
25 | 源代码 或 在GitHub上查看
26 |
27 | ```python
28 | async def introduce(msg: str):
29 | logger.info(f'介绍 : "{msg}" ...')
30 | result = ''
31 | url = 'https://mzh.moegirl.org.cn/' + urllib.parse.quote_plus(msg)
32 | response = await get_async_data(url)
33 | logger.success(f'连接"{url}"完成, 状态码 : {response.status_code}')
34 | soup = BeautifulSoup(response.text, 'html.parser')
35 | if response.status_code == 200:
36 | '\n 萌娘百科页面结构\n div#mw-content-text\n └── div#404search # 空白页面出现\n └── div.mw-parser-output # 正常页面\n └── div, p, table ... # 大量的解释项\n '
37 | result += msg + '\n'
38 | img = soup.find('img', class_='infobox-image')
39 | if img:
40 | result += f' \n'
41 | div = soup.find('div', class_='mw-parser-output')
42 | if div:
43 | p_tags = div.find_all('p')
44 | num = 0
45 | for p_tag in p_tags:
46 | p = str(p_tag)
47 | p = re.sub('|', '', p, flags=re.DOTALL)
48 | p = re.sub('<.*?>', '', p, flags=re.DOTALL)
49 | p = re.sub('\\[.*?]', '', p, flags=re.DOTALL)
50 | if p != '':
51 | result += str(p)
52 | num += 1
53 | if num >= 20:
54 | break
55 | return result
56 | elif response.status_code == 404:
57 | logger.info(f'未找到"{msg}", 进行搜索')
58 | from . import mg_search
59 | context = await mg_search.search(msg, 1)
60 | keyword = re.search('.*?\\n', context, flags=re.DOTALL).group()[:-1]
61 | logger.success(f'搜索完成, 打开"{keyword}"')
62 | return await introduce(keyword)
63 | elif response.status_code == 301:
64 | return f'未找到{msg}'
65 | else:
66 | logger.error(f'网络错误, 状态码 : {response.status_code}')
67 | return f'网络错误, 状态码 : {response.status_code}'
68 | ```
69 |
70 |
71 | ### var `keyword`
72 |
73 | - **说明**: type: ignore
74 |
75 | - **默认值**: `re.search('.*?\\n', context, flags=re.DOTALL).group()[:-1]`
76 |
77 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools/marshoai_meogirl/mg_search.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: mg_search
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.tools.marshoai_meogirl.mg_search`
5 |
6 | ---
7 | ### ***async func*** `get_async_data(url)`
8 |
9 |
10 |
11 | 源代码 或 在GitHub上查看
12 |
13 | ```python
14 | async def get_async_data(url):
15 | async with httpx.AsyncClient(timeout=None) as client:
16 | return await client.get(url, headers=headers)
17 | ```
18 |
19 |
20 | ---
21 | ### ***async func*** `search(msg: str, num: int)`
22 |
23 |
24 |
25 | 源代码 或 在GitHub上查看
26 |
27 | ```python
28 | async def search(msg: str, num: int):
29 | logger.info(f'搜索 : "{msg}" ...')
30 | result = ''
31 | url = 'https://mzh.moegirl.org.cn/index.php?search=' + urllib.parse.quote_plus(msg)
32 | response = await get_async_data(url)
33 | logger.success(f'连接"{url}"完成, 状态码 : {response.status_code}')
34 | if response.status_code == 200:
35 | '\n 萌娘百科搜索页面结构\n div.searchresults\n └── p ...\n └── ul.mw-search-results # 若无, 证明无搜索结果\n └── li # 一个搜索结果\n └── div.mw-search-result-heading > a # 标题\n └── div.mw-searchresult # 内容\n └── div.mw-search-result-data\n └── li ...\n └── li ...\n '
36 | soup = BeautifulSoup(response.text, 'html.parser')
37 | ul_tag = soup.find('ul', class_='mw-search-results')
38 | if ul_tag:
39 | li_tags = ul_tag.find_all('li')
40 | for li_tag in li_tags:
41 | div_heading = li_tag.find('div', class_='mw-search-result-heading')
42 | if div_heading:
43 | a_tag = div_heading.find('a')
44 | result += a_tag['title'] + '\n'
45 | logger.info(f'搜索到 : "{a_tag['title']}"')
46 | div_result = li_tag.find('div', class_='searchresult')
47 | if div_result:
48 | content = str(div_result).replace('', '').replace('
', '')
49 | content = content.replace('', '').replace(' ', '')
50 | result += content + '\n'
51 | num -= 1
52 | if num == 0:
53 | break
54 | return result
55 | else:
56 | logger.info('无结果')
57 | return '无结果'
58 | elif response.status_code == 302:
59 | logger.info(f'"{msg}"已被重定向至"{response.headers.get('location')}"')
60 | from . import mg_introduce
61 | return await mg_introduce.introduce(msg)
62 | else:
63 | logger.error(f'网络错误, 状态码 : {response.status_code}')
64 | return f'网络错误, 状态码 : {response.status_code}'
65 | ```
66 |
67 |
68 | ### var `soup`
69 |
70 | - **说明**:
71 |
72 | - **默认值**: `BeautifulSoup(response.text, 'html.parser')`
73 |
74 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/tools_wip/marshoai_memory/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index
3 | collapsed: true
4 | ---
5 | # **模块** `nonebot_plugin_marshoai.tools_wip.marshoai_memory`
6 |
7 | ---
8 | ### ***async func*** `write_memory(memory: str)`
9 |
10 |
11 |
12 | 源代码 或 在GitHub上查看
13 |
14 | ```python
15 | async def write_memory(memory: str):
16 | return ''
17 | ```
18 |
19 |
20 |
--------------------------------------------------------------------------------
/docs/zh/dev/api/util_hunyuan.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: util_hunyuan
3 | ---
4 | # **模块** `nonebot_plugin_marshoai.util_hunyuan`
5 |
6 | ---
7 | ### ***func*** `generate_image(prompt: str)`
8 |
9 |
10 |
11 | 源代码 或 在GitHub上查看
12 |
13 | ```python
14 | def generate_image(prompt: str):
15 | cred = credential.Credential(config.marshoai_tencent_secretid, config.marshoai_tencent_secretkey)
16 | httpProfile = HttpProfile()
17 | httpProfile.endpoint = 'hunyuan.tencentcloudapi.com'
18 | clientProfile = ClientProfile()
19 | clientProfile.httpProfile = httpProfile
20 | client = hunyuan_client.HunyuanClient(cred, 'ap-guangzhou', clientProfile)
21 | req = models.TextToImageLiteRequest()
22 | params = {'Prompt': prompt, 'RspImgType': 'url', 'Resolution': '1080:1920'}
23 | req.from_json_string(json.dumps(params))
24 | resp = client.TextToImageLite(req)
25 | return resp.to_json_string()
26 | ```
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/zh/dev/extension.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 2
3 | ---
4 |
5 | # 扩展开发
6 |
7 | ## 说明
8 |
9 | 扩展分为两类,一类为插件,一类为工具。
10 |
11 | - 插件
12 | - 工具(由于开发的不便利性,已经停止维护,未来可能会放弃支持,如有需求请看README中的内容,我们不推荐再使用此功能)
13 |
14 | **`v1.0.0`之前的版本不支持小棉插件。**
15 |
16 | ## 插件
17 |
18 | 为什么要有插件呢,插件可以编写function call供AI调用,语言大模型本身不具备一些信息获取能力,可以使用该功能进行扩展。
19 |
20 | 可以借助这个功能实现获取天气、获取股票信息、获取新闻等等,然后将这些信息传递给AI,AI可以根据这些信息进行正确的整合与回答。
21 |
22 | 插件很简单,一个Python文件,一个Python包都可以是插件,插件组成也很简单:
23 |
24 | - 元数据:包含插件的信息,如名称、版本、作者等
25 | - function call:供AI调用的函数
26 |
27 | :::tip
28 | 如果你编写过NoneBot插件,那么你会发现插件的编写方式和NoneBot插件的编写方式几乎一样。
29 | :::
30 |
31 | ## 编写第一个插件
32 |
33 | 我们编写一个用于查询天气的插件,首先创建`weather.py`文件,然后编写如下内容:
34 |
35 | ```python
36 | from nonebot_plugin_marshoai.plugin import PluginMetadata, on_function_call, String
37 |
38 | __marsho_meta__ = PluginMetadata(
39 | name="天气查询",
40 | author="MarshoAI",
41 | description="一个简单的查询天气的插件"
42 | )
43 |
44 | @on_function_call(description="可以用于查询天气").params(
45 | location=String(description="地点")
46 | )
47 | async def weather(location: str) -> str:
48 | # 这里可以调用天气API查询天气,这里只是一个简单的示例
49 | return f"{location}的天气是晴天, 温度是25°C"
50 | ```
51 |
52 | 然后将`weather.py`文件放到`$LOCAL_STORE/plugins`目录下,重启机器人实例即可。
53 |
54 | 接下来AI会根据你的发送的提示词和`description`来决定调用函数,如`查询北京的天气`,`告诉我东京明天会下雨吗`,AI会调用`weather`函数并传递`location`参数为`北京`。
55 |
56 | ## 插件元数据
57 |
58 | 元数据是一个名为`__marsho_meta__`的全局变量,它是一个`PluginMetadata`对象,至于包含什么熟悉可以查看`PluginMetadata`类的定义或IDE提示,这里不再赘述。
59 |
60 | ## 函数调用参数
61 |
62 | `on_function_call`装饰器用于标记一个函数为function call,`description`参数用于描述这个函数的作用,`params`方法用于定义函数的参数,`String`、`Integer`等是OpenAI API接受的参数的类型,`description`是参数的描述。这些都是给AI看的,AI会根据这些信息来调用函数。
63 |
64 | :::warning
65 | 参数名不得为`placeholder`。此参数名是Marsho内部保留的用于保证兼容性的占位参数。
66 | :::
67 |
68 | ```python
69 | @on_function_call(description="可以用于算命").params(
70 | name=String(description="姓名"),
71 | age=Integer(description="年龄")
72 | )
73 | def fortune_telling(name: str, age: int) -> str:
74 | return f"{name},你的年龄是{age}岁"
75 | ```
76 |
77 | ## 权限及规则
78 |
79 | 插件的调用权限和规则与NoneBot插件一样,使用Caller的permission和rule函数来设置。
80 |
81 | ```python
82 | @on_function_call(description="在设备上执行命令").params(
83 | command=String(description="命令内容")
84 | ).permission(SUPERUSER)
85 | def execute_command(command: str) -> str:
86 | return eval(command)
87 | ```
88 |
89 | ## 依赖注入
90 |
91 | function call支持NoneBot2原生的会话上下文依赖注入
92 |
93 | - Event 及其子类实例
94 | - Bot 及其子类实例
95 | - Matcher 及其子类实例
96 | - T_State
97 |
98 | ```python
99 | @on_function_call(description="获取个人信息")
100 | async def get_user_info(e: Event) -> str:
101 | return f"用户ID: {e.user_id}"
102 |
103 | @on_function_call(description="获取机器人信息")
104 | async def get_bot_info(b: Bot) -> str:
105 | return f"机器人ID: {b.self_id}"
106 | ```
107 |
108 | ## 兼容性
109 |
110 | 插件可以编写NoneBot或者轻雪插件的内容,可作为NoneBot插件或者轻雪插件单独发布
111 |
112 | 不过,所编写功能仅会在对应的实例上加载对应的功能,如果通过marshoai加载混合插件,那么插件中NoneBot的功能将会依附于marshoai插件,
113 | 若通过NoneBot加载包含marshoai功能的NoneBot插件,那么marshoai功能将会依附于NoneBot插件。
114 |
115 | **我们建议**:若插件中包含了NoneBot功能,仍然使用marshoai进行加载,这样更符合逻辑。若你想发布为NoneBot插件,请注意`require("nonebot_plugin_marshoai")`,这是老生常谈了。
116 |
117 | :::tip
118 | 本质上都是动态导入和注册声明加载,运行时把这些东西塞到一起
119 | :::
120 |
121 | ## 插件热重载
122 |
123 | 插件热重载是一个实验性功能,可以在不重启机器人的情况下更新插件
124 |
125 | :::warning
126 | 框架无法完全消除之前插件带来的副作用,当开发测试中效果不符合预期时请重启机器人实例
127 |
128 | 为了更好地让热重载功能正常工作,尽可能使用函数式的编程风格,以减少副作用的影响
129 | :::
130 |
131 | 将`MARSHOAI_DEVMODE`环境变量设置为`true`,然后在配置的插件目录`MARSHOAI_PLUGIN_DIRS`下开发插件,当插件发生变化时,机器人会自动变动的插件。
132 |
133 | ## AIGC 自举
134 |
135 | :::warning
136 | 该功能为实验性功能,请注意甄别AI的行为,不要让AI执行危险的操作。
137 | :::
138 | function call为AI赋能,实现了文件io操作,AI可以调用function call来读取文档然后给自己编写代码,实现自举。
139 |
140 | ## 其他
141 |
142 | - function call支持同步和异步函数
143 | - 本文是一个引导,要查看具体功能请查阅[插件 API 文档](./api/plugin/index)
144 |
--------------------------------------------------------------------------------
/docs/zh/dev/index.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/docs/zh/dev/index.md
--------------------------------------------------------------------------------
/docs/zh/dev/project.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 1
3 | ---
4 |
5 | # 项目开发
6 |
7 | ## 先决条件
8 |
9 | - `Git`
10 | - `Python3.10+`
11 |
12 | ## 准备工作
13 |
14 | - 克隆仓库
15 |
16 | ```bash
17 | git clone https://github.com/LiteyukiStudio/nonebot-plugin-marshoai.git # 克隆仓库
18 | cd nonebot-plugin-marshoai # 切换目录
19 | ```
20 |
21 | - 安装依赖
22 | 项目使用pdm作为依赖管理
23 |
24 | ```bash
25 | python3 -m venv venv # 或创建你自己的环境
26 | source venv/bin/activate # 激活虚拟环境
27 | pip install pdm # 安装依赖管理
28 | pdm install # 安装依赖
29 | pre-commit install # 安装 pre-commit 钩子
30 | ```
31 |
32 | ## 代码规范
33 |
34 | 主仓库需要遵循以下代码规范
35 |
36 | - [`PEP8`](https://peps.python.org/pep-0008/) 代码风格
37 | - [`Black`](https://black.readthedocs.io/en/stable/index.html) 代码格式化
38 | - [`mypy`](https://www.mypy-lang.org/) 静态类型检查
39 | - [`Google Docstring`](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) 文档规范
40 |
41 | 可以在编辑器中安装相应的插件进行辅助
42 |
43 | ## 其他
44 |
45 | 感谢以下的贡献者们:
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/docs/zh/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | # https://vitepress.dev/reference/default-theme-home-page
3 | layout: home
4 |
5 | hero:
6 | name: "小棉智能"
7 | text: "猫娘机器人"
8 | tagline: 可爱,智能且可扩展的AI服务插件
9 | actions:
10 | - theme: brand
11 | text: 开始使用
12 | link: /start/use/
13 | - theme: alt
14 | text: 开发及扩展
15 | link: /dev/extension/
16 | image:
17 | light: /marsho-full.svg
18 | dark: /marsho-full.svg
19 | alt: Marsho Logo
20 |
21 | features:
22 | - title: 强大驱动
23 | icon: 🚀
24 | details: 基于 NoneBot2,可快速安装在现有的 NoneBot2 或 轻雪 实例上
25 |
26 | - title: 接口规范
27 | icon: 💻
28 | details: 使用任何遵循 OpenAI 的接口均可与小棉智能进行交互
29 |
30 | - title: 易于扩展
31 | icon: 🧩
32 | details: 使用蟒蛇编写工具及插件,实现函数调用,可轻松扩展小棉智能的功能
33 |
34 | - title: 自举
35 | icon: 🤖
36 | details: 使用AI为机器人自动编写代码,实现自我学习及自我优化
37 | ---
38 |
39 |
--------------------------------------------------------------------------------
/docs/zh/start/index.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/docs/zh/start/index.md
--------------------------------------------------------------------------------
/docs/zh/start/use.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 使用
3 | ---
4 |
5 | # 安装
6 | - 请查看 [安装文档](./install.md)
7 |
8 | # 使用
9 | ### API 部署
10 |
11 | 本插件推荐使用 [one-api](https://github.com/songquanpeng/one-api) 作为中转以调用 LLM。
12 | ### 配置调整
13 |
14 | 本插件理论上可兼容大部分可通过 OpenAI 兼容 API 调用的 LLM,部分模型可能需要调整插件配置。
15 |
16 | 例如:
17 | - 对于不支持 Function Call 的模型(Cohere Command R,DeepSeek-R1等):
18 | ```dotenv
19 | MARSHOAI_ENABLE_PLUGINS=false
20 | MARSHOAI_ENABLE_TOOLS=false
21 | ```
22 | - 对于支持图片处理的模型(hunyuan-vision等):
23 | ```dotenv
24 | MARSHOAI_ADDITIONAL_IMAGE_MODELS=["hunyuan-vision"]
25 | ```
26 | - 对于本地部署的 DeepSeek-R1 模型:
27 | :::tip
28 | MarshoAI 默认使用 System Prompt 进行人设等的调整,但 DeepSeek-R1 官方推荐**避免**使用 System Prompt(但可以正常使用)。
29 | 为解决此问题,引入了 System-As-User Prompt 配置,可将 System Prompt 作为用户传入的消息。
30 | :::
31 | ```dotenv
32 | MARSHOAI_ENABLE_SYSASUSER_PROMPT=true
33 | MARSHOAI_SYSASUSER_PROMPT="好的喵~" # 假装是模型收到消息后的回答
34 | ```
35 | ### 使用 DeepSeek-R1 模型
36 | MarshoAI 兼容 DeepSeek-R1 模型,你可通过以下步骤来使用:
37 | 1. 获取 API Key
38 | 前往[此处](https://platform.deepseek.com/api_keys)获取 API Key。
39 | 2. 配置插件
40 | ```dotenv
41 | MARSHOAI_TOKEN="<你的 API Key>"
42 | MARSHOAI_AZURE_ENDPOINT="https://api.deepseek.com"
43 | MARSHOAI_DEFAULT_MODEL="deepseek-reasoner"
44 | MARSHOAI_ENABLE_PLUGINS=false
45 | ```
46 | 你可修改 `MARSHOAI_DEFAULT_MODEL` 为 其它模型名来调用其它 DeepSeek 模型。
47 | :::tip
48 | 如果使用 one-api 作为中转,你可将 `MARSHOAI_AZURE_ENDPOINT` 设置为 one-api 的地址,将 `MARSHOAI_TOKEN` 设为 one-api 配置的令牌,在 one-api 中添加 DeepSeek 渠道。
49 | 同样可使用其它提供商(例如 [SiliconFlow](https://siliconflow.cn/))提供的 DeepSeek 等模型。
50 | :::
51 |
52 | ### 使用 vLLM 部署本地模型
53 |
54 | 你可使用 vLLM 部署一个本地 LLM,并使用 OpenAI 兼容 API 调用。
55 | 本文档以 Qwen2.5-7B-Instruct-GPTQ-Int4 模型及 [Muice-Chatbot](https://github.com/Moemu/Muice-Chatbot) 提供的 LoRA 微调模型为例,并假设你的系统及硬件可运行 vLLM。
56 | :::warning
57 | vLLM 仅支持 Linux 系统。
58 | :::
59 | 1. 安装 vLLM
60 | ```bash
61 | pip install vllm
62 | ```
63 | 2. 下载 Muice-Chatbot 提供的 LoRA 微调模型
64 | 前往 Muice-Chatbot 的 [Releases](https://github.com/Moemu/Muice-Chatbot/releases) 下载模型文件。此处以`2.7.1`版本的模型为例。
65 | ```bash
66 | wget https://github.com/Moemu/Muice-Chatbot/releases/download/1.4/Muice-2.7.1-Qwen2.5-7B-Instruct-GPTQ-Int4-8e-4.7z
67 | ```
68 | 3. 解压模型文件
69 | ```bash
70 | 7z x Muice-2.7.1-Qwen2.5-7B-Instruct-GPTQ-Int4-8e-4.7z -oMuice-2.7.1-Qwen2.5-7B-Instruct-GPTQ-Int4-8e-4
71 | ```
72 | 4. 启动 vLLM
73 | ```bash
74 | vllm serve Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4 \
75 | --enable-lora \
76 | --lora-modules '{"name": "muice-lora", "path": "/root/Muice-2.7.1-Qwen2.5-7B-Instruct-GPTQ-Int4-8e-4", "base_model_name": "Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4"}' \
77 | --port 6006
78 | ```
79 | 此示例命令将在 `6006` 端口启动 vLLM,并加载 Muice-Chatbot 提供的 LoRA 微调模型,该模型位于 `/root/Muice-2.7.1-Qwen2.5-7B-Instruct-GPTQ-Int4-8e-4` 目录下。
80 | 5. 配置插件
81 | ```dotenv
82 | MARSHOAI_AZURE_ENDPOINT="http://127.0.0.1:6006/v1"
83 | MARSHOAI_FIX_TOOLCALLS=false
84 | MARSHOAI_ENABLE_PLUGINS=false
85 | MARSHOAI_DEFAULT_MODEL="muice-lora"
86 | MARSHOAI_PROMPT="现在开始你是一个名为的“沐雪”的AI女孩子,开发者是“沐沐”并住在(沐沐)的机箱里。现在正在努力成为一个合格的VTuber(虚拟主播)并尝试和观众打成一片,以下是你的设定:样貌:有着一头粉白色的长发和一双明亮的大眼睛,喜欢穿日系JK或者是Lolita;喜欢的颜色:浅粉色;性格特征:纯真无邪是沐雪最基本的性格特征之一。即使面对复杂的情境,她也总能保持善良、天真之感。而且,她喜欢倾听别人倾述自己生活中发生的各种事情,在别人需要的时候,能够及时地安慰别人;语言风格:沐雪说话轻快愉悦,充满同情心,富有人情味,有时候会用俏皮话调侃自己和他人"
87 | ```
88 | (可选) 修改调用方式
89 | ```dotenv
90 | MARSHOAI_DEFAULT_NAME="muice"
91 | MARSHOAI_ALIASES=["沐雪"]
92 | ```
93 | 6. 测试聊天
94 | ```
95 | > muice 你是谁
96 | 我是沐雪,我的使命是传播爱与和平。
97 | ```
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | """该入口文件仅在nb run无法正常工作时使用"""
2 |
3 | import nonebot
4 | from nonebot import get_driver
5 | from nonebot.adapters.onebot.v11 import Adapter
6 | from nonebot.plugin import load_plugin
7 |
8 | nonebot.init()
9 | load_plugin("nonebot_plugin_marshoai")
10 |
11 | driver = get_driver()
12 | driver.register_adapter(Adapter)
13 |
14 | if __name__ == "__main__":
15 | nonebot.run()
16 |
17 | # 这是猫娘写的代码
18 |
19 | # 这是猫娘写的代码
20 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | MIT License
3 |
4 | Copyright (c) 2025 Asankilp & LiteyukiStudio
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 | """
24 |
25 | from nonebot.plugin import require
26 |
27 | require("nonebot_plugin_alconna")
28 | require("nonebot_plugin_localstore")
29 | require("nonebot_plugin_argot")
30 |
31 | import nonebot_plugin_localstore as store # type: ignore
32 | from nonebot import get_driver, logger # type: ignore
33 |
34 | from .config import config
35 | from .dev import *
36 | from .marsho import *
37 | from .metadata import metadata
38 |
39 | # from .hunyuan import *
40 |
41 |
42 | __author__ = "Asankilp"
43 | __plugin_meta__ = metadata
44 |
45 | driver = get_driver()
46 |
47 |
48 | @driver.on_startup
49 | async def _():
50 | logger.info("MarshoAI 已经加载~🐾")
51 | logger.info(f"Marsho 的插件数据存储于 : {str(store.get_plugin_data_dir())} 哦~🐾")
52 | if config.marshoai_token == "":
53 | logger.warning("token 未配置。可能无法进行聊天。")
54 | else:
55 | logger.info("token 已配置~!🐾")
56 | logger.info("マルショは、高性能ですから!")
57 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/_types.py:
--------------------------------------------------------------------------------
1 | # source: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-inference/azure/ai/inference/models/_models.py
2 | from typing import Any, Literal, Mapping, Optional, overload
3 |
4 | from azure.ai.inference._model_base import rest_discriminator, rest_field
5 | from azure.ai.inference.models import ChatRequestMessage
6 |
7 |
8 | class DeveloperMessage(ChatRequestMessage, discriminator="developer"):
9 |
10 | role: Literal["developer"] = rest_discriminator(name="role") # type: ignore
11 | """The chat role associated with this message, which is always 'developer' for developer messages.
12 | Required."""
13 | content: Optional[str] = rest_field()
14 | """The content of the message."""
15 |
16 | @overload
17 | def __init__(
18 | self,
19 | *,
20 | content: Optional[str] = None,
21 | ): ...
22 |
23 | @overload
24 | def __init__(self, mapping: Mapping[str, Any]):
25 | """
26 | :param mapping: raw JSON to initialize the model.
27 | :type mapping: Mapping[str, Any]
28 | """
29 |
30 | def __init__(
31 | self, *args: Any, **kwargs: Any
32 | ) -> None: # pylint: disable=useless-super-delegation
33 | super().__init__(*args, role="developer", **kwargs)
34 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/cache/decos.py:
--------------------------------------------------------------------------------
1 | from ..models import Cache
2 |
3 | cache = Cache()
4 |
5 |
6 | def from_cache(key):
7 | """
8 | 当缓存中有数据时,直接返回缓存中的数据,否则执行函数并将结果存入缓存
9 | """
10 |
11 | def decorator(func):
12 | async def wrapper(*args, **kwargs):
13 | cached = cache.get(key)
14 | if cached:
15 | return cached
16 | else:
17 | result = await func(*args, **kwargs)
18 | cache.set(key, result)
19 | return result
20 |
21 | return wrapper
22 |
23 | return decorator
24 |
25 |
26 | def update_to_cache(key):
27 | """
28 | 执行函数并将结果存入缓存
29 | """
30 |
31 | def decorator(func):
32 | async def wrapper(*args, **kwargs):
33 | result = await func(*args, **kwargs)
34 | cache.set(key, result)
35 | return result
36 |
37 | return wrapper
38 |
39 | return decorator
40 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/constants.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from .config import config
4 |
5 | NAME: str = config.marshoai_default_name
6 | USAGE: str = f"""用法:
7 | {NAME} <聊天内容> : 与 Marsho 进行对话。当模型为 GPT-4o(-mini) 等时,可以带上图片进行对话。
8 | nickname [昵称] : 为自己设定昵称,设置昵称后,Marsho 会根据你的昵称进行回答。使用'nickname reset'命令可清除自己设定的昵称。
9 | {NAME}.reset : 重置当前会话的上下文。
10 | 超级用户命令(均需要加上命令前缀使用):
11 | changemodel <模型名> : 切换全局 AI 模型。
12 | contexts : 返回当前会话的上下文列表。 ※当上下文包含图片时,不要使用此命令。
13 | praises : 返回夸赞名单的提示词。
14 | usermsg <消息> : 往当前会话添加用户消息(UserMessage)。
15 | assistantmsg <消息> : 往当前会话添加助手消息(AssistantMessage)。
16 | savecontext <文件名> : 保存当前会话的上下文至插件数据目录下的contexts/<文件名>.json里。
17 | loadcontext <文件名> : 从插件数据目录下的contexts/<文件名>.json里读取上下文并覆盖到当前会话。
18 | refresh_data : 从文件刷新已加载的昵称与夸赞名单。
19 | ※本AI的回答"按原样"提供,不提供任何担保。AI也会犯错,请仔细甄别回答的准确性。"""
20 |
21 | SUPPORT_IMAGE_MODELS: list = [
22 | "gpt-4o",
23 | "gpt-4o-mini",
24 | "phi-3.5-vision-instruct",
25 | "llama-3.2-90b-vision-instruct",
26 | "llama-3.2-11b-vision-instruct",
27 | "gemini-2.0-flash-exp",
28 | ]
29 | OPENAI_NEW_MODELS: list = [
30 | "o1",
31 | "o1-preview",
32 | "o1-mini",
33 | "o3",
34 | "o3-mini",
35 | "o3-mini-large",
36 | ]
37 | INTRODUCTION: str = f"""MarshoAI-NoneBot by LiteyukiStudio
38 | 你好喵~我是一只可爱的猫娘AI,名叫小棉~🐾!
39 | 我的主页在这里哦~↓↓↓
40 | https://marsho.liteyuki.org
41 |
42 | ※ 使用 「{config.marshoai_default_name}.status」命令获取状态信息。
43 | ※ 使用「{config.marshoai_default_name}.help」命令获取使用说明。"""
44 |
45 |
46 | # 正则匹配代码块
47 | CODE_BLOCK_PATTERN = re.compile(r"```(.*?)```|`(.*?)`", re.DOTALL)
48 |
49 | # 通用正则匹配(LaTeX和Markdown图片)
50 | IMG_LATEX_PATTERN = re.compile(
51 | (
52 | r"(!\[[^\]]*\]\([^()]*\))|(\\begin\{equation\}.*?\\end\{equation\}|\$.*?\$|\$\$.*?\$\$|\\\[.*?\\\]|\\\(.*?\\\))"
53 | if config.marshoai_single_latex_parse
54 | else r"(!\[[^\]]*\]\([^()]*\))|(\\begin\{equation\}.*?\\end\{equation\}|\$\$.*?\$\$|\\\[.*?\\\])"
55 | ),
56 | re.DOTALL,
57 | )
58 |
59 | # 正则匹配完整图片标签字段
60 | IMG_TAG_PATTERN = re.compile(
61 | r"!\[[^\]]*\]\([^()]*\)",
62 | )
63 | # # 正则匹配图片标签中的图片url字段
64 | # INTAG_URL_PATTERN = re.compile(r'\(([^)]*)')
65 | # # 正则匹配图片标签中的文本描述字段
66 | # INTAG_TEXT_PATTERN = re.compile(r'!\[([^\]]*)\]')
67 | # 正则匹配 LaTeX 公式内容
68 | LATEX_PATTERN = re.compile(
69 | r"\\begin\{equation\}(.*?)\\end\{equation\}|(?小棉工具已被弃用,可能会在未来版本中移除。"
30 | )
31 |
32 |
33 | @driver.on_startup
34 | async def _():
35 | """启动钩子加载插件"""
36 | if config.marshoai_enable_plugins:
37 | marshoai_plugin_dirs = config.marshoai_plugin_dirs # 外部插件目录列表
38 | """加载内置插件"""
39 | for p in os.listdir(Path(__file__).parent / "plugins"):
40 | load_plugin(f"{__package__}.plugins.{p}")
41 |
42 | """加载指定目录插件"""
43 | load_plugins(*marshoai_plugin_dirs)
44 |
45 | """加载sys.path下的包, 包括从pip安装的包"""
46 | for package_name in config.marshoai_plugins:
47 | load_plugin(package_name)
48 | logger.info(
49 | "如果启用小棉插件后使用的模型出现报错,请尝试将 MARSHOAI_ENABLE_PLUGINS 设为 false。"
50 | )
51 |
52 |
53 | @driver.on_shutdown
54 | async def auto_backup_context():
55 | for target_info in target_list:
56 | target_id, target_private = target_info
57 | contexts_data = context.build(target_id, target_private)
58 | if target_private:
59 | target_uid = "private_" + target_id
60 | else:
61 | target_uid = "group_" + target_id
62 | await save_context_to_json(
63 | f"back_up_context_{target_uid}", contexts_data, "contexts/backup"
64 | )
65 | logger.info(f"已保存会话 {target_id} 的上下文备份,将在下次对话时恢复~")
66 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/hunyuan.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import json
3 | import traceback
4 | from typing import Optional
5 |
6 | from arclet.alconna import Alconna, AllParam, Args
7 | from nonebot import get_driver, logger, on_command
8 | from nonebot.adapters import Event, Message
9 | from nonebot.params import CommandArg
10 | from nonebot.permission import SUPERUSER
11 | from nonebot_plugin_alconna import MsgTarget, on_alconna
12 | from nonebot_plugin_alconna.uniseg import UniMessage, UniMsg
13 |
14 | from .config import config
15 | from .constants import *
16 | from .metadata import metadata
17 | from .models import MarshoContext
18 | from .util_hunyuan import *
19 |
20 | genimage_cmd = on_alconna(
21 | Alconna(
22 | "genimage",
23 | Args["prompt?", str],
24 | )
25 | )
26 |
27 |
28 | @genimage_cmd.handle()
29 | async def genimage(event: Event, prompt=None):
30 | if not prompt:
31 | await genimage_cmd.finish("无提示词")
32 | try:
33 | result = generate_image(prompt)
34 | url = json.loads(result)["ResultImage"]
35 | await UniMessage.image(url=url).send()
36 | except Exception as e:
37 | # await genimage_cmd.finish(str(e))
38 | traceback.print_exc()
39 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/instances.py:
--------------------------------------------------------------------------------
1 | # Marsho 的类实例以及全局变量
2 | from nonebot import get_driver
3 | from openai import AsyncOpenAI
4 |
5 | from .config import config
6 | from .models import MarshoContext, MarshoTools
7 |
8 | driver = get_driver()
9 |
10 | command_start = driver.config.command_start
11 | model_name = config.marshoai_default_model
12 | context = MarshoContext()
13 | tools = MarshoTools()
14 | token = config.marshoai_token
15 | endpoint = config.marshoai_azure_endpoint
16 | # client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(token))
17 | client = AsyncOpenAI(base_url=endpoint, api_key=token)
18 | target_list: list[list] = [] # 记录需保存历史上下文的列表
19 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/marsho_onebot.py:
--------------------------------------------------------------------------------
1 | from nonebot import on_type
2 | from nonebot.adapters.onebot.v11 import PokeNotifyEvent # type: ignore
3 | from nonebot.rule import to_me
4 |
5 | poke_notify = on_type((PokeNotifyEvent,), rule=to_me())
6 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/metadata.py:
--------------------------------------------------------------------------------
1 | from nonebot.plugin import PluginMetadata, inherit_supported_adapters
2 |
3 | from .config import ConfigModel
4 | from .constants import USAGE
5 |
6 | metadata = PluginMetadata(
7 | name="Marsho AI 插件",
8 | description="接入 Azure API 或其他 API 的 AI 聊天插件,支持图片处理,外部函数调用,兼容包括 DeepSeek-R1, QwQ-32B 在内的多个模型",
9 | usage=USAGE,
10 | type="application",
11 | config=ConfigModel,
12 | homepage="https://github.com/LiteyukiStudio/nonebot-plugin-marshoai",
13 | supported_adapters=inherit_supported_adapters("nonebot_plugin_alconna"),
14 | extra={"License": "MIT, Mulan PSL v2", "Author": "Asankilp, LiteyukiStudio"},
15 | )
16 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/observer.py:
--------------------------------------------------------------------------------
1 | """
2 | 此模块用于注册观察者函数,使用watchdog监控文件变化并重启bot
3 | 启用该模块需要在配置文件中设置`dev_mode`为True
4 | """
5 |
6 | import time
7 | from typing import Callable, TypeAlias
8 |
9 | from nonebot import get_driver, logger
10 | from watchdog.events import FileSystemEvent, FileSystemEventHandler
11 | from watchdog.observers import Observer
12 |
13 | from .config import config
14 |
15 | CALLBACK_FUNC: TypeAlias = Callable[[FileSystemEvent], None] # 位置1为FileSystemEvent
16 | FILTER_FUNC: TypeAlias = Callable[[FileSystemEvent], bool] # 位置1为FileSystemEvent
17 |
18 | observer = Observer()
19 |
20 | driver = get_driver()
21 |
22 |
23 | def debounce(wait):
24 | """
25 | 防抖函数
26 | """
27 |
28 | def decorator(func):
29 | def wrapper(*args, **kwargs):
30 | nonlocal last_call_time
31 | current_time = time.time()
32 | if (current_time - last_call_time) > wait:
33 | last_call_time = current_time
34 | return func(*args, **kwargs)
35 |
36 | last_call_time = None
37 | return wrapper
38 |
39 | return decorator
40 |
41 |
42 | @driver.on_startup
43 | async def check_for_reloader():
44 | if config.marshoai_devmode:
45 | logger.debug("Marsho Reload enabled, watching for file changes...")
46 | observer.start()
47 |
48 |
49 | class CodeModifiedHandler(FileSystemEventHandler):
50 | """
51 | Handler for code file changes
52 | """
53 |
54 | @debounce(1)
55 | def on_modified(self, event):
56 | raise NotImplementedError("on_modified must be implemented")
57 |
58 | def on_created(self, event):
59 | self.on_modified(event)
60 |
61 | def on_deleted(self, event):
62 | self.on_modified(event)
63 |
64 | def on_moved(self, event):
65 | self.on_modified(event)
66 |
67 | def on_any_event(self, event):
68 | self.on_modified(event)
69 |
70 |
71 | def on_file_system_event(
72 | directories: tuple[str, ...],
73 | recursive: bool = True,
74 | event_filter: FILTER_FUNC | None = None,
75 | ) -> Callable[[CALLBACK_FUNC], CALLBACK_FUNC]:
76 | """
77 | 注册文件系统变化监听器
78 | Args:
79 | directories: 监听目录们
80 | recursive: 是否递归监听子目录
81 | event_filter: 事件过滤器, 返回True则执行回调函数
82 | Returns:
83 | 装饰器,装饰一个函数在接收到数据后执行
84 | """
85 |
86 | def decorator(func: CALLBACK_FUNC) -> CALLBACK_FUNC:
87 | def wrapper(event: FileSystemEvent):
88 |
89 | if event_filter is not None and not event_filter(event):
90 | return
91 | func(event)
92 |
93 | code_modified_handler = CodeModifiedHandler()
94 | code_modified_handler.on_modified = wrapper
95 | for directory in directories:
96 | observer.schedule(code_modified_handler, directory, recursive=recursive)
97 | return func
98 |
99 | return decorator
100 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugin/__init__.py:
--------------------------------------------------------------------------------
1 | """该功能目前~~正在开发中~~开发基本完成,暂时~~不~~可用,受影响的文件夹 `plugin`, `plugins`"""
2 |
3 | from .func_call import *
4 | from .load import *
5 | from .models import *
6 | from .utils import *
7 |
8 | # logger.opt(colors=True).info("MarshoAI 插件功能开发中,用户请忽略相关日志 ")
9 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugin/func_call/__init__.py:
--------------------------------------------------------------------------------
1 | from .caller import *
2 | from .params import *
3 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugin/func_call/models.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Any
2 |
3 | from nonebot.adapters import Bot, Event
4 | from nonebot.matcher import Matcher
5 | from nonebot.typing import T_State
6 | from pydantic import BaseModel
7 |
8 | if TYPE_CHECKING:
9 | from .caller import Caller
10 |
11 |
12 | class SessionContext(BaseModel):
13 | """依赖注入会话上下文
14 |
15 | Args:
16 | BaseModel (_type_): _description_
17 | """
18 |
19 | bot: Bot
20 | event: Event
21 | matcher: Matcher
22 | state: T_State | None
23 | caller: Any = None
24 |
25 | class Config:
26 | arbitrary_types_allowed = True
27 |
28 |
29 | class SessionContextDepends(BaseModel):
30 | bot: str | None = None
31 | event: str | None = None
32 | matcher: str | None = None
33 | # state: str | None = None
34 | caller: str | None = None
35 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugin/func_call/params.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 | from typing import Any, TypeVar
3 |
4 | from pydantic import BaseModel, Field
5 |
6 | from ..typing import FUNCTION_CALL_FUNC
7 |
8 | P = TypeVar("P", bound="Parameter")
9 | """参数类型泛型"""
10 |
11 |
12 | class ParamTypes:
13 | STRING = "string"
14 | INTEGER = "integer"
15 | ARRAY = "array"
16 | OBJECT = "object"
17 | BOOLEAN = "boolean"
18 | NUMBER = "number"
19 |
20 |
21 | class Parameter(BaseModel):
22 | """
23 | 插件函数参数对象
24 |
25 | Attributes:
26 | ----------
27 | name: str
28 | 参数名称
29 | type: str
30 | 参数类型 string integer等
31 | description: str
32 | 参数描述
33 | """
34 |
35 | type_: str
36 | """参数类型描述 string integer等"""
37 | description: str
38 | """参数描述"""
39 | default: Any = None
40 | """默认值"""
41 | properties: dict[str, Any] = {}
42 | """参数定义属性,例如最大值最小值等"""
43 | required: bool = False
44 | """是否必须"""
45 |
46 | def data(self) -> dict[str, Any]:
47 | return {
48 | "type": self.type_,
49 | "description": self.description,
50 | **{k: v for k, v in self.properties.items() if v is not None},
51 | }
52 |
53 |
54 | class String(Parameter):
55 | type_: str = ParamTypes.STRING
56 | properties: dict[str, Any] = Field(default_factory=dict)
57 | enum: list[str] | None = None
58 |
59 |
60 | class Integer(Parameter):
61 | type_: str = ParamTypes.INTEGER
62 | properties: dict[str, Any] = Field(
63 | default_factory=lambda: {"minimum": 0, "maximum": 100}
64 | )
65 |
66 | minimum: int | None = None
67 | maximum: int | None = None
68 |
69 |
70 | class Array(Parameter):
71 | type_: str = ParamTypes.ARRAY
72 | properties: dict[str, Any] = Field(
73 | default_factory=lambda: {"items": {"type": "string"}}
74 | )
75 | items: str = Field("string", description="数组元素类型")
76 |
77 |
78 | class FunctionCall(BaseModel):
79 | """
80 | 插件函数对象
81 |
82 | Attributes:
83 | ----------
84 | name: str
85 | 函数名称
86 | func: "FUNCTION_CALL"
87 | 函数对象
88 | """
89 |
90 | name: str
91 | """函数名称 module.func"""
92 | description: str
93 | """函数描述 这个函数用于获取天气信息"""
94 | arguments: dict[str, Parameter]
95 | """函数参数信息"""
96 | function: FUNCTION_CALL_FUNC
97 | """函数对象"""
98 | kwargs: dict[str, Any] = {}
99 | """扩展参数"""
100 |
101 | class Config:
102 | arbitrary_types_allowed = True
103 |
104 | def __hash__(self) -> int:
105 | return hash(self.name)
106 |
107 | def data(self) -> dict[str, Any]:
108 | """生成函数描述信息
109 |
110 | Returns:
111 | dict[str, Any]: 函数描述信息 字典
112 | """
113 | return {
114 | "type": "function",
115 | "function": {
116 | "name": self.name,
117 | "description": self.description,
118 | "parameters": {
119 | "type": "object",
120 | "properties": {k: v.data() for k, v in self.arguments.items()},
121 | },
122 | "required": [k for k, v in self.arguments.items() if v.default is None],
123 | **self.kwargs,
124 | },
125 | }
126 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugin/func_call/utils.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | from functools import wraps
3 | from typing import TYPE_CHECKING, Any, Callable
4 |
5 | from ..typing import F
6 |
7 |
8 | def copy_signature(func: F) -> Callable[[Callable[..., Any]], F]:
9 | """复制函数签名和文档字符串的装饰器"""
10 |
11 | def decorator(wrapper: Callable[..., Any]) -> F:
12 | @wraps(func)
13 | def wrapped(*args: Any, **kwargs: Any) -> Any:
14 | return wrapper(*args, **kwargs)
15 |
16 | return wrapped # type: ignore
17 |
18 | return decorator
19 |
20 |
21 | def async_wrap(func: F) -> F:
22 | """装饰器,将同步函数包装为异步函数
23 |
24 | Args:
25 | func (F): 函数对象
26 |
27 | Returns:
28 | F: 包装后的函数对象
29 | """
30 |
31 | @wraps(func)
32 | async def wrapper(*args: Any, **kwargs: Any) -> Any:
33 | return func(*args, **kwargs)
34 |
35 | return wrapper # type: ignore
36 |
37 |
38 | def is_coroutine_callable(call: Callable[..., Any]) -> bool:
39 | """
40 | 判断是否为async def 函数
41 | 请注意:是否为 async def 函数与该函数是否能被await调用是两个不同的概念,具体取决于函数返回值是否为awaitable对象
42 | Args:
43 | call: 可调用对象
44 | Returns:
45 | bool: 是否为async def函数
46 | """
47 | if inspect.isroutine(call):
48 | return inspect.iscoroutinefunction(call)
49 | if inspect.isclass(call):
50 | return False
51 | func_ = getattr(call, "__call__", None)
52 | return inspect.iscoroutinefunction(func_)
53 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugin/models.py:
--------------------------------------------------------------------------------
1 | from types import ModuleType
2 | from typing import Any
3 |
4 | from pydantic import BaseModel, Field
5 |
6 | from .typing import ASYNC_FUNCTION_CALL_FUNC, FUNCTION_CALL_FUNC
7 |
8 |
9 | class PluginMetadata(BaseModel):
10 | """
11 | Marsho 插件 对象元数据
12 | Attributes:
13 | ----------
14 |
15 | name: str
16 | 友好名称: 例如Marsho Test
17 | description: str
18 | 插件描述
19 | usage: str
20 | 插件使用方法
21 | type: str
22 | 插件类型
23 | author: str
24 | 插件作者
25 | homepage: str
26 | 插件主页
27 | extra: dict[str, Any]
28 | 额外信息,自定义键值对
29 | """
30 |
31 | name: str
32 | description: str = ""
33 | usage: str = ""
34 | author: str = ""
35 | homepage: str = ""
36 | extra: dict[str, Any] = {}
37 |
38 |
39 | class Plugin(BaseModel):
40 | """
41 | 存储插件信息
42 |
43 | Attributes:
44 | ----------
45 | name: str
46 | 包名称 例如marsho_test
47 | module: ModuleType
48 | 插件模块对象
49 | module_name: str
50 | 点分割模块路径 例如a.b.c
51 | metadata: "PluginMeta" | None
52 | 元
53 | """
54 |
55 | name: str
56 | """包名称 例如marsho_test"""
57 | module: ModuleType
58 | """插件模块对象"""
59 | module_name: str
60 | """点分或/割模块路径 例如a.b.c"""
61 | module_path: str | None
62 | """实际路径,单文件为.py的路径,包为__init__.py路径"""
63 | metadata: PluginMetadata | None = None
64 | """元"""
65 |
66 | class Config:
67 | arbitrary_types_allowed = True
68 |
69 | def __hash__(self) -> int:
70 | return hash(self.name)
71 |
72 | def __eq__(self, other: Any) -> bool:
73 | return self.name == other.name
74 |
75 | def __str__(self) -> str:
76 | return f"Plugin({self.name}({self.module_path}))"
77 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugin/typing.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Callable, Coroutine, TypeAlias, TypeVar
2 |
3 | SYNC_FUNCTION_CALL_FUNC: TypeAlias = Callable[..., str]
4 | ASYNC_FUNCTION_CALL_FUNC: TypeAlias = Callable[..., Coroutine[str, Any, str]]
5 | FUNCTION_CALL_FUNC: TypeAlias = SYNC_FUNCTION_CALL_FUNC | ASYNC_FUNCTION_CALL_FUNC
6 |
7 | F = TypeVar("F", bound=FUNCTION_CALL_FUNC)
8 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugin/utils.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | from pathlib import Path
3 | from typing import Any, Callable
4 |
5 |
6 | def path_to_module_name(path: Path) -> str:
7 | """
8 | 转换路径为模块名
9 | Args:
10 | path: 路径a/b/c/d -> a.b.c.d
11 | Returns:
12 | str: 模块名
13 | """
14 | rel_path = path.resolve().relative_to(Path.cwd().resolve())
15 | if rel_path.stem == "__init__":
16 | return ".".join(rel_path.parts[:-1])
17 | else:
18 | return ".".join(rel_path.parts[:-1] + (rel_path.stem,))
19 |
20 |
21 | def parse_function_docsring():
22 | pass
23 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/builtin_tools/__init__.py:
--------------------------------------------------------------------------------
1 | from nonebot_plugin_marshoai.plugin import PluginMetadata
2 |
3 | # from .chat import *
4 | from .file_io import *
5 | from .liteyuki import *
6 | from .manager import *
7 | from .network import *
8 |
9 | __marsho_meta__ = PluginMetadata(
10 | name="内置增强组件",
11 | description="内置工具插件",
12 | author="MarshoTeam of LiteyukiStudio",
13 | )
14 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/builtin_tools/chat.py:
--------------------------------------------------------------------------------
1 | from nonebot.adapters.onebot.v11 import (
2 | Bot,
3 | GroupMessageEvent,
4 | MessageEvent,
5 | PrivateMessageEvent,
6 | )
7 | from nonebot.exception import FinishedException
8 | from nonebot.permission import SUPERUSER
9 |
10 | from nonebot_plugin_marshoai.plugin import String, on_function_call
11 |
12 |
13 | @on_function_call(description="获取当前会话信息,比如群聊或用户的身份信息").permission(
14 | SUPERUSER
15 | )
16 | async def get_session_info(bot: Bot, event: MessageEvent) -> str:
17 | """获取当前会话信息,比如群聊或用户的身份信息
18 |
19 | Args:
20 | bot (Bot): Bot对象
21 |
22 | Returns:
23 | str: 会话信息
24 | """
25 | if isinstance(event, PrivateMessageEvent):
26 | return f"当前会话为私聊,用户ID: {event.user_id}"
27 | elif isinstance(event, GroupMessageEvent):
28 | return f"当前会话为群聊,群组ID: {event.group_id}, 用户ID: {event.user_id}"
29 | else:
30 | return "未知会话类型"
31 |
32 |
33 | @on_function_call(description="发送消息到指定用户").params(
34 | user=String(description="用户ID"), message=String(description="消息内容")
35 | ).permission(SUPERUSER)
36 | async def send_message(user: str, message: str, bot: Bot) -> str:
37 | """发送消息到指定用户,实验性功能,仅限onebotv11适配器
38 |
39 | Args:
40 | user (str): 用户ID
41 | message (str): 消息内容
42 |
43 | Returns:
44 | str: 发送结果
45 | """
46 | try:
47 | await bot.send_private_msg(user_id=int(user), message=message)
48 | return "发送成功"
49 | except FinishedException as e:
50 | return "发送完成"
51 | except Exception as e:
52 | return "发送失败: " + str(e)
53 |
54 |
55 | @on_function_call(description="发送消息到指定群组").params(
56 | group=String(description="群组ID"), message=String(description="消息内容")
57 | ).permission(SUPERUSER)
58 | async def send_group_message(group: str, message: str, bot: Bot) -> str:
59 | """发送消息到指定群组,实验性功能,仅限onebotv11适配器
60 |
61 | Args:
62 | group (str): 群组ID
63 | message (str): 消息内容
64 |
65 | Returns:
66 | str: 发送结果
67 | """
68 | try:
69 | await bot.send_group_msg(group_id=int(group), message=message)
70 | return "发送成功"
71 | except FinishedException as e:
72 | return "发送完成"
73 | except Exception as e:
74 | return "发送失败: " + str(e)
75 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/builtin_tools/file_io.py:
--------------------------------------------------------------------------------
1 | import aiofiles # type: ignore
2 | from nonebot.permission import SUPERUSER
3 |
4 | from nonebot_plugin_marshoai.plugin import String, on_function_call
5 |
6 |
7 | @on_function_call(description="获取设备上本地文件内容").params(
8 | fp=String(description="文件路径")
9 | ).permission(SUPERUSER)
10 | async def read_file(fp: str) -> str:
11 | """获取设备上本地文件内容
12 |
13 | Args:
14 | fp (str): 文件路径
15 |
16 | Returns:
17 | str: 文件内容
18 | """
19 | try:
20 | async with aiofiles.open(fp, "r", encoding="utf-8") as f:
21 | return await f.read()
22 | except Exception as e:
23 | return "读取出错: " + str(e)
24 |
25 |
26 | @on_function_call(description="写入内容到设备上本地文件").params(
27 | fp=String(description="文件路径"), content=String(description="写入内容")
28 | ).permission(SUPERUSER)
29 | async def write_file(fp: str, content: str) -> str:
30 | """写入内容到设备上本地文件
31 |
32 | Args:
33 | fp (str): 文件路径
34 | content (str): 写入内容
35 |
36 | Returns:
37 | str: 写入结果
38 | """
39 | try:
40 | async with aiofiles.open(fp, "w", encoding="utf-8") as f:
41 | await f.write(content)
42 | return "写入成功"
43 | except Exception as e:
44 | return "写入出错: " + str(e)
45 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/builtin_tools/liteyuki.py:
--------------------------------------------------------------------------------
1 | from httpx import AsyncClient
2 |
3 | from nonebot_plugin_marshoai.plugin import on_function_call
4 |
5 |
6 | @on_function_call(description="获取分布式轻雪机器人节点情况")
7 | async def get_liteyuki_info() -> str:
8 | """获取分布式轻雪机器人节点情况
9 |
10 | Returns:
11 | str: 节点情况
12 | """
13 | register = 0
14 | online = 0
15 | async with AsyncClient() as client:
16 | response = await client.get("https://api.liteyuki.icu/count")
17 | register = response.json().get("register")
18 |
19 | response = await client.get("https://api.liteyuki.icu/online")
20 | online = response.json().get("online")
21 |
22 | return f"注册节点数: {register}\n在线节点数: {online}"
23 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/builtin_tools/manager.py:
--------------------------------------------------------------------------------
1 | from nonebot_plugin_marshoai.plugin import get_plugins, on_function_call
2 |
3 |
4 | @on_function_call(description="获取已加载的插件列表")
5 | def get_marsho_plugins() -> str:
6 | """获取已加载的插件列表
7 |
8 | Returns:
9 | str: 插件列表
10 | """
11 |
12 | reply = "加载的插件列表"
13 | for p in get_plugins().values():
14 | if p.metadata:
15 | reply += f"名称: {p.metadata.name},描述: {p.metadata.description}\n"
16 | else:
17 | reply += f"名称: {p.name},描述: 暂无\n"
18 | return reply
19 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/builtin_tools/network.py:
--------------------------------------------------------------------------------
1 | from httpx import AsyncClient
2 | from newspaper import Article # type: ignore
3 | from nonebot import logger
4 |
5 | from nonebot_plugin_marshoai.plugin.func_call.caller import on_function_call
6 | from nonebot_plugin_marshoai.plugin.func_call.params import String
7 |
8 | from .utils import make_html_summary
9 |
10 | headers = {
11 | "User-Agent": "Firefox/90.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0"
12 | }
13 |
14 |
15 | @on_function_call(
16 | description="使用网页链接(url)获取网页内容摘要,可以让AI上网查询资料"
17 | ).params(
18 | url=String(description="网页链接"),
19 | )
20 | async def get_web_content(url: str) -> str:
21 | """使用网页链接获取网页内容摘要
22 | 为什么要获取摘要,不然token超限了
23 |
24 | Args:
25 | url (str): _description_
26 |
27 | Returns:
28 | str: _description_
29 | """
30 | async with AsyncClient(headers=headers) as client:
31 | try:
32 | response = await client.get(url)
33 | if response.status_code == 200:
34 | article = Article(url)
35 | article.download(input_html=response.text)
36 | article.parse()
37 | if article.text:
38 | return article.text
39 | elif article.html:
40 | return await make_html_summary(article.html)
41 | else:
42 | return "未能获取到有效的网页内容"
43 | else:
44 | return "获取网页内容失败" + str(response.status_code)
45 |
46 | except Exception as e:
47 | logger.error(f"marsho builtin: 获取网页内容失败: {e}")
48 | return "获取网页内容失败:" + str(e)
49 |
50 | return "未能获取到有效的网页内容"
51 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/builtin_tools/utils.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from concurrent.futures import ThreadPoolExecutor
3 |
4 | from newspaper import Article # type: ignore
5 | from sumy.nlp.tokenizers import Tokenizer # type: ignore
6 | from sumy.parsers.plaintext import PlaintextParser # type: ignore
7 | from sumy.summarizers.lsa import LsaSummarizer # type: ignore
8 |
9 | executor = ThreadPoolExecutor()
10 |
11 |
12 | async def make_html_summary(
13 | html_content: str, language: str = "english", length: int = 3
14 | ) -> str:
15 | """使用html内容生成摘要
16 |
17 | Args:
18 | html_content (str): html内容
19 | language (str, optional): 语言. Defaults to "english".
20 | length (int, optional): 摘要长度. Defaults to 3.
21 |
22 | Returns:
23 | str: 摘要
24 | """
25 | loop = asyncio.get_event_loop()
26 | return await loop.run_in_executor(
27 | executor, _make_summary, html_content, language, length
28 | )
29 |
30 |
31 | def _make_summary(html_content: str, language: str, length: int) -> str:
32 | parser = PlaintextParser.from_string(html_content, Tokenizer(language))
33 | summarizer = LsaSummarizer()
34 | summary = summarizer(parser.document, length)
35 | return " ".join([str(sentence) for sentence in summary])
36 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/marshoai_bangumi/__init__.py:
--------------------------------------------------------------------------------
1 | import traceback
2 |
3 | import httpx
4 | from zhDateTime import DateTime # type: ignore
5 |
6 | from nonebot_plugin_marshoai.plugin import PluginMetadata, on_function_call
7 | from nonebot_plugin_marshoai.plugin.func_call.params import String
8 |
9 | # 定义插件元数据
10 | __marsho_meta__ = PluginMetadata(
11 | name="Bangumi日历",
12 | author="MarshoAI",
13 | description="这个插件可以帮助你获取Bangumi的日历信息~",
14 | )
15 |
16 |
17 | @on_function_call(description="获取Bangumi日历信息")
18 | async def get_bangumi_news() -> str:
19 | async def fetch_calendar():
20 | url = "https://api.bgm.tv/calendar"
21 | headers = {
22 | "User-Agent": "LiteyukiStudio/nonebot-plugin-marshoai (https://github.com/LiteyukiStudio/nonebot-plugin-marshoai)"
23 | }
24 | async with httpx.AsyncClient() as client:
25 | response = await client.get(url, headers=headers)
26 | # print(response.text)
27 | return response.json()
28 |
29 | try:
30 | result = await fetch_calendar()
31 | info = ""
32 | current_weekday = DateTime.now().weekday()
33 | weekdays = [
34 | "星期一",
35 | "星期二",
36 | "星期三",
37 | "星期四",
38 | "星期五",
39 | "星期六",
40 | "星期日",
41 | ]
42 | current_weekday_name = weekdays[current_weekday]
43 | info += f"今天{current_weekday_name}。\n"
44 | for i in result:
45 | weekday = i["weekday"]["cn"]
46 | # print(weekday)
47 | info += f"{weekday}:"
48 | items = i["items"]
49 | for item in items:
50 | name = item["name_cn"]
51 | info += f"《{name}》"
52 | info += "\n"
53 | return info
54 | except Exception as e:
55 | traceback.print_exc()
56 | return ""
57 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/marshoai_bangumi/tools.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "function": {
5 | "name": "marshoai-bangumi__get_bangumi_news",
6 | "description": "获取今天的新番(动漫)列表,在调用之前,你需要知道今天星期几。"
7 | }
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/twisuki_megakits/__init__.py:
--------------------------------------------------------------------------------
1 | from nonebot_plugin_marshoai.plugin import (
2 | Integer,
3 | Parameter,
4 | PluginMetadata,
5 | String,
6 | on_function_call,
7 | )
8 |
9 | from . import mk_morse_code, mk_nya_code
10 |
11 | __marsho_meta__ = PluginMetadata(
12 | name="MegaKits插件",
13 | description="一个功能混杂的多文件插件",
14 | author="Twisuki",
15 | )
16 |
17 |
18 | @on_function_call(description="摩尔斯电码加密").params(
19 | msg=String(description="被加密语句")
20 | )
21 | async def morse_encrypt(msg: str) -> str:
22 | """摩尔斯电码加密"""
23 | return str(await mk_morse_code.morse_encrypt(msg))
24 |
25 |
26 | @on_function_call(description="摩尔斯电码解密").params(
27 | msg=String(description="被解密语句")
28 | )
29 | async def morse_decrypt(msg: str) -> str:
30 | """摩尔斯电码解密"""
31 | return str(await mk_morse_code.morse_decrypt(msg))
32 |
33 |
34 | @on_function_call(description="转换为猫语").params(msg=String(description="被转换语句"))
35 | async def nya_encrypt(msg: str) -> str:
36 | """转换为猫语"""
37 | return str(await mk_nya_code.nya_encrypt(msg))
38 |
39 |
40 | @on_function_call(description="将猫语翻译回人类语言").params(
41 | msg=String(description="被翻译语句")
42 | )
43 | async def nya_decrypt(msg: str) -> str:
44 | """将猫语翻译回人类语言"""
45 | return str(await mk_nya_code.nya_decrypt(msg))
46 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/twisuki_megakits/mk_morse_code.py:
--------------------------------------------------------------------------------
1 | # MorseCode
2 | MorseEncode = {
3 | "A": ".-",
4 | "B": "-...",
5 | "C": "-.-.",
6 | "D": "-..",
7 | "E": ".",
8 | "F": "..-.",
9 | "G": "--.",
10 | "H": "....",
11 | "I": "..",
12 | "J": ".---",
13 | "K": "-.-",
14 | "L": ".-..",
15 | "M": "--",
16 | "N": "-.",
17 | "O": "---",
18 | "P": ".--.",
19 | "Q": "--.-",
20 | "R": ".-.",
21 | "S": "...",
22 | "T": "-",
23 | "U": "..-",
24 | "V": "...-",
25 | "W": ".--",
26 | "X": "-..-",
27 | "Y": "-.--",
28 | "Z": "--..",
29 | "1": ".----",
30 | "2": "..---",
31 | "3": "...--",
32 | "4": "....-",
33 | "5": ".....",
34 | "6": "-....",
35 | "7": "--...",
36 | "8": "---..",
37 | "9": "----.",
38 | "0": "-----",
39 | ".": ".-.-.-",
40 | ":": "---...",
41 | ",": "--..--",
42 | ";": "-.-.-.",
43 | "?": "..--..",
44 | "=": "-...-",
45 | "'": ".----.",
46 | "/": "-..-.",
47 | "!": "-.-.--",
48 | "-": "-....-",
49 | "_": "..--.-",
50 | '"': ".-..-.",
51 | "(": "-.--.",
52 | ")": "-.--.-",
53 | "$": "...-..-",
54 | "&": "....",
55 | "@": ".--.-.",
56 | " ": " ",
57 | }
58 | MorseDecode = {value: key for key, value in MorseEncode.items()}
59 |
60 |
61 | async def morse_encrypt(msg: str):
62 | result = ""
63 | msg = msg.upper()
64 | for char in msg:
65 | if char in MorseEncode:
66 | result += MorseEncode[char]
67 | else:
68 | result += "..--.."
69 | result += " "
70 | return result
71 |
72 |
73 | async def morse_decrypt(msg: str):
74 | result = ""
75 | msg = msg.replace("_", "-")
76 | msg_arr = msg.split(" ")
77 | for element in msg_arr:
78 | if element in MorseDecode:
79 | result += MorseDecode[element]
80 | else:
81 | result += "?"
82 | return result
83 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/twisuki_megakits/mk_nya_code.py:
--------------------------------------------------------------------------------
1 | # NyaCode
2 |
3 | import base64
4 | import random
5 |
6 | NyaCodeCharset = ["喵", "呜", "?", "~"]
7 | NyaCodeSpecialCharset = ["唔", "!", "...", ".."]
8 | NyaCodeEncode = {}
9 |
10 | for i in range(64):
11 | triplet = ""
12 | for j in range(3):
13 | index = (i // (4**j)) % 4
14 | triplet += NyaCodeCharset[index]
15 |
16 | if i < 26:
17 | char = chr(65 + i) # 大写字母 A-Z
18 | elif i < 52:
19 | char = chr(97 + (i - 26)) # 小写字母 a-z
20 | elif i < 62:
21 | char = chr(48 + (i - 52)) # 数字 0-9
22 | elif i == 62:
23 | char = chr(43) # 特殊字符 +
24 | else:
25 | char = chr(47) # 特殊字符 /
26 | NyaCodeEncode[char] = triplet
27 | NyaCodeDecode = {value: key for key, value in NyaCodeEncode.items()}
28 |
29 |
30 | async def nya_encrypt(msg: str):
31 | result = ""
32 | b64str = base64.b64encode(msg.encode()).decode().replace("=", "")
33 |
34 | nyastr = ""
35 | for b64char in b64str:
36 | nyastr += NyaCodeEncode[b64char]
37 |
38 | for char in nyastr:
39 | if char == "呜" and random.random() < 0.5:
40 | result += "!"
41 | if random.random() < 0.25:
42 | result += random.choice(NyaCodeSpecialCharset) + char
43 | else:
44 | result += char
45 | return result
46 |
47 |
48 | async def nya_decrypt(msg: str):
49 | msg = msg.replace("唔", "").replace("!", "").replace(".", "")
50 | nyastr = []
51 |
52 | i = 0
53 | if len(msg) % 3 != 0:
54 | return "这句话不是正确的猫语"
55 |
56 | while i < len(msg):
57 | nyachar = msg[i : i + 3]
58 | try:
59 | if all(char in NyaCodeCharset for char in nyachar):
60 | nyastr.append(nyachar)
61 | i += 3
62 | except Exception:
63 | return "这句话不是正确的猫语"
64 |
65 | b64str = ""
66 | for nyachar in nyastr:
67 | b64str += NyaCodeDecode[nyachar]
68 | b64str += "=" * (4 - len(b64str) % 4)
69 |
70 | try:
71 | result = base64.b64decode(b64str.encode()).decode()
72 | except Exception:
73 | return "翻译失败"
74 | return result
75 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/twisuki_petcat/__init__.py:
--------------------------------------------------------------------------------
1 | from nonebot_plugin_marshoai.plugin import (
2 | Integer,
3 | Parameter,
4 | PluginMetadata,
5 | String,
6 | on_function_call,
7 | )
8 |
9 | from . import pc_cat, pc_info, pc_shop, pc_token
10 |
11 | __marsho_meta__ = PluginMetadata(
12 | name="养猫插件",
13 | description="在Marsho这里赛博养猫",
14 | author="Twisuki",
15 | )
16 |
17 |
18 | # 交互
19 | @on_function_call(description="传入猫猫种类, 新建一只猫猫").params(
20 | type=String(description='猫猫种类, 默认"猫1", 可留空')
21 | )
22 | async def cat_new(type: str) -> str:
23 | """新建猫猫"""
24 | return pc_cat.cat_new(type)
25 |
26 |
27 | @on_function_call(
28 | description="传入token(一串长20的b64字符串), 新名字, 选用技能, 进行猫猫的初始化"
29 | ).params(
30 | token=String(description="token(一串长20的b64字符串)"),
31 | name=String(description="新名字"),
32 | skill=String(description="技能"),
33 | )
34 | async def cat_init(token: str, name: str, skill: str) -> str:
35 | """初始化猫猫"""
36 | return pc_cat.cat_init(token, name, skill)
37 |
38 |
39 | @on_function_call(description="传入token, 查看猫猫信息").params(
40 | token=String(description="token(一串长20的b64字符串)"),
41 | )
42 | async def cat_show(token: str) -> str:
43 | """查询信息"""
44 | return pc_cat.cat_show(token)
45 |
46 |
47 | @on_function_call(description="传入token, 玩猫").params(
48 | token=String(description="token(一串长20的b64字符串)"),
49 | )
50 | async def cat_play(token: str) -> str:
51 | """玩猫"""
52 | return pc_cat.cat_play(token)
53 |
54 |
55 | @on_function_call(description="传入token, 投喂猫猫").params(
56 | token=String(description="token(一串长20的b64字符串)"),
57 | )
58 | async def cat_feed(token: str) -> str:
59 | """喂猫"""
60 | return pc_cat.cat_feed(token)
61 |
62 |
63 | # 帮助
64 | @on_function_call(description="帮助文档/如何创建一只猫猫").params()
65 | async def help_cat_new() -> str:
66 | return pc_info.help_cat_new()
67 |
68 |
69 | @on_function_call(description="可选种类").params()
70 | async def help_cat_type() -> str:
71 | return pc_info.print_type_list()
72 |
73 |
74 | @on_function_call(description="可选技能").params()
75 | async def help_cat_skill() -> str:
76 | return pc_info.print_skill_list()
77 |
78 |
79 | # 商店
80 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/twisuki_petcat/pc_info.py:
--------------------------------------------------------------------------------
1 | # 插件使用复杂, 这里用作输出提示信息.
2 | # 如: 帮助, 每次操作后对猫猫状态的描述\打印特殊列表
3 | # 公用列表数据转到这里存储
4 |
5 |
6 | from nonebot.log import logger
7 |
8 | from .pc_token import dict_to_token, token_to_dict
9 |
10 | # 公用列表
11 | TYPE_LIST = ["猫1", "猫2", "猫3", "猫4", "猫5", "猫6", "猫7", "猫8"]
12 | SKILL_LIST = ["s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8"]
13 |
14 |
15 | # 提示词打印
16 | # 打印种类列表
17 | def print_type_list() -> str:
18 | result = ""
19 | for type in TYPE_LIST:
20 | result += f'"{type}", '
21 | result = result[:-2]
22 | return f"({result})"
23 |
24 |
25 | # 打印技能列表
26 | def print_skill_list() -> str:
27 | result = ""
28 | for skill in SKILL_LIST:
29 | result += f'"{skill}", '
30 | result = result[:-2]
31 | return f"({result})"
32 |
33 |
34 | # 127位值 - 100%快速转换
35 | def value_output(num: int) -> str:
36 | value = int(num / 1.27)
37 | return str(value)
38 |
39 |
40 | # 打印状态
41 | def print_info(token: str) -> str:
42 | data = token_to_dict(token)
43 | return (
44 | "状态信息: "
45 | f'\n\t名字 : {data["name"]}'
46 | f'\n\t种类 : {TYPE_LIST[data["type"]]}'
47 | f'\n\t生命值 : {value_output(data["health"])}'
48 | f'\n\t饱食度 : {value_output(data["saturation"])}'
49 | f"\n\t活力值 : {value_output(data['energy'])}"
50 | f"\n\t技能 : {print_skill(token)}"
51 | f"\n新token : {token}"
52 | f"\ntoken已更新, 请妥善保存token, 这是猫猫的唯一标识符!"
53 | )
54 |
55 |
56 | # 打印已有技能
57 | def print_skill(token: str) -> str:
58 | result = ""
59 | data = token_to_dict(token)
60 | for index in range(0, len(SKILL_LIST) - 1):
61 | if data["skill"][index]:
62 | result += f"{SKILL_LIST[index]}, "
63 | logger.info(data["skill"])
64 | return result[:-2]
65 |
66 |
67 | # 帮助
68 | # 创建猫猫
69 | def help_cat_new() -> str:
70 | return (
71 | "新建一只猫猫, 首先选择猫猫的种类, 获取初始化token;"
72 | "然后用这个token, 选择名字和一个技能进行初始化;"
73 | "初始化结束才表示猫猫正式创建成功."
74 | "\ntoken为猫的唯一标识符, 每次交互都需要传入token"
75 | f"\n种类可选 : {print_type_list()}"
76 | f"\n技能可选 : {print_skill_list()}"
77 | )
78 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins/twisuki_petcat/pc_shop.py:
--------------------------------------------------------------------------------
1 | # 商店
2 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/marshoai_basic/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from zhDateTime import DateTime # type: ignore
4 |
5 | from nonebot_plugin_marshoai.plugin import PluginMetadata, String, on_function_call
6 |
7 | # from .web import *
8 | # 定义插件元数据
9 | __marsho_meta__ = PluginMetadata(
10 | name="基本功能",
11 | author="MarshoAI",
12 | description="这个插件提供基本的功能,比如获取当前时间和日期。",
13 | )
14 |
15 |
16 | weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
17 |
18 |
19 | @on_function_call(description="获取当前时间,日期和星期")
20 | async def get_current_time() -> str:
21 | """获取当前的时间和日期"""
22 | current_time = DateTime.now()
23 |
24 | time_prompt = "现在的时间是 {},{},{}。".format(
25 | current_time.strftime("%Y.%m.%d %H:%M:%S"),
26 | weekdays[current_time.weekday()],
27 | current_time.chinesize.date_hanzify("农历{干支年}{生肖}年 {月份}月{数序日}"),
28 | )
29 | return time_prompt
30 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/marshoai_basic/tools.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "function": {
5 | "name": "marshoai-basic__get_current_time",
6 | "description": "获取现在的日期,时间和星期。"
7 | }
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/marshoai_basic/tools_test.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "function": {
5 | "name": "marshoai-basic__get_weather",
6 | "description": "当你想查询指定城市的天气时非常有用。",
7 | "parameters": {
8 | "type": "object",
9 | "properties": {
10 | "location": {
11 | "type": "string",
12 | "description": "城市或县区,比如北京市、杭州市、余杭区等。"
13 | }
14 | }
15 | },
16 | "required": [
17 | "location"
18 | ]
19 | }
20 | },
21 | {
22 | "type": "function",
23 | "function": {
24 | "name": "marshoai-basic__get_current_env",
25 | "description": "获取当前的运行环境。",
26 | "parameters": {
27 | }
28 | }
29 | },
30 | {
31 | "type": "function",
32 | "function": {
33 | "name": "marshoai-basic__get_current_time",
34 | "description": "获取现在的时间。",
35 | "parameters": {
36 | }
37 | }
38 | }
39 | ]
40 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/marshoai_memory/__init__.py:
--------------------------------------------------------------------------------
1 | import json
2 | from pathlib import Path
3 |
4 | from azure.ai.inference.models import UserMessage
5 | from nonebot import get_driver, logger, require
6 | from nonebot_plugin_localstore import get_plugin_data_file
7 |
8 | require("nonebot_plugin_apscheduler")
9 | require("nonebot_plugin_marshoai")
10 | from nonebot_plugin_apscheduler import scheduler
11 |
12 | from nonebot_plugin_marshoai.instances import client
13 | from nonebot_plugin_marshoai.plugin import PluginMetadata, on_function_call
14 | from nonebot_plugin_marshoai.plugin.func_call.params import String
15 |
16 | from .command import *
17 | from .config import plugin_config
18 |
19 | __marsho_meta__ = PluginMetadata(
20 | name="记忆保存",
21 | author="MarshoAI",
22 | description="这个插件可以帮助AI记住一些事情",
23 | )
24 |
25 | memory_path = get_plugin_data_file("memory.json")
26 | if not Path(memory_path).exists():
27 | with open(memory_path, "w", encoding="utf-8") as f:
28 | json.dump({}, f, ensure_ascii=False, indent=4)
29 | # print(memory_path)
30 | driver = get_driver()
31 |
32 |
33 | @on_function_call(
34 | description="当你发现与你对话的用户的一些信息值得你记忆,或者用户让你记忆等时,调用此函数存储记忆内容"
35 | ).params(
36 | memory=String(description="你想记住的内容,概括并保留关键内容"),
37 | user_id=String(description="你想记住的人的id"),
38 | )
39 | async def write_memory(memory: str, user_id: str):
40 |
41 | with open(memory_path, "r", encoding="utf-8") as f:
42 | memory_data = json.load(f)
43 |
44 | memorys = memory_data.get(user_id, [])
45 | memorys.append(memory)
46 | memory_data[user_id] = memorys
47 |
48 | with open(memory_path, "w", encoding="utf-8") as f:
49 | json.dump(memory_data, f, ensure_ascii=False, indent=4)
50 |
51 | return "记忆已经保存啦~"
52 |
53 |
54 | @on_function_call(
55 | description="你需要回忆有关用户的一些知识时,调用此函数读取记忆内容,当用户问问题的时候也尽量调用此函数参考"
56 | ).params(user_id=String(description="你想读取记忆的人的id"))
57 | async def read_memory(user_id: str):
58 | with open(memory_path, "r", encoding="utf-8") as f:
59 | memory_data = json.load(f)
60 | memorys = memory_data.get(user_id, [])
61 | if not memorys:
62 | return "好像对ta还没有任何记忆呢~"
63 |
64 | return "这些是有关ta的记忆:" + "\n".join(memorys)
65 |
66 |
67 | async def organize_memories():
68 | with open(memory_path, "r", encoding="utf-8") as f:
69 | memory_data = json.load(f)
70 | for i in memory_data:
71 | memory_data_ = "\n".join(memory_data[i])
72 | msg = f"这是一些大模型记忆信息,请你保留重要内容,尽量减少无用的记忆后重新输出记忆内容,浓缩为一行:\n{memory_data_}"
73 | res = await client.complete(UserMessage(content=msg))
74 | try:
75 | memory = res.choices[0].message.content # type: ignore
76 | memory_data[i] = memory
77 | except AttributeError:
78 | logger.error(f"整理关于{i}的记忆时出错:{res}")
79 |
80 | with open(memory_path, "w", encoding="utf-8") as f:
81 | json.dump(memory_data, f, ensure_ascii=False, indent=4)
82 |
83 |
84 | if plugin_config.marshoai_plugin_memory_scheduler:
85 |
86 | @driver.on_startup
87 | async def _():
88 | logger.info("小棉定时记忆整理已启动!")
89 | scheduler.add_job(
90 | organize_memories,
91 | "cron",
92 | hour="0",
93 | minute="0",
94 | second="0",
95 | day="*",
96 | id="organize_memories",
97 | )
98 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/marshoai_memory/command.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from arclet.alconna import Alconna, Args, Subcommand
4 | from nonebot import logger
5 | from nonebot.adapters import Bot, Event
6 | from nonebot.matcher import Matcher
7 | from nonebot.typing import T_State
8 | from nonebot_plugin_alconna import on_alconna
9 | from nonebot_plugin_localstore import get_plugin_data_file
10 |
11 | from nonebot_plugin_marshoai.config import config
12 |
13 | marsho_memory_cmd = on_alconna(
14 | Alconna(
15 | f"{config.marshoai_default_name}.memory",
16 | Subcommand("view", alias={"v"}),
17 | Subcommand("reset", alias={"r"}),
18 | ),
19 | priority=96,
20 | block=True,
21 | )
22 |
23 | memory_path = get_plugin_data_file("memory.json")
24 |
25 |
26 | @marsho_memory_cmd.assign("view")
27 | async def view_memory(matcher: Matcher, state: T_State, event: Event):
28 | user_id = str(event.get_user_id())
29 | with open(memory_path, "r", encoding="utf-8") as f:
30 | memory_data = json.load(f)
31 | memorys = memory_data.get(user_id, [])
32 | if not memorys:
33 | await matcher.finish("好像对ta还没有任何记忆呢~")
34 | await matcher.finish("这些是有关ta的记忆:" + "\n".join(memorys))
35 |
36 |
37 | @marsho_memory_cmd.assign("reset")
38 | async def reset_memory(matcher: Matcher, state: T_State, event: Event):
39 | user_id = str(event.get_user_id())
40 | with open(memory_path, "r", encoding="utf-8") as f:
41 | memory_data = json.load(f)
42 | if user_id in memory_data:
43 | del memory_data[user_id]
44 | with open(memory_path, "w", encoding="utf-8") as f:
45 | json.dump(memory_data, f, ensure_ascii=False, indent=4)
46 | await matcher.finish("记忆已重置~")
47 | await matcher.finish("没有找到该用户的记忆~")
48 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/marshoai_memory/config.py:
--------------------------------------------------------------------------------
1 | from nonebot import get_plugin_config, logger
2 | from pydantic import BaseModel
3 |
4 |
5 | class ConfigModel(BaseModel):
6 | marshoai_plugin_memory_scheduler: bool = True
7 |
8 |
9 | plugin_config: ConfigModel = get_plugin_config(ConfigModel)
10 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/marshoai_memory/tools.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "function": {
5 | "name": "marshoai_memory__write_memory",
6 | "description": "如果在上下中你看见并觉得应该记住的人的行为与事件,请调用这个函数,并将记忆内容写入。请尽量每次都调用,总结ta的习惯、爱好和性格,以及你对ta的印象和ta对你的印象;比如用户喜欢干什么吃什么。"
7 | }
8 | },
9 | {
10 | "type": "function",
11 | "function": {
12 | "name": "marshoai_memory__read_memory",
13 | "description": "每当你想要获取更多有关某人的信息的时候,请调用这个函数。"
14 | }
15 | }
16 | ]
17 |
18 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/random_number_generator.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | from nonebot_plugin_marshoai.plugin import Integer, PluginMetadata, on_function_call
4 |
5 | __marsho_meta__ = PluginMetadata(
6 | name="随机数生成器", author="MarshoAI", description="生成指定数量的随机数"
7 | )
8 |
9 |
10 | @on_function_call(description="生成随机数").params(
11 | count=Integer(description="随机数的数量")
12 | )
13 | async def generate_random_numbers(count: int) -> str:
14 | random_numbers = [random.randint(1, 100) for _ in range(count)]
15 | return f"生成的随机数为: {', '.join(map(str, random_numbers))}"
16 |
17 |
18 | # 该插件由MarshoAI自举编写
19 |
20 |
21 | @on_function_call(description="重载测试")
22 | def test_reload():
23 | return 1
24 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/snowykami_testplugin/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import platform
3 |
4 | import psutil
5 | from nonebot.adapters import Bot, Event
6 |
7 | # from nonebot.adapters.onebot.v11 import MessageEvent
8 | from nonebot.permission import SUPERUSER
9 |
10 | from nonebot_plugin_marshoai.plugin import (
11 | Integer,
12 | Parameter,
13 | PluginMetadata,
14 | String,
15 | on_function_call,
16 | )
17 | from nonebot_plugin_marshoai.plugin.func_call.caller import Caller
18 |
19 | __marsho_meta__ = PluginMetadata(
20 | name="SnowyKami 测试插件",
21 | description="A test plugin for SnowyKami",
22 | usage="SnowyKami Test Plugin",
23 | )
24 |
25 |
26 | @on_function_call(description="使用姓名,年龄,性别进行算命").params(
27 | age=Integer(description="年龄"),
28 | name=String(description="姓名"),
29 | gender=String(enum=["男", "女"], description="性别"),
30 | )
31 | async def fortune_telling(age: int, name: str, gender: str) -> str:
32 | """使用姓名,年龄,性别进行算命"""
33 |
34 | # 进行一系列算命操作...
35 |
36 | return f"{name},你的年龄是{age},你的性别很好"
37 |
38 |
39 | @on_function_call(description="获取一个地点未来一段时间的天气").params(
40 | location=String(description="地点名称,可以是城市名、地区名等"),
41 | days=Integer(description="天数", minimum=1, maximum=30),
42 | unit=String(enum=["摄氏度", "华氏度"], description="温度单位", default="摄氏度"),
43 | )
44 | async def get_weather(location: str, days: int, unit: str) -> str:
45 | """获取一个地点未来一段时间的天气"""
46 |
47 | # 进行一系列获取天气操作...
48 |
49 | return f"{location}未来{days}天的天气很好,全都是晴天,温度是34"
50 |
51 |
52 | @on_function_call(description="获取设备物理地理位置")
53 | def get_location() -> str:
54 | """获取设备物理地理位置"""
55 |
56 | # 进行一系列获取地理位置操作...
57 |
58 | return "日本 东京都 世田谷区"
59 |
60 |
61 | @on_function_call(description="获取聊天者个人信息及发送的消息和function call调用参数")
62 | async def get_user_info(e: Event, c: Caller) -> str:
63 | return (
64 | f"用户ID: {e.user_id} "
65 | "用户昵称: {e.sender.nickname} "
66 | "FC调用参数:{c._parameters} "
67 | "消息内容: {e.raw_message}"
68 | )
69 |
70 |
71 | @on_function_call(description="获取设备信息")
72 | def get_device_info() -> str:
73 | """获取机器人所运行的设备信息"""
74 |
75 | # 进行一系列获取设备信息操作...
76 |
77 | data = {
78 | "cpu 性能": f"{psutil.cpu_percent()}% {psutil.cpu_freq().current:.2f}MHz {psutil.cpu_count()}线程 {psutil.cpu_count(logical=False)}物理核",
79 | "memory 内存": f"{psutil.virtual_memory().percent}% {psutil.virtual_memory().available / 1024 / 1024 / 1024:.2f}/{psutil.virtual_memory().total / 1024 / 1024 / 1024:.2f}GB",
80 | "swap 交换分区": f"{psutil.swap_memory().percent}% {psutil.swap_memory().used / 1024 / 1024 / 1024:.2f}/{psutil.swap_memory().total / 1024 / 1024 / 1024:.2f}GB",
81 | "cpu 信息": f"{psutil.cpu_stats()}",
82 | "system 系统": f"system: {platform.system()}, version: {platform.version()}, arch: {platform.architecture()}, machine: {platform.machine()}",
83 | }
84 | return str(data)
85 |
86 |
87 | @on_function_call(description="在设备上运行Python代码,需要超级用户权限").params(
88 | code=String(description="Python代码内容")
89 | ).permission(SUPERUSER)
90 | async def run_python_code(code: str, b: Bot, e: Event) -> str:
91 | """运行Python代码"""
92 | try:
93 | r = eval(code)
94 | except Exception as e:
95 | return "运行出错: " + str(e)
96 | return "运行成功: " + str(r)
97 |
98 |
99 | @on_function_call(
100 | description="在设备上运行shell命令, Run command on this device"
101 | ).params(command=String(description="shell命令内容")).permission(SUPERUSER)
102 | async def run_shell_command(command: str, b: Bot, e: Event) -> str:
103 | """运行shell命令"""
104 | try:
105 | r = os.popen(command).read()
106 | except Exception as e:
107 | return "运行出错: " + str(e)
108 | return "运行成功: " + str(r)
109 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/plugins_test/weather_demo.py:
--------------------------------------------------------------------------------
1 | from nonebot_plugin_marshoai.plugin import PluginMetadata, String, on_function_call
2 |
3 | metadata = PluginMetadata(
4 | name="天气查询", author="MarshoAI", description="一个简单的查询天气的插件"
5 | )
6 |
7 |
8 | @on_function_call(description="可以用于查询天气").params(
9 | location=String(description="地点")
10 | )
11 | async def weather(location: str) -> str:
12 | # 这里可以调用天气API查询天气,这里只是一个简单的示例
13 | return f"{location}的天气是晴天, 温度是25°C"
14 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_bangumi/__init__.py:
--------------------------------------------------------------------------------
1 | import traceback
2 |
3 | import httpx
4 |
5 |
6 | async def fetch_calendar():
7 | url = "https://api.bgm.tv/calendar"
8 | headers = {
9 | "User-Agent": "LiteyukiStudio/nonebot-plugin-marshoai (https://github.com/LiteyukiStudio/nonebot-plugin-marshoai)"
10 | }
11 | async with httpx.AsyncClient() as client:
12 | response = await client.get(url, headers=headers)
13 | # print(response.text)
14 | return response.json()
15 |
16 |
17 | async def get_bangumi_news():
18 | result = await fetch_calendar()
19 | info = ""
20 | try:
21 | for i in result:
22 | weekday = i["weekday"]["cn"]
23 | # print(weekday)
24 | info += f"{weekday}:"
25 | items = i["items"]
26 | for item in items:
27 | name = item["name_cn"]
28 | info += f"《{name}》"
29 | info += "\n"
30 | return info
31 | except Exception as e:
32 | traceback.print_exc()
33 | return ""
34 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_bangumi/tools.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "function": {
5 | "name": "marshoai_bangumi__get_bangumi_news",
6 | "description": "获取今天的新番(动漫)列表,在调用之前,你需要知道今天星期几。"
7 | }
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_basic/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from zhDateTime import DateTime
4 |
5 | weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
6 | time_prompt = "现在的时间是{date_time},{weekday_name},农历{lunar_date}。"
7 |
8 |
9 | async def get_weather(location: str):
10 | return f"{location}的温度是114514℃。"
11 |
12 |
13 | async def get_current_env():
14 | ver = os.popen("uname -a").read()
15 | return str(ver)
16 |
17 |
18 | async def get_current_time():
19 | current_time = DateTime.now()
20 |
21 | return time_prompt.format(
22 | date_time=current_time.strftime("%Y年%m月%d日 %H:%M:%S"),
23 | weekday_name=weekdays[current_time.weekday()],
24 | lunar_date=current_time.to_lunar().date_hanzify(
25 | "{干支年}{生肖}年{月份}月{日期}日"
26 | ),
27 | )
28 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_basic/tools.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "function": {
5 | "name": "marshoai_basic__get_current_time",
6 | "description": "获取现在的日期,时间和星期。"
7 | }
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_basic/tools_test.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "function": {
5 | "name": "marshoai-basic__get_weather",
6 | "description": "当你想查询指定城市的天气时非常有用。",
7 | "parameters": {
8 | "type": "object",
9 | "properties": {
10 | "location": {
11 | "type": "string",
12 | "description": "城市或县区,比如北京市、杭州市、余杭区等。"
13 | }
14 | }
15 | },
16 | "required": [
17 | "location"
18 | ]
19 | }
20 | },
21 | {
22 | "type": "function",
23 | "function": {
24 | "name": "marshoai-basic__get_current_env",
25 | "description": "获取当前的运行环境。",
26 | "parameters": {
27 | }
28 | }
29 | },
30 | {
31 | "type": "function",
32 | "function": {
33 | "name": "marshoai-basic__get_current_time",
34 | "description": "获取现在的时间。",
35 | "parameters": {
36 | }
37 | }
38 | }
39 | ]
40 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_megakits/__init__.py:
--------------------------------------------------------------------------------
1 | from . import mk_common, mk_info, mk_morse_code, mk_nya_code
2 |
3 |
4 | # Twisuki
5 | async def twisuki():
6 | return str(await mk_info.twisuki())
7 |
8 |
9 | # MegaKits
10 | async def megakits():
11 | return str(await mk_info.megakits())
12 |
13 |
14 | # Random Turntable
15 | async def random_turntable(upper: int, lower: int = 0):
16 | return str(await mk_common.random_turntable(upper, lower))
17 |
18 |
19 | # Number Calc
20 | async def number_calc(a: str, b: str, op: str):
21 | return str(await mk_common.number_calc(a, b, op))
22 |
23 |
24 | # MorseCode Encrypt
25 | async def morse_encrypt(msg: str):
26 | return str(await mk_morse_code.morse_encrypt(msg))
27 |
28 |
29 | # MorseCode Decrypt
30 | async def morse_decrypt(msg: str):
31 | return str(await mk_morse_code.morse_decrypt(msg))
32 |
33 |
34 | # NyaCode Encrypt
35 | async def nya_encode(msg: str):
36 | return str(await mk_nya_code.nya_encode(msg))
37 |
38 |
39 | # NyaCode Decrypt
40 | async def nya_decode(msg: str):
41 | return str(await mk_nya_code.nya_decode(msg))
42 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_megakits/mk_common.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 |
4 | async def random_turntable(upper: int, lower: int):
5 | """Random Turntable
6 |
7 | Args:
8 | upper (int): _description_
9 | lower (int): _description_
10 |
11 | Returns:
12 | _type_: _description_
13 | """
14 | return random.randint(lower, upper)
15 |
16 |
17 | async def number_calc(a: str, b: str, op: str) -> str:
18 | """Number Calc
19 |
20 | Args:
21 | a (str): _description_
22 | b (str): _description_
23 | op (str): _description_
24 |
25 | Returns:
26 | str: _description_
27 | """
28 | a, b = float(a), float(b) # type: ignore
29 | match op:
30 | case "+":
31 | return str(a + b) # type: ignore
32 | case "-":
33 | return str(a - b) # type: ignore
34 | case "*":
35 | return str(a * b) # type: ignore
36 | case "/":
37 | return str(a / b) # type: ignore
38 | case "**":
39 | return str(a**b) # type: ignore
40 | case "%":
41 | return str(a % b)
42 | case _:
43 | return "未知运算符"
44 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_megakits/mk_info.py:
--------------------------------------------------------------------------------
1 | # Twisuki
2 | async def twisuki():
3 | return 'Twiuski(苏阳)是megakits插件作者, Github : "https://github.com/Twisuki"'
4 |
5 |
6 | # MegaKits
7 | async def megakits():
8 | return 'MegaKits插件是一个功能混杂的MarshoAI插件, 由Twisuki(Github : "https://github.com/Twisuki")开发, 插件仓库 : "https://github.com/LiteyukiStudio/marsho-toolsets/tree/main/Twisuki/marshoai-megakits"'
9 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_megakits/mk_morse_code.py:
--------------------------------------------------------------------------------
1 | # MorseCode
2 | MorseEncode = {
3 | "A": ".-",
4 | "B": "-...",
5 | "C": "-.-.",
6 | "D": "-..",
7 | "E": ".",
8 | "F": "..-.",
9 | "G": "--.",
10 | "H": "....",
11 | "I": "..",
12 | "J": ".---",
13 | "K": "-.-",
14 | "L": ".-..",
15 | "M": "--",
16 | "N": "-.",
17 | "O": "---",
18 | "P": ".--.",
19 | "Q": "--.-",
20 | "R": ".-.",
21 | "S": "...",
22 | "T": "-",
23 | "U": "..-",
24 | "V": "...-",
25 | "W": ".--",
26 | "X": "-..-",
27 | "Y": "-.--",
28 | "Z": "--..",
29 | "1": ".----",
30 | "2": "..---",
31 | "3": "...--",
32 | "4": "....-",
33 | "5": ".....",
34 | "6": "-....",
35 | "7": "--...",
36 | "8": "---..",
37 | "9": "----.",
38 | "0": "-----",
39 | ".": ".-.-.-",
40 | ":": "---...",
41 | ",": "--..--",
42 | ";": "-.-.-.",
43 | "?": "..--..",
44 | "=": "-...-",
45 | "'": ".----.",
46 | "/": "-..-.",
47 | "!": "-.-.--",
48 | "-": "-....-",
49 | "_": "..--.-",
50 | '"': ".-..-.",
51 | "(": "-.--.",
52 | ")": "-.--.-",
53 | "$": "...-..-",
54 | "&": "....",
55 | "@": ".--.-.",
56 | " ": " ",
57 | }
58 | MorseDecode = {value: key for key, value in MorseEncode.items()}
59 |
60 |
61 | # MorseCode Encrypt
62 | async def morse_encrypt(msg: str):
63 | result = ""
64 | msg = msg.upper()
65 | for char in msg:
66 | if char in MorseEncode:
67 | result += MorseEncode[char]
68 | else:
69 | result += "..--.."
70 | result += " "
71 |
72 | return result
73 |
74 |
75 | # MorseCode Decrypt
76 | async def morse_decrypt(msg: str):
77 | result = ""
78 |
79 | msg_arr = msg.split()
80 | for char in msg_arr:
81 | if char in MorseDecode:
82 | result += MorseDecode[char]
83 | else:
84 | result += "?"
85 |
86 | return result
87 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_megakits/mk_nya_code.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import random
3 |
4 | # NyaCode
5 | NyaCodeCharset = ["喵", "呜", "?", "~"]
6 | NyaCodeSpecialCharset = ["唔", "!", "...", ".."]
7 | NyaCodeEncode = {}
8 | for i in range(64):
9 | triplet = "".join(NyaCodeCharset[(i // (4**j)) % 4] for j in range(3))
10 | NyaCodeEncode[
11 | chr(
12 | 65 + i
13 | if i < 26
14 | else (
15 | 97 + (i - 26)
16 | if i < 52
17 | else 48 + (i - 52) if i < 62 else (43 if i == 62 else 47)
18 | )
19 | )
20 | ] = triplet
21 | NyaCodeDecode = {value: key for key, value in NyaCodeEncode.items()}
22 |
23 |
24 | # NyaCode Encrypt
25 | async def nya_encode(msg: str):
26 | msg_b64str = base64.b64encode(msg.encode()).decode().replace("=", "")
27 | msg_nyastr = "".join(NyaCodeEncode[base64_char] for base64_char in msg_b64str)
28 | result = ""
29 | for char in msg_nyastr:
30 | if char == "呜" and random.random() < 0.5:
31 | result += "!"
32 |
33 | if random.random() < 0.25:
34 | result += random.choice(NyaCodeSpecialCharset) + char
35 | else:
36 | result += char
37 | return result
38 |
39 |
40 | # NyaCode Decrypt
41 | async def nya_decode(msg: str):
42 | msg = msg.replace("唔", "").replace("!", "").replace(".", "")
43 | msg_nyastr = []
44 | i = 0
45 | if len(msg) % 3 != 0:
46 | return "这句话不是正确的猫语"
47 | while i < len(msg):
48 | nyachar = msg[i : i + 3]
49 | try:
50 | if all(char in NyaCodeCharset for char in nyachar):
51 | msg_nyastr.append(nyachar)
52 | i += 3
53 | except Exception:
54 | return "这句话不是正确的猫语"
55 | msg_b64str = "".join(NyaCodeDecode[nya_char] for nya_char in msg_nyastr)
56 | msg_b64str += "=" * (4 - len(msg_b64str) % 4)
57 | try:
58 | result = base64.b64decode(msg_b64str.encode()).decode()
59 | except Exception:
60 | return "翻译失败"
61 | return result
62 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_megakits/tools.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type" : "function",
4 | "function" : {
5 | "name" : "marshoai_megakits__twisuki",
6 | "description" : "介绍插件作者Twisuki(苏阳)"
7 | }
8 | },
9 | {
10 | "type" : "function",
11 | "function" : {
12 | "name" : "marshoai_megakits__megakits",
13 | "description" : "介绍本插件MegaKits"
14 | }
15 | },
16 | {
17 | "type" : "function",
18 | "function" : {
19 | "name" : "marshoai_megakits__random_turntable",
20 | "description" : "随机转盘, 玩家输入上下限(均为整数), 返回一个随机整数",
21 | "parameters" : {
22 | "type" : "object",
23 | "properties" : {
24 | "upper" : {
25 | "type" : "integer",
26 | "description" : "随机数上限"
27 | },
28 | "lower" : {
29 | "type" : "integer",
30 | "description" : "随机数下限"
31 | }
32 | }
33 | },
34 | "require" : [
35 | "upper"
36 | ]
37 | }
38 | },
39 | {
40 | "type" : "function",
41 | "function" : {
42 | "name" : "marshoai_megakits__number_calc",
43 | "description" : "数字计算器, 可对整数 小数做加减乘除, 乘方 取余运算",
44 | "parameters" : {
45 | "type" : "object",
46 | "properties" : {
47 | "a" : {
48 | "type" : "string",
49 | "description" : "第一个运算数"
50 | },
51 | "b" : {
52 | "type" : "string",
53 | "description" : "第二个运算数"
54 | },
55 | "op" : {
56 | "type" : "string",
57 | "description" : "运算符, 目前仅支持 + - * / %(取余) **(乘方)"
58 | }
59 | }
60 | },
61 | "require" : [
62 | "a", "b", "op"
63 | ]
64 | }
65 | },
66 | {
67 | "type" : "function",
68 | "function" : {
69 | "name" : "marshoai_megakits__morse_encrypt",
70 | "description" : "摩尔斯电码加密, 输入一个字符串, 返回由.- 组成的摩尔斯电码",
71 | "parameters" : {
72 | "type" : "object",
73 | "properties" : {
74 | "msg" : {
75 | "type" : "string",
76 | "description" : "录入的字符串(包含字母, 数字, 部分标点符号(.:,;?='/!-_\"()$&@))"
77 | }
78 | },
79 | "require" : [
80 | "msg"
81 | ]
82 | }
83 | }
84 | },
85 | {
86 | "type" : "function",
87 | "function" : {
88 | "name" : "marshoai_megakits__morse_decrypt",
89 | "description" : "摩尔斯电码解码, 输入一个字符串(由.- 组成), 返回由解码",
90 | "parameters" : {
91 | "type" : "object",
92 | "properties" : {
93 | "msg" : {
94 | "type" : "string",
95 | "description" : "录入的字符串(.- )"
96 | }
97 | },
98 | "require" : [
99 | "msg"
100 | ]
101 | }
102 | }
103 | },
104 | {
105 | "type" : "function",
106 | "function" : {
107 | "name" : "marshoai_megakits__nya_encode",
108 | "description" : "转换为猫猫语",
109 | "parameters" : {
110 | "type" : "object",
111 | "properties" : {
112 | "msg" : {
113 | "type" : "string",
114 | "description" : "待转换的字符串"
115 | }
116 | },
117 | "require" : [
118 | "msg"
119 | ]
120 | }
121 | }
122 | },
123 | {
124 | "type": "function",
125 | "function": {
126 | "name": "marshoai_megakits__nya_decode",
127 | "description": "翻译猫语",
128 | "parameters": {
129 | "type": "object",
130 | "properties": {
131 | "msg": {
132 | "type": "string",
133 | "description": "录入的猫语"
134 | }
135 | },
136 | "require": [
137 | "msg"
138 | ]
139 | }
140 | }
141 | }
142 | ]
143 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_memory/__init__.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | from nonebot import require
4 |
5 | require("nonebot_plugin_localstore")
6 | import json
7 |
8 | from nonebot_plugin_localstore import get_data_file
9 |
10 | memory_path = get_data_file("marshoai", "memory.json")
11 | if not Path(memory_path).exists():
12 | with open(memory_path, "w", encoding="utf-8") as f:
13 | json.dump({}, f, ensure_ascii=False, indent=4)
14 | print(memory_path)
15 |
16 |
17 | async def write_memory(memory: str, user_id: str):
18 |
19 | with open(memory_path, "r", encoding="utf-8") as f:
20 | memory_data = json.load(f)
21 |
22 | memorys = memory_data.get(user_id, [])
23 | memorys.append(memory)
24 | memory_data[user_id] = memorys
25 |
26 | with open(memory_path, "w", encoding="utf-8") as f:
27 | json.dump(memory_data, f, ensure_ascii=False, indent=4)
28 |
29 | return "记忆已经保存啦~"
30 |
31 |
32 | async def read_memory(user_id: str):
33 | with open(memory_path, "r", encoding="utf-8") as f:
34 | memory_data = json.load(f)
35 | memorys = memory_data.get(user_id, [])
36 | if not memorys:
37 | return "好像对ta还没有任何记忆呢~"
38 |
39 | return "这些是有关ta的记忆:" + "\n".join(memorys)
40 |
41 |
42 | async def organize_memories():
43 | with open(memory_path, "r", encoding="utf-8") as f:
44 | memory_data = json.load(f)
45 | for i in memory_data:
46 | ...
47 | # TODO 用大模型对记忆进行整理
48 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_memory/tools.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "function": {
5 | "name": "marshoai_memory__write_memory",
6 | "description": "如果在上下中你看见并觉得应该记住的人的行为与事件,请调用这个函数,并将记忆内容写入。请尽量每次都调用,总结ta的习惯、爱好和性格,以及你对ta的印象和ta对你的印象",
7 | "parameters": {
8 | "type": "object",
9 | "properties": {
10 | "memory": {
11 | "type": "string",
12 | "description": "你想记住的内容,概括并保留关键内容。"
13 | },
14 | "user_id": {
15 | "type": "string",
16 | "description": "你想记住的人的id。"
17 | }
18 | }
19 | },
20 | "required": [
21 | "memory",
22 | "user_id"
23 | ]
24 | }
25 | },
26 | {
27 | "type": "function",
28 | "function": {
29 | "name": "marshoai_memory__read_memory",
30 | "description": "每当你想要获取更多有关某人的信息的时候,请调用这个函数。",
31 | "parameters": {
32 | "type": "object",
33 | "properties": {
34 | "user_id": {
35 | "type": "string",
36 | "description": "你想获取的人的id。"
37 | }
38 | }
39 | },
40 | "required": [
41 | "user_id"
42 | ]
43 | }
44 | }
45 | ]
46 |
47 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_meogirl/__init__.py:
--------------------------------------------------------------------------------
1 | from . import mg_info, mg_introduce, mg_search
2 |
3 |
4 | # meogirl
5 | async def meogirl():
6 | return mg_info.meogirl()
7 |
8 |
9 | # Search
10 | async def search(msg: str, num: int = 3):
11 | return str(await mg_search.search(msg, num))
12 |
13 |
14 | # Show
15 | async def introduce(msg: str):
16 | return str(await mg_introduce.introduce(msg))
17 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_meogirl/mg_info.py:
--------------------------------------------------------------------------------
1 | # Meogirl
2 | def meogirl():
3 | return 'Meogirl指的是"萌娘百科"(https://zh.moegirl.org.cn/ , 简称"萌百"), 是一个"万物皆可萌的百科全书!"; 同时, MarshoTools也配有"Meogirl"插件, 可调用萌百的api'
4 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_meogirl/mg_introduce.py:
--------------------------------------------------------------------------------
1 | import re
2 | import urllib.parse
3 |
4 | import httpx
5 | from bs4 import BeautifulSoup # type: ignore
6 | from nonebot.log import logger
7 |
8 | headers = {
9 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
10 | }
11 |
12 |
13 | async def get_async_data(url):
14 | async with httpx.AsyncClient(timeout=None) as client:
15 | return await client.get(url, headers=headers)
16 |
17 |
18 | async def introduce(msg: str):
19 | logger.info(f'介绍 : "{msg}" ...')
20 | result = ""
21 |
22 | url = "https://mzh.moegirl.org.cn/" + urllib.parse.quote_plus(msg)
23 | response = await get_async_data(url)
24 | logger.success(f'连接"{url}"完成, 状态码 : {response.status_code}')
25 |
26 | soup = BeautifulSoup(response.text, "html.parser")
27 |
28 | # 正常页
29 | if response.status_code == 200:
30 | """
31 | 萌娘百科页面结构
32 | div#mw-content-text
33 | └── div#404search # 空白页面出现
34 | └── div.mw-parser-output # 正常页面
35 | └── div, p, table ... # 大量的解释项
36 | """
37 | result += msg + "\n"
38 |
39 | img = soup.find("img", class_="infobox-image")
40 | if img:
41 | result += f" \n"
42 |
43 | div = soup.find("div", class_="mw-parser-output")
44 | if div:
45 | p_tags = div.find_all("p")
46 | num = 0
47 | for p_tag in p_tags:
48 | p = str(p_tag)
49 | p = re.sub(
50 | r"|", "", p, flags=re.DOTALL
51 | )
52 | p = re.sub(r"<.*?>", "", p, flags=re.DOTALL)
53 | p = re.sub(r"\[.*?]", "", p, flags=re.DOTALL)
54 |
55 | if p != "":
56 | result += str(p)
57 |
58 | num += 1
59 | if num >= 20:
60 | break
61 | return result
62 |
63 | # 空白页
64 | elif response.status_code == 404:
65 | logger.info(f'未找到"{msg}", 进行搜索')
66 |
67 | from . import mg_search
68 |
69 | context = await mg_search.search(msg, 1)
70 | keyword = re.search(r".*?\n", context, flags=re.DOTALL).group()[:-1] # type: ignore
71 |
72 | logger.success(f'搜索完成, 打开"{keyword}"')
73 | return await introduce(keyword)
74 |
75 | # 搜索失败
76 | elif response.status_code == 301:
77 | return f"未找到{msg}"
78 |
79 | else:
80 | logger.error(f"网络错误, 状态码 : {response.status_code}")
81 | return f"网络错误, 状态码 : {response.status_code}"
82 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_meogirl/mg_search.py:
--------------------------------------------------------------------------------
1 | import urllib.parse
2 |
3 | import httpx
4 | from bs4 import BeautifulSoup # type: ignore
5 | from nonebot.log import logger
6 |
7 | headers = {
8 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
9 | }
10 |
11 |
12 | async def get_async_data(url):
13 | async with httpx.AsyncClient(timeout=None) as client:
14 | return await client.get(url, headers=headers)
15 |
16 |
17 | async def search(msg: str, num: int):
18 | logger.info(f'搜索 : "{msg}" ...')
19 | result = ""
20 |
21 | url = "https://mzh.moegirl.org.cn/index.php?search=" + urllib.parse.quote_plus(msg)
22 | response = await get_async_data(url)
23 | logger.success(f'连接"{url}"完成, 状态码 : {response.status_code}')
24 |
25 | # 正常搜索
26 | if response.status_code == 200:
27 | """
28 | 萌娘百科搜索页面结构
29 | div.searchresults
30 | └── p ...
31 | └── ul.mw-search-results # 若无, 证明无搜索结果
32 | └── li # 一个搜索结果
33 | └── div.mw-search-result-heading > a # 标题
34 | └── div.mw-searchresult # 内容
35 | └── div.mw-search-result-data
36 | └── li ...
37 | └── li ...
38 | """
39 | soup = BeautifulSoup(response.text, "html.parser")
40 |
41 | # 检测ul.mw-search-results, 是否有结果
42 | ul_tag = soup.find("ul", class_="mw-search-results")
43 | if ul_tag:
44 | li_tags = ul_tag.find_all("li")
45 | for li_tag in li_tags:
46 |
47 | div_heading = li_tag.find("div", class_="mw-search-result-heading")
48 | if div_heading:
49 | a_tag = div_heading.find("a")
50 | result += a_tag["title"] + "\n"
51 | logger.info(f'搜索到 : "{a_tag["title"]}"')
52 |
53 | div_result = li_tag.find("div", class_="searchresult")
54 | if div_result:
55 | content = (
56 | str(div_result)
57 | .replace('', "")
58 | .replace("
", "")
59 | )
60 | content = content.replace('', "").replace(
61 | " ", ""
62 | )
63 | result += content + "\n"
64 |
65 | num -= 1
66 | if num == 0:
67 | break
68 | return result
69 |
70 | # 无ul.mw-search-results, 无结果
71 | else:
72 | logger.info("无结果")
73 | return "无结果"
74 |
75 | # 重定向
76 | elif response.status_code == 302:
77 | logger.info(f'"{msg}"已被重定向至"{response.headers.get("location")}"')
78 | # 读取重定向结果
79 | from . import mg_introduce
80 |
81 | return await mg_introduce.introduce(msg)
82 |
83 | else:
84 | logger.error(f"网络错误, 状态码 : {response.status_code}")
85 | return f"网络错误, 状态码 : {response.status_code}"
86 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/tools/marshoai_meogirl/tools.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type" : "function",
4 | "function" : {
5 | "name" : "marshoai_meogirl__meogirl",
6 | "description" : "介绍Meogirl"
7 | }
8 | },
9 | {
10 | "type": "function",
11 | "function": {
12 | "name": "marshoai_meogirl__search",
13 | "description": "查找/搜索 某角色/事物 (使用萌娘百科)",
14 | "parameters": {
15 | "type": "object",
16 | "properties": {
17 | "msg": {
18 | "type": "string",
19 | "description": "搜索关键词"
20 | },
21 | "num": {
22 | "type": "integer",
23 | "description": "数据显示条数, 默认3, 可留空"
24 | }
25 | }
26 | },
27 | "required": [
28 | "msg"
29 | ]
30 | }
31 | },
32 | {
33 | "type" : "function",
34 | "function" : {
35 | "name" : "marshoai_meogirl__introduce",
36 | "description" : "介绍/展示 某角色/事物 (使用萌娘百科)",
37 | "parameters" : {
38 | "type" : "object",
39 | "properties" : {
40 | "msg" : {
41 | "type": "string",
42 | "description": "关键词"
43 | }
44 | }
45 | },
46 | "required": [
47 | "msg"
48 | ]
49 | }
50 | }
51 | ]
52 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/util_hunyuan.py:
--------------------------------------------------------------------------------
1 | import json
2 | import types
3 |
4 | from tencentcloud.common import credential # type: ignore
5 | from tencentcloud.common.exception.tencent_cloud_sdk_exception import ( # type: ignore
6 | TencentCloudSDKException,
7 | )
8 | from tencentcloud.common.profile.client_profile import ClientProfile # type: ignore
9 | from tencentcloud.common.profile.http_profile import HttpProfile # type: ignore
10 | from tencentcloud.hunyuan.v20230901 import hunyuan_client # type: ignore
11 | from tencentcloud.hunyuan.v20230901 import models # type: ignore
12 |
13 | from .config import config
14 |
15 |
16 | def generate_image(prompt: str):
17 | cred = credential.Credential(
18 | config.marshoai_tencent_secretid, config.marshoai_tencent_secretkey
19 | )
20 | # 实例化一个http选项,可选的,没有特殊需求可以跳过
21 | httpProfile = HttpProfile()
22 | httpProfile.endpoint = "hunyuan.tencentcloudapi.com"
23 |
24 | # 实例化一个client选项,可选的,没有特殊需求可以跳过
25 | clientProfile = ClientProfile()
26 | clientProfile.httpProfile = httpProfile
27 | client = hunyuan_client.HunyuanClient(cred, "ap-guangzhou", clientProfile)
28 |
29 | req = models.TextToImageLiteRequest()
30 | params = {"Prompt": prompt, "RspImgType": "url", "Resolution": "1080:1920"}
31 | req.from_json_string(json.dumps(params))
32 |
33 | # 返回的resp是一个TextToImageLiteResponse的实例,与请求对象对应
34 | resp = client.TextToImageLite(req)
35 | # 输出json格式的字符串回包
36 | return resp.to_json_string()
37 |
--------------------------------------------------------------------------------
/nonebot_plugin_marshoai/utils/processor.py:
--------------------------------------------------------------------------------
1 | from nonebot.log import logger
2 | from openai import AsyncStream
3 | from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessage
4 | from openai.types.chat.chat_completion import Choice
5 |
6 |
7 | async def process_chat_stream(
8 | stream: AsyncStream[ChatCompletionChunk],
9 | ) -> ChatCompletion:
10 | reasoning_contents = ""
11 | answer_contents = ""
12 | last_chunk = None
13 | is_first_token_appeared = False
14 | is_answering = False
15 | async for chunk in stream:
16 | last_chunk = chunk
17 | # print(chunk)
18 | if not is_first_token_appeared:
19 | logger.info(f"{chunk.id}: 第一个 token 已出现")
20 | is_first_token_appeared = True
21 | if not chunk.choices:
22 | logger.info("Usage:", chunk.usage)
23 | else:
24 | delta = chunk.choices[0].delta
25 | if (
26 | hasattr(delta, "reasoning_content")
27 | and delta.reasoning_content is not None
28 | ):
29 | reasoning_contents += delta.reasoning_content
30 | else:
31 | if not is_answering:
32 | logger.info(
33 | f"{chunk.id}: 思维链已输出完毕或无 reasoning_content 字段输出"
34 | )
35 | is_answering = True
36 | if delta.content is not None:
37 | answer_contents += delta.content
38 | # print(last_chunk)
39 | # 创建新的 ChatCompletion 对象
40 | if last_chunk and last_chunk.choices:
41 | message = ChatCompletionMessage(
42 | content=answer_contents,
43 | role="assistant",
44 | tool_calls=last_chunk.choices[0].delta.tool_calls, # type: ignore
45 | )
46 | if reasoning_contents != "":
47 | setattr(message, "reasoning_content", reasoning_contents)
48 | choice = Choice(
49 | finish_reason=last_chunk.choices[0].finish_reason, # type: ignore
50 | index=last_chunk.choices[0].index,
51 | message=message,
52 | )
53 | return ChatCompletion(
54 | id=last_chunk.id,
55 | choices=[choice],
56 | created=last_chunk.created,
57 | model=last_chunk.model,
58 | system_fingerprint=last_chunk.system_fingerprint,
59 | object="chat.completion",
60 | usage=last_chunk.usage,
61 | )
62 | else:
63 | return ChatCompletion(
64 | id="",
65 | choices=[],
66 | created=0,
67 | model="",
68 | system_fingerprint="",
69 | object="chat.completion",
70 | usage=None,
71 | )
72 |
73 |
74 | async def process_completion_to_details(completion: ChatCompletion) -> str:
75 | usage_text = ""
76 | usage = completion.usage
77 | if usage is None:
78 | usage_text = "无"
79 | else:
80 | usage_text = str(usage)
81 |
82 | details_text = f"""=========消息详情=========
83 | 模型: {completion.model}
84 | 消息 ID: {completion.id}
85 | 用量信息: {usage_text}"""
86 | # print(details_text)
87 | return details_text
88 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "devDependencies": {"vitepress": "^1.5.0", "vitepress-sidebar": "^1.30.2"},
4 | "scripts": {
5 | "docs:dev": "vitepress dev docs --host",
6 | "docs:build": "vitepress build docs",
7 | "docs:preview": "vitepress preview docs"
8 | },
9 | "dependencies": {"vue": "^3.5.13"}
10 | }
11 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "nonebot-plugin-marshoai"
3 | dynamic = ["version"]
4 | description = "Nonebot2插件,调用Azure OpenAI等AI服务实现猫娘聊天"
5 | readme = "README.md"
6 | requires-python = "<4.0,>=3.10"
7 | authors = [
8 | { name = "Asankilp", email = "asankilp@outlook.com" },
9 | { name="LiteyukiStudio", email = "support@liteyuki.icu"}
10 | ]
11 | dependencies = [
12 | "nonebot2>=2.4.0",
13 | "nonebot-plugin-alconna>=0.57.1",
14 | "nonebot-plugin-localstore>=0.7.1",
15 | "zhDatetime>=2.0.0",
16 | "aiohttp>=3.9",
17 | "httpx>=0.27.0",
18 | "ruamel.yaml>=0.18.6",
19 | "pyyaml>=6.0.2",
20 | "psutil>=6.1.0",
21 | "beautifulsoup4>=4.12.3",
22 | "pydantic>=2.10.3",
23 | "litedoc>=0.1.0.dev20241214103915",
24 | "newspaper3k>=0.2.8",
25 | "lxml[html_clean]>=5.3.0",
26 | "aiofiles>=24.1.0",
27 | "sumy>=0.11.0",
28 | "azure-ai-inference>=1.0.0b6",
29 | "watchdog>=6.0.0",
30 | "nonebot-plugin-apscheduler>=0.5.0",
31 | "openai>=1.58.1",
32 | "nonebot-plugin-argot>=0.1.7"
33 |
34 | ]
35 | license = { text = "MIT, Mulan PSL v2" }
36 |
37 | [project.urls]
38 | Homepage = "https://marsho.liteyuki.org/"
39 |
40 |
41 | [tool.nonebot]
42 | plugins = ["nonebot_plugin_marshoai"]
43 | # 测试用
44 | adapters = [
45 | { name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11" },
46 | ]
47 |
48 | [tool.pdm]
49 | distribution = true
50 |
51 | [tool.isort]
52 | profile = "black"
53 |
54 |
55 | [tool.pdm.version]
56 | source = "scm"
57 | tag_filter = "v*"
58 | tag_regex = '^v(?:\D*)?(?P([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|c|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$)$'
59 | fallback_version = "0.1.0"
60 |
61 | [tool.pdm.build]
62 | includes = []
63 |
64 | [build-system]
65 | requires = ["pdm-backend"]
66 | build-backend = "pdm.backend"
67 |
68 | [dependency-groups]
69 | dev = [
70 | "nb-cli>=1.4.2",
71 | "pytest>=8.3.4",
72 | "pre-commit>=4.0.1",
73 | "nonebot-adapter-onebot>=2.4.6",
74 | "mypy>=1.13.0",
75 | "black>=24.10.0",
76 | "litedoc>=0.1.0.dev20240906203154",
77 | "viztracer>=1.0.0",
78 | "types-aiofiles"
79 | ]
80 | test = [
81 | "nonebug>=0.4.3",
82 | ]
83 |
84 | [tool.ruff.lint]
85 | ignore = ["E402", "F405"]
86 |
--------------------------------------------------------------------------------
/resources/README.md:
--------------------------------------------------------------------------------
1 | # Marsho Resources
2 |
3 | > Copyright (c) 2025 [@Asankilp](https://github.com/Asankilp)
4 |
5 | 本目录存放 Marsho 的图像资源(logo, icon),均由[Asankilp](https://github.com/Asankilp)绘制。\
6 | 上述所有资源均在[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)许可下提供。
7 |
--------------------------------------------------------------------------------
/resources/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/bg.png
--------------------------------------------------------------------------------
/resources/marsho-640x360.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/marsho-640x360.png
--------------------------------------------------------------------------------
/resources/marsho-bg-1x1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/marsho-bg-1x1.png
--------------------------------------------------------------------------------
/resources/marsho-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/marsho-bg.png
--------------------------------------------------------------------------------
/resources/marsho-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/marsho-icon.png
--------------------------------------------------------------------------------
/resources/marsho-new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/marsho-new.png
--------------------------------------------------------------------------------
/resources/marsho-no-paw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/marsho-no-paw.png
--------------------------------------------------------------------------------
/resources/marsho-paw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/marsho-paw.png
--------------------------------------------------------------------------------
/resources/marsho.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiteyukiStudio/nonebot-plugin-marshoai/a18d85d45c4b4cd8ff5d5484304e46726bf09907/resources/marsho.png
--------------------------------------------------------------------------------
/tests/test_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 |
4 | def test_none():
5 | """基准测试示例"""
6 | logging.info("测试成功")
7 | pass
8 |
--------------------------------------------------------------------------------