Skip to content

Commit a0c3812

Browse files
committed
fix(localization): align Chinese output and stabilize test suite
1 parent 408ad1a commit a0c3812

File tree

14 files changed

+221
-192
lines changed

14 files changed

+221
-192
lines changed

src/plugins/DicePP/core/bot/dicebot.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@
4242

4343
# noinspection PyBroadException
4444
class Bot:
45-
def __init__(self, account: str):
45+
def __init__(self, account: str, readonly: bool = False):
4646
"""
4747
实例化机器人
4848
Args:
4949
account: QQ账号
50+
readonly: 只读模式,跳过本地化文件写入(适用于测试环境)
5051
"""
5152
import core.command as command
5253
import module # module中可能会定义新的DataChunk和local text等, 所以要在一开始import
@@ -68,7 +69,7 @@ def __init__(self, account: str):
6869
self.tick_task: Optional[asyncio.Task] = None
6970
self.todo_tasks: Dict[Union[Callable, asyncio.Task], Dict] = {}
7071

71-
self.start_up()
72+
self.start_up(readonly=readonly)
7273

7374
def set_client_proxy(self, proxy):
7475
from adapter import ClientProxy
@@ -77,12 +78,14 @@ def set_client_proxy(self, proxy):
7778
else:
7879
raise TypeError("Incorrect Client Proxy!")
7980

80-
def start_up(self):
81+
def start_up(self, readonly: bool = False):
8182
self.register_command()
8283
self.loc_helper.load_localization() # 要在注册完命令后再读取本地化文件
83-
self.loc_helper.save_localization() # 更新本地文件
84+
if not readonly:
85+
self.loc_helper.save_localization() # 更新本地文件
8486
self.loc_helper.load_chat()
85-
self.loc_helper.save_chat()
87+
if not readonly:
88+
self.loc_helper.save_chat()
8689
self.cfg_helper.load_config()
8790
self.cfg_helper.save_config()
8891

@@ -544,10 +547,12 @@ async def process_message(self, msg: str, meta: MessageMetaData) -> List:
544547
feedback = self.loc_helper.format_loc_text(LOC_GROUP_ONLY_NOTICE)
545548
bot_commands += [BotSendMsgCommand(self.account, feedback, [PrivateMessagePort(meta.user_id)])]
546549
break
547-
# 无权限者/权限不足者企图使用一条需要权限的指令, 回复一条提示
550+
# 无权限者/权限不足者企图使用一条需要权限的指令
548551
if meta.permission < command.permission_require:
549-
feedback = self.loc_helper.format_loc_text(LOC_PERMISSION_DENIED_NOTICE)
550-
bot_commands += [BotSendMsgCommand(self.account, feedback, [GroupMessagePort(meta.group_id) if meta.group_id else PrivateMessagePort(meta.user_id)])]
552+
# 骰管理及以上级别的指令 (permission_require >= 3) 对普通用户静默,避免暴露管理指令
553+
if command.permission_require < 3:
554+
feedback = self.loc_helper.format_loc_text(LOC_PERMISSION_DENIED_NOTICE)
555+
bot_commands += [BotSendMsgCommand(self.account, feedback, [GroupMessagePort(meta.group_id) if meta.group_id else PrivateMessagePort(meta.user_id)])]
551556
break
552557
# 执行指令
553558
# 注意: process_msg 是异步方法,需要使用 await 调用
@@ -633,12 +638,14 @@ async def process_notice(self, data: NoticeData) -> List:
633638
activate = False
634639

635640
if activate:
636-
feedback = self.data_manager.get_data(DC_WELCOME, [data.group_id],default_val="")
641+
# "default" 表示未设置(用默认欢迎词),"" 表示已关闭(不发送)
642+
feedback = self.data_manager.get_data(DC_WELCOME, [data.group_id], default_val="default")
637643

638-
if not feedback:
644+
if feedback == "default":
639645
feedback = self.loc_helper.format_loc_text(LOC_WELCOME_DEFAULT)
640646

641-
bot_commands += [BotSendMsgCommand(self.account, choice(feedback.split("|")), [GroupMessagePort(data.group_id)])]
647+
if feedback: # 关闭时 feedback 为 "",跳过发送
648+
bot_commands += [BotSendMsgCommand(self.account, choice(feedback.split("|")), [GroupMessagePort(data.group_id)])]
642649

643650
if self.proxy:
644651
for command in bot_commands:

src/plugins/DicePP/core/data/repository.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ async def get(self, *keys: str) -> Optional[T]:
5252

5353
return self._model_class.model_validate_json(row[0])
5454

55+
async def upsert(self, item: T) -> None:
56+
"""upsert 是 save 的别名,使用 INSERT OR REPLACE 语义。"""
57+
await self.save(item)
58+
5559
async def save(self, item: T) -> None:
5660
key_values = [getattr(item, field) for field in self._key_fields]
5761
data_json = item.model_dump_json()

src/plugins/DicePP/core/localization/localization_text.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
class LocalizationText:
1111
def __init__(self, key: str, default_text: str = "", comment: str = ""):
1212
self.key = key
13+
self.default_text = default_text
1314
self.loc_texts: list = [default_text] if default_text else []
1415
self.comment = comment
1516

@@ -21,7 +22,7 @@ def add(self, text: str) -> None:
2122

2223
def get(self) -> str:
2324
"""
24-
返回一个可选择的本地化字符串, 若没有可用的本地化字符串, 返回空字符串
25+
返回一个可选择的本地化字符串, 若没有可用的本地化字符串, 返回默认值(仍为空字符串则返回空)
2526
"""
2627
def replace_image_code(match):
2728
key = match.group(1)
@@ -32,7 +33,7 @@ def replace_image_code(match):
3233
dice_log(f"[LocalImage] 找不到图片 {file_path}")
3334
return match.group(0)
3435

35-
loc_text = random.choice(self.loc_texts) if self.loc_texts else ""
36+
loc_text = random.choice(self.loc_texts) if self.loc_texts else (self.default_text or "")
3637
loc_text = re.sub(r"IMG\((.{1,50}?\.[A-Za-z]{1,10}?)\)", replace_image_code, loc_text)
3738
loc_text = re.sub(r"图片\((.{1,50}?\.[A-Za-z]{1,10}?)\)", replace_image_code, loc_text)
3839
return loc_text

src/plugins/DicePP/core/localization/manager.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ def load_localization(self):
4747
key = str(row[0].value) # 第一个元素为关键字
4848
if key not in self.all_local_texts: # 无效的关键字
4949
continue
50-
comment: str = self.all_local_texts[key].comment # 沿用原来的注释, 不用文件里的
51-
self.all_local_texts[key] = LocalizationText(key, comment=comment)
50+
old = self.all_local_texts[key]
51+
comment: str = old.comment # 沿用原来的注释, 不用文件里的
52+
default_text: str = old.default_text # 沿用原来的默认值, 以便 reset_to_default() 可以复原
53+
self.all_local_texts[key] = LocalizationText(key, default_text=default_text, comment=comment)
5254
for text in [str(cell.value) for cell in row[1:] if cell.value and cell.value.strip()]:
5355
self.all_local_texts[key].add(text)
5456
dice_log(f"[Local] [Load] 成功读取本地化文件 {self.data_path.replace(ROOT_DATA_PATH, '~')}")
@@ -108,6 +110,13 @@ def save_chat(self):
108110
dice_log(f"[Local] [ChatSave] 无法保存自定义对话文件 {self.chat_data_path.replace(ROOT_DATA_PATH, '~')}, 没有写入权限")
109111
workbook.close()
110112

113+
def reset_to_default(self):
114+
"""将所有本地化文本重置为默认值(用于测试环境,避免磁盘文件污染)"""
115+
for loc_text in self.all_local_texts.values():
116+
loc_text.loc_texts = [loc_text.default_text] if loc_text.default_text else []
117+
for loc_text in self.all_chat_texts.values():
118+
loc_text.loc_texts = [loc_text.default_text] if loc_text.default_text else []
119+
111120
def register_loc_text(self, key: str, default_text: str, comment: str = ""):
112121
"""
113122
将一个本地化语句注册至Helper中
@@ -176,21 +185,16 @@ def save_loc_text_to_row(sheet: worksheet, l_text: LocalizationText, row: int):
176185
sheet.cell(row=row, column=ci + 2, value=text)
177186

178187

179-
def load_sheet_from_path(data_path: str, identifier: str, default_id: str = DEFAULT_ID) -> (openpyxl.Workbook, worksheet):
180-
"""若指定data_path无效或id无效, 返回None. 若id无效会尝试使用default_id, 一般用来得到读取的sheet"""
188+
def load_sheet_from_path(data_path: str, identifier: str) -> (openpyxl.Workbook, worksheet):
189+
"""若指定data_path无效或identifier对应的sheet不存在, 返回(None, None). 一般用来得到读取的sheet"""
181190
if not os.path.exists(data_path):
182191
return None, None
183192
workbook = read_xlsx(data_path)
184193
if identifier in workbook.sheetnames:
185194
sheet = workbook[identifier]
186-
sheet.title = identifier
187-
elif default_id in workbook.sheetnames:
188-
sheet = workbook[default_id]
189-
sheet.title = default_id
190-
else:
191-
workbook.close()
192-
return None, None
193-
return workbook, sheet
195+
return workbook, sheet
196+
workbook.close()
197+
return None, None
194198

195199

196200
def get_sheet_from_path(data_path: str, identifier: str) -> (openpyxl.Workbook, worksheet):

src/plugins/DicePP/module/character/coc/hp_command.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ class HPCommand(UserCommandBase):
3939
def __init__(self, bot: Bot):
4040
super().__init__(bot)
4141
bot.loc_helper.register_loc_text(LOC_HP_INFO, "{name}: {hp_info}", "查看当前生命值")
42-
bot.loc_helper.register_loc_text(LOC_HP_INFO_MISS, "Cannot find hp info for: {name}", "找不到指定的生命值信息")
43-
bot.loc_helper.register_loc_text(LOC_HP_INFO_MULTI, "Possible target is: {name_list}", "匹配到多个可能的生命值信息")
44-
bot.loc_helper.register_loc_text(LOC_HP_INFO_NONE, "None of hp info in this group", "查看生命值列表时找不到任何信息")
42+
bot.loc_helper.register_loc_text(LOC_HP_INFO_MISS, "找不到{name}的生命值信息", "找不到指定的生命值信息")
43+
bot.loc_helper.register_loc_text(LOC_HP_INFO_MULTI, "存在多个匹配目标:{name_list}", "匹配到多个可能的生命值信息")
44+
bot.loc_helper.register_loc_text(LOC_HP_INFO_NONE, "本群没有任何生命值信息", "查看生命值列表时找不到任何信息")
4545
bot.loc_helper.register_loc_text(LOC_HP_MOD, "{name}: {hp_mod}", "修改生命值信息")
46-
bot.loc_helper.register_loc_text(LOC_HP_MOD_ERR, "Error when modify hp: {error}", "修改生命值信息时出现错误")
47-
bot.loc_helper.register_loc_text(LOC_HP_DEL, "Delete hp info for {name}", "成功删除生命值信息")
46+
bot.loc_helper.register_loc_text(LOC_HP_MOD_ERR, "修改生命值时出现错误:{error}", "修改生命值信息时出现错误")
47+
bot.loc_helper.register_loc_text(LOC_HP_DEL, "已删除{name}的生命值信息", "成功删除生命值信息")
4848

4949
def can_process_msg(self, msg_str: str, meta: MessageMetaData) -> Tuple[bool, bool, Any]:
5050
should_proc: bool = msg_str.startswith(".hp")

src/plugins/DicePP/module/character/dnd5e/char_command.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ class CharacterDNDCommand(UserCommandBase):
3939

4040
def __init__(self, bot: Bot):
4141
super().__init__(bot)
42-
bot.loc_helper.register_loc_text(LOC_CHAR_SET, "Already set your character", "成功设置角色卡")
43-
bot.loc_helper.register_loc_text(LOC_CHAR_MISS, "Cannot find your character", "找不到有效的角色卡")
44-
bot.loc_helper.register_loc_text(LOC_CHAR_DEL, "Already delete your character", "删除角色卡")
42+
bot.loc_helper.register_loc_text(LOC_CHAR_SET, "角色卡已设置", "成功设置角色卡")
43+
bot.loc_helper.register_loc_text(LOC_CHAR_MISS, "找不到角色卡", "找不到有效的角色卡")
44+
bot.loc_helper.register_loc_text(LOC_CHAR_DEL, "角色卡已删除", "删除角色卡")
4545
bot.loc_helper.register_loc_text(LOC_CHECK_RES, "{name} throw {check}\n{hint}\n{result}", "删除角色卡")
4646

4747
def can_process_msg(self, msg_str: str, meta: MessageMetaData) -> Tuple[bool, bool, Any]:
@@ -189,7 +189,7 @@ async def process_msg(self, msg_str: str, meta: MessageMetaData, hint: Any) -> L
189189
init_cmd: InitiativeCommand = self.bot.command_dict[InitiativeCommand.__name__]
190190
assert name_str, "Unexpected Code: name_str is empty"
191191
result_dict = {name_str: (check_value_list[0], check_result_list[0])}
192-
init_feedback = init_cmd.add_initiative_entities(result_dict, meta.user_id, meta.group_id)
192+
init_feedback = await init_cmd.add_initiative_entities(result_dict, meta.user_id, meta.group_id)
193193
if check_result_list[0] in feedback:
194194
feedback = feedback.replace(check_result_list[0], init_feedback)
195195
else:

src/plugins/DicePP/module/character/dnd5e/hp_command.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ class HPCommand(UserCommandBase):
3939
def __init__(self, bot: Bot):
4040
super().__init__(bot)
4141
bot.loc_helper.register_loc_text(LOC_HP_INFO, "{name}: {hp_info}", "查看当前生命值")
42-
bot.loc_helper.register_loc_text(LOC_HP_INFO_MISS, "Cannot find hp info for: {name}", "找不到指定的生命值信息")
43-
bot.loc_helper.register_loc_text(LOC_HP_INFO_MULTI, "Possible target is: {name_list}", "匹配到多个可能的生命值信息")
44-
bot.loc_helper.register_loc_text(LOC_HP_INFO_NONE, "None of hp info in this group", "查看生命值列表时找不到任何信息")
42+
bot.loc_helper.register_loc_text(LOC_HP_INFO_MISS, "找不到{name}的生命值信息", "找不到指定的生命值信息")
43+
bot.loc_helper.register_loc_text(LOC_HP_INFO_MULTI, "存在多个匹配目标:{name_list}", "匹配到多个可能的生命值信息")
44+
bot.loc_helper.register_loc_text(LOC_HP_INFO_NONE, "本群没有任何生命值信息", "查看生命值列表时找不到任何信息")
4545
bot.loc_helper.register_loc_text(LOC_HP_MOD, "{name}: {hp_mod}", "修改生命值信息")
46-
bot.loc_helper.register_loc_text(LOC_HP_MOD_ERR, "Error when modify hp: {error}", "修改生命值信息时出现错误")
47-
bot.loc_helper.register_loc_text(LOC_HP_DEL, "Delete hp info for {name}", "成功删除生命值信息")
46+
bot.loc_helper.register_loc_text(LOC_HP_MOD_ERR, "修改生命值时出现错误:{error}", "修改生命值信息时出现错误")
47+
bot.loc_helper.register_loc_text(LOC_HP_DEL, "已删除{name}的生命值信息", "成功删除生命值信息")
4848

4949
def can_process_msg(self, msg_str: str, meta: MessageMetaData) -> Tuple[bool, bool, Any]:
5050
should_proc: bool = msg_str.startswith(".hp")
@@ -202,6 +202,7 @@ async def process_msg(self, msg_str: str, meta: MessageMetaData, hint: Any) -> L
202202
hp_info, cmd_type, hp_cur_mod_result, hp_max_mod_result, hp_temp_mod_result,
203203
short_feedback=(len(target_list) > 1)
204204
)
205+
char_obj.is_init = True # 设置 HP 后标记为已初始化
205206
await self.bot.db.characters_dnd.save(char_obj)
206207
name = self.bot.get_nickname(target_id, meta.group_id)
207208
else:

src/plugins/DicePP/module/common/nickname_command.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ class NicknameCommand(UserCommandBase):
2626
def __init__(self, bot: Bot):
2727
super().__init__(bot)
2828
bot.loc_helper.register_loc_text(LOC_NICKNAME_SET,
29-
"Set your nickname as {nickname}",
29+
"已将您的昵称设为{nickname}",
3030
".nn {nickname}返回的语句 {nickname}:昵称")
3131
bot.loc_helper.register_loc_text(LOC_NICKNAME_RESET,
32-
"Reset your nickname from {nickname_prev} to {nickname_new}",
32+
"已将您的昵称从{nickname_prev}重置为{nickname_new}",
3333
".nn重置昵称时返回的语句 {nickname_prev}: 之前设置的昵称; {nickname_new}: 当前默认昵称")
3434
bot.loc_helper.register_loc_text(LOC_NICKNAME_RESET_FAIL,
35-
"You have not set nickname before, your current nickname is {nickname}",
35+
"您尚未设置过昵称,当前昵称为{nickname}",
3636
".nn重置昵称且没有设置过昵称时返回的语句 {nickname}: 当前默认昵称")
3737
bot.loc_helper.register_loc_text(LOC_NICKNAME_ILLEGAL,
38-
"Illegal nickname!",
38+
"非法昵称!",
3939
"设置不合法的昵称时返回的语句")
4040

4141
def can_process_msg(self, msg_str: str, meta: MessageMetaData) -> Tuple[bool, bool, Any]:

src/plugins/DicePP/module/deck/deck_command.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,21 +226,21 @@ def __init__(self, bot: Bot):
226226
super().__init__(bot)
227227
self.deck_dict: Dict[str, Deck] = {}
228228

229-
bot.loc_helper.register_loc_text(LOC_DRAW_RESULT, "Draw {times} times from {deck_name}:\n{result}",
229+
bot.loc_helper.register_loc_text(LOC_DRAW_RESULT, "从{deck_name}中抽取{times}次:\n{result}",
230230
f"抽卡回复, times为次数, deck_name为牌库名, result由{LOC_DRAW_SINGLE}{LOC_DRAW_MULTI}定义")
231231
bot.loc_helper.register_loc_text(LOC_DRAW_RESULT_INLINE, "[Draw {times} times from {deck_name}:{result}]",
232232
f"嵌套抽卡内容(可能自我嵌套), times为次数, deck_name为牌库名, "
233233
f"result由{LOC_DRAW_SINGLE}{LOC_DRAW_MULTI}定义")
234234
bot.loc_helper.register_loc_text(LOC_DRAW_RESULT_DESIGN, "{result}","每次抽卡的最终结果的显示文本(不可能自我嵌套) result为结果原文, ")
235235
bot.loc_helper.register_loc_text(LOC_DRAW_SINGLE, "{content}", "只抽一次时的回复的内容(可能自我嵌套)")
236-
bot.loc_helper.register_loc_text(LOC_DRAW_MULTI, "Result {time}: {content}", "抽取多次时单条内容(可能自我嵌套), time为当前次数")
237-
bot.loc_helper.register_loc_text(LOC_DRAW_FIN_ALL, "Finalize draw! (All)", "抽到的内容使得所有抽取提前终止")
238-
bot.loc_helper.register_loc_text(LOC_DRAW_FIN_INNER, "Finalize draw! (Inner)", "抽到的内容使得内层抽取提前终止")
239-
bot.loc_helper.register_loc_text(LOC_DRAW_ERR_EMPTY_DECK, "Current decks is empty!", "牌库被抽光了(都是不放回的)")
240-
bot.loc_helper.register_loc_text(LOC_DRAW_ERR_TIME, "The draw time {times} is invalid!",
236+
bot.loc_helper.register_loc_text(LOC_DRAW_MULTI, "{time}次:{content}", "抽取多次时单条内容(可能自我嵌套), time为当前次数")
237+
bot.loc_helper.register_loc_text(LOC_DRAW_FIN_ALL, "抽取提前结束!(全部)", "抽到的内容使得所有抽取提前终止")
238+
bot.loc_helper.register_loc_text(LOC_DRAW_FIN_INNER, "抽取提前结束!(内层)", "抽到的内容使得内层抽取提前终止")
239+
bot.loc_helper.register_loc_text(LOC_DRAW_ERR_EMPTY_DECK, "当前牌库已抽空!", "牌库被抽光了(都是不放回的)")
240+
bot.loc_helper.register_loc_text(LOC_DRAW_ERR_TIME, "抽取次数{times}无效!",
241241
"抽取次数不是合法正整数或不是合法的掷骰表达式, time为识别到的次数")
242-
bot.loc_helper.register_loc_text(LOC_DRAW_ERR_NO_DECK, "Cannot find deck {deck_name}", "找不到想要抽取的牌库")
243-
bot.loc_helper.register_loc_text(LOC_DRAW_ERR_VAGUE_DECK, "Possible decks: {deck_list}", "找到多个可能的牌库")
242+
bot.loc_helper.register_loc_text(LOC_DRAW_ERR_NO_DECK, "找不到牌库{deck_name}", "找不到想要抽取的牌库")
243+
bot.loc_helper.register_loc_text(LOC_DRAW_ERR_VAGUE_DECK, "可能的牌库:{deck_list}", "找到多个可能的牌库")
244244

245245
bot.cfg_helper.register_config(CFG_DECK_ENABLE, "1", "抽卡指令开关")
246246
bot.cfg_helper.register_config(CFG_DECK_DATA_PATH, f"./{DRAW_DATA_PATH}", "牌库指令的数据来源, .代表Data文件夹")

0 commit comments

Comments
 (0)