integrations.discord.functions

integrations/discord/functions.py

  1"""
  2integrations/discord/functions.py
  3"""
  4
  5from typing import TYPE_CHECKING, Any, cast
  6
  7from discord import Forbidden, NotFound
  8from discord.channel import TextChannel
  9
 10from integrations.base.interface import FunctionsInterface
 11from libs.types import ActionStatus
 12from libs.utils.timekit import ExtendedDatetime as ExtDt
 13
 14if TYPE_CHECKING:
 15    from discord import ClientUser, Message
 16
 17    from integrations.discord.api import AdapterAPI
 18    from integrations.discord.config import SvcConfig
 19    from integrations.discord.parser import MessageParser
 20    from integrations.protocols import MessageParserProtocol
 21
 22
 23class SvcFunctions(FunctionsInterface):
 24    """discord専用関数"""
 25
 26    def __init__(self, api: "AdapterAPI", conf: "SvcConfig") -> None:
 27        super().__init__()
 28
 29        self.api = api
 30        self.conf = conf
 31        """個別設定"""
 32
 33    def post_processing(self, m: "MessageParserProtocol") -> None:
 34        """
 35        後処理(非同期処理ラッパー)
 36
 37        Args:
 38            m (MessageParserProtocol): メッセージデータ
 39
 40        """
 41        match m.status.action:
 42            case ActionStatus.NOTHING:
 43                return
 44            case ActionStatus.CHANGE:
 45                self.api.bot.loop.create_task(self.update_reaction(m))
 46            case ActionStatus.DELETE:
 47                self.api.bot.loop.create_task(self.delete_reaction(m))
 48
 49    async def update_reaction(self, m: "MessageParserProtocol") -> None:
 50        """
 51        後処理
 52
 53        Args:
 54            m (MessageParserProtocol): メッセージデータ
 55
 56        """
 57        m = cast("MessageParser", m)
 58        self.conf.bot_name = cast("ClientUser", self.conf.bot_name)
 59
 60        if not hasattr(m, "discord_msg"):
 61            return
 62        if await self.is_deleted_message(m.discord_msg):
 63            return
 64
 65        emoji = {
 66            "ok": "\N{WHITE HEAVY CHECK MARK}",
 67            "ng": "\N{CROSS MARK}",
 68        }
 69
 70        # 既に付いているリアクションを取得
 71        has_ok = False
 72        has_ng = False
 73        for reaction in m.discord_msg.reactions:
 74            if str(reaction.emoji) == emoji["ok"]:
 75                async for user in reaction.users():
 76                    if user == self.conf.bot_name:
 77                        has_ok = True
 78                        continue
 79            if str(reaction.emoji) == emoji["ng"]:
 80                async for user in reaction.users():
 81                    if user == self.conf.bot_name:
 82                        has_ng = True
 83                        continue
 84
 85        # リアクション処理
 86        if m.status.reaction:  # NGを外してOKを付ける
 87            if has_ng:
 88                await m.discord_msg.remove_reaction(emoji["ng"], self.conf.bot_name)
 89            if not has_ok:
 90                await m.discord_msg.add_reaction(emoji["ok"])
 91        else:  # OKを外してNGを付ける
 92            if has_ok:
 93                await m.discord_msg.remove_reaction(emoji["ok"], self.conf.bot_name)
 94            if not has_ng:
 95                await m.discord_msg.add_reaction(emoji["ng"])
 96
 97    async def delete_reaction(self, m: "MessageParserProtocol") -> None:
 98        """
 99        botが付けたリアクションをすべて削除する
100
101        Args:
102            m (MessageParserProtocol): メッセージデータ
103
104        """
105        m = cast("MessageParser", m)
106
107        if hasattr(m, "discord_msg"):
108            if await self.is_deleted_message(m.discord_msg):
109                return  # 削除済み
110        else:  # Messageを持っていない場合はevent_tsとchannel_idを使って検索を試みる
111            if not m.data.channel_id:
112                return  # チャンネルIDが空
113            channel = self.api.bot.get_channel(int(m.data.channel_id))
114            if not isinstance(channel, TextChannel):
115                return  # 対象外チャンネル
116
117            async for msg in channel.history(
118                limit=10,
119                oldest_first=True,
120                after=ExtDt(float(m.data.event_ts), seconds=-1).dt,
121                before=ExtDt(float(m.data.event_ts), seconds=1).dt,
122            ):
123                if str(msg.created_at.timestamp()) == m.data.event_ts:
124                    m.discord_msg = msg
125                    break
126                return  # 該当メッセージなし(削除済み)
127
128        for reaction in m.discord_msg.reactions:
129            async for user in reaction.users():
130                if user == self.conf.bot_name:
131                    await reaction.remove(user)
132
133    async def is_deleted_message(self, message: "Message") -> bool:
134        """
135        メッセージが削除済みか調べる
136
137        Args:
138            message (Message): discordオブジェクト
139
140        Returns:
141            bool: 真偽
142
143        """
144        try:
145            await message.channel.fetch_message(message.id)
146            return False
147        except NotFound:
148            return True
149        except Forbidden:
150            return True
151
152    def get_conversations(self, m: "MessageParserProtocol") -> dict[str, Any]:
153        _ = m
154        return {}
class SvcFunctions(integrations.base.interface.FunctionsInterface):
 24class SvcFunctions(FunctionsInterface):
 25    """discord専用関数"""
 26
 27    def __init__(self, api: "AdapterAPI", conf: "SvcConfig") -> None:
 28        super().__init__()
 29
 30        self.api = api
 31        self.conf = conf
 32        """個別設定"""
 33
 34    def post_processing(self, m: "MessageParserProtocol") -> None:
 35        """
 36        後処理(非同期処理ラッパー)
 37
 38        Args:
 39            m (MessageParserProtocol): メッセージデータ
 40
 41        """
 42        match m.status.action:
 43            case ActionStatus.NOTHING:
 44                return
 45            case ActionStatus.CHANGE:
 46                self.api.bot.loop.create_task(self.update_reaction(m))
 47            case ActionStatus.DELETE:
 48                self.api.bot.loop.create_task(self.delete_reaction(m))
 49
 50    async def update_reaction(self, m: "MessageParserProtocol") -> None:
 51        """
 52        後処理
 53
 54        Args:
 55            m (MessageParserProtocol): メッセージデータ
 56
 57        """
 58        m = cast("MessageParser", m)
 59        self.conf.bot_name = cast("ClientUser", self.conf.bot_name)
 60
 61        if not hasattr(m, "discord_msg"):
 62            return
 63        if await self.is_deleted_message(m.discord_msg):
 64            return
 65
 66        emoji = {
 67            "ok": "\N{WHITE HEAVY CHECK MARK}",
 68            "ng": "\N{CROSS MARK}",
 69        }
 70
 71        # 既に付いているリアクションを取得
 72        has_ok = False
 73        has_ng = False
 74        for reaction in m.discord_msg.reactions:
 75            if str(reaction.emoji) == emoji["ok"]:
 76                async for user in reaction.users():
 77                    if user == self.conf.bot_name:
 78                        has_ok = True
 79                        continue
 80            if str(reaction.emoji) == emoji["ng"]:
 81                async for user in reaction.users():
 82                    if user == self.conf.bot_name:
 83                        has_ng = True
 84                        continue
 85
 86        # リアクション処理
 87        if m.status.reaction:  # NGを外してOKを付ける
 88            if has_ng:
 89                await m.discord_msg.remove_reaction(emoji["ng"], self.conf.bot_name)
 90            if not has_ok:
 91                await m.discord_msg.add_reaction(emoji["ok"])
 92        else:  # OKを外してNGを付ける
 93            if has_ok:
 94                await m.discord_msg.remove_reaction(emoji["ok"], self.conf.bot_name)
 95            if not has_ng:
 96                await m.discord_msg.add_reaction(emoji["ng"])
 97
 98    async def delete_reaction(self, m: "MessageParserProtocol") -> None:
 99        """
100        botが付けたリアクションをすべて削除する
101
102        Args:
103            m (MessageParserProtocol): メッセージデータ
104
105        """
106        m = cast("MessageParser", m)
107
108        if hasattr(m, "discord_msg"):
109            if await self.is_deleted_message(m.discord_msg):
110                return  # 削除済み
111        else:  # Messageを持っていない場合はevent_tsとchannel_idを使って検索を試みる
112            if not m.data.channel_id:
113                return  # チャンネルIDが空
114            channel = self.api.bot.get_channel(int(m.data.channel_id))
115            if not isinstance(channel, TextChannel):
116                return  # 対象外チャンネル
117
118            async for msg in channel.history(
119                limit=10,
120                oldest_first=True,
121                after=ExtDt(float(m.data.event_ts), seconds=-1).dt,
122                before=ExtDt(float(m.data.event_ts), seconds=1).dt,
123            ):
124                if str(msg.created_at.timestamp()) == m.data.event_ts:
125                    m.discord_msg = msg
126                    break
127                return  # 該当メッセージなし(削除済み)
128
129        for reaction in m.discord_msg.reactions:
130            async for user in reaction.users():
131                if user == self.conf.bot_name:
132                    await reaction.remove(user)
133
134    async def is_deleted_message(self, message: "Message") -> bool:
135        """
136        メッセージが削除済みか調べる
137
138        Args:
139            message (Message): discordオブジェクト
140
141        Returns:
142            bool: 真偽
143
144        """
145        try:
146            await message.channel.fetch_message(message.id)
147            return False
148        except NotFound:
149            return True
150        except Forbidden:
151            return True
152
153    def get_conversations(self, m: "MessageParserProtocol") -> dict[str, Any]:
154        _ = m
155        return {}

discord専用関数

27    def __init__(self, api: "AdapterAPI", conf: "SvcConfig") -> None:
28        super().__init__()
29
30        self.api = api
31        self.conf = conf
32        """個別設定"""
api
conf

個別設定

def post_processing(self, m: integrations.protocols.MessageParserProtocol) -> None:
34    def post_processing(self, m: "MessageParserProtocol") -> None:
35        """
36        後処理(非同期処理ラッパー)
37
38        Args:
39            m (MessageParserProtocol): メッセージデータ
40
41        """
42        match m.status.action:
43            case ActionStatus.NOTHING:
44                return
45            case ActionStatus.CHANGE:
46                self.api.bot.loop.create_task(self.update_reaction(m))
47            case ActionStatus.DELETE:
48                self.api.bot.loop.create_task(self.delete_reaction(m))

後処理(非同期処理ラッパー)

Arguments:
  • m (MessageParserProtocol): メッセージデータ
async def update_reaction(self, m: integrations.protocols.MessageParserProtocol) -> None:
50    async def update_reaction(self, m: "MessageParserProtocol") -> None:
51        """
52        後処理
53
54        Args:
55            m (MessageParserProtocol): メッセージデータ
56
57        """
58        m = cast("MessageParser", m)
59        self.conf.bot_name = cast("ClientUser", self.conf.bot_name)
60
61        if not hasattr(m, "discord_msg"):
62            return
63        if await self.is_deleted_message(m.discord_msg):
64            return
65
66        emoji = {
67            "ok": "\N{WHITE HEAVY CHECK MARK}",
68            "ng": "\N{CROSS MARK}",
69        }
70
71        # 既に付いているリアクションを取得
72        has_ok = False
73        has_ng = False
74        for reaction in m.discord_msg.reactions:
75            if str(reaction.emoji) == emoji["ok"]:
76                async for user in reaction.users():
77                    if user == self.conf.bot_name:
78                        has_ok = True
79                        continue
80            if str(reaction.emoji) == emoji["ng"]:
81                async for user in reaction.users():
82                    if user == self.conf.bot_name:
83                        has_ng = True
84                        continue
85
86        # リアクション処理
87        if m.status.reaction:  # NGを外してOKを付ける
88            if has_ng:
89                await m.discord_msg.remove_reaction(emoji["ng"], self.conf.bot_name)
90            if not has_ok:
91                await m.discord_msg.add_reaction(emoji["ok"])
92        else:  # OKを外してNGを付ける
93            if has_ok:
94                await m.discord_msg.remove_reaction(emoji["ok"], self.conf.bot_name)
95            if not has_ng:
96                await m.discord_msg.add_reaction(emoji["ng"])

後処理

Arguments:
  • m (MessageParserProtocol): メッセージデータ
async def delete_reaction(self, m: integrations.protocols.MessageParserProtocol) -> None:
 98    async def delete_reaction(self, m: "MessageParserProtocol") -> None:
 99        """
100        botが付けたリアクションをすべて削除する
101
102        Args:
103            m (MessageParserProtocol): メッセージデータ
104
105        """
106        m = cast("MessageParser", m)
107
108        if hasattr(m, "discord_msg"):
109            if await self.is_deleted_message(m.discord_msg):
110                return  # 削除済み
111        else:  # Messageを持っていない場合はevent_tsとchannel_idを使って検索を試みる
112            if not m.data.channel_id:
113                return  # チャンネルIDが空
114            channel = self.api.bot.get_channel(int(m.data.channel_id))
115            if not isinstance(channel, TextChannel):
116                return  # 対象外チャンネル
117
118            async for msg in channel.history(
119                limit=10,
120                oldest_first=True,
121                after=ExtDt(float(m.data.event_ts), seconds=-1).dt,
122                before=ExtDt(float(m.data.event_ts), seconds=1).dt,
123            ):
124                if str(msg.created_at.timestamp()) == m.data.event_ts:
125                    m.discord_msg = msg
126                    break
127                return  # 該当メッセージなし(削除済み)
128
129        for reaction in m.discord_msg.reactions:
130            async for user in reaction.users():
131                if user == self.conf.bot_name:
132                    await reaction.remove(user)

botが付けたリアクションをすべて削除する

Arguments:
  • m (MessageParserProtocol): メッセージデータ
async def is_deleted_message(self, message: discord.message.Message) -> bool:
134    async def is_deleted_message(self, message: "Message") -> bool:
135        """
136        メッセージが削除済みか調べる
137
138        Args:
139            message (Message): discordオブジェクト
140
141        Returns:
142            bool: 真偽
143
144        """
145        try:
146            await message.channel.fetch_message(message.id)
147            return False
148        except NotFound:
149            return True
150        except Forbidden:
151            return True

メッセージが削除済みか調べる

Arguments:
  • message (Message): discordオブジェクト
Returns:

bool: 真偽

def get_conversations( self, m: integrations.protocols.MessageParserProtocol) -> dict[str, typing.Any]:
153    def get_conversations(self, m: "MessageParserProtocol") -> dict[str, Any]:
154        _ = m
155        return {}

スレッド情報の取得

Arguments:
  • m (MessageParserProtocol): メッセージデータ
Returns:

dict: API response