diff --git a/.gitignore b/.gitignore index 9a05aa4d..7b4b4eed 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ .cache/ data/ db/ -plugins/ +/plugins/ _config.yml config.yml config.example.yml diff --git a/requirements.txt b/requirements.txt index 196dbdcd..d0eaf393 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,7 @@ nonebot-plugin-alconna==0.41.0 pip==24.0 psutil==5.9.8 pydantic==2.6.4 +pytz==2024.1 PyYAML~=6.0.1 typing_extensions~=4.10.0 starlette~=0.36.3 diff --git a/src/liteyuki_main/__init__.py b/src/liteyuki_main/__init__.py index ff715d20..7e6fe820 100644 --- a/src/liteyuki_main/__init__.py +++ b/src/liteyuki_main/__init__.py @@ -13,7 +13,7 @@ __plugin_meta__ = PluginMetadata( usage="", homepage="https://github.com/snowykami/LiteyukiBot", extra={ - "liteyuki_plugin": True, + "liteyuki": True, "toggleable": False, } ) diff --git a/src/plugins/liteyuki_plugin_minigame/__init__.py b/src/plugins/liteyuki_plugin_minigame/__init__.py new file mode 100644 index 00000000..a5e4df31 --- /dev/null +++ b/src/plugins/liteyuki_plugin_minigame/__init__.py @@ -0,0 +1,14 @@ +from nonebot.plugin import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="轻雪小游戏", + description="内置了一些小游戏", + usage="", + type="application", + homepage="https://github.com/snowykami/LiteyukiBot", + extra={ + "liteyuki": True, + "toggleable" : True, + "default_enable" : True, + } +) diff --git a/src/plugins/liteyuki_plugin_npm/installer.py b/src/plugins/liteyuki_plugin_npm/installer.py index cea89768..9eeb66df 100644 --- a/src/plugins/liteyuki_plugin_npm/installer.py +++ b/src/plugins/liteyuki_plugin_npm/installer.py @@ -59,8 +59,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): ulang = get_user_lang(str(event.user_id)) if not os.path.exists("data/liteyuki/plugins.json"): - shutil.copy(get_res('unsorted/plugins.json'), "data/liteyuki/plugins.json") - nonebot.logger.info("Please update plugin store data file.") + await npm_update() if result.subcommands.get("update"): r = await npm_update() diff --git a/src/plugins/liteyuki_plugin_npm/manager.py b/src/plugins/liteyuki_plugin_npm/manager.py index 895e1d8e..3529c901 100644 --- a/src/plugins/liteyuki_plugin_npm/manager.py +++ b/src/plugins/liteyuki_plugin_npm/manager.py @@ -1,3 +1,5 @@ +import os + import nonebot.plugin from nonebot import on_command from nonebot.internal.matcher import Matcher @@ -11,7 +13,7 @@ from src.utils.permission import GROUP_ADMIN, GROUP_OWNER from src.utils.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 .installer import get_store_plugin +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) @@ -25,6 +27,8 @@ toggle_plugin = on_alconna( @list_plugins.handle() async def _(event: T_MessageEvent, bot: T_Bot): + if not os.path.exists("data/liteyuki/plugins.json"): + await npm_update() lang = get_user_lang(str(event.user_id)) reply = f"# {lang.get('npm.loaded_plugins')} | {lang.get('npm.total', TOTAL=len(nonebot.get_loaded_plugins()))} \n***\n" for plugin in nonebot.get_loaded_plugins(): @@ -71,6 +75,8 @@ async def _(event: T_MessageEvent, bot: T_Bot): @toggle_plugin.handle() async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): + if not os.path.exists("data/liteyuki/plugins.json"): + await npm_update() # 判断会话类型 ulang = get_user_lang(str(event.user_id)) plugin_module_name = result.args.get("plugin_name") diff --git a/src/plugins/liteyuki_plugin_user/profile_manager.py b/src/plugins/liteyuki_plugin_user/profile_manager.py index 90347fe6..a682be03 100644 --- a/src/plugins/liteyuki_plugin_user/profile_manager.py +++ b/src/plugins/liteyuki_plugin_user/profile_manager.py @@ -1,39 +1,101 @@ +from typing import Optional + +from arclet.alconna import Arparma from nonebot import on_command from nonebot.params import CommandArg +from nonebot_plugin_alconna import on_alconna, Alconna, Args, Arparma, Option, Subcommand +from src.utils.data import LiteModel from src.utils.typing import T_Bot, T_Message, T_MessageEvent from src.utils.data_manager import User, user_db from src.utils.language import get_user_lang +from src.utils.message import Markdown as md, send_markdown + +profile_alc = on_alconna( + Alconna( + ["profile", "个人信息"], + Subcommand( + "set", + Args["key", str]["value", str, Optional], + alias=["s", "设置"], + ), + Subcommand( + "get", + Args["key", str], + alias=["g", "查询"], + ), + ) +) - -attr_map = { - "lang" : ["lang", "language", "语言"], - "username": ["username", "昵称", "用户名"] # Bot称呼用户的昵称 -} - -attr_cmd = on_command("profile", aliases={"个人设置"}, priority=0) +# json储存 +class Profile(LiteModel): + lang: str = "zh-CN" + nickname: str = "" + timezone: str = "Asia/Shanghai" + location: str = "" -@attr_cmd.handle() -async def _(bot: T_Bot, event: T_MessageEvent, args: T_Message = CommandArg()): - user = user_db.first(User, "user_id = ?", str(event.user_id), default=User(user_id=str(event.user_id))) +@profile_alc.handle() +async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): + user: User = user_db.first(User, "user_id = ?", event.user_id, default=User(user_id=str(event.user_id))) ulang = get_user_lang(str(event.user_id)) + if result.subcommands.get("set"): + if result.subcommands["set"].args.get("value"): + # TODO + pass + else: + # 没有值尝试呼出菜单,若菜单为none则提示用户输入值再次尝试 + # TODO + pass - args = str(args).split(" ", 1) - input_key = args[0] - attr_key = "username" - for attr_key, attr_values in attr_map.items(): - if input_key in attr_values: - break + user.profile[result.args["key"]] = result.args["value"] - if len(args) == 1: - # 查询 - value = user.__dict__[attr_key] - await attr_cmd.finish(f"{ulang.get('user.profile_manager.query', ATTR=attr_key, VALUE=value)}") + elif result.subcommands.get("get"): + if result.args["key"] in user.profile: + await profile_alc.finish(user.profile[result.args["key"]]) + else: + await profile_alc.finish("无此键值") else: - # 设置 - value = args[1] - user.__dict__[attr_key] = value - user_db.save(user) - await attr_cmd.finish(f"{ulang.get('user.profile_manager.set', ATTR=attr_key, VALUE=value)}") + profile = Profile(**user.profile) + + for k, v in user.profile: + profile.__setattr__(k, v) + + reply = f"# {ulang.get("user.profile.settings")}\n***\n" + + hidden_attr = ["id"] + enter_attr = ["lang", "timezone"] + + for key in sorted(profile.dict().keys()): + if key in hidden_attr: + continue + val = profile.dict()[key] + key_text = ulang.get(f"user.profile.{key}") + btn_set = md.button(ulang.get("user.profile.edit"), f"profile set {key}", + enter=True if key in enter_attr else False) + reply += (f"\n**{key_text}** **{val}**\n" + f"\n > {btn_set} {ulang.get(f'user.profile.{key}.desc')}\n\n***\n") + await send_markdown(reply, bot, event=event) + + +def get_profile_menu(key: str) -> str: + """获取属性的markdown菜单 + Args: + key: + + Returns: + + """ + + +def set_profile(key: str, value: str) -> bool: + """设置属性,使用if分支对每一个合法性进行检查 + Args: + key: + value: + + Returns: + 是否成功设置,输入合法性不通过返回False + + """ diff --git a/src/plugins/liteyuki_plugin_weather/__init__.py b/src/plugins/liteyuki_plugin_weather/__init__.py new file mode 100644 index 00000000..09fe0076 --- /dev/null +++ b/src/plugins/liteyuki_plugin_weather/__init__.py @@ -0,0 +1,15 @@ +from nonebot.plugin import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="轻雪天气", + description="基于和风天气api的天气插件", + usage="", + type="application", + homepage="https://github.com/snowykami/LiteyukiBot", + extra={ + "liteyuki": True, + "toggleable" : True, + "default_enable" : True, + } +) + diff --git a/src/__init__.py b/src/plugins/liteyuki_plugin_weather/data_model.py similarity index 100% rename from src/__init__.py rename to src/plugins/liteyuki_plugin_weather/data_model.py diff --git a/src/resources/lang/en.lang b/src/resources/lang/en.lang index c5eb6b79..f33cc553 100644 --- a/src/resources/lang/en.lang +++ b/src/resources/lang/en.lang @@ -46,5 +46,12 @@ npm.prev_page=Prev npm.plugin_cannot_be_toggled=This plugin {NAME} cannot be toggled npm.toggle_failed=Failed to {STATUS} {NAME}: {ERROR} +user.profile.edit=Edit user.profile_manager.query=Your {ATTR} is {VALUE} -user.profile_manager.set=Your {ATTR} has been set to {VALUE} \ No newline at end of file +user.profile_manager.set=Yours {ATTR} has been set to {VALUE} +user.profile.settings=Personal settings +user.profile.lang=Language +user.profile.timezone=Timezone +user.profile.theme=Theme +user.profile.location=Location +user.profile.nickname=Nickname \ No newline at end of file diff --git a/src/resources/lang/ja.lang b/src/resources/lang/ja.lang index 205db7a0..60ad461d 100644 --- a/src/resources/lang/ja.lang +++ b/src/resources/lang/ja.lang @@ -1,50 +1,57 @@ -language.name=日本語 +language.name = 日本語 -main.current_language=現在のシステム言語: {LANG} -main.enable_webdash=ウェブダッシュボードが有効になりました: {URL} -main.monitor.title=Liteyukiモニタリングパネル -main.monitor.description=Liteyukiロボットモニタリングパネル -main.monitor.cpu=CPU -main.monitor.memory=メモリ -main.monitor.swap=スワップ -main.monitor.disk=ディスク -main.monitor.usage=使用率 +main.current_language = 現在のシステム言語: {LANG } +main.enable_webdash = ウェブダッシュボードが有効になりました: {URL } +main.monitor.title = Liteyukiモニタリングパネル +main.monitor.description = Liteyukiロボットモニタリングパネル +main.monitor.cpu = CPU +main.monitor.memory = メモリ +main.monitor.swap = スワップ +main.monitor.disk = ディスク +main.monitor.usage = 使用率 -data_manager.migrate_success=データが正常に移行されました {NAME} +data_manager.migrate_success = データが正常に移行されました { NAME } -npm.loaded_plugins=読み込まれたプラグイン -npm.total=合計 {TOTAL} -npm.help=ヘルプ -npm.usage=使用法 -npm.disable=無効 -npm.disable_global=グローバル無効 -npm.enable=有効 -npm.enable_global=グローバル有効 -npm.install=インストール -npm.uninstall=アンインストール -npm.installing={NAME} インストール中 -npm.cannot_uninstall=このプラグインはアンインストールできません -npm.no_description=説明なし -npm.store_update_success=プラグインストアのデータが正常に更新されました -npm.store_update_failed=プラグインストアのデータの更新に失敗しました -npm.search_result=検索結果 -npm.search_no_result=検索結果がありません -npm.too_many_results=検索結果が多すぎます。{HIDE_NUM} 件の結果が非表示になりました -npm.install_success={NAME} が正常にインストールされました -npm.install_failed={NAME} のインストールに失敗しました, 詳細はログを参照してください, またはプラグインの作者に連絡してください{HOMEPAGE} -npm.remove_success={NAME} が正常にアンインストールされました -npm.remove_failed={NAME} のアンインストールに失敗しました -npm.load_failed={NAME} の読み込みに失敗しました,詳細はログを参照してください, またはプラグインの作者に連絡してください{HOMEPAGE} -npm.plugin_not_found={NAME} は見つかりません,スペルをチェックしてください -npm.plugin_not_installed={NAME} はインストールされていません -npm.plugin_already_installed={NAME} は既にインストールされています -npm.author=著者 -npm.homepage=ホームページ -npm.pypi=PyPI -npm.next_page=次のページ -npm.prev_page=前のページ -npm.plugin_cannot_be_toggled=このプラグイン {NAME} は無効にできません -npm.toggle_failed=プラグイン {NAME} の{STATUS}切り替えに失敗しました:{ERROR} +npm.loaded_plugins = 読み込まれたプラグイン +npm.total = 合計 { TOTAL } +npm.help = ヘルプ +npm.usage = 使用法 +npm.disable = 無効 +npm.disable_global = グローバル無効 +npm.enable = 有効 +npm.enable_global = グローバル有効 +npm.install = インストール +npm.uninstall = アンインストール +npm.installing = { NAME } インストール中 +npm.cannot_uninstall = このプラグインはアンインストールできません +npm.no_description = 説明なし +npm.store_update_success = プラグインストアのデータが正常に更新されました +npm.store_update_failed = プラグインストアのデータの更新に失敗しました +npm.search_result = 検索結果 +npm.search_no_result = 検索結果がありません +npm.too_many_results = 検索結果が多すぎます。{ HIDE_NUM } 件の結果が非表示になりました +npm.install_success = { NAME } が正常にインストールされました +npm.install_failed = { NAME } のインストールに失敗しました, 詳細はログを参照してください, またはプラグインの作者に連絡してください{HOMEPAGE } +npm.remove_success = { NAME } が正常にアンインストールされました +npm.remove_failed = { NAME } のアンインストールに失敗しました +npm.load_failed = { NAME } の読み込みに失敗しました,詳細はログを参照してください, またはプラグインの作者に連絡してください{ HOMEPAGE } +npm.plugin_not_found = { NAME } は見つかりません,スペルをチェックしてください +npm.plugin_not_installed = { NAME } はインストールされていません +npm.plugin_already_installed = { NAME } は既にインストールされています +npm.author = 著者 +npm.homepage = ホームページ +npm.pypi = PyPI +npm.next_page = 次のページ +npm.prev_page = 前のページ +npm.plugin_cannot_be_toggled = このプラグイン { NAME } は無効にできません +npm.toggle_failed = プラグイン { NAME } の{ STATUS}切り替えに失敗しました:{ ERROR } -user.profile_manager.query=あなたのプロファイル情報 {ATTR} は {VALUE} です -user.profile_manager.set=あなたのプロファイル情報 {ATTR} が {VALUE} に設定されました \ No newline at end of file +user.profile.edit=編集 +user.profile_manager.query=あなたの個人情報 {ATTR} は {VALUE} です +user.profile_manager.set=あなたの個人情報 {ATTR} は {VALUE} に設定されました +user.profile.settings=個人設定 +user.profile.lang=言語 +user.profile.timezone=タイムゾーン +user.profile.theme=テーマ +user.profile.location=位置 +user.profile.nickname=ニックネーム \ No newline at end of file diff --git a/src/resources/lang/zh-CN.lang b/src/resources/lang/zh-CN.lang index 04be77b5..6530a3ac 100644 --- a/src/resources/lang/zh-CN.lang +++ b/src/resources/lang/zh-CN.lang @@ -47,5 +47,12 @@ npm.plugin_cannot_be_toggled=插件 {NAME} 无法被启用或停用 npm.plugin_already=插件 {NAME} 已经是 {STATUS} 状态,无需重复操作 npm.toggle_failed=插件 {NAME} {STATUS} 失败: {ERROR} +user.profile.edit=修改 user.profile_manager.query=你的个人信息 {ATTR} 为 {VALUE} -user.profile_manager.set=你的个人信息 {ATTR} 已设置为 {VALUE} \ No newline at end of file +user.profile_manager.set=你的个人信息 {ATTR} 已设置为 {VALUE} +user.profile.settings=个人设置 +user.profile.lang=语言 +user.profile.timezone=时区 +user.profile.theme=主题 +user.profile.location=位置 +user.profile.nickname=称呼 \ No newline at end of file diff --git a/src/utils/data_manager.py b/src/utils/data_manager.py index f70256ee..97144870 100644 --- a/src/utils/data_manager.py +++ b/src/utils/data_manager.py @@ -12,7 +12,7 @@ plugin_db = DB(os.path.join(DATA_PATH, 'plugins.ldb')) class User(LiteModel): user_id: str username: str = "" - lang: str = "en" + profile: dict = {} enabled_plugins: list[str] = [] disabled_plugins: list[str] = [] diff --git a/src/utils/language.py b/src/utils/language.py index d28f2861..323be992 100644 --- a/src/utils/language.py +++ b/src/utils/language.py @@ -136,9 +136,10 @@ def get_user_lang(user_id: str) -> Language: """ user = user_db.first(User, "user_id = ?", user_id, default=User( user_id=user_id, - username="Unknown", - lang=config.get("default_language", get_system_lang_code()))) - return Language(user.lang) + username="Unknown" + )) + + return Language(user.profile.get('lang',config.get("default_language", get_system_lang_code()) )) def get_system_lang_code() -> str: diff --git a/src/utils/message.py b/src/utils/message.py index 4049b5d5..80f80e62 100644 --- a/src/utils/message.py +++ b/src/utils/message.py @@ -6,7 +6,7 @@ from .tools import de_escape, encode_url from .typing import T_Bot, T_MessageEvent -async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, session_id: str | int = None, event: T_MessageEvent = None) -> dict[str, Any]: +async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, session_id: str | int = None, event: T_MessageEvent = None, **kwargs) -> dict[str, Any]: formatted_md = de_escape(markdown).replace("\n", r"\n").replace("\"", r'\\\"') if event is not None and message_type is None: message_type = event.message_type @@ -45,6 +45,7 @@ async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, } ), ], + **kwargs ) except Exception as e: @@ -54,7 +55,8 @@ async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, message_type=message_type, message=markdown, user_id=int(session_id), - group_id=int(session_id) + group_id=int(session_id), + **kwargs ) elif isinstance(bot, v12.Bot): data = await bot.send_message( @@ -65,7 +67,8 @@ async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, ) ), user_id=str(session_id), - group_id=str(session_id) + group_id=str(session_id), + **kwargs ) else: nonebot.logger.error("send_markdown: bot type not supported")