diff options
author | 2020-05-26 19:09:25 +0200 | |
---|---|---|
committer | 2020-05-26 19:09:25 +0200 | |
commit | 38fcc10518ccec60b2238180bc6539f3437a2a3d (patch) | |
tree | f0045b24a2452619c7660f409e842104b1ef026a | |
parent | Add a test for RuntimeErrors. (diff) | |
parent | Merge pull request #866 from python-discord/restricted_tags (diff) |
Merge branch 'master' into redis_persistence
-rw-r--r-- | bot/cogs/filtering.py | 4 | ||||
-rw-r--r-- | bot/cogs/help_channels.py | 40 | ||||
-rw-r--r-- | bot/cogs/stats.py | 8 | ||||
-rw-r--r-- | bot/cogs/tags.py | 59 | ||||
-rw-r--r-- | bot/constants.py | 3 | ||||
-rw-r--r-- | config-default.yml | 7 |
6 files changed, 97 insertions, 24 deletions
diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 1e21a4ce3..1d9fddb12 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -214,7 +214,9 @@ class Filtering(Cog): additional_embeds = None additional_embeds_msg = None - if filter_name == "filter_invites": + # The function returns True for invalid invites. + # They have no data so additional embeds can't be created for them. + if filter_name == "filter_invites" and match is not True: additional_embeds = [] for invite, data in match.items(): embed = discord.Embed(description=( diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index a20fe2b05..d2a55fba6 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -438,13 +438,13 @@ class HelpChannels(Scheduler, commands.Cog): """Return True if `member` has the 'Help Cooldown' role.""" return any(constants.Roles.help_cooldown == role.id for role in member.roles) - def is_dormant_message(self, message: t.Optional[discord.Message]) -> bool: - """Return True if the contents of the `message` match `DORMANT_MSG`.""" + def match_bot_embed(self, message: t.Optional[discord.Message], description: str) -> bool: + """Return `True` if the bot's `message`'s embed description matches `description`.""" if not message or not message.embeds: return False embed = message.embeds[0] - return message.author == self.bot.user and embed.description.strip() == DORMANT_MSG.strip() + return message.author == self.bot.user and embed.description.strip() == description.strip() @staticmethod def is_in_category(channel: discord.TextChannel, category_id: int) -> bool: @@ -461,7 +461,11 @@ class HelpChannels(Scheduler, commands.Cog): """ log.trace(f"Handling in-use channel #{channel} ({channel.id}).") - idle_seconds = constants.HelpChannels.idle_minutes * 60 + if not await self.is_empty(channel): + idle_seconds = constants.HelpChannels.idle_minutes * 60 + else: + idle_seconds = constants.HelpChannels.deleted_idle_minutes * 60 + time_elapsed = await self.get_idle_time(channel) if time_elapsed is None or time_elapsed >= idle_seconds: @@ -713,6 +717,32 @@ class HelpChannels(Scheduler, commands.Cog): # be put in the queue. await self.move_to_available() + @commands.Cog.listener() + async def on_message_delete(self, msg: discord.Message) -> None: + """ + Reschedule an in-use channel to become dormant sooner if the channel is empty. + + The new time for the dormant task is configured with `HelpChannels.deleted_idle_minutes`. + """ + if not self.is_in_category(msg.channel, constants.Categories.help_in_use): + return + + if not await self.is_empty(msg.channel): + return + + log.info(f"Claimant of #{msg.channel} ({msg.author}) deleted message, channel is empty now. Rescheduling task.") + + # Cancel existing dormant task before scheduling new. + self.cancel_task(msg.channel.id) + + task = TaskData(constants.HelpChannels.deleted_idle_minutes * 60, self.move_idle_channel(msg.channel)) + self.schedule_task(msg.channel.id, task) + + async def is_empty(self, channel: discord.TextChannel) -> bool: + """Return True if the most recent message in `channel` is the bot's `AVAILABLE_MSG`.""" + msg = await self.get_last_message(channel) + return self.match_bot_embed(msg, AVAILABLE_MSG) + async def reset_send_permissions(self) -> None: """Reset send permissions in the Available category for claimants.""" log.trace("Resetting send permissions in the Available category.") @@ -788,7 +818,7 @@ class HelpChannels(Scheduler, commands.Cog): embed = discord.Embed(description=AVAILABLE_MSG) msg = await self.get_last_message(channel) - if self.is_dormant_message(msg): + if self.match_bot_embed(msg, DORMANT_MSG): log.trace(f"Found dormant message {msg.id} in {channel_info}; editing it.") await msg.edit(embed=embed) else: diff --git a/bot/cogs/stats.py b/bot/cogs/stats.py index b55497e68..4ebb6423c 100644 --- a/bot/cogs/stats.py +++ b/bot/cogs/stats.py @@ -6,7 +6,7 @@ from discord.ext.commands import Cog, Context from discord.ext.tasks import loop from bot.bot import Bot -from bot.constants import Channels, Guild, Stats as StatConf +from bot.constants import Categories, Channels, Guild, Stats as StatConf CHANNEL_NAME_OVERRIDES = { @@ -36,6 +36,12 @@ class Stats(Cog): if message.guild.id != Guild.id: return + if message.channel.category.id == Categories.modmail: + if message.channel.id != Channels.incidents: + # Do not report modmail channels to stats, there are too many + # of them for interesting statistics to be drawn out of this. + return + reformatted_name = message.channel.name.replace('-', '_') if CHANNEL_NAME_OVERRIDES.get(message.channel.id): diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index a813ffff5..bc7f53f68 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -4,7 +4,7 @@ import time from pathlib import Path from typing import Callable, Dict, Iterable, List, Optional -from discord import Colour, Embed +from discord import Colour, Embed, Member from discord.ext.commands import Cog, Context, group from bot import constants @@ -35,21 +35,36 @@ class Tags(Cog): @staticmethod def get_tags() -> dict: """Get all tags.""" - # Save all tags in memory. cache = {} - tag_files = Path("bot", "resources", "tags").iterdir() - for file in tag_files: - tag_title = file.stem - tag = { - "title": tag_title, - "embed": { - "description": file.read_text(encoding="utf-8") + + base_path = Path("bot", "resources", "tags") + for file in base_path.glob("**/*"): + if file.is_file(): + tag_title = file.stem + tag = { + "title": tag_title, + "embed": { + "description": file.read_text(), + }, + "restricted_to": "developers", } - } - cache[tag_title] = tag + + # Convert to a list to allow negative indexing. + parents = list(file.relative_to(base_path).parents) + if len(parents) > 1: + # -1 would be '.' hence -2 is used as the index. + tag["restricted_to"] = parents[-2].name + + cache[tag_title] = tag + return cache @staticmethod + def check_accessibility(user: Member, tag: dict) -> bool: + """Check if user can access a tag.""" + return tag["restricted_to"].lower() in [role.name.lower() for role in user.roles] + + @staticmethod def _fuzzy_search(search: str, target: str) -> float: """A simple scoring algorithm based on how many letters are found / total, with order in mind.""" current, index = 0, 0 @@ -93,7 +108,7 @@ class Tags(Cog): return self._get_suggestions(tag_name) return found - def _get_tags_via_content(self, check: Callable[[Iterable], bool], keywords: str) -> list: + def _get_tags_via_content(self, check: Callable[[Iterable], bool], keywords: str, user: Member) -> list: """ Search for tags via contents. @@ -114,7 +129,8 @@ class Tags(Cog): matching_tags = [] for tag in self._cache.values(): - if check(query in tag['embed']['description'].casefold() for query in keywords_processed): + matches = (query in tag['embed']['description'].casefold() for query in keywords_processed) + if self.check_accessibility(user, tag) and check(matches): matching_tags.append(tag) return matching_tags @@ -152,7 +168,7 @@ class Tags(Cog): Only search for tags that has ALL the keywords. """ - matching_tags = self._get_tags_via_content(all, keywords) + matching_tags = self._get_tags_via_content(all, keywords, ctx.author) await self._send_matching_tags(ctx, keywords, matching_tags) @search_tag_content.command(name='any') @@ -162,7 +178,7 @@ class Tags(Cog): Search for tags that has ANY of the keywords. """ - matching_tags = self._get_tags_via_content(any, keywords or 'any') + matching_tags = self._get_tags_via_content(any, keywords or 'any', ctx.author) await self._send_matching_tags(ctx, keywords, matching_tags) @tags_group.command(name='get', aliases=('show', 'g')) @@ -198,7 +214,13 @@ class Tags(Cog): return if tag_name is not None: - founds = self._get_tag(tag_name) + temp_founds = self._get_tag(tag_name) + + founds = [] + + for found_tag in temp_founds: + if self.check_accessibility(ctx.author, found_tag): + founds.append(found_tag) if len(founds) == 1: tag = founds[0] @@ -237,7 +259,10 @@ class Tags(Cog): else: embed: Embed = Embed(title="**Current tags**") await LinePaginator.paginate( - sorted(f"**»** {tag['title']}" for tag in tags), + sorted( + f"**»** {tag['title']}" for tag in tags + if self.check_accessibility(ctx.author, tag) + ), ctx, embed, footer_text=FOOTER_TEXT, diff --git a/bot/constants.py b/bot/constants.py index 145ae54db..eae083ab4 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -376,6 +376,7 @@ class Categories(metaclass=YAMLGetter): help_available: int help_in_use: int help_dormant: int + modmail: int class Channels(metaclass=YAMLGetter): @@ -395,6 +396,7 @@ class Channels(metaclass=YAMLGetter): esoteric: int helpers: int how_to_get_help: int + incidents: int message_log: int meta: int mod_alerts: int @@ -552,6 +554,7 @@ class HelpChannels(metaclass=YAMLGetter): claim_minutes: int cmd_whitelist: List[int] idle_minutes: int + deleted_idle_minutes: int max_available: int max_total_channels: int name_prefix: str diff --git a/config-default.yml b/config-default.yml index cee955f20..bb66890a3 100644 --- a/config-default.yml +++ b/config-default.yml @@ -124,6 +124,7 @@ guild: help_available: 691405807388196926 help_in_use: 696958401460043776 help_dormant: 691405908919451718 + modmail: 714494672835444826 channels: announcements: 354619224620138496 @@ -170,6 +171,7 @@ guild: mod_spam: &MOD_SPAM 620607373828030464 organisation: &ORGANISATION 551789653284356126 staff_lounge: &STAFF_LOUNGE 464905259261755392 + incidents: 714214212200562749 # Voice admins_voice: &ADMINS_VOICE 500734494840717332 @@ -324,6 +326,7 @@ filter: - poweredbysecurity.online - ssteam.site - steamwalletgift.com + - discord.gift word_watchlist: - goo+ks* @@ -537,6 +540,10 @@ help_channels: # Allowed duration of inactivity before making a channel dormant idle_minutes: 30 + # Allowed duration of inactivity when question message deleted + # and no one other sent before message making channel dormant. + deleted_idle_minutes: 5 + # Maximum number of channels to put in the available category max_available: 2 |