libs.utils.validator

libs/utils/validator.py

  1"""
  2libs/utils/validator.py
  3"""
  4
  5import re
  6from typing import Literal
  7
  8import libs.global_value as g
  9from cls.parser import CommandParser
 10from cls.score import GameResult
 11from cls.timekit import ExtendedDatetime as ExtDt
 12from libs.utils import formatter, textutil
 13
 14
 15def check_namepattern(name: str, kind: Literal["member", "team"]) -> tuple[bool, str]:
 16    """登録制限チェック
 17
 18    Args:
 19        name (str): チェックする名前
 20        kind (str): チェック種別
 21        - member
 22        - team
 23
 24    Returns:
 25        tuple[bool, str]: 判定結果
 26        - bool: 制限チェック結果真偽
 27        - str: 制限理由
 28    """
 29
 30    def _pattern_gen(check_list: list[str]) -> list[str]:
 31        ret: list = []
 32        for x in check_list:
 33            ret.append(x)
 34            ret.append(textutil.str_conv(x, "k2h"))  # ひらがな
 35            ret.append(textutil.str_conv(x, "h2k"))  # カタカナ
 36
 37        return list(set(ret))
 38
 39    check_pattern = _pattern_gen([name, formatter.honor_remove(name)])  # 入力パターン
 40    ret_flg: bool = True
 41    ret_msg: str = "OK"
 42
 43    # 名前チェック
 44    check_list = _pattern_gen(list(g.member_list.keys()))  # メンバーチェック
 45    if ret_flg and any(x in check_list for x in check_pattern):
 46        ret_flg, ret_msg = False, f"「{name}」は存在するメンバーです。"
 47
 48    check_list = _pattern_gen([x["team"] for x in g.team_list])  # チームチェック
 49    if ret_flg and any(x in check_list for x in check_pattern):
 50        ret_flg, ret_msg = False, f"「{name}」は存在するチームです。"
 51
 52    if ret_flg and g.cfg.member.guest_name in check_pattern:  # ゲストチェック
 53        ret_flg, ret_msg = False, "使用できない名前です。"
 54
 55    # 登録規定チェック
 56    if ret_flg and len(name) > int(getattr(g.cfg, kind).character_limit):  # 文字制限
 57        ret_flg, ret_msg = False, "登録可能文字数を超えています。"
 58
 59    if ret_flg and re.search("[\\;:<>(),!@#*?/`\"']", name) or not name.isprintable():  # 禁則記号
 60        ret_flg, ret_msg = False, "使用できない記号が含まれています。"
 61
 62    # 引数と同名になっていないかチェック
 63    if ret_flg and name in ExtDt.valid_keywords():
 64        ret_flg, ret_msg = False, "検索範囲指定に使用される単語では登録できません。"
 65
 66    if ret_flg and CommandParser().is_valid_command(name):
 67        ret_flg, ret_msg = False, "オプションに使用される単語では登録できません。"
 68
 69    if ret_flg and name in g.cfg.word_list():
 70        ret_flg, ret_msg = False, "コマンドに使用される単語では登録できません。"
 71
 72    return (ret_flg, ret_msg)
 73
 74
 75def pattern(text: str) -> GameResult:
 76    """成績記録用フォーマットチェック
 77
 78    Args:
 79        text (str): slackにポストされた内容
 80
 81    Returns:
 82        GameResult: スコアデータ
 83    """
 84
 85    # 記号を置換
 86    replace_chr = [
 87        (chr(0xff0b), "+"),  # 全角プラス符号
 88        (chr(0x2212), "-"),  # 全角マイナス符号
 89        (chr(0xff08), "("),  # 全角丸括弧
 90        (chr(0xff09), ")"),  # 全角丸括弧
 91        (chr(0x2017), "_"),  # DOUBLE LOW LINE(半角)
 92    ]
 93    for z, h in replace_chr:
 94        text = text.replace(z, h)
 95
 96    text = "".join(text.split())
 97
 98    # パターンマッチング
 99    pattern1 = re.compile(
100        rf"^({g.cfg.search.keyword})" + r"([^0-9()+-]+)([0-9+-]+)" * 4 + r"$"
101    )
102    pattern2 = re.compile(
103        r"^" + r"([^0-9()+-]+)([0-9+-]+)" * 4 + rf"({g.cfg.search.keyword})$"
104    )
105    pattern3 = re.compile(
106        rf"^({g.cfg.search.keyword})\((.+?)\)" + r"([^0-9()+-]+)([0-9+-]+)" * 4 + r"$"
107    )
108    pattern4 = re.compile(
109        r"^" + r"([^0-9()+-]+)([0-9+-]+)" * 4 + rf"({g.cfg.search.keyword})\((.+?)\)$"
110    )
111
112    # 情報取り出し
113    result = GameResult(rule_version=g.cfg.mahjong.rule_version)
114    position: dict[str, int] = {}
115    match text:
116        case text if pattern1.findall(text):
117            msg = pattern1.findall(text)[0]
118            position = {
119                "p1_name": 1, "p1_str": 2,
120                "p2_name": 3, "p2_str": 4,
121                "p3_name": 5, "p3_str": 6,
122                "p4_name": 7, "p4_str": 8,
123            }
124            comment = None
125        case text if pattern2.findall(text):
126            msg = pattern2.findall(text)[0]
127            position = {
128                "p1_name": 0, "p1_str": 1,
129                "p2_name": 2, "p2_str": 3,
130                "p3_name": 4, "p3_str": 5,
131                "p4_name": 6, "p4_str": 7,
132            }
133            comment = None
134        case text if pattern3.findall(text):
135            msg = pattern3.findall(text)[0]
136            position = {
137                "p1_name": 2, "p1_str": 3,
138                "p2_name": 4, "p2_str": 5,
139                "p3_name": 6, "p3_str": 7,
140                "p4_name": 8, "p4_str": 9,
141            }
142            comment = str(msg[1])
143        case text if pattern4.findall(text):
144            msg = pattern4.findall(text)[0]
145            position = {
146                "p1_name": 0, "p1_str": 1,
147                "p2_name": 2, "p2_str": 3,
148                "p3_name": 4, "p3_str": 5,
149                "p4_name": 6, "p4_str": 7,
150            }
151            comment = str(msg[9])
152        case _:
153            return result
154
155    g.params.update(unregistered_replace=False)  # ゲスト無効
156    g.params.update(individual=True)  # チーム戦オフ
157
158    result.set(comment=comment)
159    for k, p in position.items():
160        if str(k).endswith("_name"):
161            result.set(**{k: formatter.name_replace(str(msg[p]), False)})
162            continue
163        result.set(**{k: str(msg[p])})
164
165    return result
def check_namepattern(name: str, kind: Literal['member', 'team']) -> tuple[bool, str]:
16def check_namepattern(name: str, kind: Literal["member", "team"]) -> tuple[bool, str]:
17    """登録制限チェック
18
19    Args:
20        name (str): チェックする名前
21        kind (str): チェック種別
22        - member
23        - team
24
25    Returns:
26        tuple[bool, str]: 判定結果
27        - bool: 制限チェック結果真偽
28        - str: 制限理由
29    """
30
31    def _pattern_gen(check_list: list[str]) -> list[str]:
32        ret: list = []
33        for x in check_list:
34            ret.append(x)
35            ret.append(textutil.str_conv(x, "k2h"))  # ひらがな
36            ret.append(textutil.str_conv(x, "h2k"))  # カタカナ
37
38        return list(set(ret))
39
40    check_pattern = _pattern_gen([name, formatter.honor_remove(name)])  # 入力パターン
41    ret_flg: bool = True
42    ret_msg: str = "OK"
43
44    # 名前チェック
45    check_list = _pattern_gen(list(g.member_list.keys()))  # メンバーチェック
46    if ret_flg and any(x in check_list for x in check_pattern):
47        ret_flg, ret_msg = False, f"「{name}」は存在するメンバーです。"
48
49    check_list = _pattern_gen([x["team"] for x in g.team_list])  # チームチェック
50    if ret_flg and any(x in check_list for x in check_pattern):
51        ret_flg, ret_msg = False, f"「{name}」は存在するチームです。"
52
53    if ret_flg and g.cfg.member.guest_name in check_pattern:  # ゲストチェック
54        ret_flg, ret_msg = False, "使用できない名前です。"
55
56    # 登録規定チェック
57    if ret_flg and len(name) > int(getattr(g.cfg, kind).character_limit):  # 文字制限
58        ret_flg, ret_msg = False, "登録可能文字数を超えています。"
59
60    if ret_flg and re.search("[\\;:<>(),!@#*?/`\"']", name) or not name.isprintable():  # 禁則記号
61        ret_flg, ret_msg = False, "使用できない記号が含まれています。"
62
63    # 引数と同名になっていないかチェック
64    if ret_flg and name in ExtDt.valid_keywords():
65        ret_flg, ret_msg = False, "検索範囲指定に使用される単語では登録できません。"
66
67    if ret_flg and CommandParser().is_valid_command(name):
68        ret_flg, ret_msg = False, "オプションに使用される単語では登録できません。"
69
70    if ret_flg and name in g.cfg.word_list():
71        ret_flg, ret_msg = False, "コマンドに使用される単語では登録できません。"
72
73    return (ret_flg, ret_msg)

登録制限チェック

Arguments:
  • name (str): チェックする名前
  • kind (str): チェック種別
    • member
    • team

Returns:

tuple[bool, str]: 判定結果

  • bool: 制限チェック結果真偽
  • str: 制限理由
def pattern(text: str) -> cls.score.GameResult:
 76def pattern(text: str) -> GameResult:
 77    """成績記録用フォーマットチェック
 78
 79    Args:
 80        text (str): slackにポストされた内容
 81
 82    Returns:
 83        GameResult: スコアデータ
 84    """
 85
 86    # 記号を置換
 87    replace_chr = [
 88        (chr(0xff0b), "+"),  # 全角プラス符号
 89        (chr(0x2212), "-"),  # 全角マイナス符号
 90        (chr(0xff08), "("),  # 全角丸括弧
 91        (chr(0xff09), ")"),  # 全角丸括弧
 92        (chr(0x2017), "_"),  # DOUBLE LOW LINE(半角)
 93    ]
 94    for z, h in replace_chr:
 95        text = text.replace(z, h)
 96
 97    text = "".join(text.split())
 98
 99    # パターンマッチング
100    pattern1 = re.compile(
101        rf"^({g.cfg.search.keyword})" + r"([^0-9()+-]+)([0-9+-]+)" * 4 + r"$"
102    )
103    pattern2 = re.compile(
104        r"^" + r"([^0-9()+-]+)([0-9+-]+)" * 4 + rf"({g.cfg.search.keyword})$"
105    )
106    pattern3 = re.compile(
107        rf"^({g.cfg.search.keyword})\((.+?)\)" + r"([^0-9()+-]+)([0-9+-]+)" * 4 + r"$"
108    )
109    pattern4 = re.compile(
110        r"^" + r"([^0-9()+-]+)([0-9+-]+)" * 4 + rf"({g.cfg.search.keyword})\((.+?)\)$"
111    )
112
113    # 情報取り出し
114    result = GameResult(rule_version=g.cfg.mahjong.rule_version)
115    position: dict[str, int] = {}
116    match text:
117        case text if pattern1.findall(text):
118            msg = pattern1.findall(text)[0]
119            position = {
120                "p1_name": 1, "p1_str": 2,
121                "p2_name": 3, "p2_str": 4,
122                "p3_name": 5, "p3_str": 6,
123                "p4_name": 7, "p4_str": 8,
124            }
125            comment = None
126        case text if pattern2.findall(text):
127            msg = pattern2.findall(text)[0]
128            position = {
129                "p1_name": 0, "p1_str": 1,
130                "p2_name": 2, "p2_str": 3,
131                "p3_name": 4, "p3_str": 5,
132                "p4_name": 6, "p4_str": 7,
133            }
134            comment = None
135        case text if pattern3.findall(text):
136            msg = pattern3.findall(text)[0]
137            position = {
138                "p1_name": 2, "p1_str": 3,
139                "p2_name": 4, "p2_str": 5,
140                "p3_name": 6, "p3_str": 7,
141                "p4_name": 8, "p4_str": 9,
142            }
143            comment = str(msg[1])
144        case text if pattern4.findall(text):
145            msg = pattern4.findall(text)[0]
146            position = {
147                "p1_name": 0, "p1_str": 1,
148                "p2_name": 2, "p2_str": 3,
149                "p3_name": 4, "p3_str": 5,
150                "p4_name": 6, "p4_str": 7,
151            }
152            comment = str(msg[9])
153        case _:
154            return result
155
156    g.params.update(unregistered_replace=False)  # ゲスト無効
157    g.params.update(individual=True)  # チーム戦オフ
158
159    result.set(comment=comment)
160    for k, p in position.items():
161        if str(k).endswith("_name"):
162            result.set(**{k: formatter.name_replace(str(msg[p]), False)})
163            continue
164        result.set(**{k: str(msg[p])})
165
166    return result

成績記録用フォーマットチェック

Arguments:
  • text (str): slackにポストされた内容
Returns:

GameResult: スコアデータ