diff --git a/src/liteyuki_main/__init__.py b/liteyuki/liteyuki_main/__init__.py
similarity index 74%
rename from src/liteyuki_main/__init__.py
rename to liteyuki/liteyuki_main/__init__.py
index 7e6fe82..7e5006d 100644
--- a/src/liteyuki_main/__init__.py
+++ b/liteyuki/liteyuki_main/__init__.py
@@ -1,10 +1,10 @@
import nonebot
from nonebot.plugin import PluginMetadata
-from src.utils.language import get_system_lang
-from src.utils.data_manager import *
+from liteyuki.utils.language import get_default_lang
+from liteyuki.utils.data_manager import *
from .loader import *
from .webdash import *
-from src.utils.config import config
+from liteyuki.utils.config import config
__author__ = "snowykami"
__plugin_meta__ = PluginMetadata(
@@ -20,6 +20,6 @@ __plugin_meta__ = PluginMetadata(
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.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)}"))
diff --git a/liteyuki/liteyuki_main/loader.py b/liteyuki/liteyuki_main/loader.py
new file mode 100644
index 0000000..9c2a3af
--- /dev/null
+++ b/liteyuki/liteyuki_main/loader.py
@@ -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)
\ No newline at end of file
diff --git a/src/liteyuki_main/webdash.py b/liteyuki/liteyuki_main/webdash.py
similarity index 96%
rename from src/liteyuki_main/webdash.py
rename to liteyuki/liteyuki_main/webdash.py
index 6833e56..ad0e196 100644
--- a/src/liteyuki_main/webdash.py
+++ b/liteyuki/liteyuki_main/webdash.py
@@ -3,8 +3,8 @@ import psutil
from dash import Dash, Input, Output, dcc, html
from starlette.middleware.wsgi import WSGIMiddleware
-from src.utils.language import Language
-from src.utils.tools import convert_size
+from liteyuki.utils.language import Language
+from liteyuki.utils.tools import convert_size
app = nonebot.get_app()
diff --git a/src/plugins/liteyuki_markdowntest.py b/liteyuki/plugins/liteyuki_markdowntest.py
similarity index 93%
rename from src/plugins/liteyuki_markdowntest.py
rename to liteyuki/plugins/liteyuki_markdowntest.py
index 831f48e..b1a013c 100644
--- a/src/plugins/liteyuki_markdowntest.py
+++ b/liteyuki/plugins/liteyuki_markdowntest.py
@@ -6,8 +6,8 @@ from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from nonebot.plugin import PluginMetadata
-from src.utils.message import send_markdown
-from src.utils.ly_typing import T_Message, T_Bot, v11, T_MessageEvent
+from liteyuki.utils.message import send_markdown
+from liteyuki.utils.ly_typing import T_Message, T_Bot, v11, T_MessageEvent
md_test = on_command("mdts", aliases={"会话md"}, permission=SUPERUSER)
md_group = on_command("mdg", aliases={"群md"}, permission=SUPERUSER)
diff --git a/src/plugins/liteyuki_plugin_minigame/__init__.py b/liteyuki/plugins/liteyuki_minigame/__init__.py
similarity index 100%
rename from src/plugins/liteyuki_plugin_minigame/__init__.py
rename to liteyuki/plugins/liteyuki_minigame/__init__.py
diff --git a/src/plugins/liteyuki_plugin_npm/__init__.py b/liteyuki/plugins/liteyuki_npm/__init__.py
similarity index 100%
rename from src/plugins/liteyuki_plugin_npm/__init__.py
rename to liteyuki/plugins/liteyuki_npm/__init__.py
diff --git a/src/plugins/liteyuki_plugin_npm/common.py b/liteyuki/plugins/liteyuki_npm/common.py
similarity index 74%
rename from src/plugins/liteyuki_plugin_npm/common.py
rename to liteyuki/plugins/liteyuki_npm/common.py
index 67fb5e8..ab5a268 100644
--- a/src/plugins/liteyuki_plugin_npm/common.py
+++ b/liteyuki/plugins/liteyuki_npm/common.py
@@ -4,9 +4,9 @@ from typing import Optional
import aiofiles
import nonebot.plugin
-from src.utils.data import Database, LiteModel
-from src.utils.data_manager import GroupChat, InstalledPlugin, User, group_db, plugin_db, user_db
-from src.utils.ly_typing import T_MessageEvent
+from liteyuki.utils.data import Database, LiteModel
+from liteyuki.utils.data_manager import GroupChat, InstalledPlugin, User, group_db, plugin_db, user_db
+from liteyuki.utils.ly_typing import T_MessageEvent
LNPM_COMMAND_START = "lnpm"
@@ -58,16 +58,17 @@ def get_plugin_default_enable(plugin_module_name: str) -> bool:
Returns:
bool: 插件默认状态
"""
- return (nonebot.plugin.get_plugin(plugin_module_name).metadata.extra.get('default_enable', True)
- if nonebot.plugin.get_plugin(plugin_module_name) and nonebot.plugin.get_plugin(plugin_module_name).metadata else True) \
- if nonebot.plugin.get_plugin(plugin_module_name) else False
+ plug = nonebot.plugin.get_plugin_by_module_name(plugin_module_name)
+ return (plug.metadata.extra.get('default_enable', True)
+ 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:
+ event: 会话事件
plugin_module_name (str): 插件模块名
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
+def get_plugin_global_enable(plugin_module_name: str) -> bool:
+ return True
+
+
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:
bool: 插件是否可以被启用/停用
"""
- return nonebot.plugin.get_plugin(plugin_module_name).metadata.extra.get('toggleable', True) \
- if nonebot.plugin.get_plugin(plugin_module_name) and nonebot.plugin.get_plugin(plugin_module_name).metadata else True
+ plug = nonebot.plugin.get_plugin_by_module_name(plugin_module_name)
+ return plug.metadata.extra.get('toggleable', True) if plug and plug.metadata else True
diff --git a/src/plugins/liteyuki_plugin_npm/helper.py b/liteyuki/plugins/liteyuki_npm/helper.py
similarity index 100%
rename from src/plugins/liteyuki_plugin_npm/helper.py
rename to liteyuki/plugins/liteyuki_npm/helper.py
diff --git a/src/plugins/liteyuki_plugin_npm/installer.py b/liteyuki/plugins/liteyuki_npm/installer.py
similarity index 97%
rename from src/plugins/liteyuki_plugin_npm/installer.py
rename to liteyuki/plugins/liteyuki_npm/installer.py
index faa661b..40b9f67 100644
--- a/src/plugins/liteyuki_plugin_npm/installer.py
+++ b/liteyuki/plugins/liteyuki_npm/installer.py
@@ -9,9 +9,9 @@ from arclet.alconna import Arparma, MultiVar
from nonebot.permission import SUPERUSER
from nonebot_plugin_alconna import Alconna, Args, Subcommand, on_alconna
-from src.utils.language import get_user_lang
-from src.utils.ly_typing import T_Bot
-from src.utils.message import Markdown as md, send_markdown
+from liteyuki.utils.language import get_user_lang
+from liteyuki.utils.ly_typing import T_Bot
+from liteyuki.utils.message import Markdown as md, send_markdown
from .common import *
npm_alc = on_alconna(
@@ -45,9 +45,6 @@ npm_alc = on_alconna(
)
-
-
-
@npm_alc.handle()
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
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 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转义
await send_markdown(
f"{info}\n\n"
@@ -192,9 +189,6 @@ async def npm_search(keywords: list[str]) -> list[StorePlugin]:
return results
-
-
-
def npm_install(plugin_module_name) -> tuple[bool, str]:
"""
Args:
@@ -209,8 +203,8 @@ def npm_install(plugin_module_name) -> tuple[bool, str]:
sys.stderr = buffer
mirrors = [
- "https://pypi.mirrors.cqupt.edu.cn/simple", # 重庆邮电大学
"https://pypi.tuna.tsinghua.edu.cn/simple", # 清华大学
+ "https://pypi.mirrors.cqupt.edu.cn/simple", # 重庆邮电大学
"https://pypi.liteyuki.icu/simple", # 轻雪镜像
"https://pypi.org/simple", # 官方源
]
diff --git a/src/plugins/liteyuki_plugin_npm/manager.py b/liteyuki/plugins/liteyuki_npm/manager.py
similarity index 59%
rename from src/plugins/liteyuki_plugin_npm/manager.py
rename to liteyuki/plugins/liteyuki_npm/manager.py
index 28cf06e..7abb33b 100644
--- a/src/plugins/liteyuki_plugin_npm/manager.py
+++ b/liteyuki/plugins/liteyuki_npm/manager.py
@@ -8,23 +8,35 @@ from nonebot.message import run_preprocessor
from nonebot.permission import SUPERUSER
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 src.utils.message import Markdown as md, send_markdown
-from src.utils.permission import GROUP_ADMIN, GROUP_OWNER
-from src.utils.ly_typing import T_Bot, T_MessageEvent
-from src.utils.language import get_user_lang
-from .common import get_plugin_can_be_toggle, get_plugin_current_enable, get_plugin_default_enable
+from liteyuki.utils.data_manager import GroupChat, InstalledPlugin, User, group_db, plugin_db, user_db
+from liteyuki.utils.message import Markdown as md, send_markdown
+from liteyuki.utils.permission import GROUP_ADMIN, GROUP_OWNER
+from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
+from liteyuki.utils.language import get_user_lang
+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
-list_plugins = on_command("list-plugin", aliases={"列出插件", "插件列表"}, priority=0)
-# toggle_plugin = on_command("enable-plugin", aliases={"启用插件", "停用插件", "disable-plugin"}, priority=0)
+list_plugins = on_alconna(
+ Alconna(
+ ['list-plugins', "插件列表", "列出插件"],
+ )
+)
+
toggle_plugin = on_alconna(
Alconna(
['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()
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():
# 检查是否有 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)
+ 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:
btn_homepage = md.link(lang.get('npm.homepage'), store_plugin.homepage)
- elif plugin.metadata and plugin.metadata.extra.get('liteyuki'):
- btn_homepage = md.link(lang.get('npm.homepage'), "https://github.com/snowykami/LiteyukiBot")
+ 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")
+ else:
+ 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:
- reply += (f"\n**{md.escape(plugin.metadata.name)}**\n"
- f"\n > {plugin.metadata.description}")
+ reply += (f"\n**{md.escape(show_name)}**\n"
+ f"\n > {md.escape(show_desc)}")
else:
- reply += (f"**{md.escape(plugin.name)}**\n"
- f"\n > {lang.get('npm.no_description')}")
+ reply += (f"**{md.escape(show_name)}**\n"
+ f"\n > {md.escape(show_desc)}")
reply += f"\n > {btn_usage} {btn_homepage}"
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') \
- else md.button(lang.get('npm.disable'), f'enable-plugin {plugin.module_name}')
+ cmd_toggle = f"{'disable' if session_enable else '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}"
if await SUPERUSER(bot, event):
plugin_in_database = plugin_db.first(InstalledPlugin, 'module_name = ?', plugin.module_name)
- # 添加移除插件
+ # 添加移除插件和全局切换按钮
+ global_enable = get_plugin_global_enable(plugin.module_name)
btn_uninstall = (
md.button(lang.get('npm.uninstall'), f'npm uninstall {plugin.module_name}')) if plugin_in_database else lang.get(
'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 += "\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")
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) # 获取插件默认状态
+
can_be_toggled = get_plugin_can_be_toggle(plugin_module_name) # 获取插件是否可以被启用/停用
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))
else:
raise FinishedException(ulang.get("Permission Denied"))
- # 启用 已停用的默认启用插件 将其从停用列表移除
- # 启用 已停用的默认停用插件 将其放到启用列表
- # 停用 已启用的默认启用插件 将其放到停用列表
- # 停用 已启用的默认停用插件 将其从启用列表移除
try:
if toggle:
if default_enable:
@@ -126,14 +155,21 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
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":
- user_db.save(session)
+ user_db.upsert(session)
else:
- group_db.save(session)
+ group_db.upsert(session)
@run_preprocessor
async def _(event: T_MessageEvent, matcher: Matcher):
plugin = matcher.plugin
# TODO 插件启用/停用检查hook
- nonebot.logger.info(f"Plugin: {plugin.module_name}")
+ nonebot.logger.info(f"Plugin Callapi: {plugin.module_name}")
diff --git a/src/plugins/liteyuki_plugin_npm/permission.py b/liteyuki/plugins/liteyuki_npm/permission.py
similarity index 80%
rename from src/plugins/liteyuki_plugin_npm/permission.py
rename to liteyuki/plugins/liteyuki_npm/permission.py
index d2533fb..ee346f4 100644
--- a/src/plugins/liteyuki_plugin_npm/permission.py
+++ b/liteyuki/plugins/liteyuki_npm/permission.py
@@ -1,5 +1,5 @@
# 插件权限管理器,对api调用进行hook限制,防止插件滥用api
-from src.utils.data import LiteModel
+from liteyuki.utils.data import LiteModel
class PermissionAllow(LiteModel):
diff --git a/src/plugins/liteyuki_plugin_user/__init__.py b/liteyuki/plugins/liteyuki_user/__init__.py
similarity index 100%
rename from src/plugins/liteyuki_plugin_user/__init__.py
rename to liteyuki/plugins/liteyuki_user/__init__.py
diff --git a/src/plugins/liteyuki_plugin_user/input_handle.py b/liteyuki/plugins/liteyuki_user/input_handle.py
similarity index 100%
rename from src/plugins/liteyuki_plugin_user/input_handle.py
rename to liteyuki/plugins/liteyuki_user/input_handle.py
diff --git a/src/plugins/liteyuki_plugin_user/profile_manager.py b/liteyuki/plugins/liteyuki_user/profile_manager.py
similarity index 92%
rename from src/plugins/liteyuki_plugin_user/profile_manager.py
rename to liteyuki/plugins/liteyuki_user/profile_manager.py
index 161fe78..a78e6e3 100644
--- a/src/plugins/liteyuki_plugin_user/profile_manager.py
+++ b/liteyuki/plugins/liteyuki_user/profile_manager.py
@@ -2,11 +2,11 @@ from typing import Optional
from nonebot_plugin_alconna import Alconna, Args, Arparma, Subcommand, on_alconna
-from src.utils.data import LiteModel
-from src.utils.data_manager import User, user_db
-from src.utils.language import Language, get_all_lang, get_user_lang
-from src.utils.ly_typing import T_Bot, T_MessageEvent
-from src.utils.message import Markdown as md, send_markdown
+from liteyuki.utils.data import LiteModel
+from liteyuki.utils.data_manager import User, user_db
+from liteyuki.utils.language import Language, get_all_lang, get_user_lang
+from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
+from liteyuki.utils.message import Markdown as md, send_markdown
profile_alc = on_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"])
if r:
user.profile[result.args["key"]] = result.args["value"]
- user_db.save(user) # 数据库保存
+ user_db.upsert(user) # 数据库保存
await profile_alc.finish(
ulang.get(
"user.profile.set_success",
diff --git a/src/plugins/liteyuki_plugin_weather/__init__.py b/liteyuki/plugins/liteyuki_weather/__init__.py
similarity index 100%
rename from src/plugins/liteyuki_plugin_weather/__init__.py
rename to liteyuki/plugins/liteyuki_weather/__init__.py
diff --git a/src/plugins/liteyuki_plugin_weather/data_model.py b/liteyuki/plugins/liteyuki_weather/data_model.py
similarity index 100%
rename from src/plugins/liteyuki_plugin_weather/data_model.py
rename to liteyuki/plugins/liteyuki_weather/data_model.py
diff --git a/src/resources/fonts/bold.ttf b/liteyuki/resources/fonts/bold.ttf
similarity index 100%
rename from src/resources/fonts/bold.ttf
rename to liteyuki/resources/fonts/bold.ttf
diff --git a/src/resources/fonts/heavy.ttf b/liteyuki/resources/fonts/heavy.ttf
similarity index 100%
rename from src/resources/fonts/heavy.ttf
rename to liteyuki/resources/fonts/heavy.ttf
diff --git a/src/resources/fonts/normal.ttf b/liteyuki/resources/fonts/normal.ttf
similarity index 100%
rename from src/resources/fonts/normal.ttf
rename to liteyuki/resources/fonts/normal.ttf
diff --git a/src/resources/lang/en.lang b/liteyuki/resources/lang/en.lang
similarity index 93%
rename from src/resources/lang/en.lang
rename to liteyuki/resources/lang/en.lang
index 319bddd..4bf1a7a 100644
--- a/src/resources/lang/en.lang
+++ b/liteyuki/resources/lang/en.lang
@@ -1,6 +1,12 @@
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.monitor.title=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_already=Plugin {NAME} is already in {STATUS} state, no need for repeated operation
npm.toggle_failed=Failed to {STATUS} plugin {NAME}: {ERROR}
+npm.toggle_success=Succeeded in {STATUS} plugin {NAME}
user.profile.edit=Edit
user.profile.set=Set
diff --git a/src/resources/lang/ja.lang b/liteyuki/resources/lang/ja.lang
similarity index 95%
rename from src/resources/lang/ja.lang
rename to liteyuki/resources/lang/ja.lang
index 88e82c8..31f4162 100644
--- a/src/resources/lang/ja.lang
+++ b/liteyuki/resources/lang/ja.lang
@@ -1,5 +1,11 @@
language.name = 日本語
+log.debug=デバッグ
+log.info=情報
+log.warning=警告
+log.error=エラー
+log.success=成功
+
main.current_language = 現在のシステム言語: {LANG}
main.enable_webdash = ウェブダッシュボードが有効になりました: {URL}
main.monitor.title = Liteyukiモニタリングパネル
@@ -46,6 +52,7 @@ npm.prev_page = 前のページ
npm.plugin_cannot_be_toggled=プラグイン {NAME} は有効または無効にできません
npm.plugin_already=プラグイン {NAME} はすでに {STATUS} 状態です。繰り返し操作する必要はありません
npm.toggle_failed=プラグイン {NAME} を {STATUS} にするのに失敗しました: {ERROR}
+npm.toggle_success=プラグイン {NAME} が {STATUS} になりました
user.profile.edit=編集
user.profile.set=設定
diff --git a/src/resources/lang/zh-CN.lang b/liteyuki/resources/lang/zh-CN.lang
similarity index 91%
rename from src/resources/lang/zh-CN.lang
rename to liteyuki/resources/lang/zh-CN.lang
index f775f2d..2187622 100644
--- a/src/resources/lang/zh-CN.lang
+++ b/liteyuki/resources/lang/zh-CN.lang
@@ -1,6 +1,12 @@
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.monitor.title=轻雪监控面板
main.monitor.description=轻雪机器人监控面板
@@ -32,7 +38,7 @@ npm.search_no_result=无搜索结果
npm.too_many_results=内容过多,{HIDE_NUM}项已隐藏,请限制关键字搜索
npm.install_success={NAME} 安装成功
npm.install_failed={NAME} 安装失败,请查看日志获取详细信息,如不能解决,请访问{HOMEPAGE}寻求帮助
-npm.uninstall_success={NAME} 卸载成功
+npm.uninstall_success={NAME} 卸载成功,下次重启生效
npm.uninstall_failed={NAME} 卸载失败
npm.load_failed={NAME} 加载失败,请在控制台查看详细信息,检查依赖或配置是否正确,如不能解决,请访问{HOMEPAGE}寻求帮助
npm.plugin_not_found=未在商店中找到 {NAME},请尝试更新商店信息或检查拼写
@@ -46,6 +52,7 @@ npm.prev_page=上一页
npm.plugin_cannot_be_toggled=插件 {NAME} 无法被启用或停用
npm.plugin_already=插件 {NAME} 已经是 {STATUS} 状态,无需重复操作
npm.toggle_failed=插件 {NAME} {STATUS} 失败: {ERROR}
+npm.toggle_success=插件 {NAME} {STATUS} 成功
user.profile.edit=修改
user.profile.set=设置
diff --git a/src/resources/lang/zh-HK.lang b/liteyuki/resources/lang/zh-HK.lang
similarity index 94%
rename from src/resources/lang/zh-HK.lang
rename to liteyuki/resources/lang/zh-HK.lang
index ae3aedf..73be99a 100644
--- a/src/resources/lang/zh-HK.lang
+++ b/liteyuki/resources/lang/zh-HK.lang
@@ -1,4 +1,10 @@
-language.name=繁體中文
+language.name=繁體中文(香港)
+
+log.debug=調試
+log.info=信息
+log.warning=警告
+log.error=錯誤
+log.success=成功
main.current_language=當前系統語言為:{LANG}
main.enable_webdash=已啟用網頁監控面板:{URL}
@@ -46,6 +52,8 @@ npm.prev_page=上一頁
npm.plugin_cannot_be_toggled=無法啟用或停用插件 {NAME}
npm.plugin_already=插件 {NAME} 已處於 {STATUS} 狀態,無需重複操作
npm.toggle_failed=插件 {NAME} {STATUS} 失敗: {ERROR}
+npm.toggle_success=插件 {NAME} {STATUS} 成功
+
user.profile.edit=編輯
user.profile.set=設定
diff --git a/src/resources/lang/zh-Kawaii.lang b/liteyuki/resources/lang/zh-Kawaii.lang
similarity index 96%
rename from src/resources/lang/zh-Kawaii.lang
rename to liteyuki/resources/lang/zh-Kawaii.lang
index 002917f..40712e7 100644
--- a/src/resources/lang/zh-Kawaii.lang
+++ b/liteyuki/resources/lang/zh-Kawaii.lang
@@ -1,5 +1,11 @@
language.name=简体中文(轻雪版)
+log.debug=调试
+log.info=信息
+log.warning=有问题哦
+log.error=出错啦
+log.success=成功啦
+
main.current_language=现在系统用的语言是:{LANG} 喔!
main.enable_webdash=已经打开了网页监控板:{URL} 啦!
main.monitor.title=监控板
diff --git a/src/resources/lang/zh-WY.lang b/liteyuki/resources/lang/zh-WY.lang
similarity index 95%
rename from src/resources/lang/zh-WY.lang
rename to liteyuki/resources/lang/zh-WY.lang
index bdaadfc..ee70f6f 100644
--- a/src/resources/lang/zh-WY.lang
+++ b/liteyuki/resources/lang/zh-WY.lang
@@ -1,4 +1,10 @@
-language.name=漢字
+language.name=中文(華夏)
+
+log.debug=調試
+log.info=信息
+log.warning=警告
+log.error=錯誤
+log.success=成功
main.current_language=當前之系統語言為:{LANG}
main.enable_webdash=已啟用網頁監控板:{URL}
diff --git a/src/resources/metadata.yml b/liteyuki/resources/metadata.yml
similarity index 100%
rename from src/resources/metadata.yml
rename to liteyuki/resources/metadata.yml
diff --git a/src/resources/unsorted/plugins.json b/liteyuki/resources/unsorted/plugins.json
similarity index 100%
rename from src/resources/unsorted/plugins.json
rename to liteyuki/resources/unsorted/plugins.json
diff --git a/liteyuki/utils/__init__.py b/liteyuki/utils/__init__.py
new file mode 100644
index 0000000..a6db964
--- /dev/null
+++ b/liteyuki/utils/__init__.py
@@ -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")
diff --git a/src/utils/config.py b/liteyuki/utils/config.py
similarity index 98%
rename from src/utils/config.py
rename to liteyuki/utils/config.py
index 5395229..73ce31c 100644
--- a/src/utils/config.py
+++ b/liteyuki/utils/config.py
@@ -4,7 +4,7 @@ import nonebot
import yaml
from pydantic import BaseModel
-config = None
+config = {}
class BasicConfig(BaseModel):
diff --git a/src/utils/data.py b/liteyuki/utils/data.py
similarity index 98%
rename from src/utils/data.py
rename to liteyuki/utils/data.py
index 06d63e3..4ae3d2f 100644
--- a/src/utils/data.py
+++ b/liteyuki/utils/data.py
@@ -31,7 +31,7 @@ class BaseORMAdapter(ABC):
"""
raise NotImplementedError
- def save(self, *args, **kwargs):
+ def upsert(self, *args, **kwargs):
"""存储数据
Returns:
@@ -171,7 +171,7 @@ class Database(BaseORMAdapter):
self.conn.commit()
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字段则更新,没有则插入
Args:
@@ -192,7 +192,7 @@ class Database(BaseORMAdapter):
for field, value in model.__dict__.items():
if isinstance(value, LiteModel):
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):
key_list.append(f'{self.LIST}{field}')
value_list.append(self._flat(value))
@@ -225,7 +225,7 @@ class Database(BaseORMAdapter):
return_data = {}
for k, v in data.items():
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):
return_data[f'{self.LIST}{k}'] = self._flat(v)
elif isinstance(v, dict):
@@ -239,7 +239,7 @@ class Database(BaseORMAdapter):
return_data = []
for v in data:
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):
return_data.append(self._flat(v))
elif isinstance(v, dict):
diff --git a/liteyuki/utils/data_manager.py b/liteyuki/utils/data_manager.py
new file mode 100644
index 0000000..33773d7
--- /dev/null
+++ b/liteyuki/utils/data_manager.py
@@ -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())
diff --git a/src/utils/language.py b/liteyuki/utils/language.py
similarity index 92%
rename from src/utils/language.py
rename to liteyuki/utils/language.py
index c8fc9ed..130ce25 100644
--- a/src/utils/language.py
+++ b/liteyuki/utils/language.py
@@ -9,8 +9,8 @@ from typing import Any
import nonebot
-from src.utils.config import config
-from src.utils.data_manager import User, user_db
+from liteyuki.utils.config import config
+from liteyuki.utils.data_manager import User, user_db
_default_lang_code = "en"
_language_data = {
@@ -38,7 +38,6 @@ def load_from_lang(file_path: str, lang_code: str = None):
if not line or line.startswith('#'): # 空行或注释
continue
key, value = line.split('=', 1)
- nonebot.logger.debug(f"Loaded language text: {key.strip()} -> {value.strip()}")
data[key.strip()] = value.strip()
if lang_code not in _language_data:
_language_data[lang_code] = {}
@@ -139,7 +138,7 @@ def get_user_lang(user_id: str) -> Language:
username="Unknown"
))
- return Language(user.profile.get('lang',config.get("default_language", get_system_lang_code()) ))
+ return Language(user.profile.get('lang', config.get("default_language", get_system_lang_code())))
def get_system_lang_code() -> str:
@@ -149,11 +148,11 @@ def get_system_lang_code() -> str:
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]:
diff --git a/src/utils/log.py b/liteyuki/utils/log.py
similarity index 60%
rename from src/utils/log.py
rename to liteyuki/utils/log.py
index f888b0b..a56e6df 100644
--- a/src/utils/log.py
+++ b/liteyuki/utils/log.py
@@ -1,35 +1,14 @@
import sys
import logging
from typing import TYPE_CHECKING
-
+from colored import fg
+from .language import get_default_lang
import loguru
if TYPE_CHECKING:
- # avoid sphinx autodoc resolve annotation failed
- # because loguru module do not have `Logger` class actually
from loguru import Logger, Record
-# logger = logging.getLogger("nonebot")
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
@@ -59,7 +38,7 @@ def default_filter(record: "Record"):
default_format: str = (
- "{time:MM-DD HH:mm:ss} "
+ "{time:YYYY-MM-DD} {time:HH:mm:ss} "
"[{level.icon}] "
"<{name}> "
"{message}"
@@ -74,12 +53,12 @@ logger_id = logger.add(
filter=default_filter,
format=default_format,
)
-
-logger.level("DEBUG", color="", icon="㊙️DEBU")
-logger.level("INFO", color="", icon="ℹ️INFO")
-logger.level("SUCCESS", color="", icon="✅SUCC")
-logger.level("WARNING", color="", icon="⚠️WARN")
-logger.level("ERROR", color="", icon="☢️ERRO")
+slang = get_default_lang()
+logger.level("DEBUG", color="", icon=f"*️⃣ DDDEBUG")
+logger.level("INFO", color="", icon=f"ℹ️ IIIINFO")
+logger.level("SUCCESS", color="", icon=f"✅ SUCCESS")
+logger.level("WARNING", color="", icon=f"⚠️ WARNING")
+logger.level("ERROR", color="", icon=f"⭕ EEERROR")
"""默认日志处理器 id"""
diff --git a/src/utils/ly_typing.py b/liteyuki/utils/ly_typing.py
similarity index 100%
rename from src/utils/ly_typing.py
rename to liteyuki/utils/ly_typing.py
diff --git a/src/utils/message.py b/liteyuki/utils/message.py
similarity index 100%
rename from src/utils/message.py
rename to liteyuki/utils/message.py
diff --git a/liteyuki/utils/orm.py b/liteyuki/utils/orm.py
new file mode 100644
index 0000000..2fb7929
--- /dev/null
+++ b/liteyuki/utils/orm.py
@@ -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")
diff --git a/src/utils/permission.py b/liteyuki/utils/permission.py
similarity index 58%
rename from src/utils/permission.py
rename to liteyuki/utils/permission.py
index fcc6508..dbf092d 100644
--- a/src/utils/permission.py
+++ b/liteyuki/utils/permission.py
@@ -1,6 +1,6 @@
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_OWNER = v11.GROUP_OWNER
diff --git a/src/utils/resource.py b/liteyuki/utils/resource.py
similarity index 86%
rename from src/utils/resource.py
rename to liteyuki/utils/resource.py
index 256dfba..2ae0c5e 100644
--- a/src/utils/resource.py
+++ b/liteyuki/utils/resource.py
@@ -4,7 +4,7 @@ import nonebot
import yaml
from typing import Any
-from src.utils.data import LiteModel
+from liteyuki.utils.data import LiteModel
_resource_data = {}
_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("\\", "/")
abs_path = os.path.join(root, file).replace("\\", "/")
_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")):
with open(os.path.join(path, "metadata.yml"), "r", encoding="utf-8") as f:
metadata = yaml.safe_load(f)
@@ -39,12 +38,12 @@ def load_resource_from_dir(path: str):
metadata = ResourceMetadata()
metadata["path"] = path
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"))
_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:
diff --git a/src/utils/tools.py b/liteyuki/utils/tools.py
similarity index 88%
rename from src/utils/tools.py
rename to liteyuki/utils/tools.py
index c7142e2..c5d9ccb 100644
--- a/src/utils/tools.py
+++ b/liteyuki/utils/tools.py
@@ -1,3 +1,4 @@
+from importlib.metadata import PackageNotFoundError, version
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:
return True
return False
+
+
+def check_for_package(package_name: str) -> bool:
+ try:
+ version(package_name)
+ return True
+ except PackageNotFoundError:
+ return False
diff --git a/main.py b/main.py
index 7f53e7b..d43b5ab 100644
--- a/main.py
+++ b/main.py
@@ -1,11 +1,9 @@
import nonebot
-
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
-from src.utils.config import load_from_yaml
-
-nonebot.logger = logger
+init()
nonebot.init(**load_from_yaml("config.yml"))
adapters = [v11.Adapter, v12.Adapter]
@@ -14,7 +12,7 @@ driver = nonebot.get_driver()
for adapter in adapters:
driver.register_adapter(adapter)
-nonebot.load_plugin("src.liteyuki_main")
+nonebot.load_plugin("liteyuki.liteyuki_main")
if __name__ == "__main__":
nonebot.run()
diff --git a/requirements.txt b/requirements.txt
index 0911e55..16f0b78 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,13 +2,16 @@ aiohttp==3.9.3
aiofiles==23.2.1
arclet-alconna==1.8.5
arclet-alconna-tools==0.7.0
+colored==2.2.4
dash==2.16.1
nonebot2[fastapi]==2.2.1
nonebot-adapter-onebot==2.4.3
nonebot-plugin-alconna==0.41.0
pip==24.0
psutil==5.9.8
-pydantic==2.6.4
+pydantic==1.10.14
pytz==2024.1
PyYAML~=6.0.1
-starlette~=0.36.3
\ No newline at end of file
+starlette~=0.36.3
+loguru==0.7.2
+importlib_metadata==7.0.2
\ No newline at end of file
diff --git a/src/liteyuki_main/loader.py b/src/liteyuki_main/loader.py
deleted file mode 100644
index a3fc4b5..0000000
--- a/src/liteyuki_main/loader.py
+++ /dev/null
@@ -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)
\ No newline at end of file
diff --git a/src/plugins/liteyuki_plugin_eventpush.py b/src/plugins/liteyuki_plugin_eventpush.py
deleted file mode 100644
index dc08644..0000000
--- a/src/plugins/liteyuki_plugin_eventpush.py
+++ /dev/null
@@ -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,
- }
-)
diff --git a/src/utils/__init__.py b/src/utils/__init__.py
deleted file mode 100644
index c99114d..0000000
--- a/src/utils/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .log import logger
\ No newline at end of file
diff --git a/src/utils/data_manager.py b/src/utils/data_manager.py
deleted file mode 100644
index eddda80..0000000
--- a/src/utils/data_manager.py
+++ /dev/null
@@ -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)