feat: 更清晰的目录结构,新的markdown构建

This commit is contained in:
远野千束 2024-04-14 21:39:27 +08:00
parent 65dcf36fe7
commit 15a329029d
31 changed files with 269 additions and 141 deletions

View File

@ -9,12 +9,12 @@ from nonebot.exception import MockApiException
from nonebot.internal.matcher import Matcher
from nonebot.permission import SUPERUSER
from liteyuki.utils.config import get_config, load_from_yaml
from liteyuki.utils.data_manager import StoredConfig, TempConfig, common_db
from liteyuki.utils.language import get_user_lang
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message import Markdown as md, broadcast_to_superusers
from liteyuki.utils.reloader import Reloader
from liteyuki.utils.base.config import get_config, load_from_yaml
from liteyuki.utils.base.data_manager import StoredConfig, TempConfig, common_db
from liteyuki.utils.base.language import get_user_lang
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message import *
from liteyuki.utils.base.reloader import Reloader
from .api import update_liteyuki
require("nonebot_plugin_alconna"), require("nonebot_plugin_apscheduler")

View File

@ -1,12 +1,9 @@
import os.path
import shutil
import nonebot.plugin
from liteyuki.utils import init_log
from liteyuki.utils.data_manager import InstalledPlugin, plugin_db
from liteyuki.utils.resource import load_resource_from_dir, load_resources
from liteyuki.utils.tools import check_for_package
from liteyuki.utils.base.data_manager import InstalledPlugin, plugin_db
from liteyuki.utils.base.resource import load_resources
from liteyuki.utils.message.tools import check_for_package
load_resources()
init_log()

View File

@ -1,4 +1,3 @@
import json
import platform
import nonebot
@ -7,13 +6,12 @@ from cpuinfo import get_cpu_info
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageSegment
from nonebot.permission import SUPERUSER
from playwright.async_api import async_playwright
from liteyuki.utils import __NAME__, __VERSION__, load_from_yaml
from liteyuki.utils.htmlrender import template2image
from liteyuki.utils.language import Language, get_default_lang, get_user_lang
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.resource import get_path
from liteyuki.utils.tools import convert_size
from liteyuki.utils.message.html_tool import template2image
from liteyuki.utils.base.language import Language, get_default_lang, get_user_lang
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.base.resource import get_path
from liteyuki.utils.message.tools import convert_size
stats = on_command("status", aliases={"状态"}, priority=5, permission=SUPERUSER)

View File

@ -2,9 +2,9 @@ import nonebot
from nonebot import on_message, require
from nonebot.plugin import PluginMetadata
from liteyuki.utils.data import Database, LiteModel
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message import Markdown as md
from liteyuki.utils.base.data import Database, LiteModel
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message.message import MarkdownMessage as md
require("nonebot_plugin_alconna")
from nonebot_plugin_alconna import on_alconna

View File

@ -1,13 +1,13 @@
import nonebot
from nonebot import on_command
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from nonebot.plugin import PluginMetadata
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent, v11
from liteyuki.utils.message import Markdown as md
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent, v11
from liteyuki.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
md_test = on_command("mdts", aliases={"会话md"}, permission=SUPERUSER)
md_test = on_command("mdts", permission=SUPERUSER)
btn_test = on_command("btnts", permission=SUPERUSER)
placeholder = {
"[": "[",
@ -28,6 +28,15 @@ async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
session_id=event.user_id if event.message_type == "private" else event.group_id
)
@btn_test.handle()
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
await md.send_btn(
str(arg),
bot,
message_type=event.message_type,
session_id=event.user_id if event.message_type == "private" else event.group_id
)
__author__ = "snowykami"
__plugin_meta__ = PluginMetadata(
name="轻雪Markdown测试",

View File

@ -1,6 +1,6 @@
import random
from pydantic import BaseModel
from liteyuki.utils.message import Markdown as md
from liteyuki.utils.message.message import MarkdownMessage as md
class Dot(BaseModel):

View File

@ -1,7 +1,7 @@
from nonebot import require
from ...utils.ly_typing import T_Bot, T_MessageEvent
from ...utils.message import Markdown as md
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message.message import MarkdownMessage as md
require("nonebot_plugin_alconna")
from .game import Minesweeper

View File

@ -4,9 +4,9 @@ from typing import Optional
import aiofiles
import nonebot.plugin
from liteyuki.utils.data import LiteModel
from liteyuki.utils.data_manager import GlobalPlugin, Group, User, group_db, plugin_db, user_db
from liteyuki.utils.ly_typing import T_MessageEvent
from liteyuki.utils.base.data import LiteModel
from liteyuki.utils.base.data_manager import GlobalPlugin, Group, User, group_db, plugin_db, user_db
from liteyuki.utils.base.ly_typing import T_MessageEvent
class PluginTag(LiteModel):

View File

@ -12,12 +12,12 @@ from nonebot.internal.matcher import Matcher
from nonebot.message import run_preprocessor
from nonebot.permission import SUPERUSER
from nonebot.plugin import Plugin
from liteyuki.utils.data_manager import InstalledPlugin
from liteyuki.utils.language import get_user_lang
from liteyuki.utils.ly_typing import T_Bot
from liteyuki.utils.message import Markdown as md
from liteyuki.utils.permission import GROUP_ADMIN, GROUP_OWNER
from liteyuki.utils.tools import clamp
from liteyuki.utils.base.data_manager import InstalledPlugin
from liteyuki.utils.base.language import get_user_lang
from liteyuki.utils.base.ly_typing import T_Bot
from liteyuki.utils.message.message import MarkdownMessage as md
from liteyuki.utils.base.permission import GROUP_ADMIN, GROUP_OWNER
from liteyuki.utils.message.tools import clamp
from .common import *
require("nonebot_plugin_alconna")

View File

@ -1,16 +1,14 @@
# 轻雪资源包管理器
import os
import nonebot
import yaml
from nonebot import require
from nonebot.permission import SUPERUSER
from liteyuki.utils.config import get_config
from liteyuki.utils.language import get_user_lang
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message import Markdown as md
from liteyuki.utils.resource import (ResourceMetadata, add_resource_pack, change_priority, check_exist, check_status, get_loaded_resource_packs, get_resource_metadata, load_resources, remove_resource_pack)
from liteyuki.utils.base.language import get_user_lang
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message.message import MarkdownMessage as md
from liteyuki.utils.base.resource import (ResourceMetadata, add_resource_pack, change_priority, check_exist, check_status, get_loaded_resource_packs, get_resource_metadata, load_resources, remove_resource_pack)
require("nonebot_plugin_alconna")
from nonebot_plugin_alconna import Alconna, Args, on_alconna, Arparma, Subcommand

View File

@ -3,11 +3,11 @@ from typing import Optional
import pytz
from nonebot import require
from liteyuki.utils.data import LiteModel
from liteyuki.utils.data_manager import User, user_db
from liteyuki.utils.language import Language, get_all_lang, get_user_lang
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message import Markdown as md
from liteyuki.utils.base.data import LiteModel
from liteyuki.utils.base.data_manager import User, user_db
from liteyuki.utils.base.language import Language, get_all_lang, get_user_lang
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
from liteyuki.utils.message.message import MarkdownMessage as md
from .const import representative_timezones_list
require("nonebot_plugin_alconna")

View File

@ -1,5 +1,4 @@
from nonebot import on_command
from liteyuki.utils.data import LiteModel
from liteyuki.utils.base.data import LiteModel
class Location(LiteModel):

View File

@ -1,10 +1,9 @@
from nonebot import require
from jieba import lcut
from liteyuki.utils.ly_typing import T_MessageEvent
from liteyuki.utils.base.ly_typing import T_MessageEvent
require("nonebot_plugin_alconna")
from nonebot_plugin_alconna import on_alconna, Alconna, Subcommand, Args, MultiVar, Arparma
from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma
@on_alconna(

View File

@ -10,9 +10,9 @@ __VERSION__ = "6.2.8" # 60201
import requests
from liteyuki.utils.config import load_from_yaml, config
from .log import init_log
from .data_manager import auto_migrate
from liteyuki.utils.base.config import load_from_yaml, config
from liteyuki.utils.base.log import init_log
from liteyuki.utils.base.data_manager import auto_migrate
major, minor, patch = map(int, __VERSION__.split("."))
__VERSION_I__ = major * 10000 + minor * 100 + patch

View File

@ -4,9 +4,9 @@ import nonebot
import yaml
from pydantic import BaseModel
from liteyuki.utils.data_manager import StoredConfig, common_db
from liteyuki.utils.ly_typing import T_Bot
from liteyuki.utils.tools import random_hex_string
from .data_manager import StoredConfig, common_db
from .ly_typing import T_Bot
from ..message.tools import random_hex_string
config = {} # 全局配置,确保加载后读取

View File

@ -2,7 +2,7 @@ import os
from pydantic import Field
from liteyuki.utils.data import LiteModel, Database as DB
from .data import LiteModel, Database as DB
DATA_PATH = "data/liteyuki"

View File

@ -2,13 +2,13 @@ import sys
import loguru
from typing import TYPE_CHECKING
from .config import load_from_yaml
from .language import Language, get_default_lang
from .language import get_default_lang
logger = loguru.logger
if TYPE_CHECKING:
# avoid sphinx autodoc resolve annotation failed
# because loguru module do not have `Logger` class actually
from loguru import Logger, Record
from loguru import Record
def default_filter(record: "Record"):

View File

@ -8,8 +8,8 @@ import psutil
import requests
from aiohttp import FormData
from . import __VERSION_I__, __VERSION__, __NAME__
from .config import config, load_from_yaml
from .. import __VERSION_I__, __VERSION__, __NAME__
from .config import load_from_yaml
class LiteyukiAPI:

View File

@ -1,7 +1,5 @@
from nonebot.adapters.onebot import v11
from liteyuki.utils.ly_typing import T_GroupMessageEvent, T_MessageEvent
GROUP_ADMIN = v11.GROUP_ADMIN
GROUP_OWNER = v11.GROUP_OWNER

View File

@ -42,7 +42,7 @@ def load_resource_from_dir(path: str):
metadata["path"] = path
metadata["folder"] = os.path.basename(path)
if os.path.exists(os.path.join(path, "lang")):
from liteyuki.utils.language import load_from_dir
from liteyuki.utils.base.language import load_from_dir
load_from_dir(os.path.join(path, "lang"))
_loaded_resource_packs.insert(0, ResourceMetadata(**metadata))

View File

View File

@ -0,0 +1,186 @@
import base64
from io import BytesIO
from urllib.parse import quote
import aiohttp
from PIL import Image
from ..base.config import get_config
from ..base.ly_typing import T_Bot
def markdown_escape(text: str) -> str:
"""
转义Markdown特殊字符
Args:
text: str: 文本
Returns:
str: 转义后文本
"""
text = text.replace("\n", r"\n").replace('"', r'\\\"')
spacial_chars = r"\`*_{}[]()#+-.!"
for char in spacial_chars:
text = text.replace(char, "\\" + char)
return text
def escape_decorator(func):
def wrapper(text: str):
return func(markdown_escape(text))
return wrapper
class MarkdownComponent:
@staticmethod
@escape_decorator
def heading(text: str, level: int = 1) -> str:
"""标题"""
assert 1 <= level <= 6, "标题级别应在 1-6 之间"
return f"{'#' * level} {text}"
@staticmethod
@escape_decorator
def bold(text: str) -> str:
"""粗体"""
return f"**{text}**"
@staticmethod
@escape_decorator
def italic(text: str) -> str:
"""斜体"""
return f"*{text}*"
@staticmethod
@escape_decorator
def strike(text: str) -> str:
"""删除线"""
return f"~~{text}~~"
@staticmethod
@escape_decorator
def code(text: str) -> str:
"""行内代码"""
return f"`{text}`"
@staticmethod
@escape_decorator
def code_block(text: str, language: str = "") -> str:
"""代码块"""
return f"```{language}\n{text}\n```"
@staticmethod
@escape_decorator
def quote(text: str) -> str:
"""引用"""
return f"> {text}"
@staticmethod
@escape_decorator
def link(text: str, url: str, symbol: bool = True) -> str:
"""
链接
Args:
text: 链接文本
url: 链接地址
symbol: 是否显示链接图标, mqqapi请使用False
"""
return f"[{'🔗' if symbol else ''}{text}]({quote(url)})"
@staticmethod
@escape_decorator
def image(url: str, *, size: tuple[int, int]) -> str:
"""
图片本地图片不建议直接使用
Args:
url: 图片链接
size: 图片大小
Returns:
markdown格式的图片
"""
return f"![image #{size[0]}px #{size[1]}px]({url})"
@staticmethod
@escape_decorator
async def auto_image(image: str | bytes, bot: T_Bot) -> str:
"""
自动获取图片大小
Args:
image: 本地图片路径 | 图片url http/file | 图片bytes
bot: bot对象用于上传图片到图床
Returns:
markdown格式的图片
"""
if isinstance(image, bytes):
# 传入为二进制图片
image_obj = Image.open(BytesIO(image))
base64_string = base64.b64encode(image_obj.tobytes()).decode("utf-8")
url = await bot.call_api("upload_image", file=f"base64://{base64_string}")
size = image_obj.size
elif isinstance(image, str):
# 传入链接或本地路径
if image.startswith("http"):
# 网络请求
async with aiohttp.ClientSession() as session:
async with session.get(image) as resp:
image_data = await resp.read()
url = image
size = Image.open(BytesIO(image_data)).size
else:
# 本地路径/file://
image_obj = Image.open(image.replace("file://", ""))
base64_string = base64.b64encode(image_obj.tobytes()).decode("utf-8")
url = await bot.call_api("upload_image", file=f"base64://{base64_string}")
size = image_obj.size
else:
raise ValueError("图片类型错误")
return MarkdownComponent.image(url, size=size)
@staticmethod
@escape_decorator
def table(data: list[list[any]]) -> str:
"""
表格
Args:
data: 表格数据二维列表
Returns:
markdown格式的表格
"""
# 表头
table = "|".join(map(str, data[0])) + "\n"
table += "|".join([":-:" for _ in range(len(data[0]))]) + "\n"
# 表内容
for row in data[1:]:
table += "|".join(map(str, row)) + "\n"
return table
class Mqqapi:
@staticmethod
@escape_decorator
def cmd(text: str, cmd: str, enter: bool = True, reply: bool = False, use_cmd_start: bool = True) -> str:
"""
生成点击回调文本
Args:
text: 显示内容
cmd: 命令
enter: 是否自动发送
reply: 是否回复
use_cmd_start: 是否使用配置的命令前缀
Returns:
[text](mqqapi://) markdown格式的可点击回调文本类似于链接
"""
if use_cmd_start:
command_start = get_config("command_start", [])
if command_start:
# 若命令前缀不为空,则使用配置的第一个命令前缀
cmd = f"{command_start[0]}{cmd}"
return f"[{text}](mqqapi://aio/inlinecmd?command={quote(cmd)}&reply={str(reply).lower()}&enter={str(enter).lower()})"

View File

@ -1,4 +1,3 @@
import asyncio
import base64
import io
from urllib.parse import quote
@ -8,12 +7,14 @@ from PIL import Image
import aiohttp
import nonebot
from nonebot import require
from nonebot.adapters.onebot import v11, v12
from typing import Any
from nonebot.adapters.onebot import v11
from typing import Any, Type
from . import load_from_yaml
from .ly_api import liteyuki_api
from .ly_typing import T_Bot, T_Message, T_MessageEvent
from nonebot.internal.adapter import MessageSegment
from nonebot.internal.adapter.message import TM
from .. import load_from_yaml
from ..base.ly_typing import T_Bot, T_Message, T_MessageEvent
require("nonebot_plugin_htmlrender")
from nonebot_plugin_htmlrender import md_to_pic
@ -28,12 +29,12 @@ async def broadcast_to_superusers(message: str | T_Message, markdown: bool = Fal
for bot in nonebot.get_bots().values():
for user_id in config.get("superusers", []):
if markdown:
await Markdown.send_md(message, bot, message_type="private", session_id=user_id)
await MarkdownMessage.send_md(message, bot, message_type="private", session_id=user_id)
else:
await bot.send_private_msg(user_id=user_id, message=message)
class Markdown:
class MarkdownMessage:
@staticmethod
async def send_md(
markdown: str,
@ -158,7 +159,7 @@ class Markdown:
if method == 2:
base64_string = base64.b64encode(image).decode("utf-8")
data = await bot.call_api("upload_image", file=f"base64://{base64_string}")
await Markdown.send_md(Markdown.image(data, Image.open(io.BytesIO(image)).size), bot, event=event, message_type=message_type,
await MarkdownMessage.send_md(MarkdownMessage.image(data, Image.open(io.BytesIO(image)).size), bot, event=event, message_type=message_type,
session_id=session_id, **kwargs)
# 其他实现端方案
@ -171,8 +172,8 @@ class Markdown:
))["message_id"]
image_url = (await bot.get_msg(message_id=image_message_id))["message"][0]["data"]["url"]
image_size = Image.open(io.BytesIO(image)).size
image_md = Markdown.image(image_url, image_size)
return await Markdown.send_md(image_md, bot, message_type=message_type, session_id=session_id, event=event, **kwargs)
image_md = MarkdownMessage.image(image_url, image_size)
return await MarkdownMessage.send_md(image_md, bot, message_type=message_type, session_id=session_id, event=event, **kwargs)
if data is None:
data = await bot.send_msg(
@ -251,7 +252,7 @@ class Markdown:
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
image = Image.open(io.BytesIO(await resp.read()))
return Markdown.image(url, image.size)
return MarkdownMessage.image(url, image.size)
except Exception as e:
nonebot.logger.error(f"get image error: {e}")
return "[Image Error]"
@ -270,58 +271,3 @@ class Markdown:
for char in chars:
text = text.replace(char, f"\\\\{char}")
return text
@staticmethod
def H1(text: str, end="\n") -> str:
"""H1标题"""
return f"# {text}{end}"
@staticmethod
def H2(text: str, end="\n") -> str:
"""H2标题"""
return f"## {text}{end}"
@staticmethod
def H3(text: str, end="\n") -> str:
"""H3标题"""
return f"### {text}{end}"
@staticmethod
def H4(text: str, end="\n") -> str:
"""H4标题"""
return f"#### {text}{end}"
@staticmethod
def H5(text: str, end="\n") -> str:
"""H5标题"""
return f"##### {text}{end}"
@staticmethod
def H6(text: str, end="\n") -> str:
"""H6标题"""
return f"###### {text}{end}"
@staticmethod
def Bold(text: str) -> str:
"""加粗"""
return f"**{text}**"
@staticmethod
def Italic(text: str) -> str:
"""斜体"""
return f"*{text}*"
@staticmethod
def BoldItalic(text: str) -> str:
"""粗斜体"""
return f"***{text}***"
@staticmethod
def Underline(text: str) -> str:
"""下划线"""
return f"__{text}__"
@staticmethod
def Strike(text: str) -> str:
"""删除线"""
return f"~~{text}~~"

View File

@ -1,12 +1,10 @@
import os.path
import nonebot
from nonebot.adapters.onebot import v11, v12
from liteyuki.utils import init
from liteyuki.utils.config import load_from_yaml
from liteyuki.utils.data_manager import StoredConfig, common_db
from liteyuki.utils.ly_api import liteyuki_api
from liteyuki.utils.base.config import load_from_yaml
from liteyuki.utils.base.data_manager import StoredConfig, common_db
from liteyuki.utils.base.ly_api import liteyuki_api
init()