from __future__ import annotations import asyncio import re from datetime import datetime from typing import List, Union import discord EVERYONE_REGEX = re.compile(r"@here|@everyone") async def dummy_awaitable(*args, **kwargs): return def neuter_coroutines(klass): # I might forget to modify this with discord.py updates, so lets automate it. for attr in dir(klass): _ = getattr(klass, attr, None) if asyncio.iscoroutinefunction(_): def dummy(self): return dummy_awaitable prop = property(fget=dummy) setattr(klass, attr, prop) return klass async def replacement_delete_messages(self, messages): message_ids = list({m.id for m in messages if m.__class__.__name__ != "SchedulerMessage"}) if not message_ids: return if len(message_ids) == 1: await self._state.http.delete_message(self.id, message_ids[0]) return if len(message_ids) > 100: raise discord.ClientException("Can only bulk delete messages up to 100 messages") await self._state.http.delete_messages(self.id, message_ids) # This entire below block is such an awful hack. Don't look at it too closely. @neuter_coroutines class SchedulerMessage(discord.Message): """ Subclassed discord message with neutered coroutines. Extremely butchered class for a specific use case. Be careful when using this in other use cases. """ def __init__(self, *, content: str, author: discord.Member, channel: discord.TextChannel) -> None: # auto current time self.id = discord.utils.time_snowflake(datetime.utcnow()) # important properties for even being processed self.author = author self.channel = channel self.content = content self.guild = channel.guild # type: ignore # this attribute being in almost everything (and needing to be) is a pain self._state = self.guild._state # type: ignore # sane values below, fresh messages which are commands should exhibit these. self.call = None self.type = discord.MessageType.default self.tts = False self.pinned = False # suport for attachments somehow later maybe? self.attachments: List[discord.Attachment] = [] # mentions self.mention_everyone = self.channel.permissions_for(self.author).mention_everyone and bool( EVERYONE_REGEX.match(self.content) ) # pylint: disable=E1133 # pylint improperly detects the inherited properties here as not being iterable # This should be fixed with typehint support added to upstream lib later self.mentions: List[Union[discord.User, discord.Member]] = list( filter(None, [self.guild.get_member(idx) for idx in self.raw_mentions]) ) self.channel_mentions: List[discord.TextChannel] = list( filter( None, [self.guild.get_channel(idx) for idx in self.raw_channel_mentions], # type: ignore ) ) self.role_mentions: List[discord.Role] = list( filter(None, [self.guild.get_role(idx) for idx in self.raw_role_mentions]) )