aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Leon Sandøy <[email protected]>2020-05-26 19:09:25 +0200
committerGravatar GitHub <[email protected]>2020-05-26 19:09:25 +0200
commit38fcc10518ccec60b2238180bc6539f3437a2a3d (patch)
treef0045b24a2452619c7660f409e842104b1ef026a
parentAdd a test for RuntimeErrors. (diff)
parentMerge pull request #866 from python-discord/restricted_tags (diff)
Merge branch 'master' into redis_persistence
-rw-r--r--bot/cogs/filtering.py4
-rw-r--r--bot/cogs/help_channels.py40
-rw-r--r--bot/cogs/stats.py8
-rw-r--r--bot/cogs/tags.py59
-rw-r--r--bot/constants.py3
-rw-r--r--config-default.yml7
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