🎨 更新 Python 依赖版本至 3.10,新增 pre-commit 工作流及配置文件

This commit is contained in:
远野千束(神羽) 2024-12-13 02:23:38 +08:00
parent 8462830c91
commit 8defcfdd66
38 changed files with 350 additions and 229 deletions

24
.github/workflows/pre-commit.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Pre-commit checks
on: [push, pull_request]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.10' # 选择适合你的项目的 Python 版本
- name: Install dependencies
run: |
python -m pip install pdm
pdm install
- name: Run pre-commit
run: pre-commit run --all-files

View File

@ -43,4 +43,4 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: Pytest-test-report name: Pytest-test-report
path: report path: report

2
.gitignore vendored
View File

@ -176,4 +176,4 @@ praises.json
config/ config/
# dev # dev
.vscode/ .vscode/

25
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,25 @@
fail_fast: true
repos:
- repo: https://github.com/timothycrosley/isort
rev: 5.13.2
hooks:
- id: isort
# - repo: https://github.com/psf/black
# rev: 24.4.2
# hooks:
# - id: black
# args: [--config=./pyproject.toml]
# - repo: https://github.com/pre-commit/mirrors-mypy
# rev: v0.910
# hooks:
# - id: mypy
# - repo: https://github.com/pre-commit/pre-commit-hooks
# rev: v4.0.1
# hooks:
# - id: trailing-whitespace
# - id: end-of-file-fixer
# - id: check-yaml
# - id: check-added-large-files

View File

@ -22,9 +22,9 @@ _✨ 使用 OpenAI 标准格式 API 的聊天机器人插件 ✨_
## 📖 介绍 ## 📖 介绍
通过调用 OpenAI 标准格式 API(例如由 Azure OpenAI 驱动GitHub Models 提供访问的生成式 AI 推理 API) 来实现聊天的插件。 通过调用 OpenAI 标准格式 API(例如由 Azure OpenAI 驱动GitHub Models 提供访问的生成式 AI 推理 API) 来实现聊天的插件。
插件内置了猫娘小棉(Marsho)的人物设定,可以进行可爱的聊天! 插件内置了猫娘小棉(Marsho)的人物设定,可以进行可爱的聊天!
*谁不喜欢回复消息快又可爱的猫娘呢?* *谁不喜欢回复消息快又可爱的猫娘呢?*
**对 OneBot 以外的适配器与非 GitHub Models API的支持未经过完全验证。** **对 OneBot 以外的适配器与非 GitHub Models API的支持未经过完全验证。**
[Melobot 实现](https://github.com/LiteyukiStudio/marshoai-melo) [Melobot 实现](https://github.com/LiteyukiStudio/marshoai-melo)
@ -101,13 +101,13 @@ _✨ 使用 OpenAI 标准格式 API 的聊天机器人插件 ✨_
当 nonebot 连接到支持的 OneBot v11 实现端时,可以接收头像双击戳一戳消息并进行响应。详见`MARSHOAI_POKE_SUFFIX`配置项。 当 nonebot 连接到支持的 OneBot v11 实现端时,可以接收头像双击戳一戳消息并进行响应。详见`MARSHOAI_POKE_SUFFIX`配置项。
## 🛠️ 小棉工具 ## 🛠️ 小棉工具
小棉工具(MarshoTools)是`v0.5.0`版本的新增功能,支持加载外部函数库来为 Marsho 提供 Function Call 功能。[使用文档](./README_TOOLS.md) 小棉工具(MarshoTools)是`v0.5.0`版本的新增功能,支持加载外部函数库来为 Marsho 提供 Function Call 功能。[使用文档](./README_TOOLS.md)
## 👍 夸赞名单 ## 👍 夸赞名单
夸赞名单存储于插件数据目录下的`praises.json`里(该目录路径会在 Bot 启动时输出到日志),当配置项为`true` 夸赞名单存储于插件数据目录下的`praises.json`里(该目录路径会在 Bot 启动时输出到日志),当配置项为`true`
时发起一次聊天后自动生成,包含人物名字与人物优点两个基本数据。 时发起一次聊天后自动生成,包含人物名字与人物优点两个基本数据。
存储于其中的人物会被 Marsho “认识”和“喜欢”。 存储于其中的人物会被 Marsho “认识”和“喜欢”。
其结构类似于: 其结构类似于:
```json ```json
@ -176,10 +176,10 @@ _✨ 使用 OpenAI 标准格式 API 的聊天机器人插件 ✨_
## ❤ 鸣谢&版权说明 ## ❤ 鸣谢&版权说明
本项目使用了以下项目的代码: 本项目使用了以下项目的代码:
- [nonebot-plugin-latex](https://github.com/EillesWan/nonebot-plugin-latex) - [nonebot-plugin-latex](https://github.com/EillesWan/nonebot-plugin-latex)
"Marsho" logo 由 [@Asankilp](https://github.com/Asankilp) "Marsho" logo 由 [@Asankilp](https://github.com/Asankilp)
绘制,基于 [CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 许可下提供。 绘制,基于 [CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 许可下提供。
"nonebot-plugin-marshoai" 基于 [MIT](./LICENSE-MIT) 许可下提供。 "nonebot-plugin-marshoai" 基于 [MIT](./LICENSE-MIT) 许可下提供。
部分指定的代码基于 [Mulan PSL v2](./LICENSE-MULAN) 许可下提供。 部分指定的代码基于 [Mulan PSL v2](./LICENSE-MULAN) 许可下提供。

View File

@ -22,11 +22,11 @@ _✨ A chat bot plugin which use OpenAI standard API ✨_
## 📖 Indroduction ## 📖 Indroduction
A plugin made by call OpenAI standard API(Such as GitHub Models API) A plugin made by call OpenAI standard API(Such as GitHub Models API)
Plugin internally installed the catgirl character of Marsho, is able to have a cute conversation! Plugin internally installed the catgirl character of Marsho, is able to have a cute conversation!
*Who don't like a cute catgirl with fast answer speed* *Who don't like a cute catgirl with fast answer speed*
**Support for adapters other than OneBot and non-Github Models APIs is not fully verified.** **Support for adapters other than OneBot and non-Github Models APIs is not fully verified.**
@ -50,7 +50,7 @@ Plugin internally installed the catgirl character of Marsho, is able to have a c
<details open> <details open>
<summary>Install with nb-cli</summary> <summary>Install with nb-cli</summary>
Open shell under the root directory of nonebot2, input the command below. Open shell under the root directory of nonebot2, input the command below.
nb plugin install nonebot-plugin-marshoai nb plugin install nonebot-plugin-marshoai
@ -59,7 +59,7 @@ Open shell under the root directory of nonebot2, input the command below.
<details> <details>
<summary>Install with pack manager</summary> <summary>Install with pack manager</summary>
Open shell under the plugin directory of nonebot2, input corresponding command according to your pack manager. Open shell under the plugin directory of nonebot2, input corresponding command according to your pack manager.
<details> <details>
@ -108,7 +108,7 @@ When nonebot linked to OneBot v11 adapter, can recieve double click and response
## 🛠️ MarshoTools ## 🛠️ MarshoTools
MarshoTools is a feature added in `v0.5.0`, support loading external function library to provide Function Call for Marsho. [Documentation](./README_TOOLS_EN.md) MarshoTools is a feature added in `v0.5.0`, support loading external function library to provide Function Call for Marsho. [Documentation](./README_TOOLS_EN.md)
## 👍 Praise list ## 👍 Praise list
@ -184,7 +184,7 @@ Add options in the `.env` file from the diagram below in nonebot2 project.
## ❤ Thanks&Copyright ## ❤ Thanks&Copyright
This project uses the following code from other projects: This project uses the following code from other projects:
- [nonebot-plugin-latex](https://github.com/EillesWan/nonebot-plugin-latex) - [nonebot-plugin-latex](https://github.com/EillesWan/nonebot-plugin-latex)
"Marsho" logo contributed by [@Asankilp](https://github.com/Asankilp), "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. licensed under [CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) lisense.

View File

@ -1,5 +1,5 @@
# 🛠️小棉工具 # 🛠️小棉工具
小棉工具(MarshoTools)是一个简单的模块加载器,允许从插件数据目录下的`tools`目录内加载数个工具包与其中定义的函数,以供 AI 模型调用。 小棉工具(MarshoTools)是一个简单的模块加载器,允许从插件数据目录下的`tools`目录内加载数个工具包与其中定义的函数,以供 AI 模型调用。
有关 Function Call 的更多信息,请参阅[OpenAI 官方文档](https://platform.openai.com/docs/guides/function-calling)。 有关 Function Call 的更多信息,请参阅[OpenAI 官方文档](https://platform.openai.com/docs/guides/function-calling)。
## ✍️ 编写工具 ## ✍️ 编写工具
@ -64,11 +64,11 @@ async def get_current_time():
} }
] ]
``` ```
在这个文件中定义了两个已经编写好的函数,该定义文件将被输入到 AI 模型中,来让 AI 模型知道这些函数的存在与调用方法。 在这个文件中定义了两个已经编写好的函数,该定义文件将被输入到 AI 模型中,来让 AI 模型知道这些函数的存在与调用方法。
**函数调用名称**的命名方式比较特别。以获取天气的函数为例,它的函数调用名称`marshoai-example__get_weather`包含三个信息: **函数调用名称**的命名方式比较特别。以获取天气的函数为例,它的函数调用名称`marshoai-example__get_weather`包含三个信息:
- 前面的**marshoai-example**即为该函数所在工具包的**包名**。 - 前面的**marshoai-example**即为该函数所在工具包的**包名**。
- 后面的**get_weather**是这个函数在代码里的名称。 - 后面的**get_weather**是这个函数在代码里的名称。
- 中间的两个下划线是用于分割这两个信息的分隔符。 - 中间的两个下划线是用于分割这两个信息的分隔符。
使用这种命名方式,是为了兼容更多的 OpenAI 标准格式 API。因此在给工具包和函数取名时不要使用带有两个下划线的名称。 使用这种命名方式,是为了兼容更多的 OpenAI 标准格式 API。因此在给工具包和函数取名时不要使用带有两个下划线的名称。
### 测试函数 ### 测试函数
@ -78,7 +78,7 @@ async def get_current_time():
> marsho 深圳天气怎么样 > marsho 深圳天气怎么样
深圳的天气显示温度是114514°C真是不可思议呢这一定是个误报吧~(≧▽≦) 希望你那里有个好天气哦! 深圳的天气显示温度是114514°C真是不可思议呢这一定是个误报吧~(≧▽≦) 希望你那里有个好天气哦!
> marsho 分别告诉我下北泽,杭州,苏州的天气 > marsho 分别告诉我下北泽,杭州,苏州的天气
下北泽、杭州和苏州的天气都显示温度为114514°C呢这么奇怪的温度一定是个误报吧~(≧▽≦) 下北泽、杭州和苏州的天气都显示温度为114514°C呢这么奇怪的温度一定是个误报吧~(≧▽≦)
如果要查看真实的天气情况,建议查看专业天气预报哦~ 如果要查看真实的天气情况,建议查看专业天气预报哦~
> marsho 现在几点了 > marsho 现在几点了

View File

@ -3,8 +3,8 @@ from nonebot.plugin import require
require("nonebot_plugin_alconna") require("nonebot_plugin_alconna")
require("nonebot_plugin_localstore") require("nonebot_plugin_localstore")
from nonebot import get_driver, logger
import nonebot_plugin_localstore as store import nonebot_plugin_localstore as store
from nonebot import get_driver, logger
# from .hunyuan import * # from .hunyuan import *
from .azure import * from .azure import *

View File

@ -1,33 +1,23 @@
import traceback
import contextlib import contextlib
from typing import Optional import traceback
from pathlib import Path from pathlib import Path
from typing import Optional
from arclet.alconna import Alconna, Args, AllParam import nonebot_plugin_localstore as store
from azure.ai.inference.models import ( from arclet.alconna import Alconna, AllParam, Args
UserMessage, from azure.ai.inference.models import (AssistantMessage,
AssistantMessage, ChatCompletionsToolCall,
ToolMessage, CompletionsFinishReason,
TextContentItem, ImageContentItem, ImageUrl,
ImageContentItem, TextContentItem, ToolMessage,
ImageUrl, UserMessage)
CompletionsFinishReason,
ChatCompletionsToolCall,
)
from azure.core.credentials import AzureKeyCredential from azure.core.credentials import AzureKeyCredential
from nonebot import on_command, on_message, logger, get_driver from nonebot import get_driver, logger, on_command, on_message
from nonebot.adapters import Message, Event from nonebot.adapters import Event, Message
from nonebot.params import CommandArg from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.rule import Rule, to_me from nonebot.rule import Rule, to_me
from nonebot_plugin_alconna import ( from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg, on_alconna
on_alconna,
MsgTarget,
UniMessage,
UniMsg,
)
import nonebot_plugin_localstore as store
from .metadata import metadata from .metadata import metadata
from .models import MarshoContext, MarshoTools from .models import MarshoContext, MarshoTools
@ -203,7 +193,7 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
not text not text
and event.get_message().extract_plain_text() != config.marshoai_default_name and event.get_message().extract_plain_text() != config.marshoai_default_name
): ):
text = event.get_message() text = event.get_message() # type: ignore
if not text: if not text:
# 发送说明 # 发送说明
await UniMessage(metadata.usage + "\n当前使用的模型:" + model_name).send() await UniMessage(metadata.usage + "\n当前使用的模型:" + model_name).send()
@ -233,12 +223,12 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
for i in text: for i in text:
if i.type == "text": if i.type == "text":
if is_support_image_model: if is_support_image_model:
usermsg += [TextContentItem(text=i.data["text"] + nickname_prompt)] usermsg += [TextContentItem(text=i.data["text"] + nickname_prompt)] # type: ignore
else: else:
usermsg += str(i.data["text"] + nickname_prompt) usermsg += str(i.data["text"] + nickname_prompt) # type: ignore
elif i.type == "image": elif i.type == "image":
if is_support_image_model: if is_support_image_model:
usermsg.append( usermsg.append( # type: ignore
ImageContentItem( ImageContentItem(
image_url=ImageUrl( image_url=ImageUrl(
url=str(await get_image_b64(i.data["url"])) url=str(await get_image_b64(i.data["url"]))
@ -260,7 +250,7 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
response = await make_chat( response = await make_chat(
client=client, client=client,
model_name=model_name, model_name=model_name,
msg=context_msg + [UserMessage(content=usermsg)], msg=context_msg + [UserMessage(content=usermsg)], # type: ignore
tools=tools.get_tools_list(), tools=tools.get_tools_list(),
) )
# await UniMessage(str(response)).send() # await UniMessage(str(response)).send()
@ -268,7 +258,7 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
if choice["finish_reason"] == CompletionsFinishReason.STOPPED: if choice["finish_reason"] == CompletionsFinishReason.STOPPED:
# 当对话成功时将dict的上下文添加到上下文类中 # 当对话成功时将dict的上下文添加到上下文类中
context.append( context.append(
UserMessage(content=usermsg).as_dict(), target.id, target.private UserMessage(content=usermsg).as_dict(), target.id, target.private # type: ignore
) )
context.append(choice.message.as_dict(), target.id, target.private) context.append(choice.message.as_dict(), target.id, target.private)
if [target.id, target.private] not in target_list: if [target.id, target.private] not in target_list:
@ -314,12 +304,12 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
tool_call.function.name, function_args tool_call.function.name, function_args
) # 获取返回值 ) # 获取返回值
tool_msg.append( tool_msg.append(
ToolMessage(tool_call_id=tool_call.id, content=func_return) ToolMessage(tool_call_id=tool_call.id, content=func_return) # type: ignore
) )
response = await make_chat( response = await make_chat(
client=client, client=client,
model_name=model_name, model_name=model_name,
msg=context_msg + [UserMessage(content=usermsg)] + tool_msg, msg=context_msg + [UserMessage(content=usermsg)] + tool_msg, # type: ignore
tools=tools.get_tools_list(), tools=tools.get_tools_list(),
) )
choice = response.choices[0] choice = response.choices[0]
@ -327,7 +317,7 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
# 对话成功 添加上下文 # 对话成功 添加上下文
context.append( context.append(
UserMessage(content=usermsg).as_dict(), target.id, target.private UserMessage(content=usermsg).as_dict(), target.id, target.private # type: ignore
) )
# context.append(tool_msg, target.id, target.private) # context.append(tool_msg, target.id, target.private)
context.append(choice.message.as_dict(), target.id, target.private) context.append(choice.message.as_dict(), target.id, target.private)
@ -351,6 +341,7 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
with contextlib.suppress(ImportError): # 优化先不做() with contextlib.suppress(ImportError): # 优化先不做()
import nonebot.adapters.onebot.v11 # type: ignore import nonebot.adapters.onebot.v11 # type: ignore
from .azure_onebot import poke_notify from .azure_onebot import poke_notify
@poke_notify.handle() @poke_notify.handle()

View File

@ -1,5 +1,5 @@
from nonebot import on_type from nonebot import on_type
from nonebot.rule import to_me
from nonebot.adapters.onebot.v11 import PokeNotifyEvent # type: ignore from nonebot.adapters.onebot.v11 import PokeNotifyEvent # type: ignore
from nonebot.rule import to_me
poke_notify = on_type((PokeNotifyEvent,), rule=to_me()) poke_notify = on_type((PokeNotifyEvent,), rule=to_me())

View File

@ -1,11 +1,11 @@
import shutil import shutil
from pydantic import BaseModel
from nonebot import logger, get_plugin_config
from ruamel.yaml import YAML
import yaml as yaml_
from pathlib import Path from pathlib import Path
import yaml as yaml_
from nonebot import get_plugin_config, logger
from pydantic import BaseModel
from ruamel.yaml import YAML
class ConfigModel(BaseModel): class ConfigModel(BaseModel):
marshoai_use_yaml_config: bool = False marshoai_use_yaml_config: bool = False

View File

@ -1,4 +1,5 @@
import re import re
from .config import config from .config import config
USAGE: str = f"""MarshoAI-NoneBot Beta by Asankilp USAGE: str = f"""MarshoAI-NoneBot Beta by Asankilp

View File

@ -14,10 +14,11 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. See the Mulan PSL v2 for more details.
""" """
from typing import Optional, Literal, Tuple
from nonebot import logger
import httpx
import time import time
from typing import Literal, Optional, Tuple
import httpx
from nonebot import logger
class ConvertChannel: class ConvertChannel:
@ -90,7 +91,7 @@ class L2PChannel(ConvertChannel):
@staticmethod @staticmethod
def channel_test() -> int: def channel_test() -> int:
with httpx.Client(timeout=5,verify=False) as client: with httpx.Client(timeout=5, verify=False) as client:
try: try:
start_time = time.time_ns() start_time = time.time_ns()
latex2png = ( latex2png = (
@ -156,7 +157,7 @@ class CDCChannel(ConvertChannel):
@staticmethod @staticmethod
def channel_test() -> int: def channel_test() -> int:
with httpx.Client(timeout=5,verify=False) as client: with httpx.Client(timeout=5, verify=False) as client:
try: try:
start_time = time.time_ns() start_time = time.time_ns()
codecogs = ( codecogs = (
@ -223,7 +224,7 @@ class JRTChannel(ConvertChannel):
@staticmethod @staticmethod
def channel_test() -> int: def channel_test() -> int:
with httpx.Client(timeout=5,verify=False) as client: with httpx.Client(timeout=5, verify=False) as client:
try: try:
start_time = time.time_ns() start_time = time.time_ns()
joeraut = ( joeraut = (

View File

@ -1,36 +1,38 @@
import contextlib import contextlib
import traceback
import json import json
import traceback
from typing import Optional from typing import Optional
from arclet.alconna import Alconna, Args, AllParam from arclet.alconna import Alconna, AllParam, Args
from nonebot import on_command, logger from nonebot import get_driver, logger, on_command
from nonebot.adapters import Message, Event from nonebot.adapters import Event, Message
from nonebot.params import CommandArg from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot_plugin_alconna import on_alconna, MsgTarget from nonebot_plugin_alconna import MsgTarget, on_alconna
from nonebot_plugin_alconna.uniseg import UniMessage, UniMsg from nonebot_plugin_alconna.uniseg import UniMessage, UniMsg
from nonebot import get_driver
from .config import config
from .constants import * from .constants import *
from .metadata import metadata from .metadata import metadata
from .models import MarshoContext from .models import MarshoContext
from .util_hunyuan import * from .util_hunyuan import *
from .config import config
genimage_cmd = on_alconna( genimage_cmd = on_alconna(
Alconna( Alconna(
"genimage", "genimage",
Args["prompt?", str], Args["prompt?", str],
)
) )
)
@genimage_cmd.handle() @genimage_cmd.handle()
async def genimage(event: Event, prompt=None): async def genimage(event: Event, prompt=None):
if not prompt: if not prompt:
await genimage_cmd.finish("无提示词") await genimage_cmd.finish("无提示词")
try: try:
result = generate_image(prompt) result = generate_image(prompt)
url = json.loads(result)["ResultImage"] url = json.loads(result)["ResultImage"]
await UniMessage.image(url=url).send() await UniMessage.image(url=url).send()
except Exception as e: except Exception as e:
# await genimage_cmd.finish(str(e)) # await genimage_cmd.finish(str(e))
traceback.print_exc() traceback.print_exc()

View File

@ -1,14 +1,15 @@
from .util import *
from .config import config
import os
import json
import importlib import importlib
import json
import os
import sys import sys
# import importlib.util # import importlib.util
import traceback import traceback
from nonebot import logger from nonebot import logger
from .config import config
from .util import *
class MarshoContext: class MarshoContext:
""" """
@ -75,7 +76,7 @@ class MarshoTools:
for package_name in os.listdir(tools_dir): for package_name in os.listdir(tools_dir):
package_path = os.path.join(tools_dir, package_name) package_path = os.path.join(tools_dir, package_name)
# logger.info(f"尝试加载工具包 {package_name}") # logger.info(f"尝试加载工具包 {package_name}")
if package_name in config.marshoai_disabled_toolkits: if package_name in config.marshoai_disabled_toolkits:
logger.info(f"工具包 {package_name} 已被禁用。") logger.info(f"工具包 {package_name} 已被禁用。")
continue continue

View File

@ -1,22 +1,26 @@
import httpx
import traceback import traceback
import httpx
async def fetch_calendar(): async def fetch_calendar():
url = 'https://api.bgm.tv/calendar' url = "https://api.bgm.tv/calendar"
headers = { headers = {
'User-Agent': 'LiteyukiStudio/nonebot-plugin-marshoai (https://github.com/LiteyukiStudio/nonebot-plugin-marshoai)' "User-Agent": "LiteyukiStudio/nonebot-plugin-marshoai (https://github.com/LiteyukiStudio/nonebot-plugin-marshoai)"
} }
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
response = await client.get(url, headers=headers) response = await client.get(url, headers=headers)
#print(response.text) # print(response.text)
return response.json() return response.json()
async def get_bangumi_news(): async def get_bangumi_news():
result = await fetch_calendar() result = await fetch_calendar()
info = "" info = ""
try: try:
for i in result: for i in result:
weekday = i["weekday"]["cn"] weekday = i["weekday"]["cn"]
#print(weekday) # print(weekday)
info += f"{weekday}:" info += f"{weekday}:"
items = i["items"] items = i["items"]
for item in items: for item in items:
@ -26,4 +30,4 @@ async def get_bangumi_news():
return info return info
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
return "" return ""

View File

@ -6,4 +6,4 @@
"description": "获取今天的新番(动漫)列表,在调用之前,你需要知道今天星期几。" "description": "获取今天的新番(动漫)列表,在调用之前,你需要知道今天星期几。"
} }
} }
] ]

View File

@ -1,4 +1,5 @@
import os import os
from zhDateTime import DateTime from zhDateTime import DateTime

View File

@ -6,4 +6,4 @@
"description": "获取现在的日期,时间和星期。" "description": "获取现在的日期,时间和星期。"
} }
} }
] ]

View File

@ -4,7 +4,7 @@
"function": { "function": {
"name": "marshoai-basic__get_weather", "name": "marshoai-basic__get_weather",
"description": "当你想查询指定城市的天气时非常有用。", "description": "当你想查询指定城市的天气时非常有用。",
"parameters": { "parameters": {
"type": "object", "type": "object",
"properties": { "properties": {
"location": { "location": {
@ -23,7 +23,7 @@
"function": { "function": {
"name": "marshoai-basic__get_current_env", "name": "marshoai-basic__get_current_env",
"description": "获取当前的运行环境。", "description": "获取当前的运行环境。",
"parameters": { "parameters": {
} }
} }
}, },
@ -32,8 +32,8 @@
"function": { "function": {
"name": "marshoai-basic__get_current_time", "name": "marshoai-basic__get_current_time",
"description": "获取现在的时间。", "description": "获取现在的时间。",
"parameters": { "parameters": {
} }
} }
} }
] ]

View File

@ -1,37 +1,41 @@
from . import mk_Common, mk_Info, mk_MorseCode, mk_NyaCode
from . import mk_Info
from . import mk_Common
from . import mk_MorseCode
from . import mk_NyaCode
# Twisuki # Twisuki
async def twisuki(): async def twisuki():
return str(await mk_Info.twisuki()) return str(await mk_Info.twisuki())
# MegaKits # MegaKits
async def megakits(): async def megakits():
return str(await mk_Info.megakits()) return str(await mk_Info.megakits())
# Random Turntable # Random Turntable
async def random_turntable(upper: int, lower: int = 0): async def random_turntable(upper: int, lower: int = 0):
return str(await mk_Common.random_turntable(upper, lower)) return str(await mk_Common.random_turntable(upper, lower))
# Number Calc # Number Calc
async def number_calc(a: str, b: str, op: str): async def number_calc(a: str, b: str, op: str):
return str(await mk_Common.number_calc(a, b, op)) return str(await mk_Common.number_calc(a, b, op))
# MorseCode Encrypt # MorseCode Encrypt
async def morse_encrypt(msg: str): async def morse_encrypt(msg: str):
return str(await mk_MorseCode.morse_encrypt(msg)) return str(await mk_MorseCode.morse_encrypt(msg))
# MorseCode Decrypt # MorseCode Decrypt
async def morse_decrypt(msg: str): async def morse_decrypt(msg: str):
return str(await mk_MorseCode.morse_decrypt(msg)) return str(await mk_MorseCode.morse_decrypt(msg))
# NyaCode Encrypt # NyaCode Encrypt
async def nya_encode(msg: str): async def nya_encode(msg: str):
return str(await mk_NyaCode.nya_encode(msg)) return str(await mk_NyaCode.nya_encode(msg))
# NyaCode Decrypt # NyaCode Decrypt
async def nya_decode(msg: str): async def nya_decode(msg: str):
return str(await mk_NyaCode.nya_decode(msg)) return str(await mk_NyaCode.nya_decode(msg))

View File

@ -1,9 +1,11 @@
import random import random
# Random Turntable # Random Turntable
async def random_turntable(upper: int, lower: int): async def random_turntable(upper: int, lower: int):
return random.randint(lower, upper) return random.randint(lower, upper)
# Number Calc # Number Calc
async def number_calc(a: str, b: str, op: str): async def number_calc(a: str, b: str, op: str):
a, b = float(a), float(b) a, b = float(a), float(b)
@ -17,8 +19,8 @@ async def number_calc(a: str, b: str, op: str):
case "/": case "/":
return str(a / b) return str(a / b)
case "**": case "**":
return str(a ** b) return str(a**b)
case "%": case "%":
return str(a % b) return str(a % b)
case _: case _:
return "未知运算符" return "未知运算符"

View File

@ -1,7 +1,8 @@
# Twisuki # Twisuki
async def twisuki(): async def twisuki():
return "Twiuski(苏阳)是megakits插件作者, Github : \"https://github.com/Twisuki\"" return 'Twiuski(苏阳)是megakits插件作者, Github : "https://github.com/Twisuki"'
# MegaKits # MegaKits
async def megakits(): async def megakits():
return "MegaKits插件是一个功能混杂的MarshoAI插件, 由Twisuki(Github : \"https://github.com/Twisuki\")开发, 插件仓库 : \"https://github.com/LiteyukiStudio/marsho-toolsets/tree/main/Twisuki/marshoai-megakits\"" return 'MegaKits插件是一个功能混杂的MarshoAI插件, 由Twisuki(Github : "https://github.com/Twisuki")开发, 插件仓库 : "https://github.com/LiteyukiStudio/marsho-toolsets/tree/main/Twisuki/marshoai-megakits"'

View File

@ -1,17 +1,59 @@
# MorseCode # MorseCode
MorseEncode = { MorseEncode = {
"A": ".-", "B": "-...", "C": "-.-.", "D": "-..", "E": ".", "F": "..-.", "A": ".-",
"G": "--.", "H": "....", "I": "..", "J": ".---", "K": "-.-", "L": ".-..", "B": "-...",
"M": "--", "N": "-.", "O": "---", "P": ".--.", "Q": "--.-", "R": ".-.", "C": "-.-.",
"S": "...", "T": "-", "U": "..-", "V": "...-", "W": ".--", "X": "-..-", "D": "-..",
"Y": "-.--", "Z": "--..", "E": ".",
"1": ".----", "2": "..---", "3": "...--", "4": "....-", "5": ".....", "F": "..-.",
"6": "-....", "7": "--...", "8": "---..", "9": "----.", "0": "-----", "G": "--.",
".": ".-.-.-", ":": "---...", ",": "--..--", ";": "-.-.-.", "H": "....",
"?": "..--..", "=": "-...-", "\'": ".----.", "/": "-..-.", "I": "..",
"!": "-.-.--", "-": "-....-", "_": "..--.-", "\"": ".-..-.", "J": ".---",
"(": "-.--.", ")": "-.--.-", "$": "...-..-", "&": "....", "K": "-.-",
"@": ".--.-.", " ": " " "L": ".-..",
"M": "--",
"N": "-.",
"O": "---",
"P": ".--.",
"Q": "--.-",
"R": ".-.",
"S": "...",
"T": "-",
"U": "..-",
"V": "...-",
"W": ".--",
"X": "-..-",
"Y": "-.--",
"Z": "--..",
"1": ".----",
"2": "..---",
"3": "...--",
"4": "....-",
"5": ".....",
"6": "-....",
"7": "--...",
"8": "---..",
"9": "----.",
"0": "-----",
".": ".-.-.-",
":": "---...",
",": "--..--",
";": "-.-.-.",
"?": "..--..",
"=": "-...-",
"'": ".----.",
"/": "-..-.",
"!": "-.-.--",
"-": "-....-",
"_": "..--.-",
'"': ".-..-.",
"(": "-.--.",
")": "-.--.-",
"$": "...-..-",
"&": "....",
"@": ".--.-.",
" ": " ",
} }
MorseDecode = {value: key for key, value in MorseEncode.items()} MorseDecode = {value: key for key, value in MorseEncode.items()}

View File

@ -1,18 +1,23 @@
import random
import base64 import base64
import random
# NyaCode # NyaCode
NyaCodeCharset = [ NyaCodeCharset = ["", "", "?", "~"]
"", "", "?", "~" NyaCodeSpecialCharset = ["", "!", "...", ".."]
]
NyaCodeSpecialCharset = [
"", "!", "...", ".."
]
NyaCodeEncode = {} NyaCodeEncode = {}
for i in range(64): for i in range(64):
triplet = "".join(NyaCodeCharset[(i // (4 ** j)) % 4] for j in range(3)) triplet = "".join(NyaCodeCharset[(i // (4**j)) % 4] for j in range(3))
NyaCodeEncode[chr(65 + i if i < 26 else 97 + (i - 26) if i < 52 else 48 + (i - 52) if i < 62 else ( NyaCodeEncode[
43 if i == 62 else 47))] = triplet chr(
65 + i
if i < 26
else (
97 + (i - 26)
if i < 52
else 48 + (i - 52) if i < 62 else (43 if i == 62 else 47)
)
)
] = triplet
NyaCodeDecode = {value: key for key, value in NyaCodeEncode.items()} NyaCodeDecode = {value: key for key, value in NyaCodeEncode.items()}
@ -22,7 +27,7 @@ async def nya_encode(msg: str):
msg_nyastr = "".join(NyaCodeEncode[base64_char] for base64_char in msg_b64str) msg_nyastr = "".join(NyaCodeEncode[base64_char] for base64_char in msg_b64str)
result = "" result = ""
for char in msg_nyastr: for char in msg_nyastr:
if char == "" and random.random() < 0.5: if char == "" and random.random() < 0.5:
result += "!" result += "!"
if random.random() < 0.25: if random.random() < 0.25:
@ -37,7 +42,7 @@ async def nya_decode(msg: str):
msg = msg.replace("", "").replace("!", "").replace(".", "") msg = msg.replace("", "").replace("!", "").replace(".", "")
msg_nyastr = [] msg_nyastr = []
i = 0 i = 0
if len(msg) % 3 != 0 : if len(msg) % 3 != 0:
return "这句话不是正确的猫语" return "这句话不是正确的猫语"
while i < len(msg): while i < len(msg):
nyachar = msg[i : i + 3] nyachar = msg[i : i + 3]

View File

@ -139,4 +139,4 @@
} }
} }
} }
] ]

View File

@ -1,16 +1,16 @@
from . import mg_Info, mg_Introduce, mg_Search
from . import mg_Info
from . import mg_Search
from . import mg_Introduce
# meogirl # meogirl
async def meogirl () : async def meogirl():
return mg_Info.meogirl() return mg_Info.meogirl()
# Search # Search
async def search (msg : str, num : int = 3) : async def search(msg: str, num: int = 3):
return str(await mg_Search.search(msg, num)) return str(await mg_Search.search(msg, num))
# Show # Show
async def introduce (msg : str) : async def introduce(msg: str):
return str(await mg_Introduce.introduce(msg)) return str(await mg_Introduce.introduce(msg))

View File

@ -1,4 +1,3 @@
# Meogirl # Meogirl
def meogirl(): def meogirl():
return "Meogirl指的是\"萌娘百科\"(https://zh.moegirl.org.cn/ , 简称\"萌百\"), 是一个\"万物皆可萌的百科全书!\"; 同时, MarshoTools也配有\"Meogirl\"插件, 可调用萌百的api" return 'Meogirl指的是"萌娘百科"(https://zh.moegirl.org.cn/ , 简称"萌百"), 是一个"万物皆可萌的百科全书!"; 同时, MarshoTools也配有"Meogirl"插件, 可调用萌百的api'

View File

@ -1,36 +1,38 @@
from nonebot.log import logger
import re import re
import httpx
import urllib.parse import urllib.parse
import httpx
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from nonebot.log import logger
headers = { headers = {
"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" "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"
} }
async def get_async_data (url) :
async with httpx.AsyncClient(timeout = None) as client:
return await client.get(url, headers = headers)
async def introduce (msg : str) : async def get_async_data(url):
logger.info(f"介绍 : \"{msg}\" ...") async with httpx.AsyncClient(timeout=None) as client:
return await client.get(url, headers=headers)
async def introduce(msg: str):
logger.info(f'介绍 : "{msg}" ...')
result = "" result = ""
url = "https://mzh.moegirl.org.cn/" + urllib.parse.quote_plus(msg) url = "https://mzh.moegirl.org.cn/" + urllib.parse.quote_plus(msg)
response = await get_async_data(url) response = await get_async_data(url)
logger.success(f"连接\"{url}\"完成, 状态码 : {response.status_code}") logger.success(f'连接"{url}"完成, 状态码 : {response.status_code}')
soup = BeautifulSoup(response.text, "html.parser") soup = BeautifulSoup(response.text, "html.parser")
# 正常页 # 正常页
if response.status_code == 200 : if response.status_code == 200:
""" """
萌娘百科页面结构 萌娘百科页面结构
div#mw-content-text div#mw-content-text
div#404search # 空白页面出现 div#404search # 空白页面出现
div.mw-parser-output # 正常页面 div.mw-parser-output # 正常页面
div, p, table ... # 大量的解释项 div, p, table ... # 大量的解释项
""" """
result += msg + "\n" result += msg + "\n"
@ -44,7 +46,9 @@ async def introduce (msg : str) :
num = 0 num = 0
for p_tag in p_tags: for p_tag in p_tags:
p = str(p_tag) p = str(p_tag)
p = re.sub(r"<script.*?</script>|<style.*?</style>", "", p, flags=re.DOTALL) p = re.sub(
r"<script.*?</script>|<style.*?</style>", "", p, flags=re.DOTALL
)
p = re.sub(r"<.*?>", "", p, flags=re.DOTALL) p = re.sub(r"<.*?>", "", p, flags=re.DOTALL)
p = re.sub(r"\[.*?]", "", p, flags=re.DOTALL) p = re.sub(r"\[.*?]", "", p, flags=re.DOTALL)
@ -57,20 +61,21 @@ async def introduce (msg : str) :
return result return result
# 空白页 # 空白页
elif response.status_code == 404 : elif response.status_code == 404:
logger.info(f"未找到\"{msg}\", 进行搜索") logger.info(f'未找到"{msg}", 进行搜索')
from . import mg_Search from . import mg_Search
context = await mg_Search.search(msg, 1)
keyword = re.search(r".*?\n", context, flags = re.DOTALL).group()[: -1]
logger.success(f"搜索完成, 打开\"{keyword}\"") context = await mg_Search.search(msg, 1)
keyword = re.search(r".*?\n", context, flags=re.DOTALL).group()[:-1]
logger.success(f'搜索完成, 打开"{keyword}"')
return await introduce(keyword) return await introduce(keyword)
# 搜索失败 # 搜索失败
elif response.status_code == 301 : elif response.status_code == 301:
return f"未找到{msg}" return f"未找到{msg}"
else : else:
logger.error(f"网络错误, 状态码 : {response.status_code}") logger.error(f"网络错误, 状态码 : {response.status_code}")
return f"网络错误, 状态码 : {response.status_code}" return f"网络错误, 状态码 : {response.status_code}"

View File

@ -1,76 +1,85 @@
from nonebot.log import logger import urllib.parse
import httpx import httpx
import urllib.parse
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from nonebot.log import logger
headers = { headers = {
"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" "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"
} }
async def get_async_data (url) :
async with httpx.AsyncClient(timeout = None) as client:
return await client.get(url, headers = headers)
async def search (msg : str, num : int) : async def get_async_data(url):
logger.info(f"搜索 : \"{msg}\" ...") async with httpx.AsyncClient(timeout=None) as client:
return await client.get(url, headers=headers)
async def search(msg: str, num: int):
logger.info(f'搜索 : "{msg}" ...')
result = "" result = ""
url = "https://mzh.moegirl.org.cn/index.php?search=" + urllib.parse.quote_plus(msg) url = "https://mzh.moegirl.org.cn/index.php?search=" + urllib.parse.quote_plus(msg)
response = await get_async_data(url) response = await get_async_data(url)
logger.success(f"连接\"{url}\"完成, 状态码 : {response.status_code}") logger.success(f'连接"{url}"完成, 状态码 : {response.status_code}')
# 正常搜索 # 正常搜索
if response.status_code == 200 : if response.status_code == 200:
""" """
萌娘百科搜索页面结构 萌娘百科搜索页面结构
div.searchresults div.searchresults
p ... p ...
ul.mw-search-results # 若无, 证明无搜索结果 ul.mw-search-results # 若无, 证明无搜索结果
li # 一个搜索结果 li # 一个搜索结果
div.mw-search-result-heading > a # 标题 div.mw-search-result-heading > a # 标题
div.mw-searchresult # 内容 div.mw-searchresult # 内容
div.mw-search-result-data div.mw-search-result-data
li ... li ...
li ... li ...
""" """
soup = BeautifulSoup(response.text, "html.parser") soup = BeautifulSoup(response.text, "html.parser")
# 检测ul.mw-search-results, 是否有结果 # 检测ul.mw-search-results, 是否有结果
ul_tag = soup.find("ul", class_ = "mw-search-results") ul_tag = soup.find("ul", class_="mw-search-results")
if ul_tag : if ul_tag:
li_tags = ul_tag.find_all("li") li_tags = ul_tag.find_all("li")
for li_tag in li_tags : for li_tag in li_tags:
div_heading = li_tag.find("div", class_ = "mw-search-result-heading") div_heading = li_tag.find("div", class_="mw-search-result-heading")
if div_heading : if div_heading:
a_tag = div_heading.find("a") a_tag = div_heading.find("a")
result += a_tag["title"] + "\n" result += a_tag["title"] + "\n"
logger.info(f"搜索到 : \"{a_tag["title"]}\"") logger.info(f'搜索到 : "{a_tag["title"]}"')
div_result = li_tag.find("div", class_="searchresult") div_result = li_tag.find("div", class_="searchresult")
if div_result : if div_result:
content = str(div_result).replace("<div class=\"searchresult\">", "").replace("</div>", "") content = (
content = content.replace("<span class=\"searchmatch\">", "").replace("</span>", "") str(div_result)
.replace('<div class="searchresult">', "")
.replace("</div>", "")
)
content = content.replace('<span class="searchmatch">', "").replace(
"</span>", ""
)
result += content + "\n" result += content + "\n"
num -= 1 num -= 1
if num == 0 : if num == 0:
break break
return result return result
# 无ul.mw-search-results, 无结果 # 无ul.mw-search-results, 无结果
else : else:
logger.info("无结果") logger.info("无结果")
return "无结果" return "无结果"
# 重定向 # 重定向
elif response.status_code == 302 : elif response.status_code == 302:
logger.info(f"\"{msg}\"已被重定向至\"{response.headers.get("location")}\"") logger.info(f'"{msg}"已被重定向至"{response.headers.get("location")}"')
# 读取重定向结果 # 读取重定向结果
from . import mg_Introduce from . import mg_Introduce
return await mg_Introduce.introduce(msg) return await mg_Introduce.introduce(msg)
else : else:
logger.error(f"网络错误, 状态码 : {response.status_code}") logger.error(f"网络错误, 状态码 : {response.status_code}")
return f"网络错误, 状态码 : {response.status_code}" return f"网络错误, 状态码 : {response.status_code}"

View File

@ -48,4 +48,4 @@
] ]
} }
} }
] ]

View File

@ -1,2 +1,2 @@
async def write_memory(memory: str): async def write_memory(memory: str):
return "" return ""

View File

@ -4,7 +4,7 @@
"function": { "function": {
"name": "marshoai-memory__write_memory", "name": "marshoai-memory__write_memory",
"description": "当你想记住有关与你对话的人的一些信息的时候,调用此函数。", "description": "当你想记住有关与你对话的人的一些信息的时候,调用此函数。",
"parameters": { "parameters": {
"type": "object", "type": "object",
"properties": { "properties": {
"memory": { "memory": {
@ -18,4 +18,4 @@
] ]
} }
} }
] ]

View File

@ -1,26 +1,19 @@
import os
import json
import uuid
import httpx
import base64 import base64
import json
import mimetypes import mimetypes
import os
import uuid
from typing import Any, Optional from typing import Any, Optional
from nonebot.log import logger import httpx
import nonebot_plugin_localstore as store import nonebot_plugin_localstore as store
from nonebot_plugin_alconna import (
Text as TextMsg,
Image as ImageMsg,
UniMessage,
)
# from zhDateTime import DateTime # from zhDateTime import DateTime
from azure.ai.inference.aio import ChatCompletionsClient from azure.ai.inference.aio import ChatCompletionsClient
from azure.ai.inference.models import SystemMessage from azure.ai.inference.models import SystemMessage
from nonebot.log import logger
from nonebot_plugin_alconna import Image as ImageMsg
from nonebot_plugin_alconna import Text as TextMsg
from nonebot_plugin_alconna import UniMessage
from .config import config from .config import config
from .constants import * from .constants import *
@ -304,7 +297,7 @@ if config.marshoai_enable_richtext_parse:
if not IMG_LATEX_PATTERN.search(msg): # 没有图片和LaTeX标签 if not IMG_LATEX_PATTERN.search(msg): # 没有图片和LaTeX标签
return UniMessage(msg) return UniMessage(msg)
result_msg = UniMessage() result_msg = UniMessage() # type: ignore
code_blank_uuid_map = [ code_blank_uuid_map = [
(uuid.uuid4().hex, cbp.group()) for cbp in CODE_BLOCK_PATTERN.finditer(msg) (uuid.uuid4().hex, cbp.group()) for cbp in CODE_BLOCK_PATTERN.finditer(msg)
] ]
@ -338,7 +331,7 @@ if config.marshoai_enable_richtext_parse:
last_tag_index = msg.find(tag_found) + len(tag_found) last_tag_index = msg.find(tag_found) + len(tag_found)
if each_find_tag.group(1): if each_find_tag.group(1):
# 图形一定要优先考虑 # 图形一定要优先考虑
# 别忘了有些图形的地址就是 LaTeX所以要优先判断 # 别忘了有些图形的地址就是 LaTeX所以要优先判断
@ -380,7 +373,7 @@ if config.marshoai_enable_richtext_parse:
if latex_generate_ok: if latex_generate_ok:
result_msg.append( result_msg.append(
ImageMsg( ImageMsg(
raw=latex_generate_result, raw=latex_generate_result, # type: ignore
mimetype="image/png", mimetype="image/png",
name="latex.png", name="latex.png",
) )

View File

@ -1,12 +1,16 @@
import json import json
import types import types
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common import credential # type: ignore
from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.common.exception.tencent_cloud_sdk_exception import \
from tencentcloud.common.exception.tencent_cloud_sdk_exception import ( TencentCloudSDKException # type: ignore
TencentCloudSDKException, from tencentcloud.common.profile.client_profile import \
) ClientProfile # type: ignore
from tencentcloud.hunyuan.v20230901 import hunyuan_client, models from tencentcloud.common.profile.http_profile import \
HttpProfile # type: ignore
from tencentcloud.hunyuan.v20230901 import hunyuan_client # type: ignore
from tencentcloud.hunyuan.v20230901 import models
from .config import config from .config import config

View File

@ -3,7 +3,7 @@ name = "nonebot-plugin-marshoai"
dynamic = ["version"] dynamic = ["version"]
description = "Nonebot2插件调用Azure OpenAI等AI服务实现猫娘聊天" description = "Nonebot2插件调用Azure OpenAI等AI服务实现猫娘聊天"
readme = "README.md" readme = "README.md"
requires-python = "<4.0,>=3.9" requires-python = "<4.0,>=3.10"
authors = [{ name = "Asankilp", email = "asankilp@outlook.com" }] authors = [{ name = "Asankilp", email = "asankilp@outlook.com" }]
dependencies = [ dependencies = [
"nonebot2>=2.2.0", "nonebot2>=2.2.0",
@ -14,8 +14,10 @@ dependencies = [
"aiohttp>=3.9", "aiohttp>=3.9",
"httpx>=0.27.0", "httpx>=0.27.0",
"ruamel.yaml>=0.18.6", "ruamel.yaml>=0.18.6",
"pyyaml>=6.0.2" "pyyaml>=6.0.2",
"psutil>=6.1.0",
"beautifulsoup4>=4.12.3"
] ]
license = { text = "MIT, Mulan PSL v2" } license = { text = "MIT, Mulan PSL v2" }
@ -32,6 +34,9 @@ adapters = [
[tool.pdm] [tool.pdm]
distribution = true distribution = true
python.use_venv = true
python.venv_in_project = true
[tool.pdm.version] [tool.pdm.version]
source = "scm" source = "scm"
@ -49,4 +54,6 @@ build-backend = "pdm.backend"
dev = [ dev = [
"nb-cli>=1.4.2", "nb-cli>=1.4.2",
"pytest>=8.3.4", "pytest>=8.3.4",
] "pre-commit>=4.0.1",
"nonebot-adapter-onebot>=2.4.6",
]

View File

@ -1,4 +1,4 @@
# Marsho Resources # Marsho Resources
本目录存放 Marsho 的图像资源logo,icon由[Asankilp](https://github.com/Asankilp)绘制。 本目录存放 Marsho 的图像资源logo,icon由[Asankilp](https://github.com/Asankilp)绘制。
所有资源均在[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)许可下提供。 所有资源均在[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)许可下提供。

View File

@ -1,7 +1,7 @@
import logging import logging
def test_none(): def test_none():
"""基准测试示例 """基准测试示例"""
"""
logging.info("测试成功") logging.info("测试成功")
pass pass