🔀手动Merge轻雪主仓库a77f97f

This commit is contained in:
2024-10-06 02:39:10 +08:00
parent 4cc2ae61db
commit f8b57bfe9a
108 changed files with 3131 additions and 3574 deletions
+3 -43
View File
@@ -1,13 +1,9 @@
import json
import os.path
import platform
import sys
import time
import nonebot
__NAME__ = "尹灵温|轻雪-睿乐"
__VERSION__ = "6.3.4" # 60201
__VERSION__ = "6.3.9" # 60201
import requests
@@ -15,36 +11,10 @@ from src.utils.base.config import load_from_yaml, config
from src.utils.base.log import init_log
from git import Repo
major, minor, patch = map(int, __VERSION__.split("."))
__VERSION_I__ = 99000000 + major * 10000 + minor * 100 + patch
def register_bot():
url = "https://api.liteyuki.icu/register"
data = {
"name": __NAME__,
"version": __VERSION__,
"version_i": __VERSION_I__,
"python": f"{platform.python_implementation()} {platform.python_version()}",
"os": f"{platform.system()} {platform.version()} {platform.machine()}",
}
try:
nonebot.logger.info("正在等待 Liteyuki 注册服务器…")
resp = requests.post(url, json=data, timeout=(10, 15))
if resp.status_code == 200:
data = resp.json()
if liteyuki_id := data.get("liteyuki_id"):
with open("data/liteyuki/liteyuki.json", "wb") as f:
f.write(json.dumps(data).encode("utf-8"))
nonebot.logger.success(f"成功将 {liteyuki_id} 注册到 Liteyuki 服务器")
else:
raise ValueError(f"无法向 Liteyuki 服务器注册:{data}")
except Exception as e:
nonebot.logger.warning(f"向 Liteyuki 服务器注册失败,但无所谓:{e}")
def init():
"""
初始化
@@ -64,25 +34,15 @@ def init():
repo = Repo(".")
except Exception as e:
nonebot.logger.error(
f"无法读取 Git 仓库 {e},你是否是从仓库下载的Zip文件?请使用git clone。"
f"无法读取 Git 仓库 `{e}`,你是否是从仓库直接下载的Zip文件?请使用git clone。"
)
# temp_data: TempConfig = common_db.where_one(TempConfig(), default=TempConfig())
# temp_data.data["start_time"] = time.time()
# common_db.save(temp_data)
# 在加载完成语言后再初始化日志
nonebot.logger.info("尹灵温 正在初始化…")
if not os.path.exists("data/liteyuki/liteyuki.json"):
register_bot()
if not os.path.exists("pyproject.toml"):
with open("pyproject.toml", "w", encoding="utf-8") as f:
f.write("[tool.nonebot]\n")
nonebot.logger.info(
"正在 {} Python{}.{}.{} 上运行 尹灵温".format(
"正在 {} Python{}.{}.{} 上运行 尹灵温-NoneBot".format(
sys.executable,
sys.version_info.major,
sys.version_info.minor,
+3 -8
View File
@@ -1,7 +1,6 @@
import threading
from nonebot import logger
from liteyuki.comm.channel import get_channel
from liteyuki.comm.channel import active_channel
def reload(delay: float = 0.0, receiver: str = "nonebot"):
@@ -14,13 +13,9 @@ def reload(delay: float = 0.0, receiver: str = "nonebot"):
Returns:
"""
chan = get_channel(receiver + "-active")
if chan is None:
logger.error(f"未见 Channel {receiver}-active 以至无法重载")
return
if delay > 0:
threading.Timer(delay, chan.send, args=(1,)).start()
threading.Timer(delay, active_channel.send, args=(1,)).start()
return
else:
chan.send(1)
active_channel.send(1)
+23 -8
View File
@@ -1,4 +1,5 @@
import os
import platform
from typing import List
import nonebot
@@ -7,6 +8,7 @@ from pydantic import BaseModel
from ..message.tools import random_hex_string
config = {} # 全局配置,确保加载后读取
@@ -29,23 +31,37 @@ class BasicConfig(BaseModel):
superusers: list[str] = []
command_start: list[str] = ["/", ""]
nickname: list[str] = [f"灵温-{random_hex_string(6)}"]
default_language: str = "zh-WY"
satori: SatoriConfig = SatoriConfig()
data_path: str = "data/liteyuki"
chromium_path: str = (
"/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome" # type: ignore
if platform.system() == "Darwin"
else (
"C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
if platform.system() == "Windows"
else "/usr/bin/chromium-browser"
)
)
def load_from_yaml(file: str) -> dict:
def load_from_yaml(file_: str) -> dict:
global config
nonebot.logger.debug("Loading config from %s" % file)
if not os.path.exists(file):
nonebot.logger.warning(f"未找到配置文件 {file} ,已创建默认配置,请修改后重启。")
with open(file, "w", encoding="utf-8") as f:
nonebot.logger.debug("正在从 {} 中加载配置项".format(file_))
if not os.path.exists(file_):
nonebot.logger.warning(
f"未寻得配置文件 {file_} ,已以默认配置创建,请在重启后更改为你所需的内容。"
)
with open(file_, "w", encoding="utf-8") as f:
yaml.dump(BasicConfig().dict(), f, default_flow_style=False)
with open(file, "r", encoding="utf-8") as f:
with open(file_, "r", encoding="utf-8") as f:
conf = init_conf(yaml.load(f, Loader=yaml.FullLoader))
config = conf
if conf is None:
nonebot.logger.warning(f"配置文件 {file} 为空,已创建默认配置,请修改后重启。")
nonebot.logger.warning(
f"配置文件 {file_} 为空,已以默认配置创建,请在重启后更改为你所需的内容。"
)
conf = BasicConfig().dict()
return conf
@@ -75,7 +91,6 @@ def get_config(key: str, default=None):
return default
def init_conf(conf: dict) -> dict:
"""
初始化配置文件,确保配置文件中的必要字段存在,且不会冲突
+8 -8
View File
@@ -30,7 +30,7 @@ class Database:
os.makedirs(os.path.dirname(db_name))
self.db_name = db_name
self.conn = sqlite3.connect(db_name)
self.conn = sqlite3.connect(db_name, check_same_thread=False)
self.cursor = self.conn.cursor()
self._on_save_callbacks = []
@@ -94,7 +94,7 @@ class Database:
f"数据库 Selecting {model.TABLE_NAME} WHERE {condition.replace('?', '%s') % args}"
)
if not table_name:
raise ValueError(f"数据模型{model_type.__name__}未提供表名")
raise ValueError(f"数据模型 {model_type.__name__} 未提供表名")
# condition = f"WHERE {condition}"
# print(f"SELECT * FROM {table_name} {condition}", args)
@@ -118,7 +118,7 @@ class Database:
]
def save(self, *args: LiteModel):
"""增/改操作
self.returns_ = """增/改操作
Args:
*args:
Returns:
@@ -126,7 +126,7 @@ class Database:
table_list = [
item[0]
for item in self.cursor.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
"SELECT name FROM sqlite_master WHERE type ='table'"
).fetchall()
]
for model in args:
@@ -158,7 +158,7 @@ class Database:
new_obj[field] = value
else:
raise ValueError(
f"数据模型{table_name}包含不支持的数据类型,字段:{field} 值:{value} 值类型:{type(value)}"
f"数据模型 {table_name} 包含不支持的数据类型,字段:{field} 值:{value} 值类型:{type(value)}"
)
if table_name:
fields, values = [], []
@@ -273,9 +273,9 @@ class Database:
"""
table_name = model.TABLE_NAME
logger.debug(f"数据库 Deleting {model} WHERE {condition} {args}")
logger.debug(f"Deleting {model} WHERE {condition} {args}")
if not table_name:
raise ValueError(f"数据模型{model.__class__.__name__}未提供表名")
raise ValueError(f"数据模型 {model.__class__.__name__} 未提供表名")
if model.id is not None:
condition = f"id = {model.id}"
if not condition and not allow_empty:
@@ -297,7 +297,7 @@ class Database:
"""
for model in args:
if not model.TABLE_NAME:
raise ValueError(f"数据模型{type(model).__name__}未提供表名")
raise ValueError(f"数据模型 {type(model).__name__} 未提供表名")
# 若无则创建表
self.cursor.execute(
+2 -3
View File
@@ -2,9 +2,8 @@ import os
from pydantic import Field
from .data import Database, LiteModel, Database
from .data import Database, LiteModel
print("导入数据库模块")
DATA_PATH = "data/liteyuki"
user_db: Database = Database(os.path.join(DATA_PATH, "users.ldb"))
group_db: Database = Database(os.path.join(DATA_PATH, "groups.ldb"))
@@ -64,7 +63,7 @@ def auto_migrate():
user_db.auto_migrate(User())
group_db.auto_migrate(Group())
plugin_db.auto_migrate(InstalledPlugin(), GlobalPlugin())
common_db.auto_migrate(GlobalPlugin(), StoredConfig(), TempConfig())
common_db.auto_migrate(GlobalPlugin(), TempConfig())
auto_migrate()
-92
View File
@@ -1,92 +0,0 @@
import json
import os.path
import platform
import aiohttp
import nonebot
import psutil
import requests
from .config import load_from_yaml
from .. import __NAME__, __VERSION_I__, __VERSION__
class LiteyukiAPI:
def __init__(self):
self.liteyuki_id = None
if os.path.exists("data/liteyuki/liteyuki.json"):
with open("data/liteyuki/liteyuki.json", "rb") as f:
self.data = json.loads(f.read())
self.liteyuki_id = self.data.get("liteyuki_id")
self.report = load_from_yaml("config.yml").get("auto_report", True)
if self.report:
nonebot.logger.info("已启用自动上报")
@property
def device_info(self) -> dict:
"""
获取设备信息
Returns:
"""
return {
"name": __NAME__,
"version": __VERSION__,
"version_i": __VERSION_I__,
"python": f"{platform.python_implementation()} {platform.python_version()}",
"os": f"{platform.system()} {platform.version()} {platform.machine()}",
"cpu": f"{psutil.cpu_count(logical=False)}c{psutil.cpu_count()}t{psutil.cpu_freq().current}MHz",
"memory_total": f"{psutil.virtual_memory().total / 1024 ** 3:.2f}吉字节",
"memory_used": f"{psutil.virtual_memory().used / 1024 ** 3:.2f}吉字节",
"memory_bot": f"{psutil.Process(os.getpid()).memory_info().rss / 1024 ** 2:.2f}兆字节",
"disk": f"{psutil.disk_usage('/').total / 1024 ** 3:.2f}吉字节",
}
def bug_report(self, content: str):
"""
提交bug报告
Args:
content:
Returns:
"""
if self.report:
nonebot.logger.warning(f"正在上报查误:{content}")
url = "https://api.liteyuki.icu/bug_report"
data = {
"liteyuki_id": self.liteyuki_id,
"content": content,
"device_info": self.device_info,
}
resp = requests.post(url, json=data)
if resp.status_code == 200:
nonebot.logger.success(
f"成功上报差误信息,报文ID为:{resp.json().get('report_id')}"
)
else:
nonebot.logger.error(f"差误上报错误:{resp.text}")
else:
nonebot.logger.warning(f"已禁用自动上报:{content}")
def register(self):
pass
async def heartbeat_report(self):
"""
提交心跳,预留接口
Returns:
"""
url = "https://api.liteyuki.icu/heartbeat"
data = {
"liteyuki_id": self.liteyuki_id,
"version": __VERSION__,
}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=data) as resp:
if resp.status == 200:
nonebot.logger.success("心跳成功送达。")
else:
nonebot.logger.error("休克:{}".format(await resp.text()))
+127 -34
View File
@@ -3,7 +3,9 @@ import os
import shutil
import zipfile
from typing import Any
from pathlib import Path
# import aiofiles
import nonebot
import yaml
@@ -12,8 +14,8 @@ from .language import Language, get_default_lang_code
from .ly_function import loaded_functions
_loaded_resource_packs: list["ResourceMetadata"] = [] # 按照加载顺序排序
temp_resource_root = "data/liteyuki/resources"
temp_extract_root = "data/liteyuki/temp"
temp_resource_root = Path("data/liteyuki/resources")
temp_extract_root = Path("data/liteyuki/temp")
lang = Language(get_default_lang_code())
@@ -50,60 +52,139 @@ def load_resource_from_dir(path: str):
for root, dirs, files in os.walk(path):
for file in files:
relative_path = os.path.relpath(os.path.join(root, file), path)
copy_file(os.path.join(root, file), os.path.join(temp_resource_root, relative_path))
copy_file(
os.path.join(root, file),
os.path.join(temp_resource_root, relative_path),
)
metadata["path"] = path
metadata["folder"] = os.path.basename(path)
if os.path.exists(os.path.join(path, "lang")):
# 加载语言
from src.utils.base.language import load_from_dir
load_from_dir(os.path.join(path, "lang"))
if os.path.exists(os.path.join(path, "functions")):
# 加载功能
from src.utils.base.ly_function import load_from_dir
load_from_dir(os.path.join(path, "functions"))
if os.path.exists(os.path.join(path, "word_bank")):
# 加载词库
from src.utils.base.word_bank import load_from_dir
load_from_dir(os.path.join(path, "word_bank"))
_loaded_resource_packs.insert(0, ResourceMetadata(**metadata))
def get_path(path: str, abs_path: bool = True, default: Any = None, debug: bool = False) -> str | Any:
def get_path(
path: os.PathLike[str,] | Path | str,
abs_path: bool = True,
default: Any = None,
debug: bool = False,
) -> str | Any:
"""
获取资源包中的文件
获取资源包中的路径,且该路径必须存在
Args:
debug: 启用调试,每次都会先重载资源
path: 相对路径
abs_path: 是否返回绝对路径
default: 默认
path: 文件相对路径
Returns: 文件绝对路径
default: 默认解,当该路径不存在时使用
debug: 启用调试,每次都会先重载资源
Returns: 所需求之路径
"""
if debug:
nonebot.logger.debug("Enable resource debug, Reloading resources")
nonebot.logger.debug("由于已启用资源路径调试,正在重载资源")
load_resources()
resource_relative_path = os.path.join(temp_resource_root, path)
if os.path.exists(resource_relative_path):
return os.path.abspath(resource_relative_path) if abs_path else resource_relative_path
resource_relative_path = temp_resource_root / path
if resource_relative_path.exists():
return str(
resource_relative_path.resolve() if abs_path else resource_relative_path
)
else:
return default
def get_files(path: str, abs_path: bool = False) -> list[str]:
def get_resource_path(
path: os.PathLike[str,] | Path | str,
abs_path: bool = True,
only_exist: bool = False,
default: Any = None,
debug: bool = False,
) -> Path:
"""
获取资源包中一个文件夹的所有文件
获取资源包中的路径
Args:
abs_path:
path: 文件夹相对路径
Returns: 文件绝对路径
path: 相对路径
abs_path: 是否返回绝对路径
only_exist: 检查该路径是否存在
default: [当 `only_exist` 为 **真** 时启用]默认解,当该路径不存在时使用
debug: 启用调试,每次都会先重载资源
Returns: 所需求之路径
"""
resource_relative_path = os.path.join(temp_resource_root, path)
if os.path.exists(resource_relative_path):
return [os.path.abspath(os.path.join(resource_relative_path, file)) if abs_path else os.path.join(resource_relative_path, file) for file in
os.listdir(resource_relative_path)]
if debug:
nonebot.logger.debug("由于已启用资源路径调试,正在重载资源")
load_resources()
resource_relative_path = (
(temp_resource_root / path).resolve()
if abs_path
else (temp_resource_root / path)
)
if only_exist:
if resource_relative_path.exists():
return resource_relative_path
else:
return default
else:
return resource_relative_path
def get_files(
path: os.PathLike[str,] | Path | str, abs_path: bool = False
) -> list[str]:
"""
获取资源包中一个目录的所有内容
Args:
path: 该目录的相对路径
abs_path: 是否返回绝对路径
Returns: 目录内容路径所构成之列表
"""
resource_relative_path = temp_resource_root / path
if resource_relative_path.exists():
return [
(
str((resource_relative_path / file_).resolve())
if abs_path
else str((resource_relative_path / file_))
)
for file_ in os.listdir(resource_relative_path)
]
else:
return []
def get_resource_files(
path: os.PathLike[str,] | Path | str, abs_path: bool = False
) -> list[Path]:
"""
获取资源包中一个目录的所有内容
Args:
path: 该目录的相对路径
abs_path: 是否返回绝对路径
Returns: 目录内容路径所构成之列表
"""
resource_relative_path = temp_resource_root / path
if resource_relative_path.exists():
return [
(
(resource_relative_path / file_).resolve()
if abs_path
else (resource_relative_path / file_)
)
for file_ in os.listdir(resource_relative_path)
]
else:
return []
@@ -150,7 +231,9 @@ def load_resources():
if not os.path.exists("resources/index.json"):
json.dump([], open("resources/index.json", "w", encoding="utf-8"))
resource_index: list[str] = json.load(open("resources/index.json", "r", encoding="utf-8"))
resource_index: list[str] = json.load(
open("resources/index.json", "r", encoding="utf-8")
)
resource_index.reverse() # 优先级高的后加载,但是排在前面
for resource in resource_index:
load_resource_from_dir(os.path.join("resources", resource))
@@ -174,7 +257,9 @@ def check_exist(name: str) -> bool:
Returns: 是否存在
"""
path = os.path.join("resources", name)
return os.path.exists(os.path.join(path, "metadata.yml")) or (os.path.isfile(path) and name.endswith(".zip"))
return os.path.exists(os.path.join(path, "metadata.yml")) or (
os.path.isfile(path) and name.endswith(".zip")
)
def add_resource_pack(name: str) -> bool:
@@ -185,17 +270,19 @@ def add_resource_pack(name: str) -> bool:
Returns:
"""
if check_exist(name):
old_index: list[str] = json.load(open("resources/index.json", "r", encoding="utf-8"))
old_index: list[str] = json.load(
open("resources/index.json", "r", encoding="utf-8")
)
if name not in old_index:
old_index.append(name)
json.dump(old_index, open("resources/index.json", "w", encoding="utf-8"))
load_resource_from_dir(os.path.join("resources", name))
return True
else:
nonebot.logger.warning(lang.get("liteyuki.resource_loaded", name=name))
nonebot.logger.warning("资源包 {} 已存在,无需添加".format(name))
return False
else:
nonebot.logger.warning(lang.get("liteyuki.resource_not_exist", name=name))
nonebot.logger.warning("资源包 {} 不存在,无法添加".format(name))
return False
@@ -207,16 +294,18 @@ def remove_resource_pack(name: str) -> bool:
Returns:
"""
if check_exist(name):
old_index: list[str] = json.load(open("resources/index.json", "r", encoding="utf-8"))
old_index: list[str] = json.load(
open("resources/index.json", "r", encoding="utf-8")
)
if name in old_index:
old_index.remove(name)
json.dump(old_index, open("resources/index.json", "w", encoding="utf-8"))
return True
else:
nonebot.logger.warning(lang.get("liteyuki.resource_not_loaded", name=name))
nonebot.logger.warning("资源包 {} 不存在,无需移除".format(name))
return False
else:
nonebot.logger.warning(lang.get("liteyuki.resource_not_exist", name=name))
nonebot.logger.warning("资源包 {} 不存在,无法移除".format(name))
return False
@@ -229,7 +318,9 @@ def change_priority(name: str, delta: int) -> bool:
Returns:
"""
# 正数表示前移,负数表示后移
old_resource_list: list[str] = json.load(open("resources/index.json", "r", encoding="utf-8"))
old_resource_list: list[str] = json.load(
open("resources/index.json", "r", encoding="utf-8")
)
new_resource_list = old_resource_list.copy()
if name in old_resource_list:
index = old_resource_list.index(name)
@@ -237,13 +328,15 @@ def change_priority(name: str, delta: int) -> bool:
new_index = index + delta
new_resource_list.remove(name)
new_resource_list.insert(new_index, name)
json.dump(new_resource_list, open("resources/index.json", "w", encoding="utf-8"))
json.dump(
new_resource_list, open("resources/index.json", "w", encoding="utf-8")
)
return True
else:
nonebot.logger.warning("Priority change failed, out of range")
nonebot.logger.warning("无法更改优先级为 {} ,优先级超出范围".format(delta))
return False
else:
nonebot.logger.debug("Priority change failed, resource not loaded")
nonebot.logger.debug("资源包 {} 未加载,无法更改优先级".format(name))
return False
+6 -4
View File
@@ -1,6 +1,6 @@
from nonebot.adapters import satori
from src.utils.base.ly_typing import T_MessageEvent
from nonebot.adapters import onebot
from src.utils.base.ly_typing import T_MessageEvent, T_GroupMessageEvent
def get_user_id(event: T_MessageEvent):
@@ -10,11 +10,13 @@ def get_user_id(event: T_MessageEvent):
return event.user_id
def get_group_id(event: T_MessageEvent):
def get_group_id(event: T_GroupMessageEvent):
if isinstance(event, satori.event.Event):
return event.guild.id
else:
elif isinstance(event, onebot.v11.GroupMessageEvent):
return event.group_id
else:
return None
def get_message_type(event: T_MessageEvent) -> str:
+16 -163
View File
@@ -1,140 +1,20 @@
import os.path
# import time
from os import getcwd
import os
import aiofiles
import nonebot
from nonebot_plugin_htmlrender import * # type: ignore
from nonebot import require
require("nonebot_plugin_htmlrender")
from nonebot_plugin_htmlrender import (
template_to_html,
template_to_pic,
md_to_pic,
init,
)
from .tools import random_hex_string
# import imgkit
# from typing import Any, Dict, Literal, Optional, Union
# import uuid
# import jinja2
# from pathlib import Path
# TEMPLATES_PATH = str(Path(__file__).parent / "templates")
# env = jinja2.Environment( # noqa: S701
# extensions=["jinja2.ext.loopcontrols"],
# loader=jinja2.FileSystemLoader(TEMPLATES_PATH),
# enable_async=True,
# )
# async def template_to_html(
# template_path: str,
# template_name: str,
# **kwargs,
# ) -> str:
# """使用jinja2模板引擎通过html生成图片
# Args:
# template_path (str): 模板路径
# template_name (str): 模板名
# **kwargs: 模板内容
# Returns:
# str: html
# """
# template_env = jinja2.Environment( # noqa: S701
# loader=jinja2.FileSystemLoader(template_path),
# enable_async=True,
# )
# template = template_env.get_template(template_name)
# return await template.render_async(**kwargs)
# async def template_to_pic(
# template_path: str,
# template_name: str,
# templates: Dict[Any, Any],
# pages: Optional[Dict[Any, Any]] = None,
# wait: int = 0,
# type: Literal["jpeg", "png"] = "png", # noqa: A002
# quality: Union[int, None] = None,
# device_scale_factor: float = 2,
# ) -> bytes:
# """使用jinja2模板引擎通过html生成图片
# Args:
# template_path (str): 模板路径
# template_name (str): 模板名
# templates (Dict[Any, Any]): 模板内参数 如: {"name": "abc"}
# pages (Optional[Dict[Any, Any]]): 网页参数 Defaults to
# {"base_url": f"file://{getcwd()}", "viewport": {"width": 500, "height": 10}}
# wait (int, optional): 网页载入等待时间. Defaults to 0.
# type (Literal["jpeg", "png"]): 图片类型, 默认 png
# quality (int, optional): 图片质量 0-100 当为`png`时无效
# device_scale_factor: 缩放比例,类型为float,值越大越清晰(真正想让图片清晰更优先请调整此选项)
# Returns:
# bytes: 图片 可直接发送
# """
# if pages is None:
# pages = {
# "viewport": {"width": 500, "height": 10},
# "base_url": f"file://{getcwd()}", # noqa: PTH109
# }
# template_env = jinja2.Environment( # noqa: S701
# loader=jinja2.FileSystemLoader(template_path),
# enable_async=True,
# )
# template = template_env.get_template(template_name)
# open(
# filename := os.path.join(
# template_path,
# str(uuid.uuid4())+".html",
# ),
# "w",
# ).write(await template.render_async(**templates))
# print(pages,filename)
# img = imgkit.from_file(
# filename,
# output_path=False,
# options={
# "format": type,
# "quality": quality if (quality and type == "jpeg") else 94,
# "allow": pages["base_url"],
# # "viewport-size": "{} {}".format(pages["viewport"]["width"],pages["viewport"]["height"]),
# "zoom": device_scale_factor,
# # "load-error-handling": "ignore",
# "enable-local-file-access": None,
# "no-stop-slow-scripts": None,
# "transparent": None,
# },
# ) # type: ignore
# # os.remove(filename)
# return img
# return await html_to_pic(
# template_path=f"file://{template_path}",
# html=await template.render_async(**templates),
# wait=wait,
# type=type,
# quality=quality,
# device_scale_factor=device_scale_factor,
# **pages,
# )
async def html2image(
html: str,
wait: int = 0,
):
pass
async def template2html(
template: str,
templates: dict,
@@ -174,8 +54,8 @@ async def template2image(
if pages is None:
pages = {
"viewport": {"width": 1080, "height": 10},
"base_url": f"file://{getcwd()}",
}
template_path = os.path.dirname(template)
template_name = os.path.basename(template)
@@ -197,36 +77,9 @@ async def template2image(
template_name=template_name,
template_path=template_path,
templates=templates,
pages=pages,
wait=wait,
###
pages=pages,
device_scale_factor=scale_factor,
###
)
# async def url2image(
# url: str,
# wait: int = 0,
# scale_factor: float = 1,
# type: str = "png",
# quality: int = 100,
# **kwargs
# ) -> bytes:
# """
# Args:
# quality:
# type:
# url: str: URL
# wait: int: 等待时间
# scale_factor: float: 缩放因子
# **kwargs: page 参数
# Returns:
# 图片二进制数据
# """
# async with get_new_page(scale_factor) as page:
# await page.goto(url)
# await page.wait_for_timeout(wait)
# return await page.screenshot(
# full_page=True,
# type=type,
# quality=quality
# )
+17 -117
View File
@@ -1,33 +1,20 @@
import base64
import io
from typing import Any
from urllib.parse import quote
import aiofiles
from PIL import Image
import aiohttp
import nonebot
from nonebot import require
from nonebot.adapters import satori
from PIL import Image
from nonebot.adapters.onebot import v11
from typing import Any, Type
from nonebot.internal.adapter import MessageSegment
from nonebot.internal.adapter.message import TM
from .html_tool import md_to_pic
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
config = load_from_yaml("config.yml")
can_send_markdown = {} # 用于存储机器人是否支持发送markdown消息,id->bool
class TencentBannedMarkdownError(BaseException):
pass
async def broadcast_to_superusers(message: str | T_Message, markdown: bool = False):
"""广播消息给超级用户"""
@@ -49,9 +36,6 @@ class MarkdownMessage:
*,
message_type: str = None,
session_id: str | int = None,
event: T_MessageEvent = None,
retry_as_image: bool = True,
**kwargs,
) -> dict[str, Any] | None:
"""
发送Markdown消息,支持自动转为图片发送
@@ -60,86 +44,20 @@ class MarkdownMessage:
bot:
message_type:
session_id:
event:
retry_as_image: 发送失败后是否尝试以图片形式发送,否则失败返回None
**kwargs:
Returns:
"""
formatted_md = v11.unescape(markdown).replace("\n", r"\n").replace('"', r"\\\"")
if event is not None and message_type is None:
if isinstance(event, satori.event.Event):
message_type = "private" if event.guild is None else "group"
group_id = event.guild.id if event.guild is not None else None
else:
assert event is not None
message_type = event.message_type
group_id = event.group_id if message_type == "group" else None
user_id = (
event.user.id
if isinstance(event, satori.event.Event)
else event.user_id
)
session_id = user_id if message_type == "private" else group_id
# try:
# raise TencentBannedMarkdownError("Tencent banned markdown")
# forward_id = await bot.call_api(
# "send_private_forward_msg",
# messages=[
# {
# "type": "node",
# "data": {
# "content": [
# {
# "data": {
# "content": "{\"content\":\"%s\"}" % formatted_md,
# },
# "type": "markdown"
# }
# ],
# "name": "[]",
# "uin": bot.self_id
# }
# }
# ],
# user_id=bot.self_id
# )
# data = await bot.send_msg(
# user_id=session_id,
# group_id=session_id,
# message_type=message_type,
# message=[
# {
# "type": "longmsg",
# "data": {
# "id": forward_id
# }
# },
# ],
# **kwargs
# )
# except BaseException as e:
nonebot.logger.error(f"因未能发送Markdown消息,已转为图片发送。")
# 发送失败,渲染为图片发送
# if not retry_as_image:
# return None
# plain_markdown = markdown.replace("[🔗", "[")
md_image_bytes = await md_to_pic(md=markdown, width=540, device_scale_factor=4)
if isinstance(bot, satori.Bot):
msg_seg = satori.MessageSegment.image(raw=md_image_bytes, mime="image/png")
data = await bot.send(event=event, message=msg_seg)
else:
data = await bot.send_msg(
message_type=message_type,
group_id=session_id,
user_id=session_id,
message=v11.MessageSegment.image(md_image_bytes),
)
plain_markdown = markdown.replace("[🔗", "[")
md_image_bytes = await md_to_pic(
md=plain_markdown, width=540, device_scale_factor=4
)
print(md_image_bytes)
data = await bot.send_msg(
message_type=message_type,
group_id=session_id,
user_id=session_id,
message=v11.MessageSegment.image(md_image_bytes),
)
return data
@staticmethod
@@ -157,38 +75,25 @@ class MarkdownMessage:
Args:
image: 图片字节流或图片本地路径,链接请使用Markdown.image_async方法获取后通过send_md发送
bot: bot instance
message_type: message type
message_type: message message_type
session_id: session id
event: event
kwargs: other arguments
Returns:
dict: response data
"""
if isinstance(image, str):
async with aiofiles.open(image, "rb") as f:
image = await f.read()
method = 2
# 1.轻雪图床方案
# if method == 1:
# image_url = await liteyuki_api.upload_image(image)
# image_size = Image.open(io.BytesIO(image)).size
# image_md = Markdown.image(image_url, image_size)
# data = await Markdown.send_md(image_md, bot, message_type=message_type, session_id=session_id, event=event,
# retry_as_image=False,
# **kwargs)
# Lagrange.OneBot方案
if method == 2:
base64_string = base64.b64encode(image).decode("utf-8")
data = await bot.call_api("upload_image", file=f"base64://{base64_string}")
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,
)
# 其他实现端方案
@@ -204,12 +109,7 @@ class MarkdownMessage:
image_size = Image.open(io.BytesIO(image)).size
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,
image_md, bot, message_type=message_type, session_id=session_id
)
if data is None:
@@ -293,7 +193,7 @@ class MarkdownMessage:
image = Image.open(io.BytesIO(await resp.read()))
return MarkdownMessage.image(url, image.size)
except Exception as e:
nonebot.logger.error(f"get image error: {e}")
nonebot.logger.error(f"获取图片错误:{e}")
return "[Image Error]"
@staticmethod