libs.bootstrap.configuration

libs/bootstrap/configuration.py

  1"""
  2libs/bootstrap/configuration.py
  3"""
  4
  5import argparse
  6import logging
  7import os
  8import shutil
  9import sys
 10from functools import partial
 11from pathlib import Path
 12from typing import TYPE_CHECKING, Any, cast
 13
 14import libs.commands.graph.entry
 15import libs.commands.help.entry
 16import libs.commands.ranking.entry
 17import libs.commands.report.entry
 18import libs.commands.results.entry
 19import libs.global_value as g
 20from integrations import factory
 21from libs.bootstrap import initialization
 22from libs.bootstrap.app_config import AppConfig
 23from libs.commands.registry import member, team
 24from libs.domain.datamodels import Args
 25from libs.functions import lookup
 26from libs.functions.compose import text_item
 27from libs.types import ServiceType, StyleOptions
 28
 29if TYPE_CHECKING:
 30    from integrations.protocols import MessageParserProtocol
 31    from libs.bootstrap.section import SubCommands
 32
 33
 34def set_loglevel() -> None:
 35    """ログレベル追加"""
 36    # DEBUG : 10
 37    # INFO : 20
 38    # WARNING : 30
 39    # ERROR : 40
 40    # CRITICAL : 50
 41
 42    # TRACE
 43    logging.TRACE = 5  # type: ignore
 44    logging.trace = partial(logging.log, logging.TRACE)  # type: ignore
 45    logging.addLevelName(logging.TRACE, "TRACE")  # type: ignore
 46
 47
 48def arg_parser() -> Args:
 49    """
 50    コマンドライン解析
 51
 52    Returns:
 53        Args : ArgumentParserオブジェクト
 54
 55    """
 56    prog_name = os.path.basename(sys.argv[0])
 57    p = argparse.ArgumentParser(
 58        formatter_class=argparse.RawTextHelpFormatter,
 59        add_help=True,
 60        allow_abbrev=False,
 61    )
 62
 63    p.add_argument(
 64        "-c",
 65        "--config",
 66        type=Path,
 67        default=Path("config.ini"),
 68        help="設定ファイル(default: %(default)s)",
 69    )
 70    p.add_argument(
 71        "--no-cleanup",
 72        dest="no_cleanup",
 73        action="store_false",
 74        help="作業ディレクトリの内容を削除しない",
 75    )
 76    p.add_argument(
 77        "-s",
 78        "--service",
 79        choices=[
 80            "slack",
 81            "discord",
 82            "standard_io",
 83            "std",
 84            "web",
 85            "flask",
 86        ],
 87        type=str,
 88        default="slack",
 89        help="連携先サービス",
 90    )
 91
 92    logging_group = p.add_argument_group("logging options")
 93    logging_group.add_argument(
 94        "-d",
 95        "--debug",
 96        action="count",
 97        default=0,
 98        help="デバッグレベル(-d, -dd)",
 99    )
100    logging_group.add_argument(
101        "-v",
102        "--verbose",
103        action="count",
104        default=0,
105        help="動作ログ出力レベル(-v, -vv, -vvv)",
106    )
107    logging_group.add_argument(
108        "--moderate",
109        action="store_true",
110        help="ログレベルがエラー以下のもを非表示",
111    )
112    logging_group.add_argument(
113        "--notime",
114        action="store_true",
115        help="ログフォーマットから日時を削除",
116    )
117
118    match prog_name:
119        case "app.py":
120            service_stdio = p.add_argument_group("Only allowed when --service=standard_io")
121            service_stdio.add_argument(
122                "--text",
123                type=str,
124                help="input text strings",
125            )
126            service_web = p.add_argument_group("Only allowed when --service=web")
127            service_web.add_argument(
128                "--host",
129                type=str,
130                default="127.0.0.1",
131                help="listen  address(default: %(default)s)",
132            )
133            service_web.add_argument(
134                "--port",
135                type=int,
136                default=8000,
137                help="bind port(default: %(default)s)",
138            )
139        case "dbtools.py":  # dbtools専用オプション
140            required = p.add_argument_group("Required options(amutually exclusive)")
141            exclusive = required.add_mutually_exclusive_group()
142            exclusive.add_argument(
143                "--compar",
144                action="store_true",
145                help="データ突合",
146            )
147            exclusive.add_argument(
148                "--unification",
149                type=Path,
150                nargs="?",
151                const="rename.ini",
152                help="ファイルの内容に従って記録済みのメンバー名を修正する(default: %(const)s)",
153            )
154            exclusive.add_argument(
155                "--recalculation",
156                action="store_true",
157                help="ポイント再計算",
158            )
159            exclusive.add_argument(
160                "--export",
161                dest="export_data",
162                type=str,
163                nargs="?",
164                const="export",
165                metavar="PREFIX",
166                help="メンバー設定情報をエクスポート(default prefix: %(const)s)",
167            )
168            exclusive.add_argument(
169                "--import",
170                dest="import_data",
171                type=str,
172                nargs="?",
173                const="export",
174                metavar="PREFIX",
175                help="メンバー設定情報をインポート(default prefix: %(const)s)",
176            )
177            exclusive.add_argument(
178                "--vacuum",
179                action="store_true",
180                help="database vacuum",
181            )
182            exclusive.add_argument(
183                "--gen-test-data",
184                type=int,
185                dest="gen_test_data",
186                nargs="?",
187                const=1,
188                default=None,
189                metavar="count",
190                help="テスト用サンプルデータ生成(count=生成回数, default: %(const)s)",
191            )
192        case "test.py":  # 動作テスト用オプション
193            p.add_argument(
194                "-t",
195                "--testcase",
196                dest="testcase",
197                type=Path,
198            )
199
200    args, unknown = p.parse_known_args()
201    if unknown and prog_name in ["app.py", "dbtools.py"]:
202        p.print_usage()
203        sys.exit(f"\ninvalid args: {unknown}")
204
205    return cast(Args, args)
206
207
208def setup(init_db: bool = True) -> None:
209    """
210    設定ファイル読み込み処理
211
212    Args:
213        init_db (bool, optional): resultdbの初期化処理を行う Defaults to True.
214
215    """
216    set_loglevel()
217
218    g.args = arg_parser()
219
220    # ログフォーマット
221    if g.args.notime:
222        fmt = ""
223    else:
224        fmt = "[%(asctime)s]"
225
226    # デバッグレベル
227    match g.args.debug:
228        case 1:
229            fmt += "[%(levelname)s][%(module)s:%(funcName)s] %(message)s"
230            logging.basicConfig(level=logging.DEBUG, format=fmt)
231            logging.info("DEBUG MODE")
232        case 2:
233            fmt += "[%(levelname)s][%(module)s:%(funcName)s] %(message)s"
234            logging.basicConfig(level=logging.TRACE, format=fmt)  # type: ignore
235            logging.info("TRACE MODE")
236        case _:
237            fmt += "[%(levelname)s][%(module)s:%(funcName)s] %(message)s"
238            if g.args.moderate:
239                logging.basicConfig(level=logging.WARNING, format=fmt)
240            else:
241                logging.basicConfig(level=logging.INFO, format=fmt)
242
243    g.cfg = AppConfig(g.args.config)
244
245    # 連携サービス
246    match g.args.service:
247        case "slack":
248            g.cfg.selected_service = ServiceType.SLACK
249        case "discord":
250            g.cfg.selected_service = ServiceType.DISCORD
251        case "standard_io" | "std":
252            g.cfg.selected_service = ServiceType.STANDARD_IO
253        case "web" | "flask":
254            g.cfg.selected_service = ServiceType.WEB
255        case _:
256            sys.exit()
257
258    if not hasattr(g.args, "testcase"):
259        g.args.testcase = None
260    else:
261        g.cfg.selected_service = ServiceType.STANDARD_IO
262
263    g.adapter = factory.select_adapter(g.cfg.selected_service, g.cfg)
264
265    # ディレクトリ作成
266    if not g.args.testcase:
267        if g.cfg.setting.work_dir.is_dir() and g.args.no_cleanup:
268            shutil.rmtree(g.cfg.setting.work_dir)
269    try:
270        g.cfg.setting.work_dir.mkdir(exist_ok=True)
271    except FileExistsError as err:
272        sys.exit(str(err))
273
274    if isinstance(g.cfg.setting.backup_dir, Path):
275        try:
276            g.cfg.setting.backup_dir.mkdir(exist_ok=True)
277        except FileExistsError as err:
278            sys.exit(str(err))
279
280    # 初期化
281    initialization.main(init_db)
282    lookup.read_memberslist()
283
284    register()
285
286    # キーワード重複チェック
287    g.cfg.rule.check(
288        chk_commands=set(
289            g.cfg.results.commandword
290            + g.cfg.graph.commandword
291            + g.cfg.ranking.commandword
292            + g.cfg.report.commandword
293            + g.cfg.help.commandword
294            + g.cfg.rule.remarks_words
295            + list(g.keyword_dispatcher)
296        ),
297        chk_members=set(lookup.enumeration_all_members()),
298        default_rule=g.cfg.setting.default_rule,
299    )
300
301    # 設定情報
302    logging.info("main_config: %s", g.cfg.config_file.absolute())
303    if g.cfg.setting.rule_config:
304        logging.info("rule_config: %s", g.cfg.setting.rule_config.absolute())
305    if isinstance(g.cfg.setting.database_file, Path):
306        logging.info("resultdb: %s", g.cfg.setting.database_file.absolute())
307    logging.info(
308        "service: %s, graph_library: %s, time_adjust: %sh",
309        g.cfg.selected_service,
310        g.adapter.conf.plotting_backend,
311        g.cfg.setting.time_adjust,
312    )
313
314    g.cfg.rule.info()
315
316    drop_items = ["section", "default_commandword", "command_suffix", "main_parser", "section_proxy", "info"]
317    logging.debug("setting: %s", g.cfg.setting.to_dict(drop_items))
318    logging.debug("member: %s", g.cfg.member.to_dict(drop_items))
319    logging.debug("team: %s", g.cfg.team.to_dict(drop_items))
320    logging.debug("results: %s", g.cfg.results.to_dict(drop_items))
321    logging.debug("graph: %s", g.cfg.graph.to_dict(drop_items))
322    logging.debug("ranking: %s", g.cfg.ranking.to_dict(drop_items))
323    logging.debug("report: %s", g.cfg.report.to_dict(drop_items))
324    logging.debug("help: %s", g.cfg.help.to_dict(drop_items))
325    logging.debug("rule_set: %s", vars(g.cfg.rule))
326
327
328def register() -> None:
329    """ディスパッチテーブル登録"""
330
331    def dispatch_download(m: "MessageParserProtocol") -> None:
332        m.set_message(g.cfg.setting.database_file, StyleOptions(title="成績記録DB"))
333
334    def dispatch_members_list(m: "MessageParserProtocol") -> None:
335        m.set_message(text_item.get_members_list(), StyleOptions(title="登録済みメンバー", codeblock=True))
336        m.post.ts = m.data.event_ts
337        m.post.thread_title = "登録済みメンバー"
338
339    def dispatch_team_list(m: "MessageParserProtocol") -> None:
340        m.set_message(text_item.get_team_list(), StyleOptions(title="登録済みチーム", codeblock=True))
341        m.post.ts = m.data.event_ts
342        m.post.thread_title = "登録済みチーム"
343
344    def dispatch_member_append(m: "MessageParserProtocol") -> None:
345        m.set_message(member.append(m.argument), StyleOptions(title="メンバー追加", key_title=False))
346
347    def dispatch_member_remove(m: "MessageParserProtocol") -> None:
348        m.set_message(member.remove(m.argument), StyleOptions(title="メンバー削除", key_title=False))
349
350    def dispatch_team_create(m: "MessageParserProtocol") -> None:
351        m.set_message(team.create(m.argument), StyleOptions(title="チーム作成", key_title=False))
352
353    def dispatch_team_delete(m: "MessageParserProtocol") -> None:
354        m.set_message(team.delete(m.argument), StyleOptions(title="チーム削除", key_title=False))
355
356    def dispatch_team_append(m: "MessageParserProtocol") -> None:
357        m.set_message(team.append(m.argument), StyleOptions(title="チーム所属", key_title=False))
358
359    def dispatch_team_remove(m: "MessageParserProtocol") -> None:
360        m.set_message(team.remove(m.argument), StyleOptions(title="チーム脱退", key_title=False))
361
362    def dispatch_team_clear(m: "MessageParserProtocol") -> None:
363        m.set_message(team.clear(), StyleOptions(title="全チーム削除", key_title=False))
364
365    dispatch_table: dict[str, Any] = {
366        "results": libs.commands.results.entry.main,
367        "graph": libs.commands.graph.entry.main,
368        "ranking": libs.commands.ranking.entry.main,
369        "report": libs.commands.report.entry.main,
370        "help": libs.commands.help.entry.main,
371        "member": dispatch_members_list,
372        "team": dispatch_team_list,
373        "team_list": dispatch_team_list,
374        "download": dispatch_download,
375        "add": dispatch_member_append,
376        "delete": dispatch_member_remove,
377        "team_create": dispatch_team_create,
378        "team_del": dispatch_team_delete,
379        "team_add": dispatch_team_append,
380        "team_remove": dispatch_team_remove,
381        "team_clear": dispatch_team_clear,
382    }
383
384    commandword_list: list[str]
385    for command, ep in dispatch_table.items():
386        # 呼び出しキーワード登録
387        if hasattr(g.cfg, command):
388            commandword_list = []
389            sub_command = cast("SubCommands", getattr(g.cfg, command))
390            if not any([sub_command.commandword, sub_command.command_suffix]):  # 何も設定されていない
391                commandword_list.append(sub_command.default_commandword)
392            elif sub_command.commandword:
393                commandword_list.extend(sub_command.commandword)
394            elif sub_command.command_suffix:  # コマンドサフィックス登録
395                for rule_version in g.cfg.rule.rule_list:
396                    commandword_list.extend(
397                        [f"{prefix}{suffix}" for prefix in g.cfg.rule.keywords(rule_version) for suffix in sub_command.command_suffix],
398                    )
399            sub_command.commandword = commandword_list
400            for commandword in commandword_list:
401                g.keyword_dispatcher.update({commandword: ep})
402        # スラッシュコマンド登録
403        if hasattr(g.cfg.alias, command):
404            for alias in cast(list[str], getattr(g.cfg.alias, command)):
405                g.command_dispatcher.update({alias: ep})
406
407    # サービス別コマンド登録
408    g.command_dispatcher.update(g.adapter.conf.command_dispatcher)
409    g.keyword_dispatcher.update(g.adapter.conf.keyword_dispatcher)
410
411    logging.debug("keyword_dispatcher:\n%s", "\n".join([f"\t{k}: {v}" for k, v in g.keyword_dispatcher.items()]))
412    logging.debug("command_dispatcher:\n%s", "\n".join([f"\t{k}: {v}" for k, v in g.command_dispatcher.items()]))
def set_loglevel() -> None:
35def set_loglevel() -> None:
36    """ログレベル追加"""
37    # DEBUG : 10
38    # INFO : 20
39    # WARNING : 30
40    # ERROR : 40
41    # CRITICAL : 50
42
43    # TRACE
44    logging.TRACE = 5  # type: ignore
45    logging.trace = partial(logging.log, logging.TRACE)  # type: ignore
46    logging.addLevelName(logging.TRACE, "TRACE")  # type: ignore

ログレベル追加

def arg_parser() -> libs.domain.datamodels.Args:
 49def arg_parser() -> Args:
 50    """
 51    コマンドライン解析
 52
 53    Returns:
 54        Args : ArgumentParserオブジェクト
 55
 56    """
 57    prog_name = os.path.basename(sys.argv[0])
 58    p = argparse.ArgumentParser(
 59        formatter_class=argparse.RawTextHelpFormatter,
 60        add_help=True,
 61        allow_abbrev=False,
 62    )
 63
 64    p.add_argument(
 65        "-c",
 66        "--config",
 67        type=Path,
 68        default=Path("config.ini"),
 69        help="設定ファイル(default: %(default)s)",
 70    )
 71    p.add_argument(
 72        "--no-cleanup",
 73        dest="no_cleanup",
 74        action="store_false",
 75        help="作業ディレクトリの内容を削除しない",
 76    )
 77    p.add_argument(
 78        "-s",
 79        "--service",
 80        choices=[
 81            "slack",
 82            "discord",
 83            "standard_io",
 84            "std",
 85            "web",
 86            "flask",
 87        ],
 88        type=str,
 89        default="slack",
 90        help="連携先サービス",
 91    )
 92
 93    logging_group = p.add_argument_group("logging options")
 94    logging_group.add_argument(
 95        "-d",
 96        "--debug",
 97        action="count",
 98        default=0,
 99        help="デバッグレベル(-d, -dd)",
100    )
101    logging_group.add_argument(
102        "-v",
103        "--verbose",
104        action="count",
105        default=0,
106        help="動作ログ出力レベル(-v, -vv, -vvv)",
107    )
108    logging_group.add_argument(
109        "--moderate",
110        action="store_true",
111        help="ログレベルがエラー以下のもを非表示",
112    )
113    logging_group.add_argument(
114        "--notime",
115        action="store_true",
116        help="ログフォーマットから日時を削除",
117    )
118
119    match prog_name:
120        case "app.py":
121            service_stdio = p.add_argument_group("Only allowed when --service=standard_io")
122            service_stdio.add_argument(
123                "--text",
124                type=str,
125                help="input text strings",
126            )
127            service_web = p.add_argument_group("Only allowed when --service=web")
128            service_web.add_argument(
129                "--host",
130                type=str,
131                default="127.0.0.1",
132                help="listen  address(default: %(default)s)",
133            )
134            service_web.add_argument(
135                "--port",
136                type=int,
137                default=8000,
138                help="bind port(default: %(default)s)",
139            )
140        case "dbtools.py":  # dbtools専用オプション
141            required = p.add_argument_group("Required options(amutually exclusive)")
142            exclusive = required.add_mutually_exclusive_group()
143            exclusive.add_argument(
144                "--compar",
145                action="store_true",
146                help="データ突合",
147            )
148            exclusive.add_argument(
149                "--unification",
150                type=Path,
151                nargs="?",
152                const="rename.ini",
153                help="ファイルの内容に従って記録済みのメンバー名を修正する(default: %(const)s)",
154            )
155            exclusive.add_argument(
156                "--recalculation",
157                action="store_true",
158                help="ポイント再計算",
159            )
160            exclusive.add_argument(
161                "--export",
162                dest="export_data",
163                type=str,
164                nargs="?",
165                const="export",
166                metavar="PREFIX",
167                help="メンバー設定情報をエクスポート(default prefix: %(const)s)",
168            )
169            exclusive.add_argument(
170                "--import",
171                dest="import_data",
172                type=str,
173                nargs="?",
174                const="export",
175                metavar="PREFIX",
176                help="メンバー設定情報をインポート(default prefix: %(const)s)",
177            )
178            exclusive.add_argument(
179                "--vacuum",
180                action="store_true",
181                help="database vacuum",
182            )
183            exclusive.add_argument(
184                "--gen-test-data",
185                type=int,
186                dest="gen_test_data",
187                nargs="?",
188                const=1,
189                default=None,
190                metavar="count",
191                help="テスト用サンプルデータ生成(count=生成回数, default: %(const)s)",
192            )
193        case "test.py":  # 動作テスト用オプション
194            p.add_argument(
195                "-t",
196                "--testcase",
197                dest="testcase",
198                type=Path,
199            )
200
201    args, unknown = p.parse_known_args()
202    if unknown and prog_name in ["app.py", "dbtools.py"]:
203        p.print_usage()
204        sys.exit(f"\ninvalid args: {unknown}")
205
206    return cast(Args, args)

コマンドライン解析

Returns:

Args : ArgumentParserオブジェクト

def setup(init_db: bool = True) -> None:
209def setup(init_db: bool = True) -> None:
210    """
211    設定ファイル読み込み処理
212
213    Args:
214        init_db (bool, optional): resultdbの初期化処理を行う Defaults to True.
215
216    """
217    set_loglevel()
218
219    g.args = arg_parser()
220
221    # ログフォーマット
222    if g.args.notime:
223        fmt = ""
224    else:
225        fmt = "[%(asctime)s]"
226
227    # デバッグレベル
228    match g.args.debug:
229        case 1:
230            fmt += "[%(levelname)s][%(module)s:%(funcName)s] %(message)s"
231            logging.basicConfig(level=logging.DEBUG, format=fmt)
232            logging.info("DEBUG MODE")
233        case 2:
234            fmt += "[%(levelname)s][%(module)s:%(funcName)s] %(message)s"
235            logging.basicConfig(level=logging.TRACE, format=fmt)  # type: ignore
236            logging.info("TRACE MODE")
237        case _:
238            fmt += "[%(levelname)s][%(module)s:%(funcName)s] %(message)s"
239            if g.args.moderate:
240                logging.basicConfig(level=logging.WARNING, format=fmt)
241            else:
242                logging.basicConfig(level=logging.INFO, format=fmt)
243
244    g.cfg = AppConfig(g.args.config)
245
246    # 連携サービス
247    match g.args.service:
248        case "slack":
249            g.cfg.selected_service = ServiceType.SLACK
250        case "discord":
251            g.cfg.selected_service = ServiceType.DISCORD
252        case "standard_io" | "std":
253            g.cfg.selected_service = ServiceType.STANDARD_IO
254        case "web" | "flask":
255            g.cfg.selected_service = ServiceType.WEB
256        case _:
257            sys.exit()
258
259    if not hasattr(g.args, "testcase"):
260        g.args.testcase = None
261    else:
262        g.cfg.selected_service = ServiceType.STANDARD_IO
263
264    g.adapter = factory.select_adapter(g.cfg.selected_service, g.cfg)
265
266    # ディレクトリ作成
267    if not g.args.testcase:
268        if g.cfg.setting.work_dir.is_dir() and g.args.no_cleanup:
269            shutil.rmtree(g.cfg.setting.work_dir)
270    try:
271        g.cfg.setting.work_dir.mkdir(exist_ok=True)
272    except FileExistsError as err:
273        sys.exit(str(err))
274
275    if isinstance(g.cfg.setting.backup_dir, Path):
276        try:
277            g.cfg.setting.backup_dir.mkdir(exist_ok=True)
278        except FileExistsError as err:
279            sys.exit(str(err))
280
281    # 初期化
282    initialization.main(init_db)
283    lookup.read_memberslist()
284
285    register()
286
287    # キーワード重複チェック
288    g.cfg.rule.check(
289        chk_commands=set(
290            g.cfg.results.commandword
291            + g.cfg.graph.commandword
292            + g.cfg.ranking.commandword
293            + g.cfg.report.commandword
294            + g.cfg.help.commandword
295            + g.cfg.rule.remarks_words
296            + list(g.keyword_dispatcher)
297        ),
298        chk_members=set(lookup.enumeration_all_members()),
299        default_rule=g.cfg.setting.default_rule,
300    )
301
302    # 設定情報
303    logging.info("main_config: %s", g.cfg.config_file.absolute())
304    if g.cfg.setting.rule_config:
305        logging.info("rule_config: %s", g.cfg.setting.rule_config.absolute())
306    if isinstance(g.cfg.setting.database_file, Path):
307        logging.info("resultdb: %s", g.cfg.setting.database_file.absolute())
308    logging.info(
309        "service: %s, graph_library: %s, time_adjust: %sh",
310        g.cfg.selected_service,
311        g.adapter.conf.plotting_backend,
312        g.cfg.setting.time_adjust,
313    )
314
315    g.cfg.rule.info()
316
317    drop_items = ["section", "default_commandword", "command_suffix", "main_parser", "section_proxy", "info"]
318    logging.debug("setting: %s", g.cfg.setting.to_dict(drop_items))
319    logging.debug("member: %s", g.cfg.member.to_dict(drop_items))
320    logging.debug("team: %s", g.cfg.team.to_dict(drop_items))
321    logging.debug("results: %s", g.cfg.results.to_dict(drop_items))
322    logging.debug("graph: %s", g.cfg.graph.to_dict(drop_items))
323    logging.debug("ranking: %s", g.cfg.ranking.to_dict(drop_items))
324    logging.debug("report: %s", g.cfg.report.to_dict(drop_items))
325    logging.debug("help: %s", g.cfg.help.to_dict(drop_items))
326    logging.debug("rule_set: %s", vars(g.cfg.rule))

設定ファイル読み込み処理

Arguments:
  • init_db (bool, optional): resultdbの初期化処理を行う Defaults to True.
def register() -> None:
329def register() -> None:
330    """ディスパッチテーブル登録"""
331
332    def dispatch_download(m: "MessageParserProtocol") -> None:
333        m.set_message(g.cfg.setting.database_file, StyleOptions(title="成績記録DB"))
334
335    def dispatch_members_list(m: "MessageParserProtocol") -> None:
336        m.set_message(text_item.get_members_list(), StyleOptions(title="登録済みメンバー", codeblock=True))
337        m.post.ts = m.data.event_ts
338        m.post.thread_title = "登録済みメンバー"
339
340    def dispatch_team_list(m: "MessageParserProtocol") -> None:
341        m.set_message(text_item.get_team_list(), StyleOptions(title="登録済みチーム", codeblock=True))
342        m.post.ts = m.data.event_ts
343        m.post.thread_title = "登録済みチーム"
344
345    def dispatch_member_append(m: "MessageParserProtocol") -> None:
346        m.set_message(member.append(m.argument), StyleOptions(title="メンバー追加", key_title=False))
347
348    def dispatch_member_remove(m: "MessageParserProtocol") -> None:
349        m.set_message(member.remove(m.argument), StyleOptions(title="メンバー削除", key_title=False))
350
351    def dispatch_team_create(m: "MessageParserProtocol") -> None:
352        m.set_message(team.create(m.argument), StyleOptions(title="チーム作成", key_title=False))
353
354    def dispatch_team_delete(m: "MessageParserProtocol") -> None:
355        m.set_message(team.delete(m.argument), StyleOptions(title="チーム削除", key_title=False))
356
357    def dispatch_team_append(m: "MessageParserProtocol") -> None:
358        m.set_message(team.append(m.argument), StyleOptions(title="チーム所属", key_title=False))
359
360    def dispatch_team_remove(m: "MessageParserProtocol") -> None:
361        m.set_message(team.remove(m.argument), StyleOptions(title="チーム脱退", key_title=False))
362
363    def dispatch_team_clear(m: "MessageParserProtocol") -> None:
364        m.set_message(team.clear(), StyleOptions(title="全チーム削除", key_title=False))
365
366    dispatch_table: dict[str, Any] = {
367        "results": libs.commands.results.entry.main,
368        "graph": libs.commands.graph.entry.main,
369        "ranking": libs.commands.ranking.entry.main,
370        "report": libs.commands.report.entry.main,
371        "help": libs.commands.help.entry.main,
372        "member": dispatch_members_list,
373        "team": dispatch_team_list,
374        "team_list": dispatch_team_list,
375        "download": dispatch_download,
376        "add": dispatch_member_append,
377        "delete": dispatch_member_remove,
378        "team_create": dispatch_team_create,
379        "team_del": dispatch_team_delete,
380        "team_add": dispatch_team_append,
381        "team_remove": dispatch_team_remove,
382        "team_clear": dispatch_team_clear,
383    }
384
385    commandword_list: list[str]
386    for command, ep in dispatch_table.items():
387        # 呼び出しキーワード登録
388        if hasattr(g.cfg, command):
389            commandword_list = []
390            sub_command = cast("SubCommands", getattr(g.cfg, command))
391            if not any([sub_command.commandword, sub_command.command_suffix]):  # 何も設定されていない
392                commandword_list.append(sub_command.default_commandword)
393            elif sub_command.commandword:
394                commandword_list.extend(sub_command.commandword)
395            elif sub_command.command_suffix:  # コマンドサフィックス登録
396                for rule_version in g.cfg.rule.rule_list:
397                    commandword_list.extend(
398                        [f"{prefix}{suffix}" for prefix in g.cfg.rule.keywords(rule_version) for suffix in sub_command.command_suffix],
399                    )
400            sub_command.commandword = commandword_list
401            for commandword in commandword_list:
402                g.keyword_dispatcher.update({commandword: ep})
403        # スラッシュコマンド登録
404        if hasattr(g.cfg.alias, command):
405            for alias in cast(list[str], getattr(g.cfg.alias, command)):
406                g.command_dispatcher.update({alias: ep})
407
408    # サービス別コマンド登録
409    g.command_dispatcher.update(g.adapter.conf.command_dispatcher)
410    g.keyword_dispatcher.update(g.adapter.conf.keyword_dispatcher)
411
412    logging.debug("keyword_dispatcher:\n%s", "\n".join([f"\t{k}: {v}" for k, v in g.keyword_dispatcher.items()]))
413    logging.debug("command_dispatcher:\n%s", "\n".join([f"\t{k}: {v}" for k, v in g.command_dispatcher.items()]))

ディスパッチテーブル登録