From 79ee2b0d574090e7f2c6006325742f1d7f9bb469 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 19 Aug 2022 15:10:13 +0100 Subject: fix "isistance" typo --- tests/test_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index f3040b305..b2686b1d0 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -14,7 +14,7 @@ class DiscordMocksTests(unittest.TestCase): """Test if the default initialization of MockRole results in the correct object.""" role = helpers.MockRole() - # The `spec` argument makes sure `isistance` checks with `discord.Role` pass + # The `spec` argument makes sure `isinstance` checks with `discord.Role` pass self.assertIsInstance(role, discord.Role) self.assertEqual(role.name, "role") -- cgit v1.2.3 From ddd829fc235b0484064fce0c4924e980659ec2bb Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 19 Aug 2022 15:12:59 +0100 Subject: add support for keywords when using the "rules" command. This doesn't change the way the rules command originally worked and keeps the priority to rule numbers. But in case a number (or numbers) is not supplied, it will try to find a rule that maps to a the supplied keyword. --- bot/exts/info/information.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index e7d17c971..d53f4e323 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -518,12 +518,12 @@ class Information(Cog): await self.send_raw_content(ctx, message, json=True) @command(aliases=("rule",)) - async def rules(self, ctx: Context, rules: Greedy[int]) -> None: + async def rules(self, ctx: Context, rules: Greedy[int], keyword: Optional[str]) -> None: """Provides a link to all rules or, if specified, displays specific rule(s).""" rules_embed = Embed(title="Rules", color=Colour.og_blurple(), url="https://www.pythondiscord.com/pages/rules") - if not rules: - # Rules were not submitted. Return the default description. + if not rules and not keyword: + # Neither rules nor keywords were submitted. Return the default description. rules_embed.description = ( "The rules and guidelines that apply to this community can be found on" " our [rules page](https://www.pythondiscord.com/pages/rules). We expect" @@ -547,7 +547,21 @@ class Information(Cog): for rule in rules: self.bot.stats.incr(f"rule_uses.{rule}") - final_rules = tuple(f"**{pick}.** {full_rules[pick - 1]}" for pick in rules) + if rules: + final_rules = tuple(f"**{pick}.** {full_rules[pick - 1][0]}" for pick in rules) + else: + final_rules = tuple(f"**{pick + 1}** {full_rules[pick][0]}" for pick, rule in enumerate(full_rules) + if keyword in rule[1]) + + if not rules and not final_rules: + # This would mean that we didn't find a match for the used keyword + rules_embed.description = ( + f"There are currently no rules that correspond to keyword: {keyword}." + " If you think it should be added, please ask our admins and they'll take care of the rest." + ) + + await ctx.send(embed=rules_embed) + return await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From 357ed80e2a0f35172ca91ceda7a353a4a8f765dd Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Thu, 8 Sep 2022 23:11:41 +0100 Subject: send the list of pre-defined keywords along with each invoked rule --- bot/exts/info/information.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index d53f4e323..54fa62d31 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -521,6 +521,7 @@ class Information(Cog): async def rules(self, ctx: Context, rules: Greedy[int], keyword: Optional[str]) -> None: """Provides a link to all rules or, if specified, displays specific rule(s).""" rules_embed = Embed(title="Rules", color=Colour.og_blurple(), url="https://www.pythondiscord.com/pages/rules") + keyword = keyword.lower() if not rules and not keyword: # Neither rules nor keywords were submitted. Return the default description. @@ -548,9 +549,13 @@ class Information(Cog): self.bot.stats.incr(f"rule_uses.{rule}") if rules: - final_rules = tuple(f"**{pick}.** {full_rules[pick - 1][0]}" for pick in rules) + final_rules = tuple(f"**{pick}.** {full_rules[pick - 1][0]}\n\n" + f"You can also invoke this rule with the following keywords: " + f"{', '.join(full_rules[pick -1][1])}" for pick in rules) else: - final_rules = tuple(f"**{pick + 1}** {full_rules[pick][0]}" for pick, rule in enumerate(full_rules) + final_rules = tuple(f"**{pick + 1}** {full_rules[pick][0]}\n\n" + f"You can also invoke this rule with the following keywords: " + f"{', '.join(full_rules[pick][1])}" for pick, rule in enumerate(full_rules) if keyword in rule[1]) if not rules and not final_rules: -- cgit v1.2.3 From f924dd24a6846dcc00f96c7158111cfffc092014 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Thu, 8 Sep 2022 23:17:25 +0100 Subject: send the "no-match" error message in pure text format --- bot/exts/info/information.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 54fa62d31..9dd0a6d45 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -560,12 +560,9 @@ class Information(Cog): if not rules and not final_rules: # This would mean that we didn't find a match for the used keyword - rules_embed.description = ( + await ctx.send( f"There are currently no rules that correspond to keyword: {keyword}." - " If you think it should be added, please ask our admins and they'll take care of the rest." - ) - - await ctx.send(embed=rules_embed) + "If you think it should be added, please ask our admins and they'll take care of the rest.") return await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From c1ce6839ae4a944ee4207d8d283a738f64937b3a Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 11 Sep 2022 09:30:31 +0100 Subject: accept keywords and rule numbers in any order --- bot/exts/info/information.py | 58 +++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 9dd0a6d45..3dd12c005 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -8,7 +8,7 @@ from typing import Any, DefaultDict, Mapping, Optional, Tuple, Union import rapidfuzz from botcore.site_api import ResponseCodeError from discord import AllowedMentions, Colour, Embed, Guild, Message, Role -from discord.ext.commands import BucketType, Cog, Context, Greedy, Paginator, command, group, has_any_role +from discord.ext.commands import BucketType, Cog, Context, Paginator, command, group, has_any_role from discord.utils import escape_markdown from bot import constants @@ -518,12 +518,23 @@ class Information(Cog): await self.send_raw_content(ctx, message, json=True) @command(aliases=("rule",)) - async def rules(self, ctx: Context, rules: Greedy[int], keyword: Optional[str]) -> None: - """Provides a link to all rules or, if specified, displays specific rule(s).""" + async def rules(self, ctx: Context, *args: Optional[str]) -> None: + """ + Provides a link to all rules or, if specified, displays specific rules(s). + + It accepts either rule numbers or particular keywords that map to a particular rule. + Rule numbers and keywords can be sent in any order. + """ rules_embed = Embed(title="Rules", color=Colour.og_blurple(), url="https://www.pythondiscord.com/pages/rules") - keyword = keyword.lower() + keywords, rules = [], [] + + for word in args: + if word.isdigit(): + rules.append(int(word)) + else: + keywords.append(word.lower()) - if not rules and not keyword: + if not rules and not keywords: # Neither rules nor keywords were submitted. Return the default description. rules_embed.description = ( "The rules and guidelines that apply to this community can be found on" @@ -538,30 +549,39 @@ class Information(Cog): # Remove duplicates and sort the rule indices rules = sorted(set(rules)) + # Remove duplicate keywords + keywords = set(keywords) invalid = ", ".join(str(index) for index in rules if index < 1 or index > len(full_rules)) - if invalid: + if invalid and not keywords: await ctx.send(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ...")) return - for rule in rules: - self.bot.stats.incr(f"rule_uses.{rule}") + final_rules = set() - if rules: - final_rules = tuple(f"**{pick}.** {full_rules[pick - 1][0]}\n\n" - f"You can also invoke this rule with the following keywords: " - f"{', '.join(full_rules[pick -1][1])}" for pick in rules) - else: - final_rules = tuple(f"**{pick + 1}** {full_rules[pick][0]}\n\n" - f"You can also invoke this rule with the following keywords: " - f"{', '.join(full_rules[pick][1])}" for pick, rule in enumerate(full_rules) - if keyword in rule[1]) + for pick in rules: + self.bot.stats.incr(f"rule_uses.{pick}") + final_rules.add( + f"**{pick}.** {full_rules[pick - 1][0]}\n\n" + f"You can also invoke this rule with the following keywords: " + f"{', '.join(full_rules[pick -1][1])}") + + for keyword in keywords: + for pick, rule in enumerate(full_rules): + if keyword not in rule[1]: + continue + + self.bot.stats.incr(f"rule_uses.{pick + 1}") + final_rules.add( + f"**{pick + 1}.** {full_rules[pick][0]}\n\n" + f"You can also invoke this rule with the following keywords: " + f"{', '.join(full_rules[pick][1])}") if not rules and not final_rules: - # This would mean that we didn't find a match for the used keyword + # This would mean that only keywords where used and no match for them was found await ctx.send( - f"There are currently no rules that correspond to keyword: {keyword}." + f"There are currently no rules that correspond to keywords: {[', '.join(keywords)]}." "If you think it should be added, please ask our admins and they'll take care of the rest.") return -- cgit v1.2.3 From 6b3b18078d8f8a636c5c5908f41f9075ab1ebfac Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 11 Sep 2022 10:29:35 +0100 Subject: add missing blank line --- bot/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/constants.py b/bot/constants.py index db98e6f47..68a96876f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -397,6 +397,7 @@ class Categories(metaclass=YAMLGetter): # 2021 Summer Code Jam summer_code_jam: int + class Channels(metaclass=YAMLGetter): section = "guild" subsection = "channels" -- cgit v1.2.3 From 8350f3641ff13da8f4d4849a2e44627f485e6a93 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 11 Sep 2022 10:35:41 +0100 Subject: suggest adding a keyword in dev & meta channels --- bot/exts/info/information.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 3dd12c005..5f996508f 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -19,7 +19,7 @@ from bot.errors import NonExistentRoleError from bot.log import get_logger from bot.pagination import LinePaginator from bot.utils import time -from bot.utils.channel import is_mod_channel, is_staff_channel +from bot.utils.channel import get_or_fetch_channel, is_mod_channel, is_staff_channel from bot.utils.checks import cooldown_with_role_bypass, has_no_roles_check, in_whitelist_check from bot.utils.members import get_or_fetch_member @@ -580,9 +580,12 @@ class Information(Cog): if not rules and not final_rules: # This would mean that only keywords where used and no match for them was found + dev_contrib_channel = await get_or_fetch_channel(constants.Channels.dev_contrib) + meta_channel = await get_or_fetch_channel(constants.Channels.meta) await ctx.send( - f"There are currently no rules that correspond to keywords: {[', '.join(keywords)]}." - "If you think it should be added, please ask our admins and they'll take care of the rest.") + f"There are currently no rules that correspond to keywords: **{', '.join(keywords)}**.\n" + f"If you think it should be added, please suggest it in either " + f"{meta_channel.mention} or {dev_contrib_channel.mention}") return await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From 74d2b2153e5f6e53f26c9386898868b2e87aa0fb Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 19 Aug 2022 15:10:13 +0100 Subject: fix "isistance" typo --- tests/test_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index f3040b305..b2686b1d0 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -14,7 +14,7 @@ class DiscordMocksTests(unittest.TestCase): """Test if the default initialization of MockRole results in the correct object.""" role = helpers.MockRole() - # The `spec` argument makes sure `isistance` checks with `discord.Role` pass + # The `spec` argument makes sure `isinstance` checks with `discord.Role` pass self.assertIsInstance(role, discord.Role) self.assertEqual(role.name, "role") -- cgit v1.2.3 From b1fcf6c9ac1b71658a8a8484029352f1e7d59d3d Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 19 Aug 2022 15:12:59 +0100 Subject: add support for keywords when using the "rules" command. This doesn't change the way the rules command originally worked and keeps the priority to rule numbers. But in case a number (or numbers) is not supplied, it will try to find a rule that maps to a the supplied keyword. --- bot/exts/info/information.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index e7d17c971..d53f4e323 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -518,12 +518,12 @@ class Information(Cog): await self.send_raw_content(ctx, message, json=True) @command(aliases=("rule",)) - async def rules(self, ctx: Context, rules: Greedy[int]) -> None: + async def rules(self, ctx: Context, rules: Greedy[int], keyword: Optional[str]) -> None: """Provides a link to all rules or, if specified, displays specific rule(s).""" rules_embed = Embed(title="Rules", color=Colour.og_blurple(), url="https://www.pythondiscord.com/pages/rules") - if not rules: - # Rules were not submitted. Return the default description. + if not rules and not keyword: + # Neither rules nor keywords were submitted. Return the default description. rules_embed.description = ( "The rules and guidelines that apply to this community can be found on" " our [rules page](https://www.pythondiscord.com/pages/rules). We expect" @@ -547,7 +547,21 @@ class Information(Cog): for rule in rules: self.bot.stats.incr(f"rule_uses.{rule}") - final_rules = tuple(f"**{pick}.** {full_rules[pick - 1]}" for pick in rules) + if rules: + final_rules = tuple(f"**{pick}.** {full_rules[pick - 1][0]}" for pick in rules) + else: + final_rules = tuple(f"**{pick + 1}** {full_rules[pick][0]}" for pick, rule in enumerate(full_rules) + if keyword in rule[1]) + + if not rules and not final_rules: + # This would mean that we didn't find a match for the used keyword + rules_embed.description = ( + f"There are currently no rules that correspond to keyword: {keyword}." + " If you think it should be added, please ask our admins and they'll take care of the rest." + ) + + await ctx.send(embed=rules_embed) + return await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From 276dff8d08c8dc1e434a18a0a42966e3b1089186 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Thu, 8 Sep 2022 23:11:41 +0100 Subject: send the list of pre-defined keywords along with each invoked rule --- bot/exts/info/information.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index d53f4e323..54fa62d31 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -521,6 +521,7 @@ class Information(Cog): async def rules(self, ctx: Context, rules: Greedy[int], keyword: Optional[str]) -> None: """Provides a link to all rules or, if specified, displays specific rule(s).""" rules_embed = Embed(title="Rules", color=Colour.og_blurple(), url="https://www.pythondiscord.com/pages/rules") + keyword = keyword.lower() if not rules and not keyword: # Neither rules nor keywords were submitted. Return the default description. @@ -548,9 +549,13 @@ class Information(Cog): self.bot.stats.incr(f"rule_uses.{rule}") if rules: - final_rules = tuple(f"**{pick}.** {full_rules[pick - 1][0]}" for pick in rules) + final_rules = tuple(f"**{pick}.** {full_rules[pick - 1][0]}\n\n" + f"You can also invoke this rule with the following keywords: " + f"{', '.join(full_rules[pick -1][1])}" for pick in rules) else: - final_rules = tuple(f"**{pick + 1}** {full_rules[pick][0]}" for pick, rule in enumerate(full_rules) + final_rules = tuple(f"**{pick + 1}** {full_rules[pick][0]}\n\n" + f"You can also invoke this rule with the following keywords: " + f"{', '.join(full_rules[pick][1])}" for pick, rule in enumerate(full_rules) if keyword in rule[1]) if not rules and not final_rules: -- cgit v1.2.3 From 1ba79145834dc5363d1aed95e7728562353ef755 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Thu, 8 Sep 2022 23:17:25 +0100 Subject: send the "no-match" error message in pure text format --- bot/exts/info/information.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 54fa62d31..9dd0a6d45 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -560,12 +560,9 @@ class Information(Cog): if not rules and not final_rules: # This would mean that we didn't find a match for the used keyword - rules_embed.description = ( + await ctx.send( f"There are currently no rules that correspond to keyword: {keyword}." - " If you think it should be added, please ask our admins and they'll take care of the rest." - ) - - await ctx.send(embed=rules_embed) + "If you think it should be added, please ask our admins and they'll take care of the rest.") return await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From 1e286207fdfbfc0124845fc3620068db3f5657e6 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 11 Sep 2022 09:30:31 +0100 Subject: accept keywords and rule numbers in any order --- bot/exts/info/information.py | 58 +++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 9dd0a6d45..3dd12c005 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -8,7 +8,7 @@ from typing import Any, DefaultDict, Mapping, Optional, Tuple, Union import rapidfuzz from botcore.site_api import ResponseCodeError from discord import AllowedMentions, Colour, Embed, Guild, Message, Role -from discord.ext.commands import BucketType, Cog, Context, Greedy, Paginator, command, group, has_any_role +from discord.ext.commands import BucketType, Cog, Context, Paginator, command, group, has_any_role from discord.utils import escape_markdown from bot import constants @@ -518,12 +518,23 @@ class Information(Cog): await self.send_raw_content(ctx, message, json=True) @command(aliases=("rule",)) - async def rules(self, ctx: Context, rules: Greedy[int], keyword: Optional[str]) -> None: - """Provides a link to all rules or, if specified, displays specific rule(s).""" + async def rules(self, ctx: Context, *args: Optional[str]) -> None: + """ + Provides a link to all rules or, if specified, displays specific rules(s). + + It accepts either rule numbers or particular keywords that map to a particular rule. + Rule numbers and keywords can be sent in any order. + """ rules_embed = Embed(title="Rules", color=Colour.og_blurple(), url="https://www.pythondiscord.com/pages/rules") - keyword = keyword.lower() + keywords, rules = [], [] + + for word in args: + if word.isdigit(): + rules.append(int(word)) + else: + keywords.append(word.lower()) - if not rules and not keyword: + if not rules and not keywords: # Neither rules nor keywords were submitted. Return the default description. rules_embed.description = ( "The rules and guidelines that apply to this community can be found on" @@ -538,30 +549,39 @@ class Information(Cog): # Remove duplicates and sort the rule indices rules = sorted(set(rules)) + # Remove duplicate keywords + keywords = set(keywords) invalid = ", ".join(str(index) for index in rules if index < 1 or index > len(full_rules)) - if invalid: + if invalid and not keywords: await ctx.send(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ...")) return - for rule in rules: - self.bot.stats.incr(f"rule_uses.{rule}") + final_rules = set() - if rules: - final_rules = tuple(f"**{pick}.** {full_rules[pick - 1][0]}\n\n" - f"You can also invoke this rule with the following keywords: " - f"{', '.join(full_rules[pick -1][1])}" for pick in rules) - else: - final_rules = tuple(f"**{pick + 1}** {full_rules[pick][0]}\n\n" - f"You can also invoke this rule with the following keywords: " - f"{', '.join(full_rules[pick][1])}" for pick, rule in enumerate(full_rules) - if keyword in rule[1]) + for pick in rules: + self.bot.stats.incr(f"rule_uses.{pick}") + final_rules.add( + f"**{pick}.** {full_rules[pick - 1][0]}\n\n" + f"You can also invoke this rule with the following keywords: " + f"{', '.join(full_rules[pick -1][1])}") + + for keyword in keywords: + for pick, rule in enumerate(full_rules): + if keyword not in rule[1]: + continue + + self.bot.stats.incr(f"rule_uses.{pick + 1}") + final_rules.add( + f"**{pick + 1}.** {full_rules[pick][0]}\n\n" + f"You can also invoke this rule with the following keywords: " + f"{', '.join(full_rules[pick][1])}") if not rules and not final_rules: - # This would mean that we didn't find a match for the used keyword + # This would mean that only keywords where used and no match for them was found await ctx.send( - f"There are currently no rules that correspond to keyword: {keyword}." + f"There are currently no rules that correspond to keywords: {[', '.join(keywords)]}." "If you think it should be added, please ask our admins and they'll take care of the rest.") return -- cgit v1.2.3 From 1fef952458f1d5282db3c707f203739f3698802c Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 11 Sep 2022 10:29:35 +0100 Subject: add missing blank line --- bot/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/constants.py b/bot/constants.py index db98e6f47..68a96876f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -397,6 +397,7 @@ class Categories(metaclass=YAMLGetter): # 2021 Summer Code Jam summer_code_jam: int + class Channels(metaclass=YAMLGetter): section = "guild" subsection = "channels" -- cgit v1.2.3 From ae0146c6437afca5b97ac0de909efab000977b0f Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 11 Sep 2022 10:35:41 +0100 Subject: suggest adding a keyword in dev & meta channels --- bot/exts/info/information.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 3dd12c005..5f996508f 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -19,7 +19,7 @@ from bot.errors import NonExistentRoleError from bot.log import get_logger from bot.pagination import LinePaginator from bot.utils import time -from bot.utils.channel import is_mod_channel, is_staff_channel +from bot.utils.channel import get_or_fetch_channel, is_mod_channel, is_staff_channel from bot.utils.checks import cooldown_with_role_bypass, has_no_roles_check, in_whitelist_check from bot.utils.members import get_or_fetch_member @@ -580,9 +580,12 @@ class Information(Cog): if not rules and not final_rules: # This would mean that only keywords where used and no match for them was found + dev_contrib_channel = await get_or_fetch_channel(constants.Channels.dev_contrib) + meta_channel = await get_or_fetch_channel(constants.Channels.meta) await ctx.send( - f"There are currently no rules that correspond to keywords: {[', '.join(keywords)]}." - "If you think it should be added, please ask our admins and they'll take care of the rest.") + f"There are currently no rules that correspond to keywords: **{', '.join(keywords)}**.\n" + f"If you think it should be added, please suggest it in either " + f"{meta_channel.mention} or {dev_contrib_channel.mention}") return await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From 3dbb53fd11cf688289d6294210dbca3eef70a538 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 16 Sep 2022 17:04:49 +0100 Subject: use the cached channels instead of fetching them with each command execution --- bot/exts/info/information.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 5f996508f..67515bc57 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -19,7 +19,7 @@ from bot.errors import NonExistentRoleError from bot.log import get_logger from bot.pagination import LinePaginator from bot.utils import time -from bot.utils.channel import get_or_fetch_channel, is_mod_channel, is_staff_channel +from bot.utils.channel import is_mod_channel, is_staff_channel from bot.utils.checks import cooldown_with_role_bypass, has_no_roles_check, in_whitelist_check from bot.utils.members import get_or_fetch_member @@ -580,12 +580,10 @@ class Information(Cog): if not rules and not final_rules: # This would mean that only keywords where used and no match for them was found - dev_contrib_channel = await get_or_fetch_channel(constants.Channels.dev_contrib) - meta_channel = await get_or_fetch_channel(constants.Channels.meta) await ctx.send( f"There are currently no rules that correspond to keywords: **{', '.join(keywords)}**.\n" f"If you think it should be added, please suggest it in either " - f"{meta_channel.mention} or {dev_contrib_channel.mention}") + f"<#{constants.Channels.meta}> or <#{constants.Channels.dev_contrib}>") return await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From 4a8a0f63531585353c27a54539a9d5349799ef8a Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 16 Sep 2022 17:40:32 +0100 Subject: fix typo in docstrings --- bot/exts/info/information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 67515bc57..27a7b501d 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -520,7 +520,7 @@ class Information(Cog): @command(aliases=("rule",)) async def rules(self, ctx: Context, *args: Optional[str]) -> None: """ - Provides a link to all rules or, if specified, displays specific rules(s). + Provides a link to all rules or, if specified, displays specific rule(s). It accepts either rule numbers or particular keywords that map to a particular rule. Rule numbers and keywords can be sent in any order. -- cgit v1.2.3 From 32913523c0e34a91b621f6304ae95f8ba0728e14 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 16 Sep 2022 17:42:10 +0100 Subject: replace .isdigit predicate with a try except block --- bot/exts/info/information.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 27a7b501d..b66c68f87 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -529,9 +529,9 @@ class Information(Cog): keywords, rules = [], [] for word in args: - if word.isdigit(): + try: rules.append(int(word)) - else: + except ValueError: keywords.append(word.lower()) if not rules and not keywords: -- cgit v1.2.3 From 95124cafd0b23c7e4bb2c0c19b3a043f17184ef2 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 16 Sep 2022 17:44:56 +0100 Subject: rename rules to rule_numbers --- bot/exts/info/information.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index b66c68f87..c706fa4cd 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -526,15 +526,15 @@ class Information(Cog): Rule numbers and keywords can be sent in any order. """ rules_embed = Embed(title="Rules", color=Colour.og_blurple(), url="https://www.pythondiscord.com/pages/rules") - keywords, rules = [], [] + keywords, rule_numbers = [], [] for word in args: try: - rules.append(int(word)) + rule_numbers.append(int(word)) except ValueError: keywords.append(word.lower()) - if not rules and not keywords: + if not rule_numbers and not keywords: # Neither rules nor keywords were submitted. Return the default description. rules_embed.description = ( "The rules and guidelines that apply to this community can be found on" @@ -548,11 +548,11 @@ class Information(Cog): full_rules = await self.bot.api_client.get("rules", params={"link_format": "md"}) # Remove duplicates and sort the rule indices - rules = sorted(set(rules)) + rule_numbers = sorted(set(rule_numbers)) # Remove duplicate keywords keywords = set(keywords) - invalid = ", ".join(str(index) for index in rules if index < 1 or index > len(full_rules)) + invalid = ", ".join(str(index) for index in rule_numbers if index < 1 or index > len(full_rules)) if invalid and not keywords: await ctx.send(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ...")) @@ -560,7 +560,7 @@ class Information(Cog): final_rules = set() - for pick in rules: + for pick in rule_numbers: self.bot.stats.incr(f"rule_uses.{pick}") final_rules.add( f"**{pick}.** {full_rules[pick - 1][0]}\n\n" @@ -578,7 +578,7 @@ class Information(Cog): f"You can also invoke this rule with the following keywords: " f"{', '.join(full_rules[pick][1])}") - if not rules and not final_rules: + if not rule_numbers and not final_rules: # This would mean that only keywords where used and no match for them was found await ctx.send( f"There are currently no rules that correspond to keywords: **{', '.join(keywords)}**.\n" -- cgit v1.2.3 From 1c1650001f58e31721c79096642033fc005f8c82 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Fri, 16 Sep 2022 21:08:44 +0100 Subject: remove help message that displays the available keywords per rule --- bot/exts/info/information.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index c706fa4cd..42385edfb 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -562,10 +562,7 @@ class Information(Cog): for pick in rule_numbers: self.bot.stats.incr(f"rule_uses.{pick}") - final_rules.add( - f"**{pick}.** {full_rules[pick - 1][0]}\n\n" - f"You can also invoke this rule with the following keywords: " - f"{', '.join(full_rules[pick -1][1])}") + final_rules.add(f"**{pick}.** {full_rules[pick - 1][0]}") for keyword in keywords: for pick, rule in enumerate(full_rules): @@ -573,10 +570,7 @@ class Information(Cog): continue self.bot.stats.incr(f"rule_uses.{pick + 1}") - final_rules.add( - f"**{pick + 1}.** {full_rules[pick][0]}\n\n" - f"You can also invoke this rule with the following keywords: " - f"{', '.join(full_rules[pick][1])}") + final_rules.add(f"**{pick + 1}.** {full_rules[pick][0]}") if not rule_numbers and not final_rules: # This would mean that only keywords where used and no match for them was found -- cgit v1.2.3 From e883168727b74fe754f51d16d8742262daccddc6 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 15:21:57 +0100 Subject: stop matching as soon as an invalid kw is encountered --- bot/exts/info/information.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 42385edfb..1ba8cf87e 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -546,33 +546,37 @@ class Information(Cog): return full_rules = await self.bot.api_client.get("rules", params={"link_format": "md"}) + available_keywords = set() + + for _, rule_keywords in full_rules: + available_keywords = available_keywords | set(rule_keywords) # Remove duplicates and sort the rule indices rule_numbers = sorted(set(rule_numbers)) - # Remove duplicate keywords - keywords = set(keywords) + # Remove duplicate keywords and preserve the order of initial keywords + keywords = list(dict.fromkeys(keywords)) invalid = ", ".join(str(index) for index in rule_numbers if index < 1 or index > len(full_rules)) - if invalid and not keywords: + if invalid: await ctx.send(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ...")) return - final_rules = set() - - for pick in rule_numbers: - self.bot.stats.incr(f"rule_uses.{pick}") - final_rules.add(f"**{pick}.** {full_rules[pick - 1][0]}") + final_rule_numbers = [] + final_rule_numbers.extend(rule_numbers) for keyword in keywords: + if keyword not in available_keywords: + break + for pick, rule in enumerate(full_rules): if keyword not in rule[1]: continue - self.bot.stats.incr(f"rule_uses.{pick + 1}") - final_rules.add(f"**{pick + 1}.** {full_rules[pick][0]}") + final_rule_numbers.append(pick + 1) + break - if not rule_numbers and not final_rules: + if not rule_numbers and not final_rule_numbers: # This would mean that only keywords where used and no match for them was found await ctx.send( f"There are currently no rules that correspond to keywords: **{', '.join(keywords)}**.\n" @@ -580,6 +584,13 @@ class Information(Cog): f"<#{constants.Channels.meta}> or <#{constants.Channels.dev_contrib}>") return + for rule_number in final_rule_numbers: + self.bot.stats.incr(f"rule_uses.{rule_number}") + + final_rule_numbers.sort() + final_rule_numbers = set(final_rule_numbers) + final_rules = [f"**{rule_number}.** {full_rules[rule_number - 1][0]}" for rule_number in final_rule_numbers] + await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From c17ca61dcbe63b6f458e31c7d113de4a48cc3b45 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 15:41:27 +0100 Subject: stop matching keywords when they're invalid upon triaging the rule numbers & keywords --- bot/exts/info/information.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 1ba8cf87e..cd1f0f957 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -528,11 +528,19 @@ class Information(Cog): rules_embed = Embed(title="Rules", color=Colour.og_blurple(), url="https://www.pythondiscord.com/pages/rules") keywords, rule_numbers = [], [] + full_rules = await self.bot.api_client.get("rules", params={"link_format": "md"}) + available_keywords = set() + + for _, rule_keywords in full_rules: + available_keywords = available_keywords | set(rule_keywords) + for word in args: try: rule_numbers.append(int(word)) except ValueError: - keywords.append(word.lower()) + if (kw := word.lower()) not in available_keywords: + break + keywords.append(kw) if not rule_numbers and not keywords: # Neither rules nor keywords were submitted. Return the default description. @@ -545,12 +553,6 @@ class Information(Cog): await ctx.send(embed=rules_embed) return - full_rules = await self.bot.api_client.get("rules", params={"link_format": "md"}) - available_keywords = set() - - for _, rule_keywords in full_rules: - available_keywords = available_keywords | set(rule_keywords) - # Remove duplicates and sort the rule indices rule_numbers = sorted(set(rule_numbers)) # Remove duplicate keywords and preserve the order of initial keywords @@ -566,15 +568,10 @@ class Information(Cog): final_rule_numbers.extend(rule_numbers) for keyword in keywords: - if keyword not in available_keywords: - break - for pick, rule in enumerate(full_rules): - if keyword not in rule[1]: - continue - - final_rule_numbers.append(pick + 1) - break + if keyword in rule[1]: + final_rule_numbers.append(pick + 1) + break if not rule_numbers and not final_rule_numbers: # This would mean that only keywords where used and no match for them was found @@ -584,12 +581,13 @@ class Information(Cog): f"<#{constants.Channels.meta}> or <#{constants.Channels.dev_contrib}>") return - for rule_number in final_rule_numbers: - self.bot.stats.incr(f"rule_uses.{rule_number}") - + final_rules = [] final_rule_numbers.sort() final_rule_numbers = set(final_rule_numbers) - final_rules = [f"**{rule_number}.** {full_rules[rule_number - 1][0]}" for rule_number in final_rule_numbers] + + for rule_number in final_rule_numbers: + self.bot.stats.incr(f"rule_uses.{rule_number}") + final_rules.append(f"**{rule_number}.** {full_rules[rule_number - 1][0]}") await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -- cgit v1.2.3 From 3119585850d01957d074862d25d01b78eb7fc438 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 16:11:10 +0100 Subject: rename index to rule_number --- bot/exts/info/information.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index cd1f0f957..52cc79aa0 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -558,7 +558,9 @@ class Information(Cog): # Remove duplicate keywords and preserve the order of initial keywords keywords = list(dict.fromkeys(keywords)) - invalid = ", ".join(str(index) for index in rule_numbers if index < 1 or index > len(full_rules)) + invalid = ", ".join( + str(rule_number) for rule_number in rule_numbers + if rule_number < 1 or rule_number > len(full_rules)) if invalid: await ctx.send(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ...")) -- cgit v1.2.3 From 144c036b6448cc77940e6c071728191b5c705cbb Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 17:58:23 +0100 Subject: rename pick to rule_number --- bot/exts/info/information.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 52cc79aa0..c9ea025ed 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -570,9 +570,9 @@ class Information(Cog): final_rule_numbers.extend(rule_numbers) for keyword in keywords: - for pick, rule in enumerate(full_rules): + for rule_number, rule in enumerate(full_rules): if keyword in rule[1]: - final_rule_numbers.append(pick + 1) + final_rule_numbers.append(rule_number + 1) break if not rule_numbers and not final_rule_numbers: -- cgit v1.2.3 From 4d33f8ee7fcac5ffbc6957da5ac103268fcb30e3 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 18:01:44 +0100 Subject: remove unreachable code --- bot/exts/info/information.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index c9ea025ed..a0674f758 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -575,14 +575,6 @@ class Information(Cog): final_rule_numbers.append(rule_number + 1) break - if not rule_numbers and not final_rule_numbers: - # This would mean that only keywords where used and no match for them was found - await ctx.send( - f"There are currently no rules that correspond to keywords: **{', '.join(keywords)}**.\n" - f"If you think it should be added, please suggest it in either " - f"<#{constants.Channels.meta}> or <#{constants.Channels.dev_contrib}>") - return - final_rules = [] final_rule_numbers.sort() final_rule_numbers = set(final_rule_numbers) -- cgit v1.2.3 From d0dcceefd680629a2f5f9ff956ad7241727421a2 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 18:04:16 +0100 Subject: remove duplicate final rule numbers then sort --- bot/exts/info/information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index a0674f758..cbee1a519 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -576,8 +576,8 @@ class Information(Cog): break final_rules = [] - final_rule_numbers.sort() final_rule_numbers = set(final_rule_numbers) + final_rule_numbers = sorted(list(final_rule_numbers)) for rule_number in final_rule_numbers: self.bot.stats.incr(f"rule_uses.{rule_number}") -- cgit v1.2.3 From 3fd28f44eb8857c6409e739eab9cdc307d1febe9 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 18:07:24 +0100 Subject: remove useless initial sorting of keywords --- bot/exts/info/information.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index cbee1a519..b453a87cb 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -555,8 +555,6 @@ class Information(Cog): # Remove duplicates and sort the rule indices rule_numbers = sorted(set(rule_numbers)) - # Remove duplicate keywords and preserve the order of initial keywords - keywords = list(dict.fromkeys(keywords)) invalid = ", ".join( str(rule_number) for rule_number in rule_numbers -- cgit v1.2.3 From 9a867416c10d1d7543574bcb4642f645c6f98b29 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 18:11:45 +0100 Subject: enumerate full_rules with a start index of 1 --- bot/exts/info/information.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index b453a87cb..e09083f90 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -568,9 +568,9 @@ class Information(Cog): final_rule_numbers.extend(rule_numbers) for keyword in keywords: - for rule_number, rule in enumerate(full_rules): + for rule_number, rule in enumerate(full_rules, start=1): if keyword in rule[1]: - final_rule_numbers.append(rule_number + 1) + final_rule_numbers.append(rule_number) break final_rules = [] -- cgit v1.2.3 From 44d1e790681f9430836694273f93a30994643a9b Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 18:30:46 +0100 Subject: replace the keywords set with a dict that maps each keyword to its rule number --- bot/exts/info/information.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index e09083f90..315d93f1d 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -529,16 +529,17 @@ class Information(Cog): keywords, rule_numbers = [], [] full_rules = await self.bot.api_client.get("rules", params={"link_format": "md"}) - available_keywords = set() + keyword_to_rule_number = dict() - for _, rule_keywords in full_rules: - available_keywords = available_keywords | set(rule_keywords) + for rule_number, (_, rule_keywords) in enumerate(full_rules, start=1): + for rule_keyword in rule_keywords: + keyword_to_rule_number[rule_keyword] = rule_number for word in args: try: rule_numbers.append(int(word)) except ValueError: - if (kw := word.lower()) not in available_keywords: + if (kw := word.lower()) not in keyword_to_rule_number: break keywords.append(kw) @@ -568,10 +569,7 @@ class Information(Cog): final_rule_numbers.extend(rule_numbers) for keyword in keywords: - for rule_number, rule in enumerate(full_rules, start=1): - if keyword in rule[1]: - final_rule_numbers.append(rule_number) - break + final_rule_numbers.append(keyword_to_rule_number.get(keyword)) final_rules = [] final_rule_numbers = set(final_rule_numbers) -- cgit v1.2.3 From f95e4164406c76d2d0290cd05a2811ca875b3e23 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 21:56:46 +0100 Subject: remove redundant use of list transformation when sorting final rule numbers --- bot/exts/info/information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 315d93f1d..7034ca596 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -573,7 +573,7 @@ class Information(Cog): final_rules = [] final_rule_numbers = set(final_rule_numbers) - final_rule_numbers = sorted(list(final_rule_numbers)) + final_rule_numbers = sorted(final_rule_numbers) for rule_number in final_rule_numbers: self.bot.stats.incr(f"rule_uses.{rule_number}") -- cgit v1.2.3 From 032ef6512a30bb38193a9c4d01682ce67ac97697 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 22:07:18 +0100 Subject: subscribe directly to the keyword_to_rule_number dict instead of using .get --- bot/exts/info/information.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 7034ca596..ad5230a9c 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -566,10 +566,11 @@ class Information(Cog): return final_rule_numbers = [] + final_rule_numbers.extend(rule_numbers) for keyword in keywords: - final_rule_numbers.append(keyword_to_rule_number.get(keyword)) + final_rule_numbers.append(keyword_to_rule_number[keyword]) final_rules = [] final_rule_numbers = set(final_rule_numbers) -- cgit v1.2.3 From 987871da795bdfaf879f3fca5ce939761791296b Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 18 Sep 2022 22:11:02 +0100 Subject: determine final_rule_numbers value by subscribing to the keyword_to_rule_number using the matched keywords --- bot/exts/info/information.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index ad5230a9c..29b7b6fa3 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -565,16 +565,9 @@ class Information(Cog): await ctx.send(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ...")) return - final_rule_numbers = [] - - final_rule_numbers.extend(rule_numbers) - - for keyword in keywords: - final_rule_numbers.append(keyword_to_rule_number[keyword]) - final_rules = [] - final_rule_numbers = set(final_rule_numbers) - final_rule_numbers = sorted(final_rule_numbers) + final_rule_numbers = {keyword_to_rule_number[keyword] for keyword in keywords} + final_rule_numbers.update(rule_numbers) for rule_number in final_rule_numbers: self.bot.stats.incr(f"rule_uses.{rule_number}") -- cgit v1.2.3 From ae6b500ec4724a1f7e959b2b8a1eae2074407cee Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 19 Sep 2022 13:03:03 +0100 Subject: sort the list of final rule numbers --- bot/exts/info/information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 29b7b6fa3..7e27ed31c 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -569,7 +569,7 @@ class Information(Cog): final_rule_numbers = {keyword_to_rule_number[keyword] for keyword in keywords} final_rule_numbers.update(rule_numbers) - for rule_number in final_rule_numbers: + for rule_number in sorted(final_rule_numbers): self.bot.stats.incr(f"rule_uses.{rule_number}") final_rules.append(f"**{rule_number}.** {full_rules[rule_number - 1][0]}") -- cgit v1.2.3 From a7663c56c360e5f6799edce67265f0d6415c8be4 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 19 Sep 2022 16:46:54 +0100 Subject: return final list of rule numbers to be sent --- bot/exts/info/information.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 7e27ed31c..58f0764d8 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -3,7 +3,7 @@ import pprint import textwrap from collections import defaultdict from textwrap import shorten -from typing import Any, DefaultDict, Mapping, Optional, Tuple, Union +from typing import Any, DefaultDict, List, Mapping, Optional, Tuple, Union import rapidfuzz from botcore.site_api import ResponseCodeError @@ -518,7 +518,7 @@ class Information(Cog): await self.send_raw_content(ctx, message, json=True) @command(aliases=("rule",)) - async def rules(self, ctx: Context, *args: Optional[str]) -> None: + async def rules(self, ctx: Context, *args: Optional[str]) -> Optional[List[int]]: """ Provides a link to all rules or, if specified, displays specific rule(s). @@ -575,6 +575,8 @@ class Information(Cog): await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) + return final_rule_numbers + async def setup(bot: Bot) -> None: """Load the Information cog.""" -- cgit v1.2.3 From 738f2ee0e8e321d01e867a1d3f963d6991e6f22a Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 19 Sep 2022 16:50:30 +0100 Subject: add test that checks for the sent content if one invalid index is present in the input --- tests/bot/exts/info/test_information.py | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py index d896b7652..c14453bd8 100644 --- a/tests/bot/exts/info/test_information.py +++ b/tests/bot/exts/info/test_information.py @@ -2,6 +2,7 @@ import textwrap import unittest import unittest.mock from datetime import datetime +from textwrap import shorten import discord @@ -573,3 +574,48 @@ class UserCommandTests(unittest.IsolatedAsyncioTestCase): create_embed.assert_called_once_with(ctx, self.target, False) ctx.send.assert_called_once() + + +class RuleCommandTests(unittest.IsolatedAsyncioTestCase): + """Tests for the `!rule` command.""" + + def setUp(self) -> None: + """Set up steps executed before each test is run.""" + self.bot = helpers.MockBot() + self.cog = information.Information(self.bot) + self.ctx = helpers.MockContext(author=helpers.MockMember(id=1, name="Bellaluma")) + self.full_rules = [ + ( + "First rule", + ["first", "number_one"] + ), + ( + "Second rule", + ["second", "number_two"] + ), + ( + "Third rule", + ["third", "number_three"] + ) + ] + self.bot.api_client.get.return_value = self.full_rules + + async def test_return_none_if_one_rule_number_is_invalid(self): + + test_cases = [ + (('1', '6', '7', '8'), (6, 7, 8)), + (('10', "first"), (10, )), + (("first", 10), (10, )) + ] + + for raw_user_input, extracted_rule_numbers in test_cases: + invalid = ", ".join( + str(rule_number) for rule_number in extracted_rule_numbers + if rule_number < 1 or rule_number > len(self.full_rules)) + + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + + self.assertEqual( + self.ctx.send.call_args, + unittest.mock.call(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ..."))) + self.assertEqual(None, final_rule_numbers) -- cgit v1.2.3 From 511724a704b2ef87dad8c24006cb7250aa964b35 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 19 Sep 2022 17:20:15 +0100 Subject: fix wrong type hint of the rules function return value --- bot/exts/info/information.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 58f0764d8..d4f0ce008 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -3,7 +3,7 @@ import pprint import textwrap from collections import defaultdict from textwrap import shorten -from typing import Any, DefaultDict, List, Mapping, Optional, Tuple, Union +from typing import Any, DefaultDict, Mapping, Optional, Set, Tuple, Union import rapidfuzz from botcore.site_api import ResponseCodeError @@ -518,7 +518,7 @@ class Information(Cog): await self.send_raw_content(ctx, message, json=True) @command(aliases=("rule",)) - async def rules(self, ctx: Context, *args: Optional[str]) -> Optional[List[int]]: + async def rules(self, ctx: Context, *args: Optional[str]) -> Optional[Set[int]]: """ Provides a link to all rules or, if specified, displays specific rule(s). -- cgit v1.2.3 From 45b27ed9ac6a1ca55547defd1922fafcc7ac1ab9 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 19 Sep 2022 17:32:47 +0100 Subject: test the cases where default rules message is supposed to be sent --- tests/bot/exts/info/test_information.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py index c14453bd8..61e8895b9 100644 --- a/tests/bot/exts/info/test_information.py +++ b/tests/bot/exts/info/test_information.py @@ -619,3 +619,34 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): self.ctx.send.call_args, unittest.mock.call(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ..."))) self.assertEqual(None, final_rule_numbers) + + async def test_return_correct_rule_numberstest_return_correct_rule_numbers(self): + + test_cases = [ + (("1", "2", "first"), {1, 2}), + (("1", "hello", "2", "second"), {1}), + (("second", "third", "unknown", "999"), {2, 3}) + ] + + for raw_user_input, expected_matched_rule_numbers in test_cases: + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) + + async def test_return_default_rules_when_no_input_or_no_match_are_found(self): + test_cases = [ + ((), None), + (("hello", "2", "second"), None), + (("hello", "999"), None), + ] + + description = ( + "The rules and guidelines that apply to this community can be found on" + " our [rules page](https://www.pythondiscord.com/pages/rules). We expect" + " all members of the community to have read and understood these." + ) + + for raw_user_input, expected_matched_rule_numbers in test_cases: + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + embed = self.ctx.send.call_args.kwargs['embed'] + self.assertEqual(description, embed.description) + self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) -- cgit v1.2.3 From 2e25073f807e02a72fe4cf2bbfa5cefe5024c8d7 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 19 Sep 2022 20:23:33 +0100 Subject: fix redundant test name --- tests/bot/exts/info/test_information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py index 61e8895b9..626c12c86 100644 --- a/tests/bot/exts/info/test_information.py +++ b/tests/bot/exts/info/test_information.py @@ -620,7 +620,7 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): unittest.mock.call(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ..."))) self.assertEqual(None, final_rule_numbers) - async def test_return_correct_rule_numberstest_return_correct_rule_numbers(self): + async def test_return_correct_rule_numbers(self): test_cases = [ (("1", "2", "first"), {1, 2}), -- cgit v1.2.3 From ae0c94b1d4dc676519301fe67a21425e5649effe Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 19 Sep 2022 22:57:54 +0100 Subject: add DEFAULT_RULES_DESCRIPTION to avoid duplication --- bot/constants.py | 6 ++++++ bot/exts/info/information.py | 8 ++------ tests/bot/exts/info/test_information.py | 9 ++------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 68a96876f..b1d392822 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -774,3 +774,9 @@ ERROR_REPLIES = [ "Noooooo!!", "I can't believe you've done this", ] + +DEFAULT_RULES_DESCRIPTION = ( + "The rules and guidelines that apply to this community can be found on" + " our [rules page](https://www.pythondiscord.com/pages/rules). We expect" + " all members of the community to have read and understood these." +) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index d4f0ce008..a4816450f 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -13,6 +13,7 @@ from discord.utils import escape_markdown from bot import constants from bot.bot import Bot +from bot.constants import DEFAULT_RULES_DESCRIPTION from bot.converters import MemberOrUser from bot.decorators import in_whitelist from bot.errors import NonExistentRoleError @@ -545,12 +546,7 @@ class Information(Cog): if not rule_numbers and not keywords: # Neither rules nor keywords were submitted. Return the default description. - rules_embed.description = ( - "The rules and guidelines that apply to this community can be found on" - " our [rules page](https://www.pythondiscord.com/pages/rules). We expect" - " all members of the community to have read and understood these." - ) - + rules_embed.description = DEFAULT_RULES_DESCRIPTION await ctx.send(embed=rules_embed) return diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py index 626c12c86..c0cfac430 100644 --- a/tests/bot/exts/info/test_information.py +++ b/tests/bot/exts/info/test_information.py @@ -7,6 +7,7 @@ from textwrap import shorten import discord from bot import constants +from bot.constants import DEFAULT_RULES_DESCRIPTION from bot.exts.info import information from bot.utils.checks import InWhitelistCheckFailure from tests import helpers @@ -639,14 +640,8 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): (("hello", "999"), None), ] - description = ( - "The rules and guidelines that apply to this community can be found on" - " our [rules page](https://www.pythondiscord.com/pages/rules). We expect" - " all members of the community to have read and understood these." - ) - for raw_user_input, expected_matched_rule_numbers in test_cases: final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) embed = self.ctx.send.call_args.kwargs['embed'] - self.assertEqual(description, embed.description) + self.assertEqual(DEFAULT_RULES_DESCRIPTION, embed.description) self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) -- cgit v1.2.3 From 06b2cde5e4cf7f5a9b914e4f4d1599d949773ecc Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 19 Sep 2022 23:01:01 +0100 Subject: use subTest to isolate assertions --- tests/bot/exts/info/test_information.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py index c0cfac430..157c7141b 100644 --- a/tests/bot/exts/info/test_information.py +++ b/tests/bot/exts/info/test_information.py @@ -610,16 +610,17 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): ] for raw_user_input, extracted_rule_numbers in test_cases: - invalid = ", ".join( - str(rule_number) for rule_number in extracted_rule_numbers - if rule_number < 1 or rule_number > len(self.full_rules)) + with self.subTest(identifier=raw_user_input): + invalid = ", ".join( + str(rule_number) for rule_number in extracted_rule_numbers + if rule_number < 1 or rule_number > len(self.full_rules)) - final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) - self.assertEqual( - self.ctx.send.call_args, - unittest.mock.call(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ..."))) - self.assertEqual(None, final_rule_numbers) + self.assertEqual( + self.ctx.send.call_args, + unittest.mock.call(shorten(":x: Invalid rule indices: " + invalid, 75, placeholder=" ..."))) + self.assertEqual(None, final_rule_numbers) async def test_return_correct_rule_numbers(self): @@ -630,8 +631,9 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): ] for raw_user_input, expected_matched_rule_numbers in test_cases: - final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) - self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) + with self.subTest(identifier=raw_user_input): + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) async def test_return_default_rules_when_no_input_or_no_match_are_found(self): test_cases = [ @@ -641,7 +643,8 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): ] for raw_user_input, expected_matched_rule_numbers in test_cases: - final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) - embed = self.ctx.send.call_args.kwargs['embed'] - self.assertEqual(DEFAULT_RULES_DESCRIPTION, embed.description) - self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) + with self.subTest(identifier=raw_user_input): + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + embed = self.ctx.send.call_args.kwargs['embed'] + self.assertEqual(DEFAULT_RULES_DESCRIPTION, embed.description) + self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) -- cgit v1.2.3 From edc9c4089d2f7f7cb70b813b3cfdcf52ab834714 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Tue, 20 Sep 2022 08:36:29 +0100 Subject: move DEFAULT_RULES_DESCRIPTION under information.py --- bot/constants.py | 6 ------ bot/exts/info/information.py | 7 ++++++- tests/bot/exts/info/test_information.py | 3 +-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index b1d392822..68a96876f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -774,9 +774,3 @@ ERROR_REPLIES = [ "Noooooo!!", "I can't believe you've done this", ] - -DEFAULT_RULES_DESCRIPTION = ( - "The rules and guidelines that apply to this community can be found on" - " our [rules page](https://www.pythondiscord.com/pages/rules). We expect" - " all members of the community to have read and understood these." -) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index a4816450f..2592e093d 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -13,7 +13,6 @@ from discord.utils import escape_markdown from bot import constants from bot.bot import Bot -from bot.constants import DEFAULT_RULES_DESCRIPTION from bot.converters import MemberOrUser from bot.decorators import in_whitelist from bot.errors import NonExistentRoleError @@ -26,6 +25,12 @@ from bot.utils.members import get_or_fetch_member log = get_logger(__name__) +DEFAULT_RULES_DESCRIPTION = ( + "The rules and guidelines that apply to this community can be found on" + " our [rules page](https://www.pythondiscord.com/pages/rules). We expect" + " all members of the community to have read and understood these." +) + class Information(Cog): """A cog with commands for generating embeds with server info, such as server stats and user info.""" diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py index 157c7141b..9f5143c01 100644 --- a/tests/bot/exts/info/test_information.py +++ b/tests/bot/exts/info/test_information.py @@ -7,7 +7,6 @@ from textwrap import shorten import discord from bot import constants -from bot.constants import DEFAULT_RULES_DESCRIPTION from bot.exts.info import information from bot.utils.checks import InWhitelistCheckFailure from tests import helpers @@ -646,5 +645,5 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): with self.subTest(identifier=raw_user_input): final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) embed = self.ctx.send.call_args.kwargs['embed'] - self.assertEqual(DEFAULT_RULES_DESCRIPTION, embed.description) + self.assertEqual(information.DEFAULT_RULES_DESCRIPTION, embed.description) self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) -- cgit v1.2.3