async def _deal_with_attachments(self, message: Message): for attachment in message.attachments: try: extension = attachment.filename.rsplit('.', 1)[1] except IndexError: extension = "" extension = extension.lower() if extension in extension_to_pastebin: file_content = await attachment.read() url = await self.create_pastebin_link(file_content) reply = ( f"It looks like you tried to attach a {extension} file which is not allowed, " "however since it could be code related you can find the paste link here:\n" f"[**{attachment.filename}** {url}]" ) await message.channel.send(f"Hey {message.author.mention}!", embed=warning(reply)) await message.delete() elif extension not in allowed_file_extensions: reply = ( f"It looks like you tried to attach a {extension} file which is not allowed, " "as it could potentially contain malicious code." ) await message.channel.send(f"Hey {message.author.mention}!", embed=warning(reply)) await message.delete()
async def on_message(self, message): if message.guild is None: return elif message.guild.id != constants.tortoise_guild_id: return if not message.author.bot and len( message.content) > constants.max_message_length: # Below part is when someone sends too long message, bot will recommend them to use our pastebin # TODO we are skipping message deletion for now until we implement system to check # if sent message is code or not msg = ( "Your message is quite long.\n" f"You should consider using our paste service {constants.tortoise_paste_service_link}" ) await message.channel.send(embed=warning(msg)) if message.channel.id == constants.suggestions_channel_id: if (message.author == self.bot.user and message.embeds and message.embeds[0].description == self.SUGGESTION_MESSAGE_CONTENT): await self.bot.api_client.edit_suggestion_message_id(message.id ) else: old_suggestion_msg_id = await self.bot.api_client.get_suggestion_message_id( ) try: old_message = await message.channel.fetch_message( old_suggestion_msg_id) except discord.NotFound: pass else: await old_message.delete() await self.create_new_suggestion_message()
async def deal_with_invites(self, message: Message) -> bool: """ Checks if the message has any invites that are not for Tortoise guild, if so deletes the message. Works both with discord.com/invite and discord.gg ,including link shorteners. :param message: message to check for Discord invites :return: bool, was the passed message deleted or not? """ base_url = re.findall( r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+", # Find any url message.content) for invite in base_url: # Get the endpoint of that url (for discord invite url shorteners) try: async with self.session.get(invite) as response: invite = str(response.url) except aiohttp.ClientConnectorError: # The link is not valid continue if "discord.com/invite/" in invite or "discord.gg/" in invite: if not await self.check_if_invite_is_our_guild( invite, message.guild): # TODO give him warning points etc / send to deterrence channel embed = warning( f"{message.author.mention} You are not allowed to send other server invites here." ) await message.channel.send(embed=embed) await message.delete() return True # If we've come here we did not delete our message return False
async def deal_with_long_code(self, message: Message) -> bool: """ When someone sends long message containing code, bot will delete it and upload message content to our pastebin and reply with it's link. Guessing is quite CPU intensive so be sure to check it only for long messages (not for each). :param message: message to check :return: bool, was the passed message deleted or not? """ if len(message.content) <= constants.max_message_length: return False await message.channel.trigger_typing() language = await self.bot.loop.run_in_executor( None, functools.partial(self.guess_language.language_name, source_code=message.content)) if not language: return False pastebin_link = await self.create_pastebin_link( message.content.encode()) await message.delete() msg = ( f"Detected a long message containing {language} code.\n" f"To improve readability I've uploaded it to our pastebin: {pastebin_link}" ) await message.channel.send(embed=warning(msg)) return True
async def _mass_ban_timestamp_helper(self, ctx, timestamp_start: datetime, timestamp_end: datetime, reason: str): members_to_ban = [] for member in self.tortoise_guild.members: if member.joined_at is None: continue if timestamp_start < member.joined_at < timestamp_end: members_to_ban.append(member) if not members_to_ban: return await ctx.send(embed=failure("Could not find any members, aborting..")) members_to_ban.sort(key=lambda m: m.joined_at) reaction_msg = await ctx.send( embed=warning( f"This will ban {len(members_to_ban)} members, " f"first one being {members_to_ban[0]} and last one being {members_to_ban[-1]}.\n" f"Are you sure you want to continue?" ) ) confirmation = await ConfirmationMessage.create_instance(self.bot, reaction_msg, ctx.author) if confirmation: one_tenth = len(members_to_ban) // 10 notify_interval = one_tenth if one_tenth > 50 else 50 await ctx.send( embed=info( f"Starting the ban process, please be patient.\n" f"You will be notified for each {notify_interval} banned members.", ctx.author ) ) logger.info(f"{ctx.author} is timestamp banning: {', '.join(str(member.id) for member in members_to_ban)}") for count, member in enumerate(members_to_ban): if count != 0 and count % notify_interval == 0: await ctx.send(embed=info(f"Banned {count} members..", ctx.author)) await ctx.guild.ban(member, reason=reason) message = f"Successfully mass banned {len(members_to_ban)} members!" await ctx.send(embed=success(message)) await self.deterrence_log_channel.send(embed=authored(message, author=ctx.author)) else: await ctx.send(embed=info("Aborting mass ban.", ctx.me))
async def ban_timestamp(self, ctx, timestamp_start: DatetimeConverter, timestamp_end: DatetimeConverter, *, reason="Mass ban with timestamp."): """Bans member from the guild if he joined at specific time. Both arguments need to be in this specific format: %Y-%m-%d %H:%M Example: t.ban_timestamp "2020-09-15 13:00" "2020-10-15 13:00" All values need to be padded with 0. Timezones are not accounted for. """ members_to_ban = [] for member in self.tortoise_guild.members: if member.joined_at is None: continue if timestamp_start < member.joined_at < timestamp_end: members_to_ban.append(member) if not members_to_ban: return await ctx.send( embed=failure("Could not find any members, aborting..")) reaction_msg = await ctx.send(embed=warning( f"This will ban {len(members_to_ban)} members, " f"first one being {members_to_ban[0]} and last one being {members_to_ban[-1]}.\n" f"Are you sure you want to continue?")) confirmation = await ConfirmationMessage.create_instance( self.bot, reaction_msg, ctx.author) if confirmation: logger.info( f"{ctx.author} is timestamp banning: {', '.join(member.id for member in members_to_ban)}" ) for member in members_to_ban: await self._ban_helper(ctx, member, reason) await ctx.send(embed=success( f"Successfully mass banned {len(members_to_ban)} members!")) else: await ctx.send(embed=info("Aborting mass ban.", ctx.me))
async def deal_with_attachments(self, message: Message) -> bool: """ Will delete message if it has attachment that we don't allow or if it is a whitelisted attachment extension it will upload it's content to our pastebin and reply with link to it. :param message: message to check for attachments :return: bool, was the passed message deleted or not? """ reply = None for attachment in message.attachments: try: extension = attachment.filename.rsplit('.', 1)[1] except IndexError: extension = "" # file has no extension extension = extension.lower() if extension in extension_to_pastebin: if attachment.size > 4096: reply = ( f"It looks like you tried to attach a {extension} file which " f"could be code related but since it's too big in size I will not be uploading it " f"to our pastebin for viewing.") else: file_content = await attachment.read() url = await self.create_pastebin_link(file_content) reply = ( f"It looks like you tried to attach a {extension} file which is not allowed, " "however since it could be code related you can find the paste link here:\n" f"[**{attachment.filename}** {url}]") elif extension not in allowed_file_extensions: reply = ( f"It looks like you tried to attach a {extension} file which is not allowed, " "as it could potentially contain malicious code.") if reply: await message.channel.send(f"Hey {message.author.mention}!", embed=warning(reply)) await message.delete() return True # If we've come here we did not delete our message return False
async def _deal_with_invites(self, message: Message): base_url = re.findall( r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+", # Find any url message.content ) for invite in base_url: # Get the endpoint of that url (for discord invite url shorteners) try: async with self.session.get(invite) as response: invite = str(response.url) except aiohttp.ClientConnectorError: # The link is not valid continue if "discord.com/invite/" in invite or "discord.gg/" in invite: if not await Security.check_if_invite_is_our_guild(invite, message.guild): # TODO give him warning points etc / send to deterrence channel embed = warning(f"{message.author.mention} You are not allowed to send other server invites here.") await message.channel.send(embed=embed) await message.delete()