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, Optional, Protocol
  7
  8from libs.types import ActionStatus, ChannelType, CommandType, MessageStatus
  9
 10if TYPE_CHECKING:
 11    from pathlib import Path  # noqa: F401
 12
 13    import pandas as pd  # noqa: F401
 14
 15    from libs.types import MessageType, StyleOptions
 16
 17
 18class DataMixin:
 19    """共通処理"""
 20
 21    def reset(self) -> None:
 22        """デフォルト値にリセット"""
 23        if not is_dataclass(self):
 24            raise TypeError(f"{self.__class__.__name__} must be a dataclass")
 25
 26        default = type(self)()
 27        for f in fields(self):
 28            setattr(self, f.name, getattr(default, f.name))
 29
 30
 31@dataclass
 32class MsgData(DataMixin):
 33    """ポストされたメッセージデータ"""
 34
 35    text: str = field(default=str())
 36    """本文"""
 37    event_ts: str = field(default="undetermined")
 38    """イベント発生タイムスタンプ"""
 39    thread_ts: str = field(default="undetermined")
 40    """スレッド元タイムスタンプ
 41    - *0*: スレッドになっていない
 42    - *undetermined*: 未定義状態
 43    """
 44    edited_ts: str = field(default="undetermined")
 45    """イベント編集タイムスタンプ"""
 46    channel_id: str = field(default=str())
 47    """チャンネルID"""
 48    channel_type: ChannelType = field(default=ChannelType.UNDETERMINED)
 49    """チャンネルタイプ"""
 50    user_id: str = field(default=str())
 51    """ユーザーID"""
 52    status: MessageStatus = field(default=MessageStatus.UNDETERMINED)
 53    """イベントステータス"""
 54    reaction_ok: list[str] = field(default_factory=list)
 55    reaction_ng: list[str] = field(default_factory=list)
 56    remarks: list[str] = field(default_factory=list)
 57    """メモ格納用"""
 58
 59
 60@dataclass
 61class PostData(DataMixin):
 62    """ポストするデータ"""
 63
 64    headline: Optional[tuple["MessageType", "StyleOptions"]] = field(default=None)
 65    """ヘッダメッセージ"""
 66    message: list[tuple["MessageType", "StyleOptions"]] = field(default_factory=list)
 67    """本文メッセージ"""
 68    thread: bool = field(default=True)
 69    """スレッドに返す"""
 70    ts: str = field(default="undetermined")
 71    """指定タイムスタンプへの強制リプライ"""
 72    thread_title: str = field(default="")
 73    """スレッドに付けるタイトル"""
 74
 75
 76@dataclass
 77class StatusData(DataMixin):
 78    """処理した結果"""
 79
 80    command_type: CommandType = field(default=CommandType.UNKNOWN)
 81    """実行(する/した)サブコマンド"""
 82    command_flg: bool = field(default=False)
 83    """コマンドとして実行されたかチェック
 84    - *True*: コマンド実行
 85    - *False*: キーワード呼び出し
 86    """
 87    command_name: str = field(default="")
 88    """実行したコマンド名"""
 89
 90    reaction: bool = field(default=False)
 91    """データステータス状態
 92    - *True*: 矛盾なくデータを取り込んだ(OK)
 93    - *False*: 矛盾があったがデータを取り込んだ or データを取り込めなかった(NG)
 94    """
 95    action: ActionStatus = field(default=ActionStatus.NOTHING)
 96    """DBに対する操作"""
 97    target_ts: list[str] = field(default_factory=list)
 98    """同じ処理をしたタイムスタンプリスト(1件だけの処理でもセットされる)"""
 99    rpoint_sum: int = field(default=0)
100    """素点合計値格納用"""
101
102    result: bool = field(default=True)
103    """メッセージデータに対する処理結果
104    - *True*: 目的の処理が達成できた
105    - *False*: 何らかの原因で処理が達成できなかった
106    """
107    message: Any = field(default=None)
108    """汎用メッセージ"""
109    source: str = field(default="")
110    """データ入力元識別子"""
111
112
113class MessageParserProtocol(Protocol):
114    """メッセージ解析クラス"""
115
116    data: MsgData
117    """受け取ったメッセージデータ"""
118    post: PostData
119    """送信する内容"""
120    status: StatusData
121    """処理した結果"""
122
123    @property
124    def in_thread(self) -> bool:
125        """スレッド内のメッセージか判定"""
126
127    @property
128    def is_command(self) -> bool:
129        """
130        コマンドとして実行されたかチェック
131
132        Returns:
133            bool: 真偽値
134            - *True*: スラッシュコマンド
135            - *False*: チャンネル内呼び出しキーワード
136
137        """
138
139    @property
140    def is_bot(self) -> bool:
141        """
142        botによる操作かチェック
143
144        Returns:
145            bool: 真偽値
146            - *True*: botが操作
147            - *False*: ユーザが操作
148
149        """
150
151    @property
152    def keyword(self) -> str:
153        """コマンドとして認識している文字列を返す"""
154
155    @property
156    def argument(self) -> list[str]:
157        """コマンド引数として認識しているオプションを文字列のリストで返す"""
158
159    @property
160    def reply_ts(self) -> str:
161        """リプライ先のタイムスタンプ"""
162
163    @property
164    def check_updatable(self) -> bool:
165        """DB更新可能チャンネルか判定"""
166
167    @property
168    def ignore_user(self) -> bool:
169        """コマンドを拒否するユーザか判定"""
170
171    def set_headline(self, data: "MessageType", options: "StyleOptions") -> None:
172        """ヘッドラインメッセージをセット"""
173
174    def set_message(self, data: "MessageType", options: "StyleOptions") -> None:
175        """本文メッセージをセット"""
176
177    def parser(self, body: Any) -> None:
178        """メッセージ解析メソッド"""
179
180    def reset(self) -> None:
181        """状態リセット"""
class DataMixin:
19class DataMixin:
20    """共通処理"""
21
22    def reset(self) -> None:
23        """デフォルト値にリセット"""
24        if not is_dataclass(self):
25            raise TypeError(f"{self.__class__.__name__} must be a dataclass")
26
27        default = type(self)()
28        for f in fields(self):
29            setattr(self, f.name, getattr(default, f.name))

共通処理

def reset(self) -> None:
22    def reset(self) -> None:
23        """デフォルト値にリセット"""
24        if not is_dataclass(self):
25            raise TypeError(f"{self.__class__.__name__} must be a dataclass")
26
27        default = type(self)()
28        for f in fields(self):
29            setattr(self, f.name, getattr(default, f.name))

デフォルト値にリセット

@dataclass
class MsgData(DataMixin):
32@dataclass
33class MsgData(DataMixin):
34    """ポストされたメッセージデータ"""
35
36    text: str = field(default=str())
37    """本文"""
38    event_ts: str = field(default="undetermined")
39    """イベント発生タイムスタンプ"""
40    thread_ts: str = field(default="undetermined")
41    """スレッド元タイムスタンプ
42    - *0*: スレッドになっていない
43    - *undetermined*: 未定義状態
44    """
45    edited_ts: str = field(default="undetermined")
46    """イベント編集タイムスタンプ"""
47    channel_id: str = field(default=str())
48    """チャンネルID"""
49    channel_type: ChannelType = field(default=ChannelType.UNDETERMINED)
50    """チャンネルタイプ"""
51    user_id: str = field(default=str())
52    """ユーザーID"""
53    status: MessageStatus = field(default=MessageStatus.UNDETERMINED)
54    """イベントステータス"""
55    reaction_ok: list[str] = field(default_factory=list)
56    reaction_ng: list[str] = field(default_factory=list)
57    remarks: list[str] = field(default_factory=list)
58    """メモ格納用"""

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

MsgData( text: str = '', event_ts: str = 'undetermined', thread_ts: str = 'undetermined', edited_ts: str = 'undetermined', channel_id: str = '', channel_type: libs.types.ChannelType = <ChannelType.UNDETERMINED: 'undetermined'>, user_id: str = '', status: libs.types.MessageStatus = <MessageStatus.UNDETERMINED: 'undetermined'>, reaction_ok: list[str] = <factory>, reaction_ng: list[str] = <factory>, remarks: list[str] = <factory>)
text: str = ''

本文

event_ts: str = 'undetermined'

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

thread_ts: str = 'undetermined'

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

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

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

channel_id: str = ''

チャンネルID

channel_type: libs.types.ChannelType = <ChannelType.UNDETERMINED: 'undetermined'>

チャンネルタイプ

user_id: str = ''

ユーザーID

status: libs.types.MessageStatus = <MessageStatus.UNDETERMINED: 'undetermined'>

イベントステータス

reaction_ok: list[str]
reaction_ng: list[str]
remarks: list[str]

メモ格納用

Inherited Members
DataMixin
reset
@dataclass
class PostData(DataMixin):
61@dataclass
62class PostData(DataMixin):
63    """ポストするデータ"""
64
65    headline: Optional[tuple["MessageType", "StyleOptions"]] = field(default=None)
66    """ヘッダメッセージ"""
67    message: list[tuple["MessageType", "StyleOptions"]] = field(default_factory=list)
68    """本文メッセージ"""
69    thread: bool = field(default=True)
70    """スレッドに返す"""
71    ts: str = field(default="undetermined")
72    """指定タイムスタンプへの強制リプライ"""
73    thread_title: str = field(default="")
74    """スレッドに付けるタイトル"""

ポストするデータ

PostData( headline: tuple[None | str | pathlib.Path | pandas.DataFrame, libs.types.StyleOptions] | None = None, message: list[tuple[None | str | pathlib.Path | pandas.DataFrame, libs.types.StyleOptions]] = <factory>, thread: bool = True, ts: str = 'undetermined', thread_title: str = '')
headline: tuple[None | str | pathlib.Path | pandas.DataFrame, libs.types.StyleOptions] | None = None

ヘッダメッセージ

message: list[tuple[None | str | pathlib.Path | pandas.DataFrame, libs.types.StyleOptions]]

本文メッセージ

thread: bool = True

スレッドに返す

ts: str = 'undetermined'

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

thread_title: str = ''

スレッドに付けるタイトル

Inherited Members
DataMixin
reset
@dataclass
class StatusData(DataMixin):
 77@dataclass
 78class StatusData(DataMixin):
 79    """処理した結果"""
 80
 81    command_type: CommandType = field(default=CommandType.UNKNOWN)
 82    """実行(する/した)サブコマンド"""
 83    command_flg: bool = field(default=False)
 84    """コマンドとして実行されたかチェック
 85    - *True*: コマンド実行
 86    - *False*: キーワード呼び出し
 87    """
 88    command_name: str = field(default="")
 89    """実行したコマンド名"""
 90
 91    reaction: bool = field(default=False)
 92    """データステータス状態
 93    - *True*: 矛盾なくデータを取り込んだ(OK)
 94    - *False*: 矛盾があったがデータを取り込んだ or データを取り込めなかった(NG)
 95    """
 96    action: ActionStatus = field(default=ActionStatus.NOTHING)
 97    """DBに対する操作"""
 98    target_ts: list[str] = field(default_factory=list)
 99    """同じ処理をしたタイムスタンプリスト(1件だけの処理でもセットされる)"""
100    rpoint_sum: int = field(default=0)
101    """素点合計値格納用"""
102
103    result: bool = field(default=True)
104    """メッセージデータに対する処理結果
105    - *True*: 目的の処理が達成できた
106    - *False*: 何らかの原因で処理が達成できなかった
107    """
108    message: Any = field(default=None)
109    """汎用メッセージ"""
110    source: str = field(default="")
111    """データ入力元識別子"""

処理した結果

StatusData( command_type: libs.types.CommandType = <CommandType.UNKNOWN: 'unknown'>, command_flg: bool = False, command_name: str = '', reaction: bool = False, action: libs.types.ActionStatus = <ActionStatus.NOTHING: 'nothing'>, target_ts: list[str] = <factory>, rpoint_sum: int = 0, result: bool = True, message: Any = None, source: str = '')
command_type: libs.types.CommandType = <CommandType.UNKNOWN: 'unknown'>

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

command_flg: bool = False

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

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

実行したコマンド名

reaction: bool = False

データステータス状態

  • True: 矛盾なくデータを取り込んだ(OK)
  • False: 矛盾があったがデータを取り込んだ or データを取り込めなかった(NG)
action: libs.types.ActionStatus = <ActionStatus.NOTHING: 'nothing'>

DBに対する操作

target_ts: list[str]

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

rpoint_sum: int = 0

素点合計値格納用

result: bool = True

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

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

汎用メッセージ

source: str = ''

データ入力元識別子

Inherited Members
DataMixin
reset
class MessageParserProtocol(typing.Protocol):
114class MessageParserProtocol(Protocol):
115    """メッセージ解析クラス"""
116
117    data: MsgData
118    """受け取ったメッセージデータ"""
119    post: PostData
120    """送信する内容"""
121    status: StatusData
122    """処理した結果"""
123
124    @property
125    def in_thread(self) -> bool:
126        """スレッド内のメッセージか判定"""
127
128    @property
129    def is_command(self) -> bool:
130        """
131        コマンドとして実行されたかチェック
132
133        Returns:
134            bool: 真偽値
135            - *True*: スラッシュコマンド
136            - *False*: チャンネル内呼び出しキーワード
137
138        """
139
140    @property
141    def is_bot(self) -> bool:
142        """
143        botによる操作かチェック
144
145        Returns:
146            bool: 真偽値
147            - *True*: botが操作
148            - *False*: ユーザが操作
149
150        """
151
152    @property
153    def keyword(self) -> str:
154        """コマンドとして認識している文字列を返す"""
155
156    @property
157    def argument(self) -> list[str]:
158        """コマンド引数として認識しているオプションを文字列のリストで返す"""
159
160    @property
161    def reply_ts(self) -> str:
162        """リプライ先のタイムスタンプ"""
163
164    @property
165    def check_updatable(self) -> bool:
166        """DB更新可能チャンネルか判定"""
167
168    @property
169    def ignore_user(self) -> bool:
170        """コマンドを拒否するユーザか判定"""
171
172    def set_headline(self, data: "MessageType", options: "StyleOptions") -> None:
173        """ヘッドラインメッセージをセット"""
174
175    def set_message(self, data: "MessageType", options: "StyleOptions") -> None:
176        """本文メッセージをセット"""
177
178    def parser(self, body: Any) -> None:
179        """メッセージ解析メソッド"""
180
181    def reset(self) -> None:
182        """状態リセット"""

メッセージ解析クラス

MessageParserProtocol(*args, **kwargs)
1866def _no_init_or_replace_init(self, *args, **kwargs):
1867    cls = type(self)
1868
1869    if cls._is_protocol:
1870        raise TypeError('Protocols cannot be instantiated')
1871
1872    # Already using a custom `__init__`. No need to calculate correct
1873    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1874    if cls.__init__ is not _no_init_or_replace_init:
1875        return
1876
1877    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1878    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1879    # searches for a proper new `__init__` in the MRO. The new `__init__`
1880    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1881    # instantiation of the protocol subclass will thus use the new
1882    # `__init__` and no longer call `_no_init_or_replace_init`.
1883    for base in cls.__mro__:
1884        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1885        if init is not _no_init_or_replace_init:
1886            cls.__init__ = init
1887            break
1888    else:
1889        # should not happen
1890        cls.__init__ = object.__init__
1891
1892    cls.__init__(self, *args, **kwargs)
data: MsgData

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

post: PostData

送信する内容

status: StatusData

処理した結果

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

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

is_command: bool
128    @property
129    def is_command(self) -> bool:
130        """
131        コマンドとして実行されたかチェック
132
133        Returns:
134            bool: 真偽値
135            - *True*: スラッシュコマンド
136            - *False*: チャンネル内呼び出しキーワード
137
138        """

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

Returns:

bool: 真偽値

  • True: スラッシュコマンド
  • False: チャンネル内呼び出しキーワード
is_bot: bool
140    @property
141    def is_bot(self) -> bool:
142        """
143        botによる操作かチェック
144
145        Returns:
146            bool: 真偽値
147            - *True*: botが操作
148            - *False*: ユーザが操作
149
150        """

botによる操作かチェック

Returns:

bool: 真偽値

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

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

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

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

reply_ts: str
160    @property
161    def reply_ts(self) -> str:
162        """リプライ先のタイムスタンプ"""

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

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

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

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

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

def set_headline( self, data: None | str | pathlib.Path | pandas.DataFrame, options: libs.types.StyleOptions) -> None:
172    def set_headline(self, data: "MessageType", options: "StyleOptions") -> None:
173        """ヘッドラインメッセージをセット"""

ヘッドラインメッセージをセット

def set_message( self, data: None | str | pathlib.Path | pandas.DataFrame, options: libs.types.StyleOptions) -> None:
175    def set_message(self, data: "MessageType", options: "StyleOptions") -> None:
176        """本文メッセージをセット"""

本文メッセージをセット

def parser(self, body: Any) -> None:
178    def parser(self, body: Any) -> None:
179        """メッセージ解析メソッド"""

メッセージ解析メソッド

def reset(self) -> None:
181    def reset(self) -> None:
182        """状態リセット"""

状態リセット