diff --git a/src/plugins/liteyuki_plugin_npm/manager.py b/src/plugins/liteyuki_plugin_npm/manager.py index 3529c901..b8c75277 100644 --- a/src/plugins/liteyuki_plugin_npm/manager.py +++ b/src/plugins/liteyuki_plugin_npm/manager.py @@ -66,7 +66,7 @@ async def _(event: T_MessageEvent, bot: T_Bot): md.button(lang.get('npm.uninstall'), f'npm remove {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} global') + else md.button(lang.get('npm.disable_global'), f'disable-plugin {plugin.module_name} true') reply += f" {btn_remove} {btn_toggle_global}" reply += "\n\n***\n" diff --git a/src/plugins/liteyuki_plugin_npm/permission.py b/src/plugins/liteyuki_plugin_npm/permission.py new file mode 100644 index 00000000..d2533fbf --- /dev/null +++ b/src/plugins/liteyuki_plugin_npm/permission.py @@ -0,0 +1,8 @@ +# 插件权限管理器,对api调用进行hook限制,防止插件滥用api +from src.utils.data import LiteModel + + +class PermissionAllow(LiteModel): + plugin_name: str + api_name: str + allow: bool \ No newline at end of file diff --git a/src/plugins/liteyuki_plugin_user/input_handle.py b/src/plugins/liteyuki_plugin_user/input_handle.py new file mode 100644 index 00000000..ded0aebb --- /dev/null +++ b/src/plugins/liteyuki_plugin_user/input_handle.py @@ -0,0 +1,2 @@ +def detect_lang(input_str: str) -> str: + return "zh-CN" if input_str == "zh" else "en" \ No newline at end of file diff --git a/src/plugins/liteyuki_plugin_user/profile_manager.py b/src/plugins/liteyuki_plugin_user/profile_manager.py index 21a6d670..787fcada 100644 --- a/src/plugins/liteyuki_plugin_user/profile_manager.py +++ b/src/plugins/liteyuki_plugin_user/profile_manager.py @@ -8,7 +8,7 @@ from nonebot_plugin_alconna import on_alconna, Alconna, Args, Arparma, Option, S 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.language import Language, get_all_lang, get_user_lang from src.utils.message import Markdown as md, send_markdown profile_alc = on_alconna( @@ -16,7 +16,7 @@ profile_alc = on_alconna( ["profile", "个人信息"], Subcommand( "set", - Args["key", str]["value", str, Optional], + Args["key", str]["value", str, None], alias=["s", "设置"], ), Subcommand( @@ -42,13 +42,27 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): ulang = get_user_lang(str(event.user_id)) if result.subcommands.get("set"): if result.subcommands["set"].args.get("value"): - # TODO - pass + # 对合法性进行校验后设置 + r = set_profile(result.args["key"], result.args["value"]) + if r: + user.profile[result.args["key"]] = result.args["value"] + user_db.save(user) # 数据库保存 + await profile_alc.finish( + ulang.get( + "user.profile.set_success", + ATTR=ulang.get(f"user.profile.{result.args['key']}"), + VALUE=result.args["value"] + ) + ) + else: + await profile_alc.finish(ulang.get("user.profile.set_failed", ATTR=ulang.get(f"user.profile.{result.args['key']}"))) else: - # 没有值尝试呼出菜单,若菜单为none则提示用户输入值再次尝试 - # TODO - pass - + # 未输入值,尝试呼出菜单 + menu = get_profile_menu(result.args["key"], ulang) + if menu: + await send_markdown(menu, bot, event=event) + else: + await profile_alc.finish(ulang.get("user.profile.input_value", ATTR=ulang.get(f"user.profile.{result.args['key']}"))) user.profile[result.args["key"]] = result.args["value"] @@ -60,10 +74,10 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): else: profile = Profile(**user.profile) - for k, v in user.profile: + for k, v in user.profile.items(): profile.__setattr__(k, v) - reply = f"# {ulang.get("user.profile.settings")}\n***\n" + reply = f"# {ulang.get('user.profile.info')}\n***\n" hidden_attr = ["id"] enter_attr = ["lang", "timezone"] @@ -80,14 +94,29 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): await send_markdown(reply, bot, event=event) -def get_profile_menu(key: str) -> str: +def get_profile_menu(key: str, ulang: Language) -> Optional[str]: """获取属性的markdown菜单 Args: - key: + ulang: 用户语言 + key: 属性键 Returns: """ + setting_name = ulang.get(f"user.profile.{key}") + + no_menu = ["id", "nickname", "location"] + + if key in no_menu: + return None + + reply = f"{setting_name} {ulang.get('user.profile.settings')}\n***\n" + if key == "lang": + for lang_code, lang_name in get_all_lang().items(): + btn_set = md.button(ulang.get('user.profile.set'), f"profile set {key} {lang_code}") + reply += (f"\n**{lang_name}** **{lang_code}**\n" + f"\n> {btn_set}\n\n***") + return reply def set_profile(key: str, value: str) -> bool: @@ -100,3 +129,10 @@ def set_profile(key: str, value: str) -> bool: 是否成功设置,输入合法性不通过返回False """ + if key == 'lang': + if value in get_all_lang(): + return True + return False + elif key == 'timezone': + # TODO + pass diff --git a/src/resources/lang/en.lang b/src/resources/lang/en.lang index f33cc553..d1d87aac 100644 --- a/src/resources/lang/en.lang +++ b/src/resources/lang/en.lang @@ -43,15 +43,27 @@ npm.homepage=Homepage npm.pypi=PyPI npm.next_page=Next npm.prev_page=Prev -npm.plugin_cannot_be_toggled=This plugin {NAME} cannot be toggled -npm.toggle_failed=Failed to {STATUS} {NAME}: {ERROR} +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} user.profile.edit=Edit +user.profile.set=Set user.profile_manager.query=Your {ATTR} is {VALUE} user.profile_manager.set=Yours {ATTR} has been set to {VALUE} -user.profile.settings=Personal settings +user.profile.settings=settings +user.profile.info=Personal information user.profile.lang=Language +user.profile.lang.desc=Set user language user.profile.timezone=Timezone +user.profile.timezone.desc=Set user's current timezone user.profile.theme=Theme +user.profile.theme.desc=Set user interface theme user.profile.location=Location -user.profile.nickname=Nickname \ No newline at end of file +user.profile.location.desc=Set user location information +user.profile.nickname=Nickname +user.profile.nickname.desc=Set Bot's nickname for the user + +user.profile.input_value=Please enter a value of {ATTR} +user.profile.set_success=Succeeded in setting {ATTR} as {VALUE} +user.profile.set_failed=Setting {ATTR} failed, please check the input value \ No newline at end of file diff --git a/src/resources/lang/ja.lang b/src/resources/lang/ja.lang index 60ad461d..54a67e5f 100644 --- a/src/resources/lang/ja.lang +++ b/src/resources/lang/ja.lang @@ -43,15 +43,27 @@ npm.homepage = ホームページ npm.pypi = PyPI npm.next_page = 次のページ npm.prev_page = 前のページ -npm.plugin_cannot_be_toggled = このプラグイン { NAME } は無効にできません -npm.toggle_failed = プラグイン { NAME } の{ STATUS}切り替えに失敗しました:{ ERROR } +npm.plugin_cannot_be_toggled=プラグイン {NAME} は有効または無効にできません +npm.plugin_already=プラグイン {NAME} はすでに {STATUS} 状態です。繰り返し操作する必要はありません +npm.toggle_failed=プラグイン {NAME} を {STATUS} にするのに失敗しました: {ERROR} user.profile.edit=編集 +user.profile.set=設定 user.profile_manager.query=あなたの個人情報 {ATTR} は {VALUE} です user.profile_manager.set=あなたの個人情報 {ATTR} は {VALUE} に設定されました -user.profile.settings=個人設定 +user.profile.settings=設定 +user.profile.info=個人情報 user.profile.lang=言語 +user.profile.lang.desc=ユーザーの言語を設定します user.profile.timezone=タイムゾーン +user.profile.timezone.desc=ユーザーの現在のタイムゾーンを設定します user.profile.theme=テーマ -user.profile.location=位置 -user.profile.nickname=ニックネーム \ No newline at end of file +user.profile.theme.desc=ユーザーインターフェースのテーマを設定します +user.profile.location=場所 +user.profile.location.desc=ユーザーの位置情報を設定します +user.profile.nickname=ニックネーム +user.profile.nickname.desc=ユーザーのニックネームを設定します + +user.profile.input_value=新しい{ATTR}を入力してください +user.profile.set_success=設定{ATTR}成功{VALUE} +user.profile.set_failed=設定{ATTR}失敗,詳細はInputを参照してください \ No newline at end of file diff --git a/src/resources/lang/zh-CN.lang b/src/resources/lang/zh-CN.lang index 6530a3ac..d88a37e1 100644 --- a/src/resources/lang/zh-CN.lang +++ b/src/resources/lang/zh-CN.lang @@ -48,11 +48,22 @@ npm.plugin_already=插件 {NAME} 已经是 {STATUS} 状态,无需重复操作 npm.toggle_failed=插件 {NAME} {STATUS} 失败: {ERROR} user.profile.edit=修改 +user.profile.set=设置 user.profile_manager.query=你的个人信息 {ATTR} 为 {VALUE} user.profile_manager.set=你的个人信息 {ATTR} 已设置为 {VALUE} -user.profile.settings=个人设置 +user.profile.settings=设置 +user.profile.info=个人信息 user.profile.lang=语言 +user.profile.lang.desc=设置用户语言 user.profile.timezone=时区 +user.profile.timezone.desc=设置用户当前时区 user.profile.theme=主题 +user.profile.theme.desc=设置用户界面主题 user.profile.location=位置 -user.profile.nickname=称呼 \ No newline at end of file +user.profile.location.desc=设置用户位置信息 +user.profile.nickname=称呼 +user.profile.nickname.desc=设置Bot对用户的称呼 + +user.profile.input_value=请输入 {ATTR} 的值 +user.profile.set_success=成功将 {ATTR} 设置为 {VALUE} +user.profile.set_failed=设置 {ATTR} 失败,请检查输入是否合法 \ No newline at end of file diff --git a/src/utils/data.py b/src/utils/data.py index 2fd9babb..cb84a1b4 100644 --- a/src/utils/data.py +++ b/src/utils/data.py @@ -141,10 +141,10 @@ class Database(BaseORMAdapter): if isinstance(r_type, type(LiteModel)): model_fields.append(f'{self.FOREIGNID}{field}') model_types.append('TEXT') - elif isinstance(r_type, list): + elif r_type in [list[str], list[int], list[float], list[bool], list]: model_fields.append(f'{self.LIST}{field}') model_types.append('TEXT') - elif isinstance(r_type, dict): + elif r_type in [dict[str, str], dict[str, int], dict[str, float], dict[str, bool], dict]: model_fields.append(f'{self.DICT}{field}') model_types.append('TEXT') elif isinstance(r_type, types.GenericAlias): @@ -155,12 +155,12 @@ class Database(BaseORMAdapter): model_types.append(self.type_map.get(r_type, 'TEXT')) # 检测新字段或字段类型是否有变化,有则增删字段,已经加了前缀类型 - for field, type_, r_type in zip(model_fields, model_types, raw_types): - if field not in table_fields: - nonebot.logger.debug(f'ALTER TABLE {table_name} ADD COLUMN {field} {type_}') - self.cursor.execute(f'ALTER TABLE {table_name} ADD COLUMN {field} {type_}') + for field_changed, type_, r_type in zip(model_fields, model_types, raw_types): + if field_changed not in table_fields: + nonebot.logger.debug(f'ALTER TABLE {table_name} ADD COLUMN {field_changed} {type_}') + self.cursor.execute(f'ALTER TABLE {table_name} ADD COLUMN {field_changed} {type_}') # 在原有的行中添加新字段对应类型的默认值,从DEFAULT_TYPE中获取 - self.cursor.execute(f'UPDATE {table_name} SET {field} = ? WHERE {field} IS NULL', (self.DEFAULT_VALUE.get(type_, ""),)) + self.cursor.execute(f'UPDATE {table_name} SET {field_changed} = ? WHERE {field_changed} IS NULL', (self.DEFAULT_VALUE.get(type_, ""),)) # 检测多余字段,除了id字段 for field in table_fields: @@ -193,6 +193,12 @@ class Database(BaseORMAdapter): if isinstance(value, LiteModel): key_list.append(f'{self.FOREIGNID}{field}') value_list.append(f'{self.ID}:{value.__class__.__name__}:{self.save(value)}') + elif isinstance(value, list): + key_list.append(f'{self.LIST}{field}') + value_list.append(self._flat(value)) + elif isinstance(value, dict): + key_list.append(f'{self.DICT}{field}') + value_list.append(self._flat(value)) elif isinstance(value, BaseIterable): key_list.append(f'{self.JSON}{field}') value_list.append(self._flat(value)) @@ -220,6 +226,10 @@ class Database(BaseORMAdapter): 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)}' + elif isinstance(v, list): + return_data[f'{self.LIST}{k}'] = self._flat(v) + elif isinstance(v, dict): + return_data[f'{self.DICT}{k}'] = self._flat(v) elif isinstance(v, BaseIterable): return_data[f'{self.JSON}{k}'] = self._flat(v) else: @@ -230,6 +240,10 @@ class Database(BaseORMAdapter): for v in data: if isinstance(v, LiteModel): return_data.append(f'{self.ID}:{v.__class__.__name__}:{self.save(v)}') + elif isinstance(v, list): + return_data.append(self._flat(v)) + elif isinstance(v, dict): + return_data.append(self._flat(v)) elif isinstance(v, BaseIterable): return_data.append(self._flat(v)) else: @@ -333,15 +347,16 @@ class Database(BaseORMAdapter): if k.startswith(self.FOREIGNID): new_d[k.replace(self.FOREIGNID, '')] = load( dict(self.cursor.execute(f'SELECT * FROM {v.split(":", 2)[1]} WHERE id = ?', (v.split(":", 2)[2],)).fetchone())) - elif k.startswith(self.JSON): - if v == '': v = '[]' - new_d[k.replace(self.JSON, '')] = load(json.loads(v)) + elif k.startswith(self.LIST): if v == '': v = '[]' new_d[k.replace(self.LIST, '')] = load(json.loads(v)) elif k.startswith(self.DICT): if v == '': v = '{}' new_d[k.replace(self.DICT, '')] = load(json.loads(v)) + elif k.startswith(self.JSON): + if v == '': v = '[]' + new_d[k.replace(self.JSON, '')] = load(json.loads(v)) else: new_d[k] = v elif isinstance(d, list | tuple | set): diff --git a/src/utils/data_manager.py b/src/utils/data_manager.py index 97144870..eddda80c 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 = "" - profile: dict = {} + profile: dict[str, str] = {} enabled_plugins: list[str] = [] disabled_plugins: list[str] = [] diff --git a/src/utils/language.py b/src/utils/language.py index 323be992..234c032c 100644 --- a/src/utils/language.py +++ b/src/utils/language.py @@ -154,3 +154,15 @@ def get_system_lang() -> Language: 获取系统语言 """ return Language(get_system_lang_code()) + + +def get_all_lang() -> dict[str, str]: + """ + 获取所有语言 + Returns + {'en': 'English'} + """ + d = {} + for key in _language_data: + d[key] = _language_data[key].get("language.name", key) + return d diff --git a/src/utils/message.py b/src/utils/message.py index 80f80e62..299758ce 100644 --- a/src/utils/message.py +++ b/src/utils/message.py @@ -115,7 +115,7 @@ class Markdown: 转义后的文本 """ - chars = "*[]()~_-`>#+-=|{}.!" + chars = "*[]()~_`>#+=|{}.!" for char in chars: text = text.replace(char, f"\\\\{char}") return text