integrations.discord.functions

integrations/discord/functions.py

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

discord専用関数

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

個別設定

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

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

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

後処理

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

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

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

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

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

bool: 真偽

def get_conversations(self, m: integrations.protocols.MessageParserProtocol) -> dict:
147    def get_conversations(self, m: "MessageParserProtocol") -> dict:
148        _ = m
149        return {}

スレッド情報の取得

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

dict: API response