mirror of
https://github.com/LiteyukiStudio/LiteyukiBot.git
synced 2024-11-22 20:17:39 +08:00
feat: 配置项目的热修改
This commit is contained in:
parent
c8851bd696
commit
f9e61fd184
@ -9,6 +9,7 @@ tag:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### 轻雪配置项(Nonebot插件配置项也可以写在此,与dotenv格式不同,应为小写)
|
### 轻雪配置项(Nonebot插件配置项也可以写在此,与dotenv格式不同,应为小写)
|
||||||
|
|
||||||
配置文件会在首次启动后生成,你可以在`config.yaml`中修改配置项后重启轻雪
|
配置文件会在首次启动后生成,你可以在`config.yaml`中修改配置项后重启轻雪
|
||||||
如果不确定字段的含义,请不要修改(部分在自动生成配置文件中未列出,需手动添加)
|
如果不确定字段的含义,请不要修改(部分在自动生成配置文件中未列出,需手动添加)
|
||||||
|
|
||||||
@ -26,6 +27,14 @@ default_language: "zh-CN" # 默认语言,支持i18n部分语言和自行扩展
|
|||||||
log_level: "INFO" # 日志等级
|
log_level: "INFO" # 日志等级
|
||||||
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
|
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
|
||||||
auto_report: true # 是否自动上报问题给轻雪服务器,仅包含硬件信息和运行软件版本
|
auto_report: true # 是否自动上报问题给轻雪服务器,仅包含硬件信息和运行软件版本
|
||||||
|
fake_device_info: # 统计卡片显示的虚假设备信息,用于保护隐私
|
||||||
|
cpu:
|
||||||
|
brand: AMD
|
||||||
|
cores: 16 # 物理核心数
|
||||||
|
logical_cores: 32 # 逻辑核心数
|
||||||
|
frequency: 3600 # CPU主频:MHz
|
||||||
|
mem:
|
||||||
|
total: 32768000000 # 内存总数:字节
|
||||||
|
|
||||||
# 其他Nonebot插件的配置项
|
# 其他Nonebot插件的配置项
|
||||||
custom_config_1: "custom_value1"
|
custom_config_1: "custom_value1"
|
||||||
@ -43,8 +52,8 @@ custom_config_2: "custom_value2"
|
|||||||
| 地址 | ws://`address`/onebot/v11 | 地址取决于配置文件,本机默认为`127.0.0.1:20216` |
|
| 地址 | ws://`address`/onebot/v11 | 地址取决于配置文件,本机默认为`127.0.0.1:20216` |
|
||||||
| AccessToken | `""` | 如果你给轻雪配置了`AccessToken`,请在此填写相同的值 |
|
| AccessToken | `""` | 如果你给轻雪配置了`AccessToken`,请在此填写相同的值 |
|
||||||
|
|
||||||
|
|
||||||
### 其他通信方式
|
### 其他通信方式
|
||||||
|
|
||||||
- 实现端与轻雪的通信方式不局限为反向WebSocket,但是推荐使用反向WebSocket。
|
- 实现端与轻雪的通信方式不局限为反向WebSocket,但是推荐使用反向WebSocket。
|
||||||
- 反向WebSocket的优点是轻雪作为服务端,可以更好的控制连接,适用于生产环境。
|
- 反向WebSocket的优点是轻雪作为服务端,可以更好的控制连接,适用于生产环境。
|
||||||
- 在某些情况下,你也可以使用正向WebSocket,比如你在开发轻雪插件时,可以使用正向WebSocket主动连接实现端
|
- 在某些情况下,你也可以使用正向WebSocket,比如你在开发轻雪插件时,可以使用正向WebSocket主动连接实现端
|
@ -9,7 +9,7 @@ from liteyuki.utils.config import config, load_from_yaml
|
|||||||
from liteyuki.utils.data_manager import StoredConfig, common_db
|
from liteyuki.utils.data_manager import StoredConfig, common_db
|
||||||
from liteyuki.utils.language import get_user_lang
|
from liteyuki.utils.language import get_user_lang
|
||||||
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message import Markdown as md, send_markdown
|
from liteyuki.utils.message import Markdown as md
|
||||||
from .reloader import Reloader
|
from .reloader import Reloader
|
||||||
from liteyuki.utils import htmlrender
|
from liteyuki.utils import htmlrender
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ async def _(bot: T_Bot, event: T_MessageEvent):
|
|||||||
reply += f"```\n{logs}\n```\n"
|
reply += f"```\n{logs}\n```\n"
|
||||||
btn_restart = md.button(ulang.get("liteyuki.restart_now"), "restart-liteyuki")
|
btn_restart = md.button(ulang.get("liteyuki.restart_now"), "restart-liteyuki")
|
||||||
reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}"
|
reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}"
|
||||||
await send_markdown(reply, bot, event=event, at_sender=False)
|
await md.send_md(reply, bot, event=event, at_sender=False)
|
||||||
|
|
||||||
|
|
||||||
@reload_liteyuki.handle()
|
@reload_liteyuki.handle()
|
||||||
@ -119,7 +119,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
for k, v in stored_config.config.items():
|
for k, v in stored_config.config.items():
|
||||||
reply += f"\n{k}={v}"
|
reply += f"\n{k}={v}"
|
||||||
reply += "\n```"
|
reply += "\n```"
|
||||||
await send_markdown(reply, bot, event=event)
|
await md.send_md(reply, bot, event=event)
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
|
@ -1,19 +1,131 @@
|
|||||||
|
import json
|
||||||
|
import random
|
||||||
|
|
||||||
|
import psutil
|
||||||
import requests
|
import requests
|
||||||
|
from PIL import Image
|
||||||
from nonebot.adapters.onebot.v11 import MessageSegment
|
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from liteyuki.utils.htmlrender import template_to_pic, html_to_pic
|
from liteyuki.utils.htmlrender import template_to_pic, html_to_pic
|
||||||
|
from liteyuki.utils.language import get_user_lang
|
||||||
|
from liteyuki.utils.liteyuki_api import liteyuki_api
|
||||||
|
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
||||||
|
from liteyuki.utils.message import Markdown as md
|
||||||
from liteyuki.utils.resource import get_path
|
from liteyuki.utils.resource import get_path
|
||||||
from nonebot import on_command
|
from nonebot import on_command
|
||||||
|
from cpuinfo import get_cpu_info
|
||||||
|
|
||||||
stats = on_command("stats", priority=5, permission=SUPERUSER)
|
from liteyuki.utils.tools import convert_size
|
||||||
|
|
||||||
|
stats = on_command("stats", aliases={"状态"}, priority=5, permission=SUPERUSER)
|
||||||
|
|
||||||
|
protocol_names = {
|
||||||
|
0: "iPad",
|
||||||
|
1: "Android Phone",
|
||||||
|
2: "Android Watch",
|
||||||
|
3: "Mac",
|
||||||
|
5: "iPad",
|
||||||
|
6: "Android Pad",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@stats.handle()
|
@stats.handle()
|
||||||
async def _():
|
async def _(bot: T_Bot, event: T_MessageEvent):
|
||||||
image_bytes = await template_to_pic(
|
ulang = get_user_lang(str(event.user_id))
|
||||||
template_path=get_path("templates/index.html", abs_path=True),
|
fake_device_info: dict = bot.config.dict().get("fake_device_info", {})
|
||||||
templates={}
|
mem_total = fake_device_info.get('mem', {}).get('total', psutil.virtual_memory().total)
|
||||||
|
|
||||||
|
mem_used_bot = psutil.Process().memory_info().rss
|
||||||
|
mem_used_other = psutil.virtual_memory().used - mem_used_bot
|
||||||
|
mem_free = mem_total - mem_used_other - mem_used_bot
|
||||||
|
|
||||||
|
groups = len(await bot.get_group_list())
|
||||||
|
friends = len(await bot.get_friend_list())
|
||||||
|
|
||||||
|
status = await bot.get_status()
|
||||||
|
statistics = status.get("stat", {})
|
||||||
|
version_info = await bot.get_version_info()
|
||||||
|
|
||||||
|
cpu_info = get_cpu_info()
|
||||||
|
if "AMD" in cpu_info.get("brand_raw", ""):
|
||||||
|
brand = "AMD"
|
||||||
|
elif "Intel" in cpu_info.get("brand_raw", ""):
|
||||||
|
brand = "Intel"
|
||||||
|
else:
|
||||||
|
brand = "Unknown"
|
||||||
|
|
||||||
|
if fake_device_info.get("cpu", {}).get("brand"):
|
||||||
|
brand = fake_device_info.get("cpu", {}).get("brand")
|
||||||
|
|
||||||
|
cpu_info = get_cpu_info()
|
||||||
|
templ = {
|
||||||
|
"CPUDATA" : [
|
||||||
|
{
|
||||||
|
"name" : "USED",
|
||||||
|
"value": psutil.cpu_percent(interval=1)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "FREE",
|
||||||
|
"value": 100 - psutil.cpu_percent(interval=1)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MEMDATA" : [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name" : "OTHER",
|
||||||
|
"value": mem_used_other
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "FREE",
|
||||||
|
"value": mem_free
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "BOT",
|
||||||
|
"value": mem_used_bot
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"SWAPDATA" : [
|
||||||
|
{
|
||||||
|
"name" : "USED",
|
||||||
|
"value": psutil.swap_memory().used
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "FREE",
|
||||||
|
"value": psutil.swap_memory().free
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BOT_ID" : bot.self_id,
|
||||||
|
"BOT_NAME" : (await bot.get_login_info())["nickname"],
|
||||||
|
"BOT_TAGS" : [
|
||||||
|
protocol_names.get(version_info.get("protocol_name"), "Linux"), version_info.get("app_name"), version_info.get("app_version"),
|
||||||
|
f"{ulang.get('liteyuki.stats.groups')} {groups}", f"{ulang.get('liteyuki.stats.friends')} {friends}",
|
||||||
|
f"{ulang.get('liteyuki.stats.sent')} {statistics.get('message_sent', 0)}",
|
||||||
|
f"{ulang.get('liteyuki.stats.received')} {statistics.get('message_received', 0)}" \
|
||||||
|
],
|
||||||
|
"CPU_TAGS" : [
|
||||||
|
f"{brand} {cpu_info.get('arch', 'Unknown')}",
|
||||||
|
f"{fake_device_info.get('cpu', {}).get('cores', psutil.cpu_count(logical=False))}C "
|
||||||
|
f"{fake_device_info.get('cpu', {}).get('logical_cores', psutil.cpu_count(logical=True))}T",
|
||||||
|
f"{fake_device_info.get('cpu', {}).get('frequency', psutil.cpu_freq().current) / 1000}GHz"
|
||||||
|
],
|
||||||
|
"MEM_TAGS" : [
|
||||||
|
f"Bot {convert_size(mem_used_bot, 1)}",
|
||||||
|
f"{ulang.get('main.monitor.used')} {convert_size(mem_used_other + mem_used_bot, 1)}",
|
||||||
|
f"{ulang.get('main.monitor.total')} {convert_size(mem_total, 1)}",
|
||||||
|
],
|
||||||
|
"SWAP_TAGS": [
|
||||||
|
f"{ulang.get('main.monitor.used')} {convert_size(psutil.swap_memory().used, 1)}",
|
||||||
|
f"{ulang.get('main.monitor.total')} {convert_size(psutil.swap_memory().total, 1)}",
|
||||||
|
],
|
||||||
|
"CPU" : ulang.get("main.monitor.cpu"),
|
||||||
|
"MEM" : ulang.get("main.monitor.memory"),
|
||||||
|
"SWAP" : ulang.get("main.monitor.swap"),
|
||||||
|
}
|
||||||
|
image_bytes = await template_to_pic(
|
||||||
|
template_path=get_path("templates/stats.html", abs_path=True),
|
||||||
|
templates=templ,
|
||||||
|
wait=1,
|
||||||
|
device_scale_factor=4,
|
||||||
)
|
)
|
||||||
|
# await md.send_image(image_bytes, bot, event=event)
|
||||||
await stats.finish(MessageSegment.image(image_bytes))
|
await stats.finish(MessageSegment.image(image_bytes))
|
||||||
|
@ -4,7 +4,7 @@ from nonebot.plugin import PluginMetadata
|
|||||||
|
|
||||||
from liteyuki.utils.data import Database, LiteModel
|
from liteyuki.utils.data import Database, LiteModel
|
||||||
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message import send_markdown
|
from liteyuki.utils.message import Markdown as md
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna
|
from nonebot_plugin_alconna import on_alconna
|
||||||
@ -108,7 +108,7 @@ async def _(event: T_MessageEvent, bot: T_Bot):
|
|||||||
push_message = (
|
push_message = (
|
||||||
f"> From {event.sender.nickname}@{push.source.session_type}.{push.source.session_id}\n> Bot {bot.self_id}\n\n"
|
f"> From {event.sender.nickname}@{push.source.session_type}.{push.source.session_id}\n> Bot {bot.self_id}\n\n"
|
||||||
f"{msg_formatted}")
|
f"{msg_formatted}")
|
||||||
await send_markdown(push_message, bot2, message_type=push.target.session_type, session_id=push.target.session_id)
|
await md.send_md(push_message, bot2, message_type=push.target.session_type, session_id=push.target.session_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from nonebot.permission import SUPERUSER
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent, v11
|
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent, v11
|
||||||
from liteyuki.utils.message import send_markdown
|
from liteyuki.utils.message import Markdown as md
|
||||||
|
|
||||||
md_test = on_command("mdts", aliases={"会话md"}, permission=SUPERUSER)
|
md_test = on_command("mdts", aliases={"会话md"}, permission=SUPERUSER)
|
||||||
md_group = on_command("mdg", aliases={"群md"}, permission=SUPERUSER)
|
md_group = on_command("mdg", aliases={"群md"}, permission=SUPERUSER)
|
||||||
@ -23,7 +23,7 @@ placeholder = {
|
|||||||
|
|
||||||
@md_test.handle()
|
@md_test.handle()
|
||||||
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
|
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
|
||||||
await send_markdown(
|
await md.send_md(
|
||||||
str(arg),
|
str(arg),
|
||||||
bot,
|
bot,
|
||||||
message_type=event.message_type,
|
message_type=event.message_type,
|
||||||
@ -40,8 +40,8 @@ async def _(bot: v11.Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()
|
|||||||
if str(event.user_id) == str(bot.self_id) and str(bot.self_id) in ["2751454815"]:
|
if str(event.user_id) == str(bot.self_id) and str(bot.self_id) in ["2751454815"]:
|
||||||
nonebot.logger.info("开始处理:%s" % str(event.message_id))
|
nonebot.logger.info("开始处理:%s" % str(event.message_id))
|
||||||
|
|
||||||
data = await send_markdown(str(arg), bot, message_type=event.message_type,
|
data = await md.send_md(str(arg), bot, message_type=event.message_type,
|
||||||
session_id=event.user_id if event.message_type == "private" else event.group_id)
|
session_id=event.user_id if event.message_type == "private" else event.group_id)
|
||||||
await bot.delete_msg(message_id=event.message_id)
|
await bot.delete_msg(message_id=event.message_id)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
from ...utils.ly_typing import T_Bot, T_MessageEvent
|
from ...utils.ly_typing import T_Bot, T_MessageEvent
|
||||||
from ...utils.message import send_markdown
|
from ...utils.message import Markdown as md
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from .game import Minesweeper
|
from .game import Minesweeper
|
||||||
@ -63,7 +63,7 @@ async def _(event: T_MessageEvent, result: Arparma, bot: T_Bot):
|
|||||||
)
|
)
|
||||||
minesweeper_cache.append(new_game)
|
minesweeper_cache.append(new_game)
|
||||||
await minesweeper.send("游戏开始")
|
await minesweeper.send("游戏开始")
|
||||||
await send_markdown(new_game.board_markdown(), bot, event=event)
|
await md.send_md(new_game.board_markdown(), bot, event=event)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
await minesweeper.finish("参数错误")
|
await minesweeper.finish("参数错误")
|
||||||
elif result.subcommands.get("end"):
|
elif result.subcommands.get("end"):
|
||||||
@ -82,9 +82,9 @@ async def _(event: T_MessageEvent, result: Arparma, bot: T_Bot):
|
|||||||
await minesweeper.finish("参数错误")
|
await minesweeper.finish("参数错误")
|
||||||
if not game.reveal(row, col):
|
if not game.reveal(row, col):
|
||||||
minesweeper_cache.remove(game)
|
minesweeper_cache.remove(game)
|
||||||
await send_markdown(game.board_markdown(), bot, event=event)
|
await md.send_md(game.board_markdown(), bot, event=event)
|
||||||
await minesweeper.finish("游戏结束")
|
await minesweeper.finish("游戏结束")
|
||||||
await send_markdown(game.board_markdown(), bot, event=event)
|
await md.send_md(game.board_markdown(), bot, event=event)
|
||||||
if game.is_win():
|
if game.is_win():
|
||||||
minesweeper_cache.remove(game)
|
minesweeper_cache.remove(game)
|
||||||
await minesweeper.finish("游戏胜利")
|
await minesweeper.finish("游戏胜利")
|
||||||
@ -97,6 +97,6 @@ async def _(event: T_MessageEvent, result: Arparma, bot: T_Bot):
|
|||||||
if not (0 <= row < game.rows and 0 <= col < game.cols):
|
if not (0 <= row < game.rows and 0 <= col < game.cols):
|
||||||
await minesweeper.finish("参数错误")
|
await minesweeper.finish("参数错误")
|
||||||
game.board[row][col].flagged = not game.board[row][col].flagged
|
game.board[row][col].flagged = not game.board[row][col].flagged
|
||||||
await send_markdown(game.board_markdown(), bot, event=event)
|
await md.send_md(game.board_markdown(), bot, event=event)
|
||||||
else:
|
else:
|
||||||
await minesweeper.finish("参数错误")
|
await minesweeper.finish("参数错误")
|
||||||
|
@ -10,7 +10,7 @@ from nonebot import require
|
|||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from liteyuki.utils.language import get_user_lang
|
from liteyuki.utils.language import get_user_lang
|
||||||
from liteyuki.utils.ly_typing import T_Bot
|
from liteyuki.utils.ly_typing import T_Bot
|
||||||
from liteyuki.utils.message import Markdown as md, send_markdown
|
from liteyuki.utils.message import Markdown as md
|
||||||
from .common import *
|
from .common import *
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
@ -81,7 +81,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
reply += f"\n{ulang.get('npm.too_many_results', HIDE_NUM=len(rs) - max_show)}"
|
reply += f"\n{ulang.get('npm.too_many_results', HIDE_NUM=len(rs) - max_show)}"
|
||||||
else:
|
else:
|
||||||
reply = ulang.get("npm.search_no_result")
|
reply = ulang.get("npm.search_no_result")
|
||||||
await send_markdown(reply, bot, event=event)
|
await md.send_md(reply, bot, event=event)
|
||||||
|
|
||||||
elif result.subcommands.get("install"):
|
elif result.subcommands.get("install"):
|
||||||
plugin_module_name: str = result.subcommands["install"].args.get("plugin_name")
|
plugin_module_name: str = result.subcommands["install"].args.get("plugin_name")
|
||||||
@ -104,7 +104,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
if found_in_db_plugin is None:
|
if found_in_db_plugin is None:
|
||||||
plugin_db.upsert(installed_plugin)
|
plugin_db.upsert(installed_plugin)
|
||||||
info = md.escape(ulang.get("npm.install_success", NAME=store_plugin.name)) # markdown转义
|
info = md.escape(ulang.get("npm.install_success", NAME=store_plugin.name)) # markdown转义
|
||||||
await send_markdown(
|
await md.send_md(
|
||||||
f"{info}\n\n"
|
f"{info}\n\n"
|
||||||
f"```\n{log}\n```",
|
f"```\n{log}\n```",
|
||||||
bot,
|
bot,
|
||||||
@ -114,7 +114,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
await npm_alc.finish(ulang.get("npm.plugin_already_installed", NAME=store_plugin.name))
|
await npm_alc.finish(ulang.get("npm.plugin_already_installed", NAME=store_plugin.name))
|
||||||
else:
|
else:
|
||||||
info = ulang.get("npm.load_failed", NAME=plugin_module_name, HOMEPAGE=homepage_btn).replace("_", r"\\_")
|
info = ulang.get("npm.load_failed", NAME=plugin_module_name, HOMEPAGE=homepage_btn).replace("_", r"\\_")
|
||||||
await send_markdown(
|
await md.send_md(
|
||||||
f"{info}\n\n"
|
f"{info}\n\n"
|
||||||
f"```\n{log}\n```\n",
|
f"```\n{log}\n```\n",
|
||||||
bot,
|
bot,
|
||||||
@ -122,7 +122,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
info = ulang.get("npm.install_failed", NAME=plugin_module_name, HOMEPAGE=homepage_btn).replace("_", r"\\_")
|
info = ulang.get("npm.install_failed", NAME=plugin_module_name, HOMEPAGE=homepage_btn).replace("_", r"\\_")
|
||||||
await send_markdown(
|
await md.send_md(
|
||||||
f"{info}\n\n"
|
f"{info}\n\n"
|
||||||
f"```\n{log}\n```",
|
f"```\n{log}\n```",
|
||||||
bot,
|
bot,
|
||||||
|
@ -13,7 +13,7 @@ from nonebot.plugin import Plugin
|
|||||||
from liteyuki.utils.data_manager import GlobalPlugin, Group, InstalledPlugin, User, group_db, plugin_db, user_db
|
from liteyuki.utils.data_manager import GlobalPlugin, Group, InstalledPlugin, User, group_db, plugin_db, user_db
|
||||||
from liteyuki.utils.language import get_user_lang
|
from liteyuki.utils.language import get_user_lang
|
||||||
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message import Markdown as md, send_markdown
|
from liteyuki.utils.message import Markdown as md
|
||||||
from liteyuki.utils.permission import GROUP_ADMIN, GROUP_OWNER
|
from liteyuki.utils.permission import GROUP_ADMIN, GROUP_OWNER
|
||||||
from .common import get_plugin_can_be_toggle, get_plugin_default_enable, get_plugin_global_enable, get_plugin_session_enable
|
from .common import get_plugin_can_be_toggle, get_plugin_default_enable, get_plugin_global_enable, get_plugin_session_enable
|
||||||
from .installer import get_store_plugin, npm_update
|
from .installer import get_store_plugin, npm_update
|
||||||
@ -107,7 +107,7 @@ async def _(event: T_MessageEvent, bot: T_Bot):
|
|||||||
reply += f" {btn_uninstall} {btn_toggle_global}"
|
reply += f" {btn_uninstall} {btn_toggle_global}"
|
||||||
|
|
||||||
reply += "\n\n***\n"
|
reply += "\n\n***\n"
|
||||||
await send_markdown(reply, bot, event=event)
|
await md.send_md(reply, bot, event=event)
|
||||||
|
|
||||||
|
|
||||||
@toggle_plugin.handle()
|
@toggle_plugin.handle()
|
||||||
@ -228,6 +228,6 @@ async def pre_handle(event: Event, matcher: Matcher):
|
|||||||
raise IgnoredException("Plugin disabled in session")
|
raise IgnoredException("Plugin disabled in session")
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_calling_api
|
# @Bot.on_calling_api
|
||||||
async def _(bot: Bot, api: str, data: dict[str, any]):
|
# async def _(bot: Bot, api: str, data: dict[str, any]):
|
||||||
nonebot.logger.info(f"Plugin Callapi: {api}: {data}")
|
# nonebot.logger.info(f"Plugin Callapi: {api}: {data}")
|
||||||
|
@ -7,7 +7,7 @@ from liteyuki.utils.data import LiteModel
|
|||||||
from liteyuki.utils.data_manager import User, user_db
|
from liteyuki.utils.data_manager import User, user_db
|
||||||
from liteyuki.utils.language import Language, get_all_lang, get_user_lang
|
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.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message import Markdown as md, send_markdown
|
from liteyuki.utils.message import Markdown as md
|
||||||
from .const import representative_timezones_list
|
from .const import representative_timezones_list
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
@ -62,7 +62,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
# 未输入值,尝试呼出菜单
|
# 未输入值,尝试呼出菜单
|
||||||
menu = get_profile_menu(result.args["key"], ulang)
|
menu = get_profile_menu(result.args["key"], ulang)
|
||||||
if menu:
|
if menu:
|
||||||
await send_markdown(menu, bot, event=event)
|
await md.send_md(menu, bot, event=event)
|
||||||
else:
|
else:
|
||||||
await profile_alc.finish(ulang.get("user.profile.input_value", ATTR=ulang.get(f"user.profile.{result.args['key']}")))
|
await profile_alc.finish(ulang.get("user.profile.input_value", ATTR=ulang.get(f"user.profile.{result.args['key']}")))
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
reply += (f"\n**{key_text}** **{val}**\n"
|
reply += (f"\n**{key_text}** **{val}**\n"
|
||||||
f"\n> {ulang.get(f'user.profile.{key}.desc')}"
|
f"\n> {ulang.get(f'user.profile.{key}.desc')}"
|
||||||
f"\n> {btn_set} \n\n***\n")
|
f"\n> {btn_set} \n\n***\n")
|
||||||
await send_markdown(reply, bot, event=event)
|
await md.send_md(reply, bot, event=event)
|
||||||
|
|
||||||
|
|
||||||
def get_profile_menu(key: str, ulang: Language) -> Optional[str]:
|
def get_profile_menu(key: str, ulang: Language) -> Optional[str]:
|
||||||
|
@ -13,6 +13,14 @@ liteyuki.current_config=当前配置项如下
|
|||||||
liteyuki.static_config=静态文件配置项
|
liteyuki.static_config=静态文件配置项
|
||||||
liteyuki.stored_config=储存的配置项
|
liteyuki.stored_config=储存的配置项
|
||||||
liteyuki.config_set_success=配置项 {KEY}={VAL} 设置成功
|
liteyuki.config_set_success=配置项 {KEY}={VAL} 设置成功
|
||||||
|
liteyuki.stats.group=群
|
||||||
|
liteyuki.stats.user=好友
|
||||||
|
liteyuki.stats.plugin=插件
|
||||||
|
liteyuki.stats.sent=发送
|
||||||
|
liteyuki.stats.received=接收
|
||||||
|
liteyuki.stats.run_time=运行时间
|
||||||
|
liteyuki.stats.groups=群
|
||||||
|
liteyuki.stats.friends=好友
|
||||||
|
|
||||||
main.current_language=当前配置语言为: {LANG}
|
main.current_language=当前配置语言为: {LANG}
|
||||||
main.enable_webdash=已启用网页监控面板: {URL}
|
main.enable_webdash=已启用网页监控面板: {URL}
|
||||||
@ -23,6 +31,8 @@ main.monitor.memory=内存
|
|||||||
main.monitor.swap=交换空间
|
main.monitor.swap=交换空间
|
||||||
main.monitor.disk=磁盘
|
main.monitor.disk=磁盘
|
||||||
main.monitor.usage=使用率
|
main.monitor.usage=使用率
|
||||||
|
main.monitor.total=总计
|
||||||
|
main.monitor.used=已用
|
||||||
|
|
||||||
data_manager.migrate_success=数据模型{NAME}迁移成功
|
data_manager.migrate_success=数据模型{NAME}迁移成功
|
||||||
|
|
||||||
|
13
liteyuki/resources/templates/css/fonts.css
Normal file
13
liteyuki/resources/templates/css/fonts.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'MiSans';
|
||||||
|
src: url('../../fonts/normal.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'MiSans';
|
||||||
|
src: url('../../fonts/bold.ttf') format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
BIN
liteyuki/resources/templates/img/bg1.jpg
Normal file
BIN
liteyuki/resources/templates/img/bg1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 900 KiB |
85683
liteyuki/resources/templates/js/echarts.js
Normal file
85683
liteyuki/resources/templates/js/echarts.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,236 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title></title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
<link rel="stylesheet" href="css/fonts.css">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'MiSans', serif;
|
||||||
|
/* 使背景图不重复 */
|
||||||
|
background-repeat: repeat-y;
|
||||||
|
/* 设置背景图居中裁剪 */
|
||||||
|
background-size: cover;
|
||||||
|
/* 使背景图相对于视窗居中 */
|
||||||
|
background-position: center;
|
||||||
|
/* 设置背景图 */
|
||||||
|
background-image: url('img/bg1.jpg');
|
||||||
|
color: white;
|
||||||
|
// 上10px,左右10px,下0px
|
||||||
|
margin: 10px 10px 0;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
backdrop-filter: blur(30px);
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cpu-chart, #mem-chart, #swap-chart {
|
||||||
|
height: 150px;
|
||||||
|
width: 100px;
|
||||||
|
margin: -10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bot-info {
|
||||||
|
// 垂直方向居中
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hardware-info {
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bot-icon {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bot-name, #bot-tag {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bot-name {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bot-id {
|
||||||
|
margin-left: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bot-tag {
|
||||||
|
/* 这将使标签在容器宽度满时自动换行 */
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-label {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag::after {
|
||||||
|
content: "|";
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 5px;
|
||||||
|
height: 50%; /* 调整这个值来改变竖线的高度 */
|
||||||
|
line-height: 50%; /* 使竖线垂直居中 */
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript" src="js/echarts.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div>
|
|
||||||
<!-- 横向放置三个饼图,分别表示CPU/内存/SWAP占用-->
|
<div class="info-box" id="bot-info">
|
||||||
|
<span>
|
||||||
|
<img id="bot-icon" src="https://q.qlogo.cn/g?b=qq&nk={{BOT_ID}}}&s=640" alt="BotIcon">
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<span id="bot-name">
|
||||||
|
{{ BOT_NAME }}
|
||||||
|
</span>
|
||||||
|
<span id="bot-id">
|
||||||
|
{{ BOT_ID }}
|
||||||
|
</span>
|
||||||
|
<div id="bot-tag"></div>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
|
<div class="info-box" id="hardware-info">
|
||||||
|
<div id="cpu-info">
|
||||||
|
<div id="cpu-chart"></div>
|
||||||
|
</div>
|
||||||
|
<div id="mem-info">
|
||||||
|
<div id="mem-chart"></div>
|
||||||
|
</div>
|
||||||
|
<div id="swap-info">
|
||||||
|
<div id="swap-chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cpuData" style="display: none;">{{ CPUDATA | tojson }}</div>
|
||||||
|
<div id="memData" style="display: none;">{{ MEMDATA | tojson }}</div>
|
||||||
|
<div id="swapData" style="display: none;">{{ SWAPDATA | tojson }}</div>
|
||||||
|
<div id="botTag" style="display: none;">{{ BOT_TAGS | tojson }}</div>
|
||||||
|
<div id="cpuTag" style="display: none;">{{ CPU_TAGS | tojson }}</div>
|
||||||
|
<div id="memTag" style="display: none;">{{ MEM_TAGS | tojson }}</div>
|
||||||
|
<div id="swapTag" style="display: none;">{{ SWAP_TAGS | tojson }}</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 环形图
|
||||||
|
{
|
||||||
|
let bgs = ["bg1.jpg"]
|
||||||
|
// 随机选择背景图片
|
||||||
|
document.body.style.backgroundImage = `url(./img/${bgs[Math.floor(Math.random() * bgs.length)]})`;
|
||||||
|
|
||||||
|
let botTags = JSON.parse(document.getElementById('botTag').innerText);
|
||||||
|
// 获取tag,是字符串数组,将其处理后变成一个一个的span标签,并且class为tag
|
||||||
|
botTags.forEach(tag => {
|
||||||
|
let tagSpan = document.createElement('span');
|
||||||
|
tagSpan.innerText = tag;
|
||||||
|
tagSpan.className = 'tag';
|
||||||
|
document.getElementById('bot-tag').appendChild(tagSpan);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let cpuInfo = echarts.init(document.getElementById('cpu-chart'));
|
||||||
|
let memInfo = echarts.init(document.getElementById('mem-chart'));
|
||||||
|
let swapInfo = echarts.init(document.getElementById('swap-chart'));
|
||||||
|
let cpuData = JSON.parse(document.getElementById('cpuData').innerText);
|
||||||
|
let memData = JSON.parse(document.getElementById('memData').innerText);
|
||||||
|
let swapData = JSON.parse(document.getElementById('swapData').innerText);
|
||||||
|
|
||||||
|
sub_tag_data = {
|
||||||
|
cpu: JSON.parse(document.getElementById('cpuTag').innerText),
|
||||||
|
mem: JSON.parse(document.getElementById('memTag').innerText),
|
||||||
|
swap: JSON.parse(document.getElementById('swapTag').innerText)
|
||||||
|
}
|
||||||
|
// 遍历key和value,key是cpu,mem,swap,value是对应的tag数组,添加div标签class为chart-label
|
||||||
|
for (let key in sub_tag_data) {
|
||||||
|
let infoDiv = document.getElementById(key + '-info');
|
||||||
|
sub_tag_data[key].forEach(tag => {
|
||||||
|
let tagSpan = document.createElement('div');
|
||||||
|
tagSpan.innerText = tag;
|
||||||
|
tagSpan.className = 'chart-label';
|
||||||
|
infoDiv.appendChild(tagSpan);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOption(title, data) {
|
||||||
|
return {
|
||||||
|
animation: false,
|
||||||
|
title: {
|
||||||
|
text: title,
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
textStyle: {
|
||||||
|
//文字颜色
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
trigger: "item",
|
||||||
|
backgroundColor: "#ffffff00",
|
||||||
|
// {a}(系列名称),{b}(数据项名称),{c}(数值), {d}(百分比)
|
||||||
|
},
|
||||||
|
color: ['#a2d8f4', "#ffffff44", '#00a6ff'],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['80%', '100%'],
|
||||||
|
center: ['50%', '50%'],
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: '25',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuInfo.setOption(getOption("{{ CPU }}", cpuData));
|
||||||
|
memInfo.setOption(getOption('{{ MEM }}', memData));
|
||||||
|
swapInfo.setOption(getOption('{{ SWAP }}', swapData));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "canvas",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "rect",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"width": 100,
|
|
||||||
"height": 100,
|
|
||||||
"fill": "red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "rect",
|
|
||||||
"x": 100,
|
|
||||||
"y": 100,
|
|
||||||
"width": 100,
|
|
||||||
"height": 100,
|
|
||||||
"fill": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "rect",
|
|
||||||
"x": 200,
|
|
||||||
"y": 200,
|
|
||||||
"width": 100,
|
|
||||||
"height": 100,
|
|
||||||
"fill": "blue"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
93
liteyuki/resources/templates/test.html
Normal file
93
liteyuki/resources/templates/test.html
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #fff; // 设置背景色为灰色
|
||||||
|
background-repeat: no-repeat; // 设置背景图片不重复
|
||||||
|
background-size: 100% auto;
|
||||||
|
}
|
||||||
|
#pieHuan {
|
||||||
|
width: 400px;
|
||||||
|
height: 400px;
|
||||||
|
margin-top: 50px;
|
||||||
|
// 圆角矩形,使背景高斯模糊
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript" src="js/echarts.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="pieHuan"></div>
|
||||||
|
<script>{
|
||||||
|
// 设置背景图为img/bg1.jpg
|
||||||
|
let bgs = ["bg1.jpg"]
|
||||||
|
// 随机选择背景图片
|
||||||
|
document.body.style.backgroundImage = `url(./img/${bgs[Math.floor(Math.random() * bgs.length)]})`;
|
||||||
|
// 环形图
|
||||||
|
let pieHuan = echarts.init(document.getElementById('pieHuan'));
|
||||||
|
pieHuanOption = {
|
||||||
|
// 标题
|
||||||
|
title: {
|
||||||
|
text: 'echarts实现环形图'
|
||||||
|
},
|
||||||
|
// 图例
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
trigger: "item",
|
||||||
|
backgroundColor: "#1677FF",
|
||||||
|
// {a}(系列名称),{b}(数据项名称),{c}(数值), {d}(百分比)
|
||||||
|
formatter: "{a}:{b}<br/>{c}条({d}%)"
|
||||||
|
},
|
||||||
|
// 不同区域的颜色
|
||||||
|
color: ['#65a5ff', '#dcebff'],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '访问来源',
|
||||||
|
type: 'pie',
|
||||||
|
// 数组的第一项是内半径,第二项是外半径;可以设置不同的内外半径显示成圆环图
|
||||||
|
radius: ['30%', '50%'],
|
||||||
|
// 饼图的中心(圆心)坐标,数组的第一项是横坐标,第二项是纵坐标;设置成百分比时第一项是相对于容器宽度,第二项是相对于容器高度
|
||||||
|
center: ['50%', '50%'],
|
||||||
|
itemStyle: {
|
||||||
|
// 显示图例
|
||||||
|
normal: {
|
||||||
|
label: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
// 标签内容是否高亮
|
||||||
|
show: true,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: '30',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{value: 335, name: '百度'},
|
||||||
|
{value: 335, name: '搜狐'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
pieHuan.setOption(pieHuanOption);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -20,7 +20,7 @@ from playwright.async_api import Browser, Error, Page, Playwright, async_playwri
|
|||||||
from .config import Config
|
from .config import Config
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
config = get_plugin_config(Config)
|
config = Config()
|
||||||
|
|
||||||
_browser: Optional[Browser] = None
|
_browser: Optional[Browser] = None
|
||||||
_playwright: Optional[Playwright] = None
|
_playwright: Optional[Playwright] = None
|
||||||
|
@ -130,6 +130,20 @@ class Language:
|
|||||||
nonebot.logger.error(f"Failed to get language text or format: {e}")
|
nonebot.logger.error(f"Failed to get language text or format: {e}")
|
||||||
return default or item
|
return default or item
|
||||||
|
|
||||||
|
def get_many(self, *args) -> dict[str, str]:
|
||||||
|
"""
|
||||||
|
获取多个文本
|
||||||
|
Args:
|
||||||
|
*args: 文本键
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 文本字典
|
||||||
|
"""
|
||||||
|
d = {}
|
||||||
|
for item in args:
|
||||||
|
d[item] = self.get(item)
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
def get_user_lang(user_id: str) -> Language:
|
def get_user_lang(user_id: str) -> Language:
|
||||||
"""
|
"""
|
||||||
|
@ -2,9 +2,11 @@ import json
|
|||||||
import os.path
|
import os.path
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
import nonebot
|
import nonebot
|
||||||
import psutil
|
import psutil
|
||||||
import requests
|
import requests
|
||||||
|
from aiohttp import FormData
|
||||||
|
|
||||||
from . import __VERSION_I__, __VERSION__, __NAME__
|
from . import __VERSION_I__, __VERSION__, __NAME__
|
||||||
from .config import config, load_from_yaml
|
from .config import config, load_from_yaml
|
||||||
@ -66,5 +68,31 @@ class LiteyukiAPI:
|
|||||||
else:
|
else:
|
||||||
nonebot.logger.warning(f"Bug report is disabled: {content}")
|
nonebot.logger.warning(f"Bug report is disabled: {content}")
|
||||||
|
|
||||||
|
async def upload_image(self, image: bytes) -> str | None:
|
||||||
|
"""
|
||||||
|
上传图片到图床
|
||||||
|
Args:
|
||||||
|
image:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
图片url
|
||||||
|
"""
|
||||||
|
assert self.liteyuki_id, "Liteyuki ID is not set"
|
||||||
|
assert isinstance(image, bytes), "Image must be bytes"
|
||||||
|
url = "https://api.liteyuki.icu/upload_image"
|
||||||
|
data = FormData()
|
||||||
|
data.add_field("liteyuki_id", self.liteyuki_id)
|
||||||
|
data.add_field('image', image, filename='image', content_type='application/octet-stream')
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.post(
|
||||||
|
url,
|
||||||
|
data=data
|
||||||
|
) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
return (await resp.json()).get("url")
|
||||||
|
else:
|
||||||
|
nonebot.logger.error(f"Upload image failed: {await resp.text()}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
liteyuki_api = LiteyukiAPI()
|
liteyuki_api = LiteyukiAPI()
|
||||||
|
@ -1,81 +1,135 @@
|
|||||||
|
import asyncio
|
||||||
|
import io
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
import aiofiles
|
||||||
|
from PIL import Image
|
||||||
|
import aiohttp
|
||||||
import nonebot
|
import nonebot
|
||||||
from nonebot.adapters.onebot import v11, v12
|
from nonebot.adapters.onebot import v11, v12
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from .liteyuki_api import liteyuki_api
|
||||||
from .ly_typing import T_Bot, T_MessageEvent
|
from .ly_typing import T_Bot, T_MessageEvent
|
||||||
|
|
||||||
|
|
||||||
async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, session_id: str | int = None, event: T_MessageEvent = None, **kwargs) -> dict[
|
|
||||||
str, Any]:
|
|
||||||
formatted_md = v11.unescape(markdown).replace("\n", r"\n").replace('"', r'\\\"')
|
|
||||||
if event is not None and message_type is None:
|
|
||||||
message_type = event.message_type
|
|
||||||
session_id = event.user_id if event.message_type == "private" else event.group_id
|
|
||||||
try:
|
|
||||||
forward_id = await bot.call_api(
|
|
||||||
api="send_forward_msg",
|
|
||||||
messages=[
|
|
||||||
v11.MessageSegment(
|
|
||||||
type="node",
|
|
||||||
data={
|
|
||||||
"name" : "Liteyuki.OneBot",
|
|
||||||
"uin" : bot.self_id,
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "markdown",
|
|
||||||
"data": {
|
|
||||||
"content": '{"content":"%s"}' % formatted_md
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data = await bot.send_msg(
|
|
||||||
user_id=session_id,
|
|
||||||
group_id=session_id,
|
|
||||||
message_type=message_type,
|
|
||||||
message=[
|
|
||||||
v11.MessageSegment(
|
|
||||||
type="longmsg",
|
|
||||||
data={
|
|
||||||
"id": forward_id
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
nonebot.logger.warning("send_markdown error, send as plain text: %s" % e.__repr__())
|
|
||||||
if isinstance(bot, v11.Bot):
|
|
||||||
data = await bot.send_msg(
|
|
||||||
message_type=message_type,
|
|
||||||
message=markdown,
|
|
||||||
user_id=int(session_id),
|
|
||||||
group_id=int(session_id),
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
elif isinstance(bot, v12.Bot):
|
|
||||||
data = await bot.send_message(
|
|
||||||
detail_type=message_type,
|
|
||||||
message=v12.Message(
|
|
||||||
v12.MessageSegment.text(
|
|
||||||
text=markdown
|
|
||||||
)
|
|
||||||
),
|
|
||||||
user_id=str(session_id),
|
|
||||||
group_id=str(session_id),
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
nonebot.logger.error("send_markdown: bot type not supported")
|
|
||||||
data = {}
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class Markdown:
|
class Markdown:
|
||||||
|
@staticmethod
|
||||||
|
async def send_md(
|
||||||
|
markdown: str,
|
||||||
|
bot: T_Bot, *,
|
||||||
|
message_type: str = None,
|
||||||
|
session_id: str | int = None,
|
||||||
|
event: T_MessageEvent = None,
|
||||||
|
**kwargs
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
formatted_md = v11.unescape(markdown).replace("\n", r"\n").replace('"', r'\\\"')
|
||||||
|
if event is not None and message_type is None:
|
||||||
|
message_type = event.message_type
|
||||||
|
session_id = event.user_id if event.message_type == "private" else event.group_id
|
||||||
|
try:
|
||||||
|
forward_id = await bot.call_api(
|
||||||
|
api="send_forward_msg",
|
||||||
|
messages=[
|
||||||
|
v11.MessageSegment(
|
||||||
|
type="node",
|
||||||
|
data={
|
||||||
|
"name" : "Liteyuki.OneBot",
|
||||||
|
"uin" : bot.self_id,
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"type": "markdown",
|
||||||
|
"data": {
|
||||||
|
"content": '{"content":"%s"}' % formatted_md
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data = await bot.send_msg(
|
||||||
|
user_id=session_id,
|
||||||
|
group_id=session_id,
|
||||||
|
message_type=message_type,
|
||||||
|
message=[
|
||||||
|
v11.MessageSegment(
|
||||||
|
type="longmsg",
|
||||||
|
data={
|
||||||
|
"id": forward_id
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
nonebot.logger.warning("send_markdown error, send as plain text: %s" % e.__repr__())
|
||||||
|
if isinstance(bot, v11.Bot):
|
||||||
|
data = await bot.send_msg(
|
||||||
|
message_type=message_type,
|
||||||
|
message=markdown,
|
||||||
|
user_id=int(session_id),
|
||||||
|
group_id=int(session_id),
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
elif isinstance(bot, v12.Bot):
|
||||||
|
data = await bot.send_message(
|
||||||
|
detail_type=message_type,
|
||||||
|
message=v12.Message(
|
||||||
|
v12.MessageSegment.text(
|
||||||
|
text=markdown
|
||||||
|
)
|
||||||
|
),
|
||||||
|
user_id=str(session_id),
|
||||||
|
group_id=str(session_id),
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
nonebot.logger.error("send_markdown: bot type not supported")
|
||||||
|
data = {}
|
||||||
|
return data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def send_image(
|
||||||
|
image: bytes | str,
|
||||||
|
bot: T_Bot, *,
|
||||||
|
message_type: str = None,
|
||||||
|
session_id: str | int = None,
|
||||||
|
event: T_MessageEvent = None,
|
||||||
|
**kwargs
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
发送单张装逼大图
|
||||||
|
Args:
|
||||||
|
image: 图片字节流或图片本地路径,链接请使用Markdown.image_async方法获取后通过send_md发送
|
||||||
|
bot: bot instance
|
||||||
|
message_type: 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()
|
||||||
|
|
||||||
|
image_url = await liteyuki_api.upload_image(image)
|
||||||
|
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)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_image_url(image: bytes | str, bot: T_Bot) -> str:
|
||||||
|
"""把图片上传到图床,返回链接
|
||||||
|
Args:
|
||||||
|
bot: 发送的bot
|
||||||
|
image: 图片字节流或图片本地路径
|
||||||
|
Returns:
|
||||||
|
"""
|
||||||
|
# 等林文轩修好Lagrange.OneBot再说
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def button(name: str, cmd: str, reply: bool = False, enter: bool = True) -> str:
|
def button(name: str, cmd: str, reply: bool = False, enter: bool = True) -> str:
|
||||||
"""生成点击回调按钮
|
"""生成点击回调按钮
|
||||||
@ -104,6 +158,38 @@ class Markdown:
|
|||||||
"""
|
"""
|
||||||
return f"[🔗{name}]({url})"
|
return f"[🔗{name}]({url})"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def image(url: str, size: tuple[int, int]) -> str:
|
||||||
|
"""生成图片
|
||||||
|
Args:
|
||||||
|
size:
|
||||||
|
url: 图片链接
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
markdown格式的图片
|
||||||
|
|
||||||
|
"""
|
||||||
|
return f"![image #{size[0]}px #{size[1]}px]({url})"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def image_async(url: str) -> str:
|
||||||
|
"""获取图片,自动获取大小
|
||||||
|
Args:
|
||||||
|
url: 图片链接
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
图片bytes
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
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)
|
||||||
|
except Exception as e:
|
||||||
|
nonebot.logger.error(f"get image error: {e}")
|
||||||
|
return "[Image Error]"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def escape(text: str) -> str:
|
def escape(text: str) -> str:
|
||||||
"""转义特殊字符
|
"""转义特殊字符
|
||||||
|
@ -47,9 +47,21 @@ def get_path(path: str, abs_path: bool = False, default: Any = None) -> str | An
|
|||||||
"""
|
"""
|
||||||
获取资源包中的文件
|
获取资源包中的文件
|
||||||
Args:
|
Args:
|
||||||
abs_path:
|
abs_path: 是否返回绝对路径
|
||||||
default: 默认
|
default: 默认
|
||||||
path: 文件相对路径
|
path: 文件相对路径
|
||||||
Returns: 文件绝对路径
|
Returns: 文件绝对路径
|
||||||
"""
|
"""
|
||||||
return _resource_data.get(path, default) if not abs_path else os.path.abspath(_resource_data.get(path, default))
|
return _resource_data.get(path, default) if not abs_path else os.path.abspath(_resource_data.get(path, default))
|
||||||
|
|
||||||
|
|
||||||
|
def get_files(path: str, abs_path: bool = False) -> list[str]:
|
||||||
|
"""
|
||||||
|
获取资源包中一个文件夹的所有文件
|
||||||
|
Args:
|
||||||
|
abs_path:
|
||||||
|
path: 文件夹相对路径
|
||||||
|
Returns: 文件绝对路径
|
||||||
|
"""
|
||||||
|
return [os.path.abspath(file) for file in _resource_data if file.startswith(path)] if abs_path else [
|
||||||
|
file for file in _resource_data if file.startswith(path)]
|
||||||
|
@ -3,6 +3,7 @@ aiofiles==23.2.1
|
|||||||
arclet-alconna==1.8.5
|
arclet-alconna==1.8.5
|
||||||
arclet-alconna-tools==0.7.0
|
arclet-alconna-tools==0.7.0
|
||||||
colored==2.2.4
|
colored==2.2.4
|
||||||
|
py-cpuinfo==9.0.0
|
||||||
dash==2.16.1
|
dash==2.16.1
|
||||||
GitPython==3.1.42
|
GitPython==3.1.42
|
||||||
nonebot2[fastapi]==2.2.1
|
nonebot2[fastapi]==2.2.1
|
||||||
|
Loading…
Reference in New Issue
Block a user