integrations.protocols

integrations/protocols.py

  1"""
  2integrations/protocols.py
  3"""
  4
  5from dataclasses import dataclass, field, fields, is_dataclass
  6from typing import TYPE_CHECKING, Any, Literal, Protocol
  7
  8if TYPE_CHECKING:
  9    from libs.types import MessageType, MessageTypeDict, StyleOptions
 10
 11
 12class DataMixin:
 13    """共通処理"""
 14
 15    def reset(self) -> None:
 16        """デフォルト値にリセット"""
 17        if not is_dataclass(self):
 18            raise TypeError(f"{self.__class__.__name__} must be a dataclass")
 19
 20        default = type(self)()
 21        for f in fields(self):
 22            setattr(self, f.name, getattr(default, f.name))
 23
 24
 25@dataclass
 26class MsgData(DataMixin):
 27    """ポストされたメッセージデータ"""
 28
 29    text: str = field(default=str())
 30    """本文"""
 31    event_ts: str = field(default="undetermined")
 32    """イベント発生タイムスタンプ"""
 33    thread_ts: str = field(default="undetermined")
 34    """スレッド元タイムスタンプ
 35    - *0*: スレッドになっていない
 36    - *undetermined*: 未定義状態
 37    """
 38    edited_ts: str = field(default="undetermined")
 39    """イベント編集タイムスタンプ"""
 40    channel_id: str = field(default=str())
 41    """チャンネルID"""
 42    channel_type: Literal[
 43        "channel",
 44        "group",
 45        "im",
 46        "search_messages",
 47        "undetermined",
 48    ] = field(default="undetermined")
 49    """チャンネルタイプ
 50    - *channel*: 通常チャンネル
 51    - *group*: プライベートチャンネル
 52    - *im*: ダイレクトメッセージ
 53    - *search_messages*: 検索API
 54    - *undetermined*: 未定義状態
 55    """
 56    user_id: str = field(default=str())
 57    """ユーザーID"""
 58    status: Literal[
 59        "message_append",
 60        "message_changed",
 61        "message_deleted",
 62        "undetermined",
 63    ] = field(default="undetermined")
 64    """イベントステータス
 65    - *message_append*: 新規ポスト
 66    - *message_changed*: 編集
 67    - *message_deleted*: 削除
 68    - *undetermined*: 未定義状態
 69    """
 70    reaction_ok: list = field(default_factory=list)
 71    reaction_ng: list = field(default_factory=list)
 72    remarks: list = field(default_factory=list)
 73    """メモ格納用"""
 74
 75
 76@dataclass
 77class PostData(DataMixin):
 78    """ポストするデータ"""
 79
 80    headline: dict[str, str] = field(default_factory=dict)
 81    """ヘッダ文"""
 82    message: list[dict[str, "MessageTypeDict"]] = field(default_factory=list)
 83    """本文
 84    識別子(タイトルなど)をキーにした辞書型
 85    """
 86    thread: bool = field(default=True)
 87    """スレッドに返す"""
 88    ts: str = field(default="undetermined")
 89    """指定タイムスタンプへの強制リプライ"""
 90
 91
 92@dataclass
 93class StatusData(DataMixin):
 94    """処理した結果"""
 95
 96    command_type: Literal[
 97        "results",
 98        "graph",
 99        "ranking",
100        "rating",
101        "report",
102        "comparison",
103        "unknown",
104    ] = field(default="unknown")
105    """実行(する/した)サブコマンド
106    - *results*: 成績サマリ
107    - *graph*: グラフ生成
108    - *ranking*: ランキング
109    - *rating*: レーティング
110    - *report*: レポート
111    - *comparison*: 突合処理
112    - *unknown*: 未定義
113    """
114
115    command_flg: bool = field(default=False)
116    """コマンドとして実行されたかチェック
117    - *True*: コマンド実行
118    - *False*: キーワード呼び出し
119    """
120    command_name: str = field(default="")
121    """実行したコマンド名"""
122
123    reaction: bool = field(default=False)
124    """データステータス状態
125    - *True*: 矛盾なくデータを取り込んだ(OK)
126    - *False*: 矛盾があったがデータを取り込んだ or データを取り込めなかった(NG)
127    """
128    action: Literal["change", "delete", "nothing"] = field(default="nothing")
129    """DBに対する操作
130    - *change*: insert/updateが実行された
131    - *delete*: deleteが実行された
132    - *nothing*: 何もしてない
133    """
134    target_ts: list = field(default_factory=list)
135    """同じ処理をしたタイムスタンプリスト(1件だけの処理でもセットされる)"""
136    rpoint_sum: int = field(default=0)
137    """素点合計値格納用"""
138
139    result: bool = field(default=True)
140    """メッセージデータに対する処理結果
141    - *True*: 目的の処理が達成できた
142    - *False*: 何らかの原因で処理が達成できなかった
143    """
144    message: Any = field(default=None)
145    """汎用メッセージ"""
146    source: str = field(default="")
147    """入力元識別子"""
148
149
150class MessageParserProtocol(Protocol):
151    """メッセージ解析クラス"""
152
153    data: MsgData
154    """受け取ったメッセージデータ"""
155    post: PostData
156    """送信する内容"""
157    status: StatusData
158    """処理した結果"""
159
160    @property
161    def in_thread(self) -> bool:
162        """スレッド内のメッセージか判定"""
163
164    @property
165    def is_command(self) -> bool:
166        """コマンドとして実行されたかチェック
167        - *True*: スラッシュコマンド
168        - *False*: チャンネル内呼び出しキーワード
169        """
170
171    @property
172    def is_bot(self) -> bool:
173        """botによる操作かチェック
174        - *True*: botが操作
175        - *False*: ユーザが操作
176        """
177
178    @property
179    def keyword(self) -> str:
180        """コマンドとして認識している文字列を返す"""
181
182    @property
183    def argument(self) -> list:
184        """コマンド引数として認識しているオプションを文字列のリストで返す"""
185
186    @property
187    def reply_ts(self) -> str:
188        """リプライ先のタイムスタンプ"""
189
190    @property
191    def check_updatable(self) -> bool:
192        """DB更新可能チャンネルか判定"""
193
194    @property
195    def ignore_user(self) -> bool:
196        """コマンドを拒否するユーザか判定"""
197
198    def set_data(self, title: str, data: "MessageType", options: "StyleOptions"):
199        """メッセージデータをセット"""
200
201    def get_score(self, keyword: str) -> dict:
202        """本文からスコアデータを取り出す"""
203
204    def get_remarks(self, keyword: str) -> list:
205        """本文からメモデータを取り出す"""
206
207    def parser(self, body: Any):
208        """メッセージ解析メソッド"""
209
210    def reset(self):
211        """状態リセット"""
class DataMixin:
13class DataMixin:
14    """共通処理"""
15
16    def reset(self) -> None:
17        """デフォルト値にリセット"""
18        if not is_dataclass(self):
19            raise TypeError(f"{self.__class__.__name__} must be a dataclass")
20
21        default = type(self)()
22        for f in fields(self):
23            setattr(self, f.name, getattr(default, f.name))

共通処理

def reset(self) -> None:
16    def reset(self) -> None:
17        """デフォルト値にリセット"""
18        if not is_dataclass(self):
19            raise TypeError(f"{self.__class__.__name__} must be a dataclass")
20
21        default = type(self)()
22        for f in fields(self):
23            setattr(self, f.name, getattr(default, f.name))

デフォルト値にリセット

@dataclass
class MsgData(DataMixin):
26@dataclass
27class MsgData(DataMixin):
28    """ポストされたメッセージデータ"""
29
30    text: str = field(default=str())
31    """本文"""
32    event_ts: str = field(default="undetermined")
33    """イベント発生タイムスタンプ"""
34    thread_ts: str = field(default="undetermined")
35    """スレッド元タイムスタンプ
36    - *0*: スレッドになっていない
37    - *undetermined*: 未定義状態
38    """
39    edited_ts: str = field(default="undetermined")
40    """イベント編集タイムスタンプ"""
41    channel_id: str = field(default=str())
42    """チャンネルID"""
43    channel_type: Literal[
44        "channel",
45        "group",
46        "im",
47        "search_messages",
48        "undetermined",
49    ] = field(default="undetermined")
50    """チャンネルタイプ
51    - *channel*: 通常チャンネル
52    - *group*: プライベートチャンネル
53    - *im*: ダイレクトメッセージ
54    - *search_messages*: 検索API
55    - *undetermined*: 未定義状態
56    """
57    user_id: str = field(default=str())
58    """ユーザーID"""
59    status: Literal[
60        "message_append",
61        "message_changed",
62        "message_deleted",
63        "undetermined",
64    ] = field(default="undetermined")
65    """イベントステータス
66    - *message_append*: 新規ポスト
67    - *message_changed*: 編集
68    - *message_deleted*: 削除
69    - *undetermined*: 未定義状態
70    """
71    reaction_ok: list = field(default_factory=list)
72    reaction_ng: list = field(default_factory=list)
73    remarks: list = field(default_factory=list)
74    """メモ格納用"""

ポストされたメッセージデータ

MsgData( text: str = '', event_ts: str = 'undetermined', thread_ts: str = 'undetermined', edited_ts: str = 'undetermined', channel_id: str = '', channel_type: Literal['channel', 'group', 'im', 'search_messages', 'undetermined'] = 'undetermined', user_id: str = '', status: Literal['message_append', 'message_changed', 'message_deleted', 'undetermined'] = 'undetermined', reaction_ok: list = <factory>, reaction_ng: list = <factory>, remarks: list = <factory>)
text: str = ''

本文

event_ts: str = 'undetermined'

イベント発生タイムスタンプ

thread_ts: str = 'undetermined'

スレッド元タイムスタンプ

  • 0: スレッドになっていない
  • undetermined: 未定義状態
edited_ts: str = 'undetermined'

イベント編集タイムスタンプ

channel_id: str = ''

チャンネルID

channel_type: Literal['channel', 'group', 'im', 'search_messages', 'undetermined'] = 'undetermined'

チャンネルタイプ

  • channel: 通常チャンネル
  • group: プライベートチャンネル
  • im: ダイレクトメッセージ
  • search_messages: 検索API
  • undetermined: 未定義状態
user_id: str = ''

ユーザーID

status: Literal['message_append', 'message_changed', 'message_deleted', 'undetermined'] = 'undetermined'

イベントステータス

  • message_append: 新規ポスト
  • message_changed: 編集
  • message_deleted: 削除
  • undetermined: 未定義状態
reaction_ok: list
reaction_ng: list
remarks: list

メモ格納用

Inherited Members
DataMixin
reset
@dataclass
class PostData(DataMixin):
77@dataclass
78class PostData(DataMixin):
79    """ポストするデータ"""
80
81    headline: dict[str, str] = field(default_factory=dict)
82    """ヘッダ文"""
83    message: list[dict[str, "MessageTypeDict"]] = field(default_factory=list)
84    """本文
85    識別子(タイトルなど)をキーにした辞書型
86    """
87    thread: bool = field(default=True)
88    """スレッドに返す"""
89    ts: str = field(default="undetermined")
90    """指定タイムスタンプへの強制リプライ"""

ポストするデータ

PostData( headline: dict[str, str] = <factory>, message: list[dict[str, libs.types.MessageTypeDict]] = <factory>, thread: bool = True, ts: str = 'undetermined')
headline: dict[str, str]

ヘッダ文

message: list[dict[str, libs.types.MessageTypeDict]]

本文 識別子(タイトルなど)をキーにした辞書型

thread: bool = True

スレッドに返す

ts: str = 'undetermined'

指定タイムスタンプへの強制リプライ

Inherited Members
DataMixin
reset
@dataclass
class StatusData(DataMixin):
 93@dataclass
 94class StatusData(DataMixin):
 95    """処理した結果"""
 96
 97    command_type: Literal[
 98        "results",
 99        "graph",
100        "ranking",
101        "rating",
102        "report",
103        "comparison",
104        "unknown",
105    ] = field(default="unknown")
106    """実行(する/した)サブコマンド
107    - *results*: 成績サマリ
108    - *graph*: グラフ生成
109    - *ranking*: ランキング
110    - *rating*: レーティング
111    - *report*: レポート
112    - *comparison*: 突合処理
113    - *unknown*: 未定義
114    """
115
116    command_flg: bool = field(default=False)
117    """コマンドとして実行されたかチェック
118    - *True*: コマンド実行
119    - *False*: キーワード呼び出し
120    """
121    command_name: str = field(default="")
122    """実行したコマンド名"""
123
124    reaction: bool = field(default=False)
125    """データステータス状態
126    - *True*: 矛盾なくデータを取り込んだ(OK)
127    - *False*: 矛盾があったがデータを取り込んだ or データを取り込めなかった(NG)
128    """
129    action: Literal["change", "delete", "nothing"] = field(default="nothing")
130    """DBに対する操作
131    - *change*: insert/updateが実行された
132    - *delete*: deleteが実行された
133    - *nothing*: 何もしてない
134    """
135    target_ts: list = field(default_factory=list)
136    """同じ処理をしたタイムスタンプリスト(1件だけの処理でもセットされる)"""
137    rpoint_sum: int = field(default=0)
138    """素点合計値格納用"""
139
140    result: bool = field(default=True)
141    """メッセージデータに対する処理結果
142    - *True*: 目的の処理が達成できた
143    - *False*: 何らかの原因で処理が達成できなかった
144    """
145    message: Any = field(default=None)
146    """汎用メッセージ"""
147    source: str = field(default="")
148    """入力元識別子"""

処理した結果

StatusData( command_type: Literal['results', 'graph', 'ranking', 'rating', 'report', 'comparison', 'unknown'] = 'unknown', command_flg: bool = False, command_name: str = '', reaction: bool = False, action: Literal['change', 'delete', 'nothing'] = 'nothing', target_ts: list = <factory>, rpoint_sum: int = 0, result: bool = True, message: Any = None, source: str = '')
command_type: Literal['results', 'graph', 'ranking', 'rating', 'report', 'comparison', 'unknown'] = 'unknown'

実行(する/した)サブコマンド

  • results: 成績サマリ
  • graph: グラフ生成
  • ranking: ランキング
  • rating: レーティング
  • report: レポート
  • comparison: 突合処理
  • unknown: 未定義
command_flg: bool = False

コマンドとして実行されたかチェック

  • True: コマンド実行
  • False: キーワード呼び出し
command_name: str = ''

実行したコマンド名

reaction: bool = False

データステータス状態

  • True: 矛盾なくデータを取り込んだ(OK)
  • False: 矛盾があったがデータを取り込んだ or データを取り込めなかった(NG)
action: Literal['change', 'delete', 'nothing'] = 'nothing'

DBに対する操作

  • change: insert/updateが実行された
  • delete: deleteが実行された
  • nothing: 何もしてない
target_ts: list

同じ処理をしたタイムスタンプリスト(1件だけの処理でもセットされる)

rpoint_sum: int = 0

素点合計値格納用

result: bool = True

メッセージデータに対する処理結果

  • True: 目的の処理が達成できた
  • False: 何らかの原因で処理が達成できなかった
message: Any = None

汎用メッセージ

source: str = ''

入力元識別子

Inherited Members
DataMixin
reset
class MessageParserProtocol(typing.Protocol):
151class MessageParserProtocol(Protocol):
152    """メッセージ解析クラス"""
153
154    data: MsgData
155    """受け取ったメッセージデータ"""
156    post: PostData
157    """送信する内容"""
158    status: StatusData
159    """処理した結果"""
160
161    @property
162    def in_thread(self) -> bool:
163        """スレッド内のメッセージか判定"""
164
165    @property
166    def is_command(self) -> bool:
167        """コマンドとして実行されたかチェック
168        - *True*: スラッシュコマンド
169        - *False*: チャンネル内呼び出しキーワード
170        """
171
172    @property
173    def is_bot(self) -> bool:
174        """botによる操作かチェック
175        - *True*: botが操作
176        - *False*: ユーザが操作
177        """
178
179    @property
180    def keyword(self) -> str:
181        """コマンドとして認識している文字列を返す"""
182
183    @property
184    def argument(self) -> list:
185        """コマンド引数として認識しているオプションを文字列のリストで返す"""
186
187    @property
188    def reply_ts(self) -> str:
189        """リプライ先のタイムスタンプ"""
190
191    @property
192    def check_updatable(self) -> bool:
193        """DB更新可能チャンネルか判定"""
194
195    @property
196    def ignore_user(self) -> bool:
197        """コマンドを拒否するユーザか判定"""
198
199    def set_data(self, title: str, data: "MessageType", options: "StyleOptions"):
200        """メッセージデータをセット"""
201
202    def get_score(self, keyword: str) -> dict:
203        """本文からスコアデータを取り出す"""
204
205    def get_remarks(self, keyword: str) -> list:
206        """本文からメモデータを取り出す"""
207
208    def parser(self, body: Any):
209        """メッセージ解析メソッド"""
210
211    def reset(self):
212        """状態リセット"""

メッセージ解析クラス

MessageParserProtocol(*args, **kwargs)
1771def _no_init_or_replace_init(self, *args, **kwargs):
1772    cls = type(self)
1773
1774    if cls._is_protocol:
1775        raise TypeError('Protocols cannot be instantiated')
1776
1777    # Already using a custom `__init__`. No need to calculate correct
1778    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1779    if cls.__init__ is not _no_init_or_replace_init:
1780        return
1781
1782    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1783    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1784    # searches for a proper new `__init__` in the MRO. The new `__init__`
1785    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1786    # instantiation of the protocol subclass will thus use the new
1787    # `__init__` and no longer call `_no_init_or_replace_init`.
1788    for base in cls.__mro__:
1789        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1790        if init is not _no_init_or_replace_init:
1791            cls.__init__ = init
1792            break
1793    else:
1794        # should not happen
1795        cls.__init__ = object.__init__
1796
1797    cls.__init__(self, *args, **kwargs)
data: MsgData

受け取ったメッセージデータ

post: PostData

送信する内容

status: StatusData

処理した結果

in_thread: bool
161    @property
162    def in_thread(self) -> bool:
163        """スレッド内のメッセージか判定"""

スレッド内のメッセージか判定

is_command: bool
165    @property
166    def is_command(self) -> bool:
167        """コマンドとして実行されたかチェック
168        - *True*: スラッシュコマンド
169        - *False*: チャンネル内呼び出しキーワード
170        """

コマンドとして実行されたかチェック

  • True: スラッシュコマンド
  • False: チャンネル内呼び出しキーワード
is_bot: bool
172    @property
173    def is_bot(self) -> bool:
174        """botによる操作かチェック
175        - *True*: botが操作
176        - *False*: ユーザが操作
177        """

botによる操作かチェック

  • True: botが操作
  • False: ユーザが操作
keyword: str
179    @property
180    def keyword(self) -> str:
181        """コマンドとして認識している文字列を返す"""

コマンドとして認識している文字列を返す

argument: list
183    @property
184    def argument(self) -> list:
185        """コマンド引数として認識しているオプションを文字列のリストで返す"""

コマンド引数として認識しているオプションを文字列のリストで返す

reply_ts: str
187    @property
188    def reply_ts(self) -> str:
189        """リプライ先のタイムスタンプ"""

リプライ先のタイムスタンプ

check_updatable: bool
191    @property
192    def check_updatable(self) -> bool:
193        """DB更新可能チャンネルか判定"""

DB更新可能チャンネルか判定

ignore_user: bool
195    @property
196    def ignore_user(self) -> bool:
197        """コマンドを拒否するユーザか判定"""

コマンドを拒否するユーザか判定

def set_data( self, title: str, data: 'MessageType', options: libs.types.StyleOptions):
199    def set_data(self, title: str, data: "MessageType", options: "StyleOptions"):
200        """メッセージデータをセット"""

メッセージデータをセット

def get_score(self, keyword: str) -> dict:
202    def get_score(self, keyword: str) -> dict:
203        """本文からスコアデータを取り出す"""

本文からスコアデータを取り出す

def get_remarks(self, keyword: str) -> list:
205    def get_remarks(self, keyword: str) -> list:
206        """本文からメモデータを取り出す"""

本文からメモデータを取り出す

def parser(self, body: Any):
208    def parser(self, body: Any):
209        """メッセージ解析メソッド"""

メッセージ解析メソッド

def reset(self):
211    def reset(self):
212        """状態リセット"""

状態リセット