forked from bot/app
fix: 插件列表显示错误问题
This commit is contained in:
parent
de0c073c26
commit
fab5be70b3
@ -1,10 +1,10 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from src.utils.language import get_system_lang
|
from liteyuki.utils.language import get_default_lang
|
||||||
from src.utils.data_manager import *
|
from liteyuki.utils.data_manager import *
|
||||||
from .loader import *
|
from .loader import *
|
||||||
from .webdash import *
|
from .webdash import *
|
||||||
from src.utils.config import config
|
from liteyuki.utils.config import config
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
@ -20,6 +20,6 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
|
|
||||||
auto_migrate() # 自动迁移数据库
|
auto_migrate() # 自动迁移数据库
|
||||||
|
|
||||||
sys_lang = get_system_lang()
|
sys_lang = get_default_lang()
|
||||||
nonebot.logger.info(sys_lang.get("main.current_language", LANG=sys_lang.get("language.name")))
|
nonebot.logger.info(sys_lang.get("main.current_language", LANG=sys_lang.get("language.name")))
|
||||||
nonebot.logger.info(sys_lang.get("main.enable_webdash", URL=f"http://127.0.0.1:{config.get('port', 8080)}"))
|
nonebot.logger.info(sys_lang.get("main.enable_webdash", URL=f"http://127.0.0.1:{config.get('port', 20216)}"))
|
22
liteyuki/liteyuki_main/loader.py
Normal file
22
liteyuki/liteyuki_main/loader.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import nonebot.plugin
|
||||||
|
|
||||||
|
from liteyuki.utils.data_manager import InstalledPlugin, plugin_db
|
||||||
|
from liteyuki.utils.resource import load_resource_from_dir
|
||||||
|
from liteyuki.utils.tools import check_for_package
|
||||||
|
|
||||||
|
THIS_PLUGIN_NAME = os.path.basename(os.path.dirname(__file__))
|
||||||
|
RESOURCE_PATH = "liteyuki/resources"
|
||||||
|
load_resource_from_dir(RESOURCE_PATH)
|
||||||
|
|
||||||
|
nonebot.plugin.load_plugins("liteyuki/plugins")
|
||||||
|
nonebot.plugin.load_plugins("plugins")
|
||||||
|
|
||||||
|
installed_plugins = plugin_db.all(InstalledPlugin)
|
||||||
|
if installed_plugins:
|
||||||
|
for installed_plugin in plugin_db.all(InstalledPlugin):
|
||||||
|
if not check_for_package(installed_plugin.module_name):
|
||||||
|
nonebot.logger.error(f"{installed_plugin.module_name} not installed, but in loading database. please run `npm fixup` in chat to reinstall it.")
|
||||||
|
else:
|
||||||
|
nonebot.load_plugin(installed_plugin.module_name)
|
@ -3,8 +3,8 @@ import psutil
|
|||||||
from dash import Dash, Input, Output, dcc, html
|
from dash import Dash, Input, Output, dcc, html
|
||||||
from starlette.middleware.wsgi import WSGIMiddleware
|
from starlette.middleware.wsgi import WSGIMiddleware
|
||||||
|
|
||||||
from src.utils.language import Language
|
from liteyuki.utils.language import Language
|
||||||
from src.utils.tools import convert_size
|
from liteyuki.utils.tools import convert_size
|
||||||
|
|
||||||
app = nonebot.get_app()
|
app = nonebot.get_app()
|
||||||
|
|
@ -6,8 +6,8 @@ from nonebot.params import CommandArg
|
|||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from src.utils.message import send_markdown
|
from liteyuki.utils.message import send_markdown
|
||||||
from src.utils.ly_typing import T_Message, T_Bot, v11, T_MessageEvent
|
from liteyuki.utils.ly_typing import T_Message, T_Bot, v11, T_MessageEvent
|
||||||
|
|
||||||
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)
|
@ -4,9 +4,9 @@ from typing import Optional
|
|||||||
import aiofiles
|
import aiofiles
|
||||||
import nonebot.plugin
|
import nonebot.plugin
|
||||||
|
|
||||||
from src.utils.data import Database, LiteModel
|
from liteyuki.utils.data import Database, LiteModel
|
||||||
from src.utils.data_manager import GroupChat, InstalledPlugin, User, group_db, plugin_db, user_db
|
from liteyuki.utils.data_manager import GroupChat, InstalledPlugin, User, group_db, plugin_db, user_db
|
||||||
from src.utils.ly_typing import T_MessageEvent
|
from liteyuki.utils.ly_typing import T_MessageEvent
|
||||||
|
|
||||||
LNPM_COMMAND_START = "lnpm"
|
LNPM_COMMAND_START = "lnpm"
|
||||||
|
|
||||||
@ -58,16 +58,17 @@ def get_plugin_default_enable(plugin_module_name: str) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
bool: 插件默认状态
|
bool: 插件默认状态
|
||||||
"""
|
"""
|
||||||
return (nonebot.plugin.get_plugin(plugin_module_name).metadata.extra.get('default_enable', True)
|
plug = nonebot.plugin.get_plugin_by_module_name(plugin_module_name)
|
||||||
if nonebot.plugin.get_plugin(plugin_module_name) and nonebot.plugin.get_plugin(plugin_module_name).metadata else True) \
|
return (plug.metadata.extra.get('default_enable', True)
|
||||||
if nonebot.plugin.get_plugin(plugin_module_name) else False
|
if plug.metadata else True) if plug else True
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_current_enable(event: T_MessageEvent, plugin_module_name: str) -> bool:
|
def get_plugin_session_enable(event: T_MessageEvent, plugin_module_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
获取插件当前启用状态
|
获取插件当前会话启用状态
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
event: 会话事件
|
||||||
plugin_module_name (str): 插件模块名
|
plugin_module_name (str): 插件模块名
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -88,6 +89,10 @@ def get_plugin_current_enable(event: T_MessageEvent, plugin_module_name: str) ->
|
|||||||
return plugin_module_name in session.enabled_plugins
|
return plugin_module_name in session.enabled_plugins
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin_global_enable(plugin_module_name: str) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_can_be_toggle(plugin_module_name: str) -> bool:
|
def get_plugin_can_be_toggle(plugin_module_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
获取插件是否可以被启用/停用
|
获取插件是否可以被启用/停用
|
||||||
@ -98,5 +103,5 @@ def get_plugin_can_be_toggle(plugin_module_name: str) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
bool: 插件是否可以被启用/停用
|
bool: 插件是否可以被启用/停用
|
||||||
"""
|
"""
|
||||||
return nonebot.plugin.get_plugin(plugin_module_name).metadata.extra.get('toggleable', True) \
|
plug = nonebot.plugin.get_plugin_by_module_name(plugin_module_name)
|
||||||
if nonebot.plugin.get_plugin(plugin_module_name) and nonebot.plugin.get_plugin(plugin_module_name).metadata else True
|
return plug.metadata.extra.get('toggleable', True) if plug and plug.metadata else True
|
@ -9,9 +9,9 @@ from arclet.alconna import Arparma, MultiVar
|
|||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from nonebot_plugin_alconna import Alconna, Args, Subcommand, on_alconna
|
from nonebot_plugin_alconna import Alconna, Args, Subcommand, on_alconna
|
||||||
|
|
||||||
from src.utils.language import get_user_lang
|
from liteyuki.utils.language import get_user_lang
|
||||||
from src.utils.ly_typing import T_Bot
|
from liteyuki.utils.ly_typing import T_Bot
|
||||||
from src.utils.message import Markdown as md, send_markdown
|
from liteyuki.utils.message import Markdown as md, send_markdown
|
||||||
from .common import *
|
from .common import *
|
||||||
|
|
||||||
npm_alc = on_alconna(
|
npm_alc = on_alconna(
|
||||||
@ -45,9 +45,6 @@ npm_alc = on_alconna(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@npm_alc.handle()
|
@npm_alc.handle()
|
||||||
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
||||||
ulang = get_user_lang(str(event.user_id))
|
ulang = get_user_lang(str(event.user_id))
|
||||||
@ -103,7 +100,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
|
|
||||||
if r_load:
|
if r_load:
|
||||||
if found_in_db_plugin is None:
|
if found_in_db_plugin is None:
|
||||||
plugin_db.save(installed_plugin)
|
plugin_db.upsert(installed_plugin)
|
||||||
info = ulang.get('npm.install_success', NAME=store_plugin.name).replace('_', r'\\_') # markdown转义
|
info = ulang.get('npm.install_success', NAME=store_plugin.name).replace('_', r'\\_') # markdown转义
|
||||||
await send_markdown(
|
await send_markdown(
|
||||||
f"{info}\n\n"
|
f"{info}\n\n"
|
||||||
@ -192,9 +189,6 @@ async def npm_search(keywords: list[str]) -> list[StorePlugin]:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def npm_install(plugin_module_name) -> tuple[bool, str]:
|
def npm_install(plugin_module_name) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@ -209,8 +203,8 @@ def npm_install(plugin_module_name) -> tuple[bool, str]:
|
|||||||
sys.stderr = buffer
|
sys.stderr = buffer
|
||||||
|
|
||||||
mirrors = [
|
mirrors = [
|
||||||
"https://pypi.mirrors.cqupt.edu.cn/simple", # 重庆邮电大学
|
|
||||||
"https://pypi.tuna.tsinghua.edu.cn/simple", # 清华大学
|
"https://pypi.tuna.tsinghua.edu.cn/simple", # 清华大学
|
||||||
|
"https://pypi.mirrors.cqupt.edu.cn/simple", # 重庆邮电大学
|
||||||
"https://pypi.liteyuki.icu/simple", # 轻雪镜像
|
"https://pypi.liteyuki.icu/simple", # 轻雪镜像
|
||||||
"https://pypi.org/simple", # 官方源
|
"https://pypi.org/simple", # 官方源
|
||||||
]
|
]
|
@ -8,23 +8,35 @@ from nonebot.message import run_preprocessor
|
|||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Arparma
|
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Arparma
|
||||||
|
|
||||||
from src.utils.data_manager import GroupChat, InstalledPlugin, User, group_db, plugin_db, user_db
|
from liteyuki.utils.data_manager import GroupChat, InstalledPlugin, User, group_db, plugin_db, user_db
|
||||||
from src.utils.message import Markdown as md, send_markdown
|
from liteyuki.utils.message import Markdown as md, send_markdown
|
||||||
from src.utils.permission import GROUP_ADMIN, GROUP_OWNER
|
from liteyuki.utils.permission import GROUP_ADMIN, GROUP_OWNER
|
||||||
from src.utils.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
||||||
from src.utils.language import get_user_lang
|
from liteyuki.utils.language import get_user_lang
|
||||||
from .common import get_plugin_can_be_toggle, get_plugin_current_enable, get_plugin_default_enable
|
from .common import get_plugin_can_be_toggle, get_plugin_global_enable, get_plugin_session_enable, get_plugin_default_enable
|
||||||
from .installer import get_store_plugin, npm_update
|
from .installer import get_store_plugin, npm_update
|
||||||
|
|
||||||
list_plugins = on_command("list-plugin", aliases={"列出插件", "插件列表"}, priority=0)
|
list_plugins = on_alconna(
|
||||||
# toggle_plugin = on_command("enable-plugin", aliases={"启用插件", "停用插件", "disable-plugin"}, priority=0)
|
Alconna(
|
||||||
|
['list-plugins', "插件列表", "列出插件"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
toggle_plugin = on_alconna(
|
toggle_plugin = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
['enable-plugin', 'disable-plugin'],
|
['enable-plugin', 'disable-plugin'],
|
||||||
Args['plugin_name', str]['global', bool, False],
|
Args['plugin_name', str],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
global_toggle = on_alconna(
|
||||||
|
Alconna(
|
||||||
|
['toggle-global'],
|
||||||
|
Args['plugin_name', str],
|
||||||
|
),
|
||||||
|
permission=SUPERUSER
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@list_plugins.handle()
|
@list_plugins.handle()
|
||||||
async def _(event: T_MessageEvent, bot: T_Bot):
|
async def _(event: T_MessageEvent, bot: T_Bot):
|
||||||
@ -35,39 +47,59 @@ async def _(event: T_MessageEvent, bot: T_Bot):
|
|||||||
for plugin in nonebot.get_loaded_plugins():
|
for plugin in nonebot.get_loaded_plugins():
|
||||||
# 检查是否有 metadata 属性
|
# 检查是否有 metadata 属性
|
||||||
# 添加帮助按钮
|
# 添加帮助按钮
|
||||||
btn_usage = md.button(lang.get('npm.usage'), f'help {plugin.name}', False)
|
btn_usage = md.button(lang.get('npm.usage'), f'help {plugin.module_name}', False)
|
||||||
store_plugin = await get_store_plugin(plugin.module_name)
|
store_plugin = await get_store_plugin(plugin.module_name)
|
||||||
|
|
||||||
|
session_enable = get_plugin_session_enable(event, plugin.module_name)
|
||||||
|
default_enable = get_plugin_default_enable(plugin.module_name)
|
||||||
|
print(session_enable, default_enable, plugin.module_name)
|
||||||
|
|
||||||
if store_plugin:
|
if store_plugin:
|
||||||
btn_homepage = md.link(lang.get('npm.homepage'), store_plugin.homepage)
|
btn_homepage = md.link(lang.get('npm.homepage'), store_plugin.homepage)
|
||||||
elif plugin.metadata and plugin.metadata.extra.get('liteyuki'):
|
show_name = store_plugin.name
|
||||||
|
show_desc = store_plugin.desc
|
||||||
|
elif plugin.metadata:
|
||||||
|
if plugin.metadata.extra.get('liteyuki'):
|
||||||
btn_homepage = md.link(lang.get('npm.homepage'), "https://github.com/snowykami/LiteyukiBot")
|
btn_homepage = md.link(lang.get('npm.homepage'), "https://github.com/snowykami/LiteyukiBot")
|
||||||
else:
|
else:
|
||||||
btn_homepage = lang.get('npm.homepage')
|
btn_homepage = lang.get('npm.homepage')
|
||||||
|
show_name = plugin.metadata.name
|
||||||
|
show_desc = plugin.metadata.description
|
||||||
|
else:
|
||||||
|
btn_homepage = lang.get('npm.homepage')
|
||||||
|
show_name = plugin.name
|
||||||
|
show_desc = lang.get('npm.no_description')
|
||||||
|
|
||||||
if plugin.metadata:
|
if plugin.metadata:
|
||||||
reply += (f"\n**{md.escape(plugin.metadata.name)}**\n"
|
reply += (f"\n**{md.escape(show_name)}**\n"
|
||||||
f"\n > {plugin.metadata.description}")
|
f"\n > {md.escape(show_desc)}")
|
||||||
else:
|
else:
|
||||||
reply += (f"**{md.escape(plugin.name)}**\n"
|
reply += (f"**{md.escape(show_name)}**\n"
|
||||||
f"\n > {lang.get('npm.no_description')}")
|
f"\n > {md.escape(show_desc)}")
|
||||||
|
|
||||||
reply += f"\n > {btn_usage} {btn_homepage}"
|
reply += f"\n > {btn_usage} {btn_homepage}"
|
||||||
|
|
||||||
if await GROUP_ADMIN(bot, event) or await GROUP_OWNER(bot, event) or await SUPERUSER(bot, event):
|
if await GROUP_ADMIN(bot, event) or await GROUP_OWNER(bot, event) or await SUPERUSER(bot, event):
|
||||||
# 添加启用/停用插件按钮
|
# 添加启用/停用插件按钮
|
||||||
btn_toggle = lang.get('npm.disable') if plugin.metadata and not plugin.metadata.extra.get('toggleable') \
|
cmd_toggle = f"{'disable' if session_enable else 'enable'}-plugin {plugin.module_name}"
|
||||||
else md.button(lang.get('npm.disable'), f'enable-plugin {plugin.module_name}')
|
text_toggle = lang.get('npm.disable' if session_enable else 'npm.enable')
|
||||||
|
can_be_toggle = get_plugin_can_be_toggle(plugin.module_name)
|
||||||
|
btn_toggle = text_toggle if not can_be_toggle else md.button(text_toggle, cmd_toggle)
|
||||||
|
|
||||||
reply += f" {btn_toggle}"
|
reply += f" {btn_toggle}"
|
||||||
|
|
||||||
if await SUPERUSER(bot, event):
|
if await SUPERUSER(bot, event):
|
||||||
plugin_in_database = plugin_db.first(InstalledPlugin, 'module_name = ?', plugin.module_name)
|
plugin_in_database = plugin_db.first(InstalledPlugin, 'module_name = ?', plugin.module_name)
|
||||||
# 添加移除插件
|
# 添加移除插件和全局切换按钮
|
||||||
|
global_enable = get_plugin_global_enable(plugin.module_name)
|
||||||
btn_uninstall = (
|
btn_uninstall = (
|
||||||
md.button(lang.get('npm.uninstall'), f'npm uninstall {plugin.module_name}')) if plugin_in_database else lang.get(
|
md.button(lang.get('npm.uninstall'), f'npm uninstall {plugin.module_name}')) if plugin_in_database else lang.get(
|
||||||
'npm.uninstall')
|
'npm.uninstall')
|
||||||
btn_toggle_global = lang.get('npm.disable') if plugin.metadata and not plugin.metadata.extra.get('toggleable') \
|
|
||||||
else md.button(lang.get('npm.disable_global'), f'disable-plugin {plugin.module_name} true')
|
btn_toggle_global_text = lang.get('npm.disable_global' if global_enable else 'npm.enable_global')
|
||||||
|
cmd_toggle_global = f'npm toggle-global {plugin.module_name}'
|
||||||
|
btn_toggle_global = btn_toggle_global_text if not can_be_toggle else md.button(btn_toggle_global_text, cmd_toggle_global)
|
||||||
|
|
||||||
reply += f" {btn_uninstall} {btn_toggle_global}"
|
reply += f" {btn_uninstall} {btn_toggle_global}"
|
||||||
|
|
||||||
reply += "\n\n***\n"
|
reply += "\n\n***\n"
|
||||||
@ -83,9 +115,10 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
plugin_module_name = result.args.get("plugin_name")
|
plugin_module_name = result.args.get("plugin_name")
|
||||||
|
|
||||||
toggle = result.header_result == 'enable-plugin' # 判断是启用还是停用
|
toggle = result.header_result == 'enable-plugin' # 判断是启用还是停用
|
||||||
current_enable = get_plugin_current_enable(event, plugin_module_name) # 获取插件当前状态
|
current_enable = get_plugin_session_enable(event, plugin_module_name) # 获取插件当前状态
|
||||||
|
|
||||||
default_enable = get_plugin_default_enable(plugin_module_name) # 获取插件默认状态
|
default_enable = get_plugin_default_enable(plugin_module_name) # 获取插件默认状态
|
||||||
|
|
||||||
can_be_toggled = get_plugin_can_be_toggle(plugin_module_name) # 获取插件是否可以被启用/停用
|
can_be_toggled = get_plugin_can_be_toggle(plugin_module_name) # 获取插件是否可以被启用/停用
|
||||||
|
|
||||||
if not can_be_toggled:
|
if not can_be_toggled:
|
||||||
@ -102,10 +135,6 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
session = group_db.first(GroupChat, "group_id = ?", event.group_id, default=GroupChat(group_id=event.group_id))
|
session = group_db.first(GroupChat, "group_id = ?", event.group_id, default=GroupChat(group_id=event.group_id))
|
||||||
else:
|
else:
|
||||||
raise FinishedException(ulang.get("Permission Denied"))
|
raise FinishedException(ulang.get("Permission Denied"))
|
||||||
# 启用 已停用的默认启用插件 将其从停用列表移除
|
|
||||||
# 启用 已停用的默认停用插件 将其放到启用列表
|
|
||||||
# 停用 已启用的默认启用插件 将其放到停用列表
|
|
||||||
# 停用 已启用的默认停用插件 将其从启用列表移除
|
|
||||||
try:
|
try:
|
||||||
if toggle:
|
if toggle:
|
||||||
if default_enable:
|
if default_enable:
|
||||||
@ -126,14 +155,21 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
ERROR=str(e))
|
ERROR=str(e))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await toggle_plugin.finish(
|
||||||
|
ulang.get(
|
||||||
|
"npm.toggle_success",
|
||||||
|
NAME=plugin_module_name,
|
||||||
|
STATUS=ulang.get("npm.enable") if toggle else ulang.get("npm.disable"))
|
||||||
|
)
|
||||||
|
|
||||||
if event.message_type == "private":
|
if event.message_type == "private":
|
||||||
user_db.save(session)
|
user_db.upsert(session)
|
||||||
else:
|
else:
|
||||||
group_db.save(session)
|
group_db.upsert(session)
|
||||||
|
|
||||||
|
|
||||||
@run_preprocessor
|
@run_preprocessor
|
||||||
async def _(event: T_MessageEvent, matcher: Matcher):
|
async def _(event: T_MessageEvent, matcher: Matcher):
|
||||||
plugin = matcher.plugin
|
plugin = matcher.plugin
|
||||||
# TODO 插件启用/停用检查hook
|
# TODO 插件启用/停用检查hook
|
||||||
nonebot.logger.info(f"Plugin: {plugin.module_name}")
|
nonebot.logger.info(f"Plugin Callapi: {plugin.module_name}")
|
@ -1,5 +1,5 @@
|
|||||||
# 插件权限管理器,对api调用进行hook限制,防止插件滥用api
|
# 插件权限管理器,对api调用进行hook限制,防止插件滥用api
|
||||||
from src.utils.data import LiteModel
|
from liteyuki.utils.data import LiteModel
|
||||||
|
|
||||||
|
|
||||||
class PermissionAllow(LiteModel):
|
class PermissionAllow(LiteModel):
|
@ -2,11 +2,11 @@ from typing import Optional
|
|||||||
|
|
||||||
from nonebot_plugin_alconna import Alconna, Args, Arparma, Subcommand, on_alconna
|
from nonebot_plugin_alconna import Alconna, Args, Arparma, Subcommand, on_alconna
|
||||||
|
|
||||||
from src.utils.data import LiteModel
|
from liteyuki.utils.data import LiteModel
|
||||||
from src.utils.data_manager import User, user_db
|
from liteyuki.utils.data_manager import User, user_db
|
||||||
from src.utils.language import Language, get_all_lang, get_user_lang
|
from liteyuki.utils.language import Language, get_all_lang, get_user_lang
|
||||||
from src.utils.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
||||||
from src.utils.message import Markdown as md, send_markdown
|
from liteyuki.utils.message import Markdown as md, send_markdown
|
||||||
|
|
||||||
profile_alc = on_alconna(
|
profile_alc = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
@ -43,7 +43,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
|||||||
r = set_profile(result.args["key"], result.args["value"])
|
r = set_profile(result.args["key"], result.args["value"])
|
||||||
if r:
|
if r:
|
||||||
user.profile[result.args["key"]] = result.args["value"]
|
user.profile[result.args["key"]] = result.args["value"]
|
||||||
user_db.save(user) # 数据库保存
|
user_db.upsert(user) # 数据库保存
|
||||||
await profile_alc.finish(
|
await profile_alc.finish(
|
||||||
ulang.get(
|
ulang.get(
|
||||||
"user.profile.set_success",
|
"user.profile.set_success",
|
@ -1,6 +1,12 @@
|
|||||||
language.name=English
|
language.name=English
|
||||||
|
|
||||||
main.current_language=Current system language: {LANG}
|
log.debug=Debug
|
||||||
|
log.info=Info
|
||||||
|
log.warning=WARN
|
||||||
|
log.error=Error
|
||||||
|
log.success=Success
|
||||||
|
|
||||||
|
main.current_language=Current config language: {LANG}
|
||||||
main.enable_webdash=Web dashboard is enabled: {URL}
|
main.enable_webdash=Web dashboard is enabled: {URL}
|
||||||
main.monitor.title=Liteyuki Monitor
|
main.monitor.title=Liteyuki Monitor
|
||||||
main.monitor.description=Monitor your server with Liteyuki Monitor
|
main.monitor.description=Monitor your server with Liteyuki Monitor
|
||||||
@ -46,6 +52,7 @@ npm.prev_page=Prev
|
|||||||
npm.plugin_cannot_be_toggled=Plugin {NAME} cannot be toggled
|
npm.plugin_cannot_be_toggled=Plugin {NAME} cannot be toggled
|
||||||
npm.plugin_already=Plugin {NAME} is already in {STATUS} state, no need for repeated operation
|
npm.plugin_already=Plugin {NAME} is already in {STATUS} state, no need for repeated operation
|
||||||
npm.toggle_failed=Failed to {STATUS} plugin {NAME}: {ERROR}
|
npm.toggle_failed=Failed to {STATUS} plugin {NAME}: {ERROR}
|
||||||
|
npm.toggle_success=Succeeded in {STATUS} plugin {NAME}
|
||||||
|
|
||||||
user.profile.edit=Edit
|
user.profile.edit=Edit
|
||||||
user.profile.set=Set
|
user.profile.set=Set
|
@ -1,5 +1,11 @@
|
|||||||
language.name = 日本語
|
language.name = 日本語
|
||||||
|
|
||||||
|
log.debug=デバッグ
|
||||||
|
log.info=情報
|
||||||
|
log.warning=警告
|
||||||
|
log.error=エラー
|
||||||
|
log.success=成功
|
||||||
|
|
||||||
main.current_language = 現在のシステム言語: {LANG}
|
main.current_language = 現在のシステム言語: {LANG}
|
||||||
main.enable_webdash = ウェブダッシュボードが有効になりました: {URL}
|
main.enable_webdash = ウェブダッシュボードが有効になりました: {URL}
|
||||||
main.monitor.title = Liteyukiモニタリングパネル
|
main.monitor.title = Liteyukiモニタリングパネル
|
||||||
@ -46,6 +52,7 @@ npm.prev_page = 前のページ
|
|||||||
npm.plugin_cannot_be_toggled=プラグイン {NAME} は有効または無効にできません
|
npm.plugin_cannot_be_toggled=プラグイン {NAME} は有効または無効にできません
|
||||||
npm.plugin_already=プラグイン {NAME} はすでに {STATUS} 状態です。繰り返し操作する必要はありません
|
npm.plugin_already=プラグイン {NAME} はすでに {STATUS} 状態です。繰り返し操作する必要はありません
|
||||||
npm.toggle_failed=プラグイン {NAME} を {STATUS} にするのに失敗しました: {ERROR}
|
npm.toggle_failed=プラグイン {NAME} を {STATUS} にするのに失敗しました: {ERROR}
|
||||||
|
npm.toggle_success=プラグイン {NAME} が {STATUS} になりました
|
||||||
|
|
||||||
user.profile.edit=編集
|
user.profile.edit=編集
|
||||||
user.profile.set=設定
|
user.profile.set=設定
|
@ -1,6 +1,12 @@
|
|||||||
language.name=简体中文
|
language.name=简体中文
|
||||||
|
|
||||||
main.current_language=当前系统语言为: {LANG}
|
log.debug=调试
|
||||||
|
log.info=信息
|
||||||
|
log.warning=警告
|
||||||
|
log.error=错误
|
||||||
|
log.success=成功
|
||||||
|
|
||||||
|
main.current_language=当前配置语言为: {LANG}
|
||||||
main.enable_webdash=已启用网页监控面板: {URL}
|
main.enable_webdash=已启用网页监控面板: {URL}
|
||||||
main.monitor.title=轻雪监控面板
|
main.monitor.title=轻雪监控面板
|
||||||
main.monitor.description=轻雪机器人监控面板
|
main.monitor.description=轻雪机器人监控面板
|
||||||
@ -32,7 +38,7 @@ npm.search_no_result=无搜索结果
|
|||||||
npm.too_many_results=内容过多,{HIDE_NUM}项已隐藏,请限制关键字搜索
|
npm.too_many_results=内容过多,{HIDE_NUM}项已隐藏,请限制关键字搜索
|
||||||
npm.install_success={NAME} 安装成功
|
npm.install_success={NAME} 安装成功
|
||||||
npm.install_failed={NAME} 安装失败,请查看日志获取详细信息,如不能解决,请访问{HOMEPAGE}寻求帮助
|
npm.install_failed={NAME} 安装失败,请查看日志获取详细信息,如不能解决,请访问{HOMEPAGE}寻求帮助
|
||||||
npm.uninstall_success={NAME} 卸载成功
|
npm.uninstall_success={NAME} 卸载成功,下次重启生效
|
||||||
npm.uninstall_failed={NAME} 卸载失败
|
npm.uninstall_failed={NAME} 卸载失败
|
||||||
npm.load_failed={NAME} 加载失败,请在控制台查看详细信息,检查依赖或配置是否正确,如不能解决,请访问{HOMEPAGE}寻求帮助
|
npm.load_failed={NAME} 加载失败,请在控制台查看详细信息,检查依赖或配置是否正确,如不能解决,请访问{HOMEPAGE}寻求帮助
|
||||||
npm.plugin_not_found=未在商店中找到 {NAME},请尝试更新商店信息或检查拼写
|
npm.plugin_not_found=未在商店中找到 {NAME},请尝试更新商店信息或检查拼写
|
||||||
@ -46,6 +52,7 @@ npm.prev_page=上一页
|
|||||||
npm.plugin_cannot_be_toggled=插件 {NAME} 无法被启用或停用
|
npm.plugin_cannot_be_toggled=插件 {NAME} 无法被启用或停用
|
||||||
npm.plugin_already=插件 {NAME} 已经是 {STATUS} 状态,无需重复操作
|
npm.plugin_already=插件 {NAME} 已经是 {STATUS} 状态,无需重复操作
|
||||||
npm.toggle_failed=插件 {NAME} {STATUS} 失败: {ERROR}
|
npm.toggle_failed=插件 {NAME} {STATUS} 失败: {ERROR}
|
||||||
|
npm.toggle_success=插件 {NAME} {STATUS} 成功
|
||||||
|
|
||||||
user.profile.edit=修改
|
user.profile.edit=修改
|
||||||
user.profile.set=设置
|
user.profile.set=设置
|
@ -1,4 +1,10 @@
|
|||||||
language.name=繁體中文
|
language.name=繁體中文(香港)
|
||||||
|
|
||||||
|
log.debug=調試
|
||||||
|
log.info=信息
|
||||||
|
log.warning=警告
|
||||||
|
log.error=錯誤
|
||||||
|
log.success=成功
|
||||||
|
|
||||||
main.current_language=當前系統語言為:{LANG}
|
main.current_language=當前系統語言為:{LANG}
|
||||||
main.enable_webdash=已啟用網頁監控面板:{URL}
|
main.enable_webdash=已啟用網頁監控面板:{URL}
|
||||||
@ -46,6 +52,8 @@ npm.prev_page=上一頁
|
|||||||
npm.plugin_cannot_be_toggled=無法啟用或停用插件 {NAME}
|
npm.plugin_cannot_be_toggled=無法啟用或停用插件 {NAME}
|
||||||
npm.plugin_already=插件 {NAME} 已處於 {STATUS} 狀態,無需重複操作
|
npm.plugin_already=插件 {NAME} 已處於 {STATUS} 狀態,無需重複操作
|
||||||
npm.toggle_failed=插件 {NAME} {STATUS} 失敗: {ERROR}
|
npm.toggle_failed=插件 {NAME} {STATUS} 失敗: {ERROR}
|
||||||
|
npm.toggle_success=插件 {NAME} {STATUS} 成功
|
||||||
|
|
||||||
|
|
||||||
user.profile.edit=編輯
|
user.profile.edit=編輯
|
||||||
user.profile.set=設定
|
user.profile.set=設定
|
@ -1,5 +1,11 @@
|
|||||||
language.name=简体中文(轻雪版)
|
language.name=简体中文(轻雪版)
|
||||||
|
|
||||||
|
log.debug=调试
|
||||||
|
log.info=信息
|
||||||
|
log.warning=有问题哦
|
||||||
|
log.error=出错啦
|
||||||
|
log.success=成功啦
|
||||||
|
|
||||||
main.current_language=现在系统用的语言是:{LANG} 喔!
|
main.current_language=现在系统用的语言是:{LANG} 喔!
|
||||||
main.enable_webdash=已经打开了网页监控板:{URL} 啦!
|
main.enable_webdash=已经打开了网页监控板:{URL} 啦!
|
||||||
main.monitor.title=监控板
|
main.monitor.title=监控板
|
@ -1,4 +1,10 @@
|
|||||||
language.name=漢字
|
language.name=中文(華夏)
|
||||||
|
|
||||||
|
log.debug=調試
|
||||||
|
log.info=信息
|
||||||
|
log.warning=警告
|
||||||
|
log.error=錯誤
|
||||||
|
log.success=成功
|
||||||
|
|
||||||
main.current_language=當前之系統語言為:{LANG}
|
main.current_language=當前之系統語言為:{LANG}
|
||||||
main.enable_webdash=已啟用網頁監控板:{URL}
|
main.enable_webdash=已啟用網頁監控板:{URL}
|
37
liteyuki/utils/__init__.py
Normal file
37
liteyuki/utils/__init__.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import nonebot
|
||||||
|
|
||||||
|
from .log import logger
|
||||||
|
import sys
|
||||||
|
|
||||||
|
__NAME__ = "LiteyukiBot"
|
||||||
|
__VERSION__ = "6.2.1" # 60201
|
||||||
|
major, minor, patch = map(int, __VERSION__.split("."))
|
||||||
|
__VERSION_I__ = major * 10000 + minor * 100 + patch
|
||||||
|
|
||||||
|
|
||||||
|
def init():
|
||||||
|
"""
|
||||||
|
初始化
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# 检测python版本是否高于3.10
|
||||||
|
if sys.version_info < (3, 10):
|
||||||
|
nonebot.logger.error("This project requires Python3.10+ to run, please upgrade your Python Environment.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
print("\033[34m" + r""" __ ______ ________ ________ __ __ __ __ __ __ ______
|
||||||
|
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
||||||
|
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
||||||
|
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
||||||
|
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
||||||
|
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
||||||
|
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
||||||
|
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
||||||
|
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/ """ + "\033[0m")
|
||||||
|
nonebot.logger.info(
|
||||||
|
f"Run Liteyuki with Python{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} "
|
||||||
|
f"at {sys.executable}"
|
||||||
|
)
|
||||||
|
|
||||||
|
nonebot.logger.info(f"{__NAME__} {__VERSION__}({__VERSION_I__}) is running")
|
@ -4,7 +4,7 @@ import nonebot
|
|||||||
import yaml
|
import yaml
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
config = None
|
config = {}
|
||||||
|
|
||||||
|
|
||||||
class BasicConfig(BaseModel):
|
class BasicConfig(BaseModel):
|
@ -31,7 +31,7 @@ class BaseORMAdapter(ABC):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def upsert(self, *args, **kwargs):
|
||||||
"""存储数据
|
"""存储数据
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -171,7 +171,7 @@ class Database(BaseORMAdapter):
|
|||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
nonebot.logger.debug(f'Table {table_name} migrated successfully')
|
nonebot.logger.debug(f'Table {table_name} migrated successfully')
|
||||||
|
|
||||||
def save(self, *models: LiteModel) -> int | tuple:
|
def upsert(self, *models: LiteModel) -> int | tuple:
|
||||||
"""存储数据,检查id字段,如果有id字段则更新,没有则插入
|
"""存储数据,检查id字段,如果有id字段则更新,没有则插入
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -192,7 +192,7 @@ class Database(BaseORMAdapter):
|
|||||||
for field, value in model.__dict__.items():
|
for field, value in model.__dict__.items():
|
||||||
if isinstance(value, LiteModel):
|
if isinstance(value, LiteModel):
|
||||||
key_list.append(f'{self.FOREIGNID}{field}')
|
key_list.append(f'{self.FOREIGNID}{field}')
|
||||||
value_list.append(f'{self.ID}:{value.__class__.__name__}:{self.save(value)}')
|
value_list.append(f'{self.ID}:{value.__class__.__name__}:{self.upsert(value)}')
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
key_list.append(f'{self.LIST}{field}')
|
key_list.append(f'{self.LIST}{field}')
|
||||||
value_list.append(self._flat(value))
|
value_list.append(self._flat(value))
|
||||||
@ -225,7 +225,7 @@ class Database(BaseORMAdapter):
|
|||||||
return_data = {}
|
return_data = {}
|
||||||
for k, v in data.items():
|
for k, v in data.items():
|
||||||
if isinstance(v, LiteModel):
|
if isinstance(v, LiteModel):
|
||||||
return_data[f'{self.FOREIGNID}{k}'] = f'{self.ID}:{v.__class__.__name__}:{self.save(v)}'
|
return_data[f'{self.FOREIGNID}{k}'] = f'{self.ID}:{v.__class__.__name__}:{self.upsert(v)}'
|
||||||
elif isinstance(v, list):
|
elif isinstance(v, list):
|
||||||
return_data[f'{self.LIST}{k}'] = self._flat(v)
|
return_data[f'{self.LIST}{k}'] = self._flat(v)
|
||||||
elif isinstance(v, dict):
|
elif isinstance(v, dict):
|
||||||
@ -239,7 +239,7 @@ class Database(BaseORMAdapter):
|
|||||||
return_data = []
|
return_data = []
|
||||||
for v in data:
|
for v in data:
|
||||||
if isinstance(v, LiteModel):
|
if isinstance(v, LiteModel):
|
||||||
return_data.append(f'{self.ID}:{v.__class__.__name__}:{self.save(v)}')
|
return_data.append(f'{self.ID}:{v.__class__.__name__}:{self.upsert(v)}')
|
||||||
elif isinstance(v, list):
|
elif isinstance(v, list):
|
||||||
return_data.append(self._flat(v))
|
return_data.append(self._flat(v))
|
||||||
elif isinstance(v, dict):
|
elif isinstance(v, dict):
|
45
liteyuki/utils/data_manager.py
Normal file
45
liteyuki/utils/data_manager.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from liteyuki.utils.data import LiteModel, Database as DB
|
||||||
|
|
||||||
|
DATA_PATH = "data/liteyuki"
|
||||||
|
|
||||||
|
user_db = DB(os.path.join(DATA_PATH, 'users.ldb'))
|
||||||
|
group_db = DB(os.path.join(DATA_PATH, 'groups.ldb'))
|
||||||
|
plugin_db = DB(os.path.join(DATA_PATH, 'plugins.ldb'))
|
||||||
|
common_db = DB(os.path.join(DATA_PATH, 'common.ldb'))
|
||||||
|
|
||||||
|
|
||||||
|
class User(LiteModel):
|
||||||
|
user_id: str = Field(str(), alias='user_id')
|
||||||
|
username: str = Field(str(), alias='username')
|
||||||
|
profile: dict[str, str] = Field(dict(), alias='profile')
|
||||||
|
enabled_plugins: list[str] = Field(list(), alias='enabled_plugins')
|
||||||
|
disabled_plugins: list[str] = Field(list(), alias='disabled_plugins')
|
||||||
|
|
||||||
|
|
||||||
|
class GroupChat(LiteModel):
|
||||||
|
# Group是一个关键字,所以这里用GroupChat
|
||||||
|
group_id: str = Field(str(), alias='group_id')
|
||||||
|
group_name: str = Field(str(), alias='group_name')
|
||||||
|
enabled_plugins: list[str] = Field([], alias='enabled_plugins')
|
||||||
|
disabled_plugins: list[str] = Field([], alias='disabled_plugins')
|
||||||
|
|
||||||
|
|
||||||
|
class InstalledPlugin(LiteModel):
|
||||||
|
module_name: str = Field(str(), alias='module_name')
|
||||||
|
version: str = Field(str(), alias='version')
|
||||||
|
|
||||||
|
|
||||||
|
class GlobalPlugin(LiteModel):
|
||||||
|
module_name: str = Field(str(), alias='module_name')
|
||||||
|
enabled: bool = Field(True, alias='enabled')
|
||||||
|
|
||||||
|
|
||||||
|
def auto_migrate():
|
||||||
|
user_db.auto_migrate(User())
|
||||||
|
group_db.auto_migrate(GroupChat())
|
||||||
|
plugin_db.auto_migrate(InstalledPlugin())
|
||||||
|
common_db.auto_migrate(GlobalPlugin())
|
@ -9,8 +9,8 @@ from typing import Any
|
|||||||
|
|
||||||
import nonebot
|
import nonebot
|
||||||
|
|
||||||
from src.utils.config import config
|
from liteyuki.utils.config import config
|
||||||
from src.utils.data_manager import User, user_db
|
from liteyuki.utils.data_manager import User, user_db
|
||||||
|
|
||||||
_default_lang_code = "en"
|
_default_lang_code = "en"
|
||||||
_language_data = {
|
_language_data = {
|
||||||
@ -38,7 +38,6 @@ def load_from_lang(file_path: str, lang_code: str = None):
|
|||||||
if not line or line.startswith('#'): # 空行或注释
|
if not line or line.startswith('#'): # 空行或注释
|
||||||
continue
|
continue
|
||||||
key, value = line.split('=', 1)
|
key, value = line.split('=', 1)
|
||||||
nonebot.logger.debug(f"Loaded language text: {key.strip()} -> {value.strip()}")
|
|
||||||
data[key.strip()] = value.strip()
|
data[key.strip()] = value.strip()
|
||||||
if lang_code not in _language_data:
|
if lang_code not in _language_data:
|
||||||
_language_data[lang_code] = {}
|
_language_data[lang_code] = {}
|
||||||
@ -149,11 +148,11 @@ def get_system_lang_code() -> str:
|
|||||||
return locale.getdefaultlocale()[0].replace('_', '-')
|
return locale.getdefaultlocale()[0].replace('_', '-')
|
||||||
|
|
||||||
|
|
||||||
def get_system_lang() -> Language:
|
def get_default_lang() -> Language:
|
||||||
"""
|
"""
|
||||||
获取系统语言
|
获取默认/系统语言
|
||||||
"""
|
"""
|
||||||
return Language(get_system_lang_code())
|
return Language(config.get("default_language", get_system_lang_code()))
|
||||||
|
|
||||||
|
|
||||||
def get_all_lang() -> dict[str, str]:
|
def get_all_lang() -> dict[str, str]:
|
@ -1,35 +1,14 @@
|
|||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
from colored import fg
|
||||||
|
from .language import get_default_lang
|
||||||
import loguru
|
import loguru
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
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 Logger, Record
|
||||||
|
|
||||||
# logger = logging.getLogger("nonebot")
|
|
||||||
logger: "Logger" = loguru.logger
|
logger: "Logger" = loguru.logger
|
||||||
"""NoneBot 日志记录器对象。
|
|
||||||
|
|
||||||
默认信息:
|
|
||||||
|
|
||||||
- 格式: `[%(asctime)s %(name)s] %(levelname)s: %(message)s`
|
|
||||||
- 等级: `INFO` ,根据 `config.log_level` 配置改变
|
|
||||||
- 输出: 输出至 stdout
|
|
||||||
|
|
||||||
用法:
|
|
||||||
```python
|
|
||||||
from nonebot.log import logger
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# default_handler = logging.StreamHandler(sys.stdout)
|
|
||||||
# default_handler.setFormatter(
|
|
||||||
# logging.Formatter("[%(asctime)s %(name)s] %(levelname)s: %(message)s"))
|
|
||||||
# logger.addHandler(default_handler)
|
|
||||||
|
|
||||||
|
|
||||||
class LoguruHandler(logging.Handler): # pragma: no cover
|
class LoguruHandler(logging.Handler): # pragma: no cover
|
||||||
@ -59,7 +38,7 @@ def default_filter(record: "Record"):
|
|||||||
|
|
||||||
|
|
||||||
default_format: str = (
|
default_format: str = (
|
||||||
"<g>{time:MM-DD HH:mm:ss}</g> "
|
"<c>{time:YYYY-MM-DD}</c> <blue>{time:HH:mm:ss}</blue> "
|
||||||
"<lvl>[{level.icon}]</lvl> "
|
"<lvl>[{level.icon}]</lvl> "
|
||||||
"<c><{name}></c> "
|
"<c><{name}></c> "
|
||||||
"{message}"
|
"{message}"
|
||||||
@ -74,12 +53,12 @@ logger_id = logger.add(
|
|||||||
filter=default_filter,
|
filter=default_filter,
|
||||||
format=default_format,
|
format=default_format,
|
||||||
)
|
)
|
||||||
|
slang = get_default_lang()
|
||||||
logger.level("DEBUG", color="<cyan>", icon="㊙️DEBU")
|
logger.level("DEBUG", color="<blue>", icon=f"*️⃣ DDDEBUG")
|
||||||
logger.level("INFO", color="<white>", icon="ℹ️INFO")
|
logger.level("INFO", color="<white>", icon=f"ℹ️ IIIINFO")
|
||||||
logger.level("SUCCESS", color="<green>", icon="✅SUCC")
|
logger.level("SUCCESS", color="<green>", icon=f"✅ SUCCESS")
|
||||||
logger.level("WARNING", color="<yellow>", icon="⚠️WARN")
|
logger.level("WARNING", color="<yellow>", icon=f"⚠️ WARNING")
|
||||||
logger.level("ERROR", color="<red>", icon="☢️ERRO")
|
logger.level("ERROR", color="<red>", icon=f"⭕ EEERROR")
|
||||||
|
|
||||||
"""默认日志处理器 id"""
|
"""默认日志处理器 id"""
|
||||||
|
|
207
liteyuki/utils/orm.py
Normal file
207
liteyuki/utils/orm.py
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import sqlite3
|
||||||
|
from types import NoneType
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import nonebot
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class LiteModel(BaseModel):
|
||||||
|
"""轻量级模型基类
|
||||||
|
类型注解统一使用Python3.9的PEP585标准,如需使用泛型请使用typing模块的泛型类型
|
||||||
|
不允许使用id, table_name以及其他SQLite关键字作为字段名,不允许使用JSON和ID,必须指定默认值,且默认值类型必须与字段类型一致
|
||||||
|
"""
|
||||||
|
__ID__: int = Field(None, alias='id')
|
||||||
|
__TABLE_NAME__: str = Field(None, alias='table_name')
|
||||||
|
|
||||||
|
|
||||||
|
class Database:
|
||||||
|
TYPE_MAPPING = {
|
||||||
|
int : "INTEGER",
|
||||||
|
float : "REAL",
|
||||||
|
str : "TEXT",
|
||||||
|
bool : "INTEGER",
|
||||||
|
bytes : "BLOB",
|
||||||
|
NoneType: "NULL",
|
||||||
|
|
||||||
|
dict : "BLOB", # LITEYUKIDICT{key_name}
|
||||||
|
list : "BLOB", # LITEYUKILIST{key_name}
|
||||||
|
tuple : "BLOB", # LITEYUKITUPLE{key_name}
|
||||||
|
set : "BLOB", # LITEYUKISET{key_name}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 基础类型
|
||||||
|
BASIC_TYPE = [int, float, str, bool, bytes, NoneType]
|
||||||
|
# 可序列化类型
|
||||||
|
ITERABLE_TYPE = [dict, list, tuple, set]
|
||||||
|
|
||||||
|
LITEYUKI = "LITEYUKI"
|
||||||
|
|
||||||
|
# 字段前缀映射,默认基础类型为""
|
||||||
|
FIELD_PREFIX_MAPPING = {
|
||||||
|
dict : f"{LITEYUKI}DICT",
|
||||||
|
list : f"{LITEYUKI}LIST",
|
||||||
|
tuple : f"{LITEYUKI}TUPLE",
|
||||||
|
set : f"{LITEYUKI}SET",
|
||||||
|
type(LiteModel): f"{LITEYUKI}MODEL"
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, db_name: str):
|
||||||
|
if not os.path.exists(os.path.dirname(db_name)):
|
||||||
|
os.makedirs(os.path.dirname(db_name))
|
||||||
|
self.conn = sqlite3.connect(db_name) # 连接对象
|
||||||
|
self.conn.row_factory = sqlite3.Row # 以字典形式返回查询结果
|
||||||
|
self.cursor = self.conn.cursor() # 游标对象
|
||||||
|
|
||||||
|
def auto_migrate(self, *args: LiteModel):
|
||||||
|
"""
|
||||||
|
自动迁移模型
|
||||||
|
Args:
|
||||||
|
*args: 模型类实例化对象,支持空默认值,不支持嵌套迁移
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
for model in args:
|
||||||
|
if not model.__TABLE_NAME__:
|
||||||
|
raise ValueError(f"数据模型{model.__class__.__name__}未提供表名")
|
||||||
|
|
||||||
|
# 若无则创建表
|
||||||
|
self.cursor.execute(
|
||||||
|
f'CREATE TABLE IF NOT EXISTS {model.__TABLE_NAME__} (id INTEGER PRIMARY KEY AUTOINCREMENT)'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取表结构
|
||||||
|
new_fields, new_stored_types = (
|
||||||
|
zip(
|
||||||
|
*[(self._get_stored_field_prefix(model.__getattribute__(field)) + field, self._get_stored_type(model.__getattribute__(field)))
|
||||||
|
for field in model.__annotations__]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 原有的字段列表
|
||||||
|
existing_fields = self.cursor.execute(f'PRAGMA table_info({model.__TABLE_NAME__})').fetchall()
|
||||||
|
existing_types = [field['name'] for field in existing_fields]
|
||||||
|
|
||||||
|
# 检测缺失字段,由于SQLite是动态类型,所以不需要检测类型
|
||||||
|
for n_field, n_type in zip(new_fields, new_stored_types):
|
||||||
|
if n_field not in existing_types:
|
||||||
|
nonebot.logger.debug(f'ALTER TABLE {model.__TABLE_NAME__} ADD COLUMN {n_field} {n_type}')
|
||||||
|
self.cursor.execute(
|
||||||
|
f'ALTER TABLE {model.__TABLE_NAME__} ADD COLUMN {n_field} {n_type}'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 检测多余字段进行删除
|
||||||
|
for e_field in existing_types:
|
||||||
|
if e_field not in new_fields and e_field not in ['id']:
|
||||||
|
nonebot.logger.debug(f'ALTER TABLE {model.__TABLE_NAME__} DROP COLUMN {e_field}')
|
||||||
|
self.cursor.execute(
|
||||||
|
f'ALTER TABLE {model.__TABLE_NAME__} DROP COLUMN {e_field}'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def save(self, *args: LiteModel) -> [int | tuple[int, ...]]:
|
||||||
|
"""
|
||||||
|
保存或更新模型
|
||||||
|
Args:
|
||||||
|
*args: 模型类实例化对象,支持空默认值,不支持嵌套迁移
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
ids = []
|
||||||
|
for model in args:
|
||||||
|
if not model.__TABLE_NAME__:
|
||||||
|
raise ValueError(f"数据模型{model.__class__.__name__}未提供表名")
|
||||||
|
if not self.cursor.execute(f'PRAGMA table_info({model.__TABLE_NAME__})').fetchall():
|
||||||
|
raise ValueError(f"数据表{model.__TABLE_NAME__}不存在,请先迁移{model.__class__.__name__}模型")
|
||||||
|
|
||||||
|
stored_fields, stored_values = [], []
|
||||||
|
for r_field in model.__annotations__:
|
||||||
|
r_value = model.__getattribute__(r_field)
|
||||||
|
stored_fields.append(self._get_stored_field_prefix(r_value) + r_field)
|
||||||
|
|
||||||
|
if type(r_value) in Database.BASIC_TYPE:
|
||||||
|
# int str float bool bytes NoneType
|
||||||
|
stored_values.append(r_value)
|
||||||
|
|
||||||
|
elif type(r_value) in Database.ITERABLE_TYPE:
|
||||||
|
# dict list tuple set
|
||||||
|
stored_values.append(pickle.dumps(self._flat_save(r_value)))
|
||||||
|
|
||||||
|
elif isinstance(r_value, LiteModel):
|
||||||
|
# LiteModel TABLE_NAME:ID
|
||||||
|
stored_values.append(f"{r_value.__TABLE_NAME__}:{self.save(r_value)}")
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的数据类型{type(r_value)}")
|
||||||
|
nonebot.logger.debug(f"INSERT OR REPLACE INTO {model.__TABLE_NAME__} ({','.join(stored_fields)}) VALUES ({','.join([_ for _ in stored_values])})")
|
||||||
|
self.cursor.execute(
|
||||||
|
f"INSERT OR REPLACE INTO {model.__TABLE_NAME__} ({','.join(stored_fields)}) VALUES ({','.join(['?' for _ in stored_values])})",
|
||||||
|
stored_values
|
||||||
|
)
|
||||||
|
ids.append(self.cursor.lastrowid)
|
||||||
|
self.conn.commit()
|
||||||
|
return tuple(ids) if len(ids) > 1 else ids[0]
|
||||||
|
|
||||||
|
# 检测id字段是否有1,有则更新,无则插入
|
||||||
|
|
||||||
|
def _flat_save(self, obj) -> Any:
|
||||||
|
"""扁平化存储
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj: 需要存储的对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
存储的字节流
|
||||||
|
"""
|
||||||
|
# TODO 递归扁平化存储
|
||||||
|
if type(obj) in Database.ITERABLE_TYPE:
|
||||||
|
for i, item in enumerate(obj) if type(obj) in [list, tuple, set] else obj.items():
|
||||||
|
if type(item) in Database.BASIC_TYPE:
|
||||||
|
continue
|
||||||
|
elif type(item) in Database.ITERABLE_TYPE:
|
||||||
|
obj[i] = pickle.dumps(self._flat_save(item))
|
||||||
|
elif isinstance(item, LiteModel):
|
||||||
|
obj[i] = f"{item.__TABLE_NAME__}:{self.save(item)}"
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的数据类型{type(item)}")
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的数据类型{type(obj)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_stored_field_prefix(value) -> str:
|
||||||
|
"""获取存储字段前缀,一定在后加上字段名
|
||||||
|
|
||||||
|
LiteModel -> LITEYUKIID
|
||||||
|
|
||||||
|
dict -> LITEYUKIDICT
|
||||||
|
|
||||||
|
list -> LITEYUKILIST
|
||||||
|
|
||||||
|
tuple -> LITEYUKITUPLE
|
||||||
|
|
||||||
|
set -> LITEYUKISET
|
||||||
|
|
||||||
|
* -> ""
|
||||||
|
Args:
|
||||||
|
value: 储存的值
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Sqlite3存储字段
|
||||||
|
"""
|
||||||
|
return Database.FIELD_PREFIX_MAPPING.get(type(value), "")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_stored_type(value) -> str:
|
||||||
|
"""获取存储类型
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: 储存的值
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Sqlite3存储类型
|
||||||
|
"""
|
||||||
|
return Database.TYPE_MAPPING.get(type(value), "TEXT")
|
@ -1,6 +1,6 @@
|
|||||||
from nonebot.adapters.onebot import v11
|
from nonebot.adapters.onebot import v11
|
||||||
|
|
||||||
from src.utils.ly_typing import T_GroupMessageEvent, T_MessageEvent
|
from liteyuki.utils.ly_typing import T_GroupMessageEvent, T_MessageEvent
|
||||||
|
|
||||||
GROUP_ADMIN = v11.GROUP_ADMIN
|
GROUP_ADMIN = v11.GROUP_ADMIN
|
||||||
GROUP_OWNER = v11.GROUP_OWNER
|
GROUP_OWNER = v11.GROUP_OWNER
|
@ -4,7 +4,7 @@ import nonebot
|
|||||||
import yaml
|
import yaml
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from src.utils.data import LiteModel
|
from liteyuki.utils.data import LiteModel
|
||||||
|
|
||||||
_resource_data = {}
|
_resource_data = {}
|
||||||
_loaded_resource_packs = [] # 按照加载顺序排序
|
_loaded_resource_packs = [] # 按照加载顺序排序
|
||||||
@ -31,7 +31,6 @@ def load_resource_from_dir(path: str):
|
|||||||
relative_path = os.path.relpath(os.path.join(root, file), path).replace("\\", "/")
|
relative_path = os.path.relpath(os.path.join(root, file), path).replace("\\", "/")
|
||||||
abs_path = os.path.join(root, file).replace("\\", "/")
|
abs_path = os.path.join(root, file).replace("\\", "/")
|
||||||
_resource_data[relative_path] = abs_path
|
_resource_data[relative_path] = abs_path
|
||||||
nonebot.logger.debug(f"Loaded {relative_path} -> {abs_path}")
|
|
||||||
if os.path.exists(os.path.join(path, "metadata.yml")):
|
if os.path.exists(os.path.join(path, "metadata.yml")):
|
||||||
with open(os.path.join(path, "metadata.yml"), "r", encoding="utf-8") as f:
|
with open(os.path.join(path, "metadata.yml"), "r", encoding="utf-8") as f:
|
||||||
metadata = yaml.safe_load(f)
|
metadata = yaml.safe_load(f)
|
||||||
@ -39,12 +38,12 @@ def load_resource_from_dir(path: str):
|
|||||||
metadata = ResourceMetadata()
|
metadata = ResourceMetadata()
|
||||||
metadata["path"] = path
|
metadata["path"] = path
|
||||||
if os.path.exists(os.path.join(path, "lang")):
|
if os.path.exists(os.path.join(path, "lang")):
|
||||||
from src.utils.language import load_from_dir
|
from liteyuki.utils.language import load_from_dir
|
||||||
load_from_dir(os.path.join(path, "lang"))
|
load_from_dir(os.path.join(path, "lang"))
|
||||||
_loaded_resource_packs.append(ResourceMetadata(**metadata))
|
_loaded_resource_packs.append(ResourceMetadata(**metadata))
|
||||||
|
|
||||||
|
|
||||||
def get_res(path: str, default: Any = None) -> str | Any:
|
def get(path: str, default: Any = None) -> str | Any:
|
||||||
"""
|
"""
|
||||||
获取资源包中的文件
|
获取资源包中的文件
|
||||||
Args:
|
Args:
|
@ -1,3 +1,4 @@
|
|||||||
|
from importlib.metadata import PackageNotFoundError, version
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
|
||||||
@ -72,3 +73,11 @@ def keywords_in_text(keywords: list[str], text: str, all_matched: bool) -> bool:
|
|||||||
if keyword in text:
|
if keyword in text:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_package(package_name: str) -> bool:
|
||||||
|
try:
|
||||||
|
version(package_name)
|
||||||
|
return True
|
||||||
|
except PackageNotFoundError:
|
||||||
|
return False
|
10
main.py
10
main.py
@ -1,11 +1,9 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
|
|
||||||
from nonebot.adapters.onebot import v11, v12
|
from nonebot.adapters.onebot import v11, v12
|
||||||
|
from liteyuki.utils.config import load_from_yaml
|
||||||
|
from liteyuki.utils import init
|
||||||
|
|
||||||
from src.utils import logger
|
init()
|
||||||
from src.utils.config import load_from_yaml
|
|
||||||
|
|
||||||
nonebot.logger = logger
|
|
||||||
nonebot.init(**load_from_yaml("config.yml"))
|
nonebot.init(**load_from_yaml("config.yml"))
|
||||||
|
|
||||||
adapters = [v11.Adapter, v12.Adapter]
|
adapters = [v11.Adapter, v12.Adapter]
|
||||||
@ -14,7 +12,7 @@ driver = nonebot.get_driver()
|
|||||||
for adapter in adapters:
|
for adapter in adapters:
|
||||||
driver.register_adapter(adapter)
|
driver.register_adapter(adapter)
|
||||||
|
|
||||||
nonebot.load_plugin("src.liteyuki_main")
|
nonebot.load_plugin("liteyuki.liteyuki_main")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
nonebot.run()
|
nonebot.run()
|
||||||
|
@ -2,13 +2,16 @@ aiohttp==3.9.3
|
|||||||
aiofiles==23.2.1
|
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
|
||||||
dash==2.16.1
|
dash==2.16.1
|
||||||
nonebot2[fastapi]==2.2.1
|
nonebot2[fastapi]==2.2.1
|
||||||
nonebot-adapter-onebot==2.4.3
|
nonebot-adapter-onebot==2.4.3
|
||||||
nonebot-plugin-alconna==0.41.0
|
nonebot-plugin-alconna==0.41.0
|
||||||
pip==24.0
|
pip==24.0
|
||||||
psutil==5.9.8
|
psutil==5.9.8
|
||||||
pydantic==2.6.4
|
pydantic==1.10.14
|
||||||
pytz==2024.1
|
pytz==2024.1
|
||||||
PyYAML~=6.0.1
|
PyYAML~=6.0.1
|
||||||
starlette~=0.36.3
|
starlette~=0.36.3
|
||||||
|
loguru==0.7.2
|
||||||
|
importlib_metadata==7.0.2
|
@ -1,18 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import nonebot.plugin
|
|
||||||
|
|
||||||
from src.utils.data_manager import InstalledPlugin, plugin_db
|
|
||||||
from src.utils.resource import load_resource_from_dir
|
|
||||||
|
|
||||||
THIS_PLUGIN_NAME = os.path.basename(os.path.dirname(__file__))
|
|
||||||
RESOURCE_PATH = "src/resources"
|
|
||||||
load_resource_from_dir(RESOURCE_PATH)
|
|
||||||
|
|
||||||
nonebot.plugin.load_plugins("src/plugins")
|
|
||||||
nonebot.plugin.load_plugins("plugins")
|
|
||||||
|
|
||||||
installed_plugins = plugin_db.all(InstalledPlugin)
|
|
||||||
if installed_plugins:
|
|
||||||
for install_plugin in plugin_db.all(InstalledPlugin):
|
|
||||||
nonebot.load_plugin(install_plugin.module_name)
|
|
@ -1,122 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
import nonebot
|
|
||||||
from nonebot import on_message
|
|
||||||
from arclet.alconna import Arparma, Alconna, Args, Option, Subcommand, Arg
|
|
||||||
from nonebot.plugin import PluginMetadata
|
|
||||||
from nonebot_plugin_alconna import on_alconna
|
|
||||||
from src.utils.data import LiteModel
|
|
||||||
from src.utils.message import send_markdown
|
|
||||||
from src.utils.ly_typing import T_Bot, T_MessageEvent
|
|
||||||
from src.utils.data import Database
|
|
||||||
|
|
||||||
|
|
||||||
class Node(LiteModel):
|
|
||||||
bot_id: str
|
|
||||||
session_type: str
|
|
||||||
session_id: str
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.bot_id}.{self.session_type}.{self.session_id}"
|
|
||||||
|
|
||||||
|
|
||||||
class Push(LiteModel):
|
|
||||||
source: Node
|
|
||||||
target: Node
|
|
||||||
inde: int
|
|
||||||
|
|
||||||
|
|
||||||
pushes_db = Database("data/pushes.ldb")
|
|
||||||
pushes_db.auto_migrate(Push, Node)
|
|
||||||
|
|
||||||
alc = Alconna(
|
|
||||||
"lep",
|
|
||||||
Subcommand(
|
|
||||||
"add",
|
|
||||||
Args["source", str],
|
|
||||||
Args["target", str],
|
|
||||||
Option("bidirectional", Args["bidirectional", bool])
|
|
||||||
),
|
|
||||||
Subcommand(
|
|
||||||
"rm",
|
|
||||||
Args["index", int],
|
|
||||||
|
|
||||||
),
|
|
||||||
Subcommand(
|
|
||||||
"list",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
add_push = on_alconna(alc)
|
|
||||||
|
|
||||||
|
|
||||||
@add_push.handle()
|
|
||||||
async def _(result: Arparma):
|
|
||||||
"""bot_id.session_type.session_id"""
|
|
||||||
if result.subcommands.get("add"):
|
|
||||||
source = result.subcommands["add"].args.get("source")
|
|
||||||
target = result.subcommands["add"].args.get("target")
|
|
||||||
if source and target:
|
|
||||||
source = source.split(".")
|
|
||||||
target = target.split(".")
|
|
||||||
push1 = Push(
|
|
||||||
source=Node(bot_id=source[0], session_type=source[1], session_id=source[2]),
|
|
||||||
target=Node(bot_id=target[0], session_type=target[1], session_id=target[2]),
|
|
||||||
inde=len(pushes_db.all(Push, default=[]))
|
|
||||||
)
|
|
||||||
pushes_db.save(push1)
|
|
||||||
|
|
||||||
if result.subcommands["add"].args.get("bidirectional"):
|
|
||||||
push2 = Push(
|
|
||||||
source=Node(bot_id=target[0], session_type=target[1], session_id=target[2]),
|
|
||||||
target=Node(bot_id=source[0], session_type=source[1], session_id=source[2]),
|
|
||||||
inde=len(pushes_db.all(Push, default=[]))
|
|
||||||
)
|
|
||||||
pushes_db.save(push2)
|
|
||||||
await add_push.finish("添加成功")
|
|
||||||
else:
|
|
||||||
await add_push.finish("参数缺失")
|
|
||||||
elif result.subcommands.get("rm"):
|
|
||||||
index = result.subcommands["rm"].args.get("index")
|
|
||||||
if index is not None:
|
|
||||||
try:
|
|
||||||
pushes_db.delete(Push, "inde = ?", index)
|
|
||||||
await add_push.finish("删除成功")
|
|
||||||
except IndexError:
|
|
||||||
await add_push.finish("索引错误")
|
|
||||||
else:
|
|
||||||
await add_push.finish("参数缺失")
|
|
||||||
elif result.subcommands.get("list"):
|
|
||||||
await add_push.finish(
|
|
||||||
"\n".join([f"{push.inde} {push.source.bot_id}.{push.source.session_type}.{push.source.session_id} -> "
|
|
||||||
f"{push.target.bot_id}.{push.target.session_type}.{push.target.session_id}" for i, push in
|
|
||||||
enumerate(pushes_db.all(Push, default=[]))]))
|
|
||||||
else:
|
|
||||||
await add_push.finish("参数错误")
|
|
||||||
|
|
||||||
|
|
||||||
@on_message(block=False).handle()
|
|
||||||
async def _(event: T_MessageEvent, bot: T_Bot):
|
|
||||||
for push in pushes_db.all(Push, default=[]):
|
|
||||||
if str(push.source) == f"{bot.self_id}.{event.message_type}.{event.user_id if event.message_type == 'private' else event.group_id}":
|
|
||||||
bot2 = nonebot.get_bot(push.target.bot_id)
|
|
||||||
msg_formatted = ""
|
|
||||||
for l in str(event.message).split("\n"):
|
|
||||||
msg_formatted += f"**{l.strip()}**\n"
|
|
||||||
push_message = (
|
|
||||||
f"> From {event.sender.nickname}@{push.source.session_type}.{push.source.session_id}\n> Bot {bot.self_id}\n\n"
|
|
||||||
f"{msg_formatted}")
|
|
||||||
await send_markdown(push_message, bot2, message_type=push.target.session_type, session_id=push.target.session_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
__author__ = "snowykami"
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
|
||||||
name="轻雪事件推送",
|
|
||||||
description="事件推送插件,支持单向和双向推送,支持跨Bot推送",
|
|
||||||
usage="",
|
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
|
||||||
extra={
|
|
||||||
"liteyuki": True,
|
|
||||||
}
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
from .log import logger
|
|
@ -1,35 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from src.utils.data import LiteModel, Database as DB
|
|
||||||
|
|
||||||
DATA_PATH = "data/liteyuki"
|
|
||||||
|
|
||||||
user_db = DB(os.path.join(DATA_PATH, 'users.ldb'))
|
|
||||||
group_db = DB(os.path.join(DATA_PATH, 'groups.ldb'))
|
|
||||||
plugin_db = DB(os.path.join(DATA_PATH, 'plugins.ldb'))
|
|
||||||
|
|
||||||
|
|
||||||
class User(LiteModel):
|
|
||||||
user_id: str
|
|
||||||
username: str = ""
|
|
||||||
profile: dict[str, str] = {}
|
|
||||||
enabled_plugins: list[str] = []
|
|
||||||
disabled_plugins: list[str] = []
|
|
||||||
|
|
||||||
|
|
||||||
class GroupChat(LiteModel):
|
|
||||||
# Group是一个关键字,所以这里用GroupChat
|
|
||||||
group_id: str
|
|
||||||
group_name: str = ""
|
|
||||||
enabled_plugins: list[str] = []
|
|
||||||
disabled_plugins: list[str] = []
|
|
||||||
|
|
||||||
|
|
||||||
class InstalledPlugin(LiteModel):
|
|
||||||
module_name: str
|
|
||||||
|
|
||||||
|
|
||||||
def auto_migrate():
|
|
||||||
user_db.auto_migrate(User)
|
|
||||||
group_db.auto_migrate(GroupChat)
|
|
||||||
plugin_db.auto_migrate(InstalledPlugin)
|
|
Loading…
Reference in New Issue
Block a user