🎨 更新 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
with:
name: Pytest-test-report
path: report
path: report

2
.gitignore vendored
View File

@ -176,4 +176,4 @@ praises.json
config/
# 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) 来实现聊天的插件。
插件内置了猫娘小棉(Marsho)的人物设定,可以进行可爱的聊天!
*谁不喜欢回复消息快又可爱的猫娘呢?*
通过调用 OpenAI 标准格式 API(例如由 Azure OpenAI 驱动GitHub Models 提供访问的生成式 AI 推理 API) 来实现聊天的插件。
插件内置了猫娘小棉(Marsho)的人物设定,可以进行可爱的聊天!
*谁不喜欢回复消息快又可爱的猫娘呢?*
**对 OneBot 以外的适配器与非 GitHub Models API的支持未经过完全验证。**
[Melobot 实现](https://github.com/LiteyukiStudio/marshoai-melo)
@ -101,13 +101,13 @@ _✨ 使用 OpenAI 标准格式 API 的聊天机器人插件 ✨_
当 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`
时发起一次聊天后自动生成,包含人物名字与人物优点两个基本数据。
存储于其中的人物会被 Marsho “认识”和“喜欢”。
时发起一次聊天后自动生成,包含人物名字与人物优点两个基本数据。
存储于其中的人物会被 Marsho “认识”和“喜欢”。
其结构类似于:
```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)
绘制,基于 [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) 许可下提供。
部分指定的代码基于 [Mulan PSL v2](./LICENSE-MULAN) 许可下提供。

View File

@ -22,11 +22,11 @@ _✨ A chat bot plugin which use OpenAI standard API ✨_
## 📖 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!
*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.**
@ -50,7 +50,7 @@ Plugin internally installed the catgirl character of Marsho, is able to have a c
<details open>
<summary>Install with nb-cli</summary>
Open shell under the root directory of nonebot2, input the command below.
nb plugin install nonebot-plugin-marshoai
@ -59,7 +59,7 @@ Open shell under the root directory of nonebot2, input the command below.
<details>
<summary>Install with pack manager</summary>
Open shell under the plugin directory of nonebot2, input corresponding command according to your pack manager.
<details>
@ -108,7 +108,7 @@ When nonebot linked to OneBot v11 adapter, can recieve double click and response
## 🛠️ 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
@ -184,7 +184,7 @@ Add options in the `.env` file from the diagram below in nonebot2 project.
## ❤ Thanks&Copyright
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),
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)。
## ✍️ 编写工具
@ -64,11 +64,11 @@ async def get_current_time():
}
]
```
在这个文件中定义了两个已经编写好的函数,该定义文件将被输入到 AI 模型中,来让 AI 模型知道这些函数的存在与调用方法。
在这个文件中定义了两个已经编写好的函数,该定义文件将被输入到 AI 模型中,来让 AI 模型知道这些函数的存在与调用方法。
**函数调用名称**的命名方式比较特别。以获取天气的函数为例,它的函数调用名称`marshoai-example__get_weather`包含三个信息:
- 前面的**marshoai-example**即为该函数所在工具包的**包名**。
- 后面的**get_weather**是这个函数在代码里的名称。
- 中间的两个下划线是用于分割这两个信息的分隔符。
- 中间的两个下划线是用于分割这两个信息的分隔符。
使用这种命名方式,是为了兼容更多的 OpenAI 标准格式 API。因此在给工具包和函数取名时不要使用带有两个下划线的名称。
### 测试函数
@ -78,7 +78,7 @@ async def get_current_time():
> marsho 深圳天气怎么样
深圳的天气显示温度是114514°C真是不可思议呢这一定是个误报吧~(≧▽≦) 希望你那里有个好天气哦!
> marsho 分别告诉我下北泽,杭州,苏州的天气
下北泽、杭州和苏州的天气都显示温度为114514°C呢这么奇怪的温度一定是个误报吧~(≧▽≦)
下北泽、杭州和苏州的天气都显示温度为114514°C呢这么奇怪的温度一定是个误报吧~(≧▽≦)
如果要查看真实的天气情况,建议查看专业天气预报哦~
> marsho 现在几点了

View File

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

View File

@ -1,33 +1,23 @@
import traceback
import contextlib
from typing import Optional
import traceback
from pathlib import Path
from typing import Optional
from arclet.alconna import Alconna, Args, AllParam
from azure.ai.inference.models import (
UserMessage,
AssistantMessage,
ToolMessage,
TextContentItem,
ImageContentItem,
ImageUrl,
CompletionsFinishReason,
ChatCompletionsToolCall,
)
import nonebot_plugin_localstore as store
from arclet.alconna import Alconna, AllParam, Args
from azure.ai.inference.models import (AssistantMessage,
ChatCompletionsToolCall,
CompletionsFinishReason,
ImageContentItem, ImageUrl,
TextContentItem, ToolMessage,
UserMessage)
from azure.core.credentials import AzureKeyCredential
from nonebot import on_command, on_message, logger, get_driver
from nonebot.adapters import Message, Event
from nonebot import get_driver, logger, on_command, on_message
from nonebot.adapters import Event, Message
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import Rule, to_me
from nonebot_plugin_alconna import (
on_alconna,
MsgTarget,
UniMessage,
UniMsg,
)
import nonebot_plugin_localstore as store
from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg, on_alconna
from .metadata import metadata
from .models import MarshoContext, MarshoTools
@ -203,7 +193,7 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
not text
and event.get_message().extract_plain_text() != config.marshoai_default_name
):
text = event.get_message()
text = event.get_message() # type: ignore
if not text:
# 发送说明
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:
if i.type == "text":
if is_support_image_model:
usermsg += [TextContentItem(text=i.data["text"] + nickname_prompt)]
usermsg += [TextContentItem(text=i.data["text"] + nickname_prompt)] # type: ignore
else:
usermsg += str(i.data["text"] + nickname_prompt)
usermsg += str(i.data["text"] + nickname_prompt) # type: ignore
elif i.type == "image":
if is_support_image_model:
usermsg.append(
usermsg.append( # type: ignore
ImageContentItem(
image_url=ImageUrl(
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(
client=client,
model_name=model_name,
msg=context_msg + [UserMessage(content=usermsg)],
msg=context_msg + [UserMessage(content=usermsg)], # type: ignore
tools=tools.get_tools_list(),
)
# 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:
# 当对话成功时将dict的上下文添加到上下文类中
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)
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_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(
client=client,
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(),
)
choice = response.choices[0]
@ -327,7 +317,7 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
# 对话成功 添加上下文
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(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): # 优化先不做()
import nonebot.adapters.onebot.v11 # type: ignore
from .azure_onebot import poke_notify
@poke_notify.handle()

View File

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

View File

@ -1,11 +1,11 @@
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
import yaml as yaml_
from nonebot import get_plugin_config, logger
from pydantic import BaseModel
from ruamel.yaml import YAML
class ConfigModel(BaseModel):
marshoai_use_yaml_config: bool = False

View File

@ -1,4 +1,5 @@
import re
from .config import config
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.
"""
from typing import Optional, Literal, Tuple
from nonebot import logger
import httpx
import time
from typing import Literal, Optional, Tuple
import httpx
from nonebot import logger
class ConvertChannel:
@ -90,7 +91,7 @@ class L2PChannel(ConvertChannel):
@staticmethod
def channel_test() -> int:
with httpx.Client(timeout=5,verify=False) as client:
with httpx.Client(timeout=5, verify=False) as client:
try:
start_time = time.time_ns()
latex2png = (
@ -156,7 +157,7 @@ class CDCChannel(ConvertChannel):
@staticmethod
def channel_test() -> int:
with httpx.Client(timeout=5,verify=False) as client:
with httpx.Client(timeout=5, verify=False) as client:
try:
start_time = time.time_ns()
codecogs = (
@ -223,7 +224,7 @@ class JRTChannel(ConvertChannel):
@staticmethod
def channel_test() -> int:
with httpx.Client(timeout=5,verify=False) as client:
with httpx.Client(timeout=5, verify=False) as client:
try:
start_time = time.time_ns()
joeraut = (

View File

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

View File

@ -1,14 +1,15 @@
from .util import *
from .config import config
import os
import json
import importlib
import json
import os
import sys
# import importlib.util
import traceback
from nonebot import logger
from .config import config
from .util import *
class MarshoContext:
"""
@ -75,7 +76,7 @@ class MarshoTools:
for package_name in os.listdir(tools_dir):
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:
logger.info(f"工具包 {package_name} 已被禁用。")
continue

View File

@ -1,22 +1,26 @@
import httpx
import traceback
import httpx
async def fetch_calendar():
url = 'https://api.bgm.tv/calendar'
url = "https://api.bgm.tv/calendar"
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:
response = await client.get(url, headers=headers)
#print(response.text)
# print(response.text)
return response.json()
async def get_bangumi_news():
result = await fetch_calendar()
info = ""
try:
for i in result:
weekday = i["weekday"]["cn"]
#print(weekday)
# print(weekday)
info += f"{weekday}:"
items = i["items"]
for item in items:
@ -26,4 +30,4 @@ async def get_bangumi_news():
return info
except Exception as e:
traceback.print_exc()
return ""
return ""

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@
"function": {
"name": "marshoai-basic__get_weather",
"description": "当你想查询指定城市的天气时非常有用。",
"parameters": {
"parameters": {
"type": "object",
"properties": {
"location": {
@ -23,7 +23,7 @@
"function": {
"name": "marshoai-basic__get_current_env",
"description": "获取当前的运行环境。",
"parameters": {
"parameters": {
}
}
},
@ -32,8 +32,8 @@
"function": {
"name": "marshoai-basic__get_current_time",
"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
async def twisuki():
return str(await mk_Info.twisuki())
# MegaKits
async def megakits():
return str(await mk_Info.megakits())
# Random Turntable
async def random_turntable(upper: int, lower: int = 0):
return str(await mk_Common.random_turntable(upper, lower))
# Number Calc
async def number_calc(a: str, b: str, op: str):
return str(await mk_Common.number_calc(a, b, op))
# MorseCode Encrypt
async def morse_encrypt(msg: str):
return str(await mk_MorseCode.morse_encrypt(msg))
# MorseCode Decrypt
async def morse_decrypt(msg: str):
return str(await mk_MorseCode.morse_decrypt(msg))
# NyaCode Encrypt
async def nya_encode(msg: str):
return str(await mk_NyaCode.nya_encode(msg))
# NyaCode Decrypt
async def nya_decode(msg: str):
return str(await mk_NyaCode.nya_decode(msg))

View File

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

View File

@ -1,7 +1,8 @@
# Twisuki
async def twisuki():
return "Twiuski(苏阳)是megakits插件作者, Github : \"https://github.com/Twisuki\""
return 'Twiuski(苏阳)是megakits插件作者, Github : "https://github.com/Twisuki"'
# 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
MorseEncode = {
"A": ".-", "B": "-...", "C": "-.-.", "D": "-..", "E": ".", "F": "..-.",
"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": "-----",
".": ".-.-.-", ":": "---...", ",": "--..--", ";": "-.-.-.",
"?": "..--..", "=": "-...-", "\'": ".----.", "/": "-..-.",
"!": "-.-.--", "-": "-....-", "_": "..--.-", "\"": ".-..-.",
"(": "-.--.", ")": "-.--.-", "$": "...-..-", "&": "....",
"@": ".--.-.", " ": " "
"A": ".-",
"B": "-...",
"C": "-.-.",
"D": "-..",
"E": ".",
"F": "..-.",
"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()}

View File

@ -1,18 +1,23 @@
import random
import base64
import random
# NyaCode
NyaCodeCharset = [
"", "", "?", "~"
]
NyaCodeSpecialCharset = [
"", "!", "...", ".."
]
NyaCodeCharset = ["", "", "?", "~"]
NyaCodeSpecialCharset = ["", "!", "...", ".."]
NyaCodeEncode = {}
for i in range(64):
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 (
43 if i == 62 else 47))] = triplet
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 (43 if i == 62 else 47)
)
)
] = triplet
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)
result = ""
for char in msg_nyastr:
if char == "" and random.random() < 0.5:
if char == "" and random.random() < 0.5:
result += "!"
if random.random() < 0.25:
@ -37,7 +42,7 @@ async def nya_decode(msg: str):
msg = msg.replace("", "").replace("!", "").replace(".", "")
msg_nyastr = []
i = 0
if len(msg) % 3 != 0 :
if len(msg) % 3 != 0:
return "这句话不是正确的猫语"
while i < len(msg):
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
async def meogirl () :
async def meogirl():
return mg_Info.meogirl()
# 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))
# Show
async def introduce (msg : str) :
async def introduce(msg: str):
return str(await mg_Introduce.introduce(msg))

View File

@ -1,4 +1,3 @@
# 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 httpx
import urllib.parse
import httpx
from bs4 import BeautifulSoup
from nonebot.log import logger
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"
}
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) :
logger.info(f"介绍 : \"{msg}\" ...")
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):
logger.info(f'介绍 : "{msg}" ...')
result = ""
url = "https://mzh.moegirl.org.cn/" + urllib.parse.quote_plus(msg)
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")
# 正常页
if response.status_code == 200 :
if response.status_code == 200:
"""
萌娘百科页面结构
div#mw-content-text
div#404search # 空白页面出现
div.mw-parser-output # 正常页面
div, p, table ... # 大量的解释项
萌娘百科页面结构
div#mw-content-text
div#404search # 空白页面出现
div.mw-parser-output # 正常页面
div, p, table ... # 大量的解释项
"""
result += msg + "\n"
@ -44,7 +46,9 @@ async def introduce (msg : str) :
num = 0
for p_tag in p_tags:
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)
@ -57,20 +61,21 @@ async def introduce (msg : str) :
return result
# 空白页
elif response.status_code == 404 :
logger.info(f"未找到\"{msg}\", 进行搜索")
elif response.status_code == 404:
logger.info(f'未找到"{msg}", 进行搜索')
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)
# 搜索失败
elif response.status_code == 301 :
elif response.status_code == 301:
return f"未找到{msg}"
else :
else:
logger.error(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 urllib.parse
from bs4 import BeautifulSoup
from nonebot.log import logger
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"
}
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) :
logger.info(f"搜索 : \"{msg}\" ...")
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):
logger.info(f'搜索 : "{msg}" ...')
result = ""
url = "https://mzh.moegirl.org.cn/index.php?search=" + urllib.parse.quote_plus(msg)
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
p ...
ul.mw-search-results # 若无, 证明无搜索结果
li # 一个搜索结果
div.mw-search-result-heading > a # 标题
div.mw-searchresult # 内容
div.mw-search-result-data
li ...
li ...
萌娘百科搜索页面结构
div.searchresults
p ...
ul.mw-search-results # 若无, 证明无搜索结果
li # 一个搜索结果
div.mw-search-result-heading > a # 标题
div.mw-searchresult # 内容
div.mw-search-result-data
li ...
li ...
"""
soup = BeautifulSoup(response.text, "html.parser")
# 检测ul.mw-search-results, 是否有结果
ul_tag = soup.find("ul", class_ = "mw-search-results")
if ul_tag :
ul_tag = soup.find("ul", class_="mw-search-results")
if ul_tag:
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")
if div_heading :
div_heading = li_tag.find("div", class_="mw-search-result-heading")
if div_heading:
a_tag = div_heading.find("a")
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")
if div_result :
content = str(div_result).replace("<div class=\"searchresult\">", "").replace("</div>", "")
content = content.replace("<span class=\"searchmatch\">", "").replace("</span>", "")
if div_result:
content = (
str(div_result)
.replace('<div class="searchresult">', "")
.replace("</div>", "")
)
content = content.replace('<span class="searchmatch">', "").replace(
"</span>", ""
)
result += content + "\n"
num -= 1
if num == 0 :
if num == 0:
break
return result
# 无ul.mw-search-results, 无结果
else :
else:
logger.info("无结果")
return "无结果"
# 重定向
elif response.status_code == 302 :
logger.info(f"\"{msg}\"已被重定向至\"{response.headers.get("location")}\"")
elif response.status_code == 302:
logger.info(f'"{msg}"已被重定向至"{response.headers.get("location")}"')
# 读取重定向结果
from . import mg_Introduce
return await mg_Introduce.introduce(msg)
else :
else:
logger.error(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):
return ""
return ""

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ name = "nonebot-plugin-marshoai"
dynamic = ["version"]
description = "Nonebot2插件调用Azure OpenAI等AI服务实现猫娘聊天"
readme = "README.md"
requires-python = "<4.0,>=3.9"
requires-python = "<4.0,>=3.10"
authors = [{ name = "Asankilp", email = "asankilp@outlook.com" }]
dependencies = [
"nonebot2>=2.2.0",
@ -14,8 +14,10 @@ dependencies = [
"aiohttp>=3.9",
"httpx>=0.27.0",
"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" }
@ -32,6 +34,9 @@ adapters = [
[tool.pdm]
distribution = true
python.use_venv = true
python.venv_in_project = true
[tool.pdm.version]
source = "scm"
@ -49,4 +54,6 @@ build-backend = "pdm.backend"
dev = [
"nb-cli>=1.4.2",
"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)绘制。
所有资源均在[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)许可下提供。
本目录存放 Marsho 的图像资源logo,icon由[Asankilp](https://github.com/Asankilp)绘制。
所有资源均在[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)许可下提供。

View File

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