mirror of
https://github.com/TriM-Organization/LiteyukiBot-TriM.git
synced 2026-06-14 00:22:26 +00:00
231 lines
6.8 KiB
Python
231 lines
6.8 KiB
Python
import json
|
|
import random
|
|
from io import BytesIO
|
|
from pathlib import Path
|
|
from typing import Dict, List, Tuple, TypedDict, Optional
|
|
|
|
# from watchdog.observers import Observer
|
|
# from watchdog.events import FileSystemEventHandler, FileModifiedEvent
|
|
|
|
from PIL import ImageFont
|
|
from PIL.Image import Image as IMG
|
|
from PIL.ImageFont import FreeTypeFont
|
|
from pypinyin import Style, pinyin as py_get_pinyin
|
|
|
|
resource_dir = Path(__file__).parent / "resources"
|
|
fonts_dir = resource_dir / "fonts"
|
|
data_dir = resource_dir / "data"
|
|
handle_common_idiom_path = data_dir / "common_idioms.json"
|
|
handle_all_idiom_path = data_dir / "idioms.json"
|
|
handle_answer_path = data_dir / "answers.json"
|
|
|
|
|
|
class IdiomEntry(TypedDict):
|
|
"""
|
|
成语答案类型
|
|
"""
|
|
|
|
explanation: str
|
|
pinyin: List[str]
|
|
|
|
|
|
HANDLE_COMMON_PHRASES: List[str] = json.load(
|
|
handle_common_idiom_path.open("r", encoding="utf-8")
|
|
)
|
|
HANDLE_LEGAL_PHRASES: List[str] = json.load(
|
|
handle_all_idiom_path.open("r", encoding="utf-8")
|
|
)
|
|
HANDLE_ANSWER_PHRASES: Dict[str, IdiomEntry] = json.load(
|
|
handle_answer_path.open("r", encoding="utf-8")
|
|
)
|
|
|
|
# class LegalPhrasesModifiedHandler(FileSystemEventHandler):
|
|
# """
|
|
# Handler for resource file changes
|
|
# """
|
|
|
|
# def on_modified(self, event):
|
|
# print(f"{event.src_path} modified, reloading resource...")
|
|
# if "idioms.txt" in event.src_path:
|
|
# global LEGAL_PHRASES
|
|
# LEGAL_PHRASES = [
|
|
# idiom.strip()
|
|
# for idiom in idiom_path.open("r", encoding="utf-8").readlines()
|
|
# ]
|
|
# elif "answers.json" in event.src_path:
|
|
# global ANSWER_PHRASES
|
|
# ANSWER_PHRASES = json.load(
|
|
# answer_path.open("r", encoding="utf-8")
|
|
# )
|
|
|
|
|
|
# Observer().schedule(
|
|
# LegalPhrasesModifiedHandler(),
|
|
# data_dir,
|
|
# recursive=False,
|
|
# event_filter=FileModifiedEvent,
|
|
# )
|
|
|
|
# 答案转换器
|
|
# json.dump({ i["word"]:{"explanation":i["explanation"], "pinyin":(lambda x : [i for j in pinyin(x, style=Style.TONE3, v_to_u=True) for i in j])(i["word"])} for i in json.load(open("answers_hard-old.json",encoding="utf-8"))},open("answers.json", "w",encoding="utf-8"), ensure_ascii=False, indent=4)
|
|
|
|
|
|
def wordbase_updater(
|
|
idiom: str,
|
|
explanation: Optional[str] = None,
|
|
pinyin: Optional[List[str]] = None,
|
|
hard: Optional[bool] = None,
|
|
) -> Tuple[bool, str, List[str]]:
|
|
"""
|
|
idiom: 需要更新或新增的成语词条
|
|
explanation: 词条的释义,若 `None` 且在词库中不存在,则默认为“未提供该成语的解释说明”
|
|
pinyin: 词条的拼音,若 `None` 则默认求出拼音
|
|
hard: 是否为难词,若 `None` 则不对难易词库修改
|
|
|
|
return: 是否为新增成语,成语释义,词条的拼音
|
|
"""
|
|
if (not idiom) or (len(idiom) != 4):
|
|
raise ValueError("不可以非四字成语载入词库")
|
|
|
|
if (existance := (idiom in HANDLE_LEGAL_PHRASES)) and (explanation is None):
|
|
# 这个判断的顺序必须高于下面的判断语句
|
|
explanation = HANDLE_ANSWER_PHRASES[idiom]["explanation"]
|
|
|
|
if explanation is None:
|
|
explanation = "未提供该成语的解释说明"
|
|
|
|
if not existance:
|
|
HANDLE_LEGAL_PHRASES.append(idiom)
|
|
json.dump(
|
|
HANDLE_LEGAL_PHRASES,
|
|
handle_all_idiom_path.open("w", encoding="utf-8"),
|
|
ensure_ascii=False,
|
|
indent=4,
|
|
sort_keys=True,
|
|
)
|
|
else:
|
|
pinyin = pinyin or HANDLE_ANSWER_PHRASES[idiom]["pinyin"].copy()
|
|
if hard:
|
|
if idiom in HANDLE_COMMON_PHRASES:
|
|
HANDLE_COMMON_PHRASES.remove(idiom)
|
|
json.dump(
|
|
HANDLE_COMMON_PHRASES,
|
|
handle_common_idiom_path.open("w", encoding="utf-8"),
|
|
ensure_ascii=False,
|
|
indent=4,
|
|
sort_keys=True,
|
|
)
|
|
else:
|
|
if (idiom not in HANDLE_COMMON_PHRASES) and (hard is not None):
|
|
HANDLE_COMMON_PHRASES.append(idiom)
|
|
json.dump(
|
|
HANDLE_COMMON_PHRASES,
|
|
handle_common_idiom_path.open("w", encoding="utf-8"),
|
|
ensure_ascii=False,
|
|
indent=4,
|
|
sort_keys=True,
|
|
)
|
|
|
|
HANDLE_ANSWER_PHRASES[idiom] = {
|
|
"explanation": explanation,
|
|
"pinyin": (
|
|
pinyin := (
|
|
pinyin
|
|
or [
|
|
j
|
|
for i in py_get_pinyin(idiom, style=Style.TONE3, v_to_u=True)
|
|
for j in i
|
|
]
|
|
)
|
|
),
|
|
}
|
|
json.dump(
|
|
HANDLE_ANSWER_PHRASES,
|
|
handle_answer_path.open("w", encoding="utf-8"),
|
|
ensure_ascii=False,
|
|
indent=4,
|
|
sort_keys=True,
|
|
)
|
|
return not existance, explanation, pinyin
|
|
|
|
|
|
def legal_idiom(word: str) -> bool:
|
|
return word in HANDLE_LEGAL_PHRASES
|
|
|
|
def random_idiom(is_hard: bool = False) -> Tuple[str, str]:
|
|
answer = random.choice(HANDLE_LEGAL_PHRASES if is_hard else HANDLE_COMMON_PHRASES)
|
|
return answer, HANDLE_ANSWER_PHRASES[answer]["explanation"]
|
|
|
|
|
|
# fmt: off
|
|
# 声母
|
|
INITIALS = [
|
|
"zh", "z", "y", "x", "w", "t", "sh", "s", "r", "q", "p",
|
|
"n", "m", "l", "k", "j", "h", "g", "f", "d", "ch", "c", "b"
|
|
]
|
|
# 韵母
|
|
FINALS = [
|
|
"ün", "üe", "üan", "ü", "uo", "un", "ui", "ue", "uang",
|
|
"uan", "uai","ua", "ou", "iu", "iong", "ong", "io", "ing",
|
|
"in", "ie", "iao", "iang", "ian", "ia", "er", "eng", "en",
|
|
"ei", "ao", "ang", "an", "ai", "u", "o", "i", "e", "a"
|
|
]
|
|
# fmt: on
|
|
|
|
|
|
def split_pinyin(py: str) -> Tuple[str, str, str]:
|
|
"""
|
|
py: 输入的拼音
|
|
return: 返回一个三元组,分别表示该拼音的声母,韵母,声调
|
|
"""
|
|
|
|
if py[-1].isdigit():
|
|
tone = py[-1]
|
|
py = py[:-1]
|
|
else:
|
|
tone = ""
|
|
initial = ""
|
|
for i in INITIALS:
|
|
if py.startswith(i):
|
|
initial = i
|
|
break
|
|
final = ""
|
|
for f in FINALS:
|
|
if py.endswith(f):
|
|
final = f
|
|
break
|
|
return initial, final, tone
|
|
|
|
|
|
def get_pinyin(
|
|
idiom: str,
|
|
) -> List[Tuple[str, str, str]]:
|
|
"""
|
|
idiom: 输入的汉字
|
|
return: 返回一个元组,每个元素是一个三元组,分别表示该汉字的声母,韵母,声调
|
|
"""
|
|
|
|
return [
|
|
split_pinyin(py)
|
|
for py in (
|
|
wordbase_updater(idiom, explanation=None, pinyin=None, hard=None)[2]
|
|
if HANDLE_ANSWER_PHRASES.get(idiom, None)
|
|
else [
|
|
j
|
|
for i in py_get_pinyin(idiom, style=Style.TONE3, v_to_u=True)
|
|
for j in i
|
|
]
|
|
)
|
|
]
|
|
|
|
|
|
def save_jpg(frame: IMG) -> BytesIO:
|
|
output = BytesIO()
|
|
frame = frame.convert("RGB")
|
|
frame.save(output, format="jpeg")
|
|
return output
|
|
|
|
|
|
def load_font(name: str, fontsize: int) -> FreeTypeFont:
|
|
return ImageFont.truetype(str(fonts_dir / name), fontsize, encoding="utf-8")
|