async def split_and_send_embed( message: str, channel: discord.abc.Messageable, use_uniform: bool = True, requesting_user: Optional[Union[discord.User, discord.Member]] = None, **kwargs, ): if not use_uniform and requesting_user is not None: raise ValueError("requesting_user argument is only valid if use_inform is True") async with channel.typing(): embeds = [] if use_uniform: for chunk in split_message( message, character_limit=6000 - len(uniform_embed(requesting_user, **kwargs)), ): embeds.append( uniform_embed(requesting_user, description=chunk, **kwargs) ) else: for chunk in split_message( message, character_limit=6000 - len(discord.Embed(**kwargs)) ): embeds.append(discord.Embed(**kwargs)) for embed in embeds: await channel.send(embed=embed)
async def purge_ids( channel: discord.abc.Messageable, ids: Iterable[int], now: Union[datetime, discord.abc.Snowflake], ): channel = await channel._get_channel() assert isinstance(channel, (discord.TextChannel, discord.DMChannel)) try: nowdt: datetime = now.created_at # type: ignore except AttributeError: assert isinstance(now, datetime) nowdt = now snowflakes = [ channel.get_partial_message(i) for i in ids if nowdt - discord.utils.snowflake_time(i) < timedelta(days=14, minutes=-5) ] if not snowflakes: return try: can_purge = channel.permissions_for( channel.guild.me).manage_messages # type: ignore except AttributeError: can_purge = False if can_purge: assert isinstance(channel, discord.TextChannel) while True: try: await channel.delete_messages(snowflakes[:100]) # type: ignore except discord.Forbidden: return except discord.HTTPException: pass snowflakes = snowflakes[100:] if snowflakes: await asyncio.sleep(1.5) else: return else: for partial in snowflakes: try: await partial.delete() except discord.Forbidden: return except discord.HTTPException: pass
async def type_message(destination: discord.abc.Messageable, content: str, **kwargs) -> discord.Message: """Simulate typing and sending a message to a destination. Will send a typing indicator, wait a variable amount of time based on the length of the text (to simulate typing speed), then send the message. """ content = common_filters.filter_urls(content) try: async with destination.typing(): await asyncio.sleep(max(0.25, min(2.5, len(content) * 0.01))) return await destination.send(content=content, **kwargs) except discord.HTTPException: pass
async def send_ooc_message(self, messageable: discord.abc.Messageable): ooc_channel = self.bot.get_channel(self.channel_id) if not ooc_channel: return await messageable.send( 'Could not find out of context channel') if len(self.attachments) == 0: return await messageable.send('No images to send') async with messageable.typing(): random_attachment = random.choice(self.attachments) attachment_file = await random_attachment.to_file() await messageable.send(file=attachment_file)
async def type_message(destination: discord.abc.Messageable, content: str, **kwargs) -> discord.Message: """Simulate typing and sending a message to a destination. Will send a typing indicator, wait a variable amount of time based on the length of the text (to simulate typing speed), then send the message. """ content = common_filters.filter_mass_mentions(content) content = common_filters.filter_urls(content) content = common_filters.filter_various_mentions(content) try: async with destination.typing(): await asyncio.sleep(len(content) * 0.01) return await destination.send(content=content, **kwargs) except discord.HTTPException: pass # Not allowed to send messages to this destination (or, sending the message failed)
async def _create_quote_embed( self, msg: discord.Message, destination: discord.abc.Messageable) -> discord.Embed: destination_guild_id = getattr(getattr(destination, "guild", None), "id", None) embed = discord.Embed( description=msg.content if getattr(msg.guild, "id", None) == destination_guild_id else msg.clean_content, color=msg.author.color.value or discord.Embed.Empty, timestamp=msg.created_at, ) embed.set_author(name=str(msg.author), url=msg.jump_url, icon_url=getattr(msg.author.avatar, "url", DEFAULT_AVATAR_URL)) # TODO: embed images from imgur, Gyazo, etc. if msg.attachments: if (not isinstance( msg.channel, (discord.abc.PrivateChannel, discord.PartialMessageable)) and msg.channel.is_nsfw() and (isinstance(destination, (discord.abc.PrivateChannel, discord.PartialMessageable, discord.User)) or not destination.is_nsfw())): embed.add_field( name= f"{await self.localize('QUOTE_quote_attachments', destination_guild_id)}", value= f":underage: {await self.localize('QUOTE_quote_nonsfw', destination_guild_id)}", ) elif len(msg.attachments) == 1 and ( url := msg.attachments[0].url).lower().endswith( (".jpg", ".jpeg", ".jfif", ".png", ".gif", ".gifv", ".webp", ".bmp", ".svg", ".tiff")): embed.set_image(url=url) else: embed.add_field( name= f"{await self.localize('QUOTE_quote_attachments', destination_guild_id)}", value="\n".join( f"[{attachment.filename}]({attachment.url})" for attachment in msg.attachments), )
async def long_send(channel: discord.abc.Messageable, message: str, quotes=False, cut_on_new_line=True, embed=False, **kwargs) -> Optional[Message]: """Send multiples messages for long contents. Returns the last message sent if one.""" if cut_on_new_line: messages = _split_on_new_lines_message(message, quotes) else: messages = _split_message(message, quotes) if embed and (isinstance(channel, TextChannel) and channel.permissions_for(channel.guild.me).embed_links): return await _send_embed(channel, messages, **kwargs) else: res = None for msg in messages: res = await channel.send(msg) return res
async def send_notification( self, destination: discord.abc.Messageable, description: str, *, title: str = None, fields: list = [], footer: str = None, thumbnail: str = None, ping=False, file: discord.File = None, react: str = None, jump_to: discord.Message = None, allow_everyone_ping=False, force_text_only=False, heat_key: str = None, no_repeat_for: datetime.timedelta = None, quick_action: QuickAction = None) -> Optional[discord.Message]: """Sends a notification to the staff channel if a guild is passed. Embed preference is respected.""" if no_repeat_for: if isinstance(destination, discord.Guild): guild = destination else: guild = destination.guild if not heat_key: # A custom heat_key can be passed to block dynamic content heat_key = f"{destination.id}-{description}-{fields}" heat_key = f"core-notif-{crc32(heat_key.encode('utf-8', 'ignore'))}" if not heat.get_custom_heat(guild, heat_key) == 0: return heat.increase_custom_heat(guild, heat_key, no_repeat_for) guild = destination is_staff_notification = False if isinstance(destination, discord.Guild): is_staff_notification = True notify_channel_id = await self.config.guild(destination ).notify_channel() destination = destination.get_channel(notify_channel_id) if destination is None: return staff_mention = "" if ping and is_staff_notification: staff_mention = f"<@&{await self.config.guild(guild).notify_role()}> " embed = None send_embed = await self.bot.embed_requested(destination, None) if send_embed is True and force_text_only is False: if jump_to: description += f"\n[Click to jump]({jump_to.jump_url})" embed = discord.Embed( title=title if title else "", description=description, ) if footer: embed.set_footer(text=footer) if thumbnail: embed.set_thumbnail(url=thumbnail) for field in fields: embed.add_field(**field) message_content = staff_mention embed.color = await self.bot.get_embed_color(destination) embed.timestamp = utcnow() else: title = f"**{title}\n**" if title else "" footer = f"\n*{footer}*" if footer else "" fields_txt = "" for field in fields: fields_txt += f"\n**{field['name']}**: {field['value']}" jump_to = f"\n{jump_to.jump_url}" if jump_to else "" message_content = f"{title}{staff_mention}{description}{fields_txt}{jump_to}{footer}" allowed_mentions = discord.AllowedMentions( roles=True, everyone=allow_everyone_ping) msg = await destination.send(message_content, file=file, embed=embed, allowed_mentions=allowed_mentions) if react: await msg.add_reaction(react) if quick_action and is_staff_notification: self.quick_actions[guild.id][msg.id] = quick_action return msg