aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar wookie184 <[email protected]>2022-11-05 16:09:04 +0000
committerGravatar GitHub <[email protected]>2022-11-05 16:09:04 +0000
commita2c30bfc9aa345d8699b1ddecc198a58d5e60e56 (patch)
tree191a37c8ad9d0295049bfee96f1f008bbd2759e0
parentRefactor some commands to avoid unnecessary API calls (diff)
parentMerge pull request #2230 from meatballs/create-nomodule-tag (diff)
Merge branch 'main' into 2306-nominations-cleanup
-rw-r--r--bot/exts/backend/security.py (renamed from bot/exts/filters/security.py)0
-rw-r--r--bot/exts/info/information.py33
-rw-r--r--bot/exts/utils/snekbox.py15
-rw-r--r--bot/resources/tags/nomodule.md13
-rw-r--r--bot/resources/tags/slicing.md24
-rw-r--r--docker-compose.yml2
-rw-r--r--tests/bot/exts/backend/test_security.py (renamed from tests/bot/exts/filters/test_security.py)2
-rw-r--r--tests/bot/exts/info/test_information.py24
8 files changed, 86 insertions, 27 deletions
diff --git a/bot/exts/filters/security.py b/bot/exts/backend/security.py
index 27e4d9752..27e4d9752 100644
--- a/bot/exts/filters/security.py
+++ b/bot/exts/backend/security.py
diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py
index 2592e093d..733597dd8 100644
--- a/bot/exts/info/information.py
+++ b/bot/exts/info/information.py
@@ -523,8 +523,19 @@ class Information(Cog):
"""Shows information about the raw API response in a copy-pasteable Python format."""
await self.send_raw_content(ctx, message, json=True)
+ async def _set_rules_command_help(self) -> None:
+ help_string = f"{self.rules.help}\n\n"
+ help_string += "__Available keywords per rule__:\n\n"
+
+ full_rules = await self.bot.api_client.get("rules", params={"link_format": "md"})
+
+ for index, (_, keywords) in enumerate(full_rules, start=1):
+ help_string += f"**Rule {index}**: {', '.join(keywords)}\n\r"
+
+ self.rules.help = help_string
+
@command(aliases=("rule",))
- async def rules(self, ctx: Context, *args: Optional[str]) -> Optional[Set[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).
@@ -541,13 +552,15 @@ class Information(Cog):
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 keyword_to_rule_number:
- break
- keywords.append(kw)
+ if args:
+ for word in args.split(maxsplit=100):
+ try:
+ rule_numbers.append(int(word))
+ except ValueError:
+ # Stop on first invalid keyword/index to allow for normal messaging after
+ if (kw := word.lower()) not in keyword_to_rule_number:
+ break
+ keywords.append(kw)
if not rule_numbers and not keywords:
# Neither rules nor keywords were submitted. Return the default description.
@@ -578,6 +591,10 @@ class Information(Cog):
return final_rule_numbers
+ async def cog_load(self) -> None:
+ """Carry out cog asynchronous initialisation."""
+ await self._set_rules_command_help()
+
async def setup(bot: Bot) -> None:
"""Load the Information cog."""
diff --git a/bot/exts/utils/snekbox.py b/bot/exts/utils/snekbox.py
index 8e961b67c..5e217a288 100644
--- a/bot/exts/utils/snekbox.py
+++ b/bot/exts/utils/snekbox.py
@@ -125,7 +125,8 @@ class PythonVersionSwitcherButton(ui.Button):
version_to_switch_to: Literal["3.10", "3.11"],
snekbox_cog: "Snekbox",
ctx: Context,
- code: str
+ code: str,
+ args: Optional[list[str]] = None
) -> None:
self.version_to_switch_to = version_to_switch_to
super().__init__(label=f"Run in {self.version_to_switch_to}", style=enums.ButtonStyle.primary)
@@ -134,6 +135,7 @@ class PythonVersionSwitcherButton(ui.Button):
self.ctx = ctx
self.job_name = job_name
self.code = code
+ self.args = args
async def callback(self, interaction: Interaction) -> None:
"""
@@ -150,7 +152,9 @@ class PythonVersionSwitcherButton(ui.Button):
# The log arg on send_job will stop the actual job from running.
await interaction.message.delete()
- await self.snekbox_cog.run_job(self.job_name, self.ctx, self.version_to_switch_to, self.code)
+ await self.snekbox_cog.run_job(
+ self.job_name, self.ctx, self.version_to_switch_to, self.code, args=self.args
+ )
class Snekbox(Cog):
@@ -165,7 +169,8 @@ class Snekbox(Cog):
job_name: str,
current_python_version: Literal["3.10", "3.11"],
ctx: Context,
- code: str
+ code: str,
+ args: Optional[list[str]] = None
) -> None:
"""Return a view that allows the user to change what version of Python their code is run on."""
if current_python_version == "3.10":
@@ -177,7 +182,7 @@ class Snekbox(Cog):
allowed_users=(ctx.author.id,),
allowed_roles=MODERATION_ROLES,
)
- view.add_item(PythonVersionSwitcherButton(job_name, alt_python_version, self, ctx, code))
+ view.add_item(PythonVersionSwitcherButton(job_name, alt_python_version, self, ctx, code, args))
view.add_item(interactions.DeleteMessageButton())
return view
@@ -357,7 +362,7 @@ class Snekbox(Cog):
response = await ctx.send("Attempt to circumvent filter detected. Moderator team has been alerted.")
else:
allowed_mentions = AllowedMentions(everyone=False, roles=False, users=[ctx.author])
- view = self.build_python_version_switcher_view(job_name, python_version, ctx, code)
+ view = self.build_python_version_switcher_view(job_name, python_version, ctx, code, args)
response = await ctx.send(msg, allowed_mentions=allowed_mentions, view=view)
view.message = response
diff --git a/bot/resources/tags/nomodule.md b/bot/resources/tags/nomodule.md
new file mode 100644
index 000000000..adae555be
--- /dev/null
+++ b/bot/resources/tags/nomodule.md
@@ -0,0 +1,13 @@
+**ModuleNotFoundError**
+
+If you've installed a package but you're getting a ModuleNotFoundError when you try to import it, it's likely that the environment where your code is running is different from the one where you did the installation.
+
+You can read about Python environments at `!tags environments` and `!tags venv`.
+
+Common causes of this problem include:
+
+• You installed your package using `pip install ...`. It could be that the `pip` command is not pointing to the environment where your code runs. For greater control, you could instead run pip as a module within the python environment you specify:
+```
+python -m pip install <your_package>
+```
+• Your editor/ide is configured to create virtual environments automatically (PyCharm is configured this way by default).
diff --git a/bot/resources/tags/slicing.md b/bot/resources/tags/slicing.md
new file mode 100644
index 000000000..717fc46b7
--- /dev/null
+++ b/bot/resources/tags/slicing.md
@@ -0,0 +1,24 @@
+---
+aliases: ["slice", "seqslice", "seqslicing", "sequence-slice", "sequence-slicing"]
+embed:
+ title: "Sequence slicing"
+---
+*Slicing* is a way of accessing a part of a sequence by specifying a start, stop, and step. As with normal indexing, negative numbers can be used to count backwards.
+
+**Examples**
+```py
+>>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
+>>> letters[2:] # from element 2 to the end
+['c', 'd', 'e', 'f', 'g']
+>>> letters[:4] # up to element 4
+['a', 'b', 'c', 'd']
+>>> letters[3:5] # elements 3 and 4 -- the right bound is not included
+['d', 'e']
+>>> letters[2:-1:2] # Every other element between 2 and the last
+['c', 'e']
+>>> letters[::-1] # The whole list in reverse
+['g', 'f', 'e', 'd', 'c', 'b', 'a']
+>>> words = "Hello world!"
+>>> words[2:7] # Strings are also sequences
+"llo w"
+```
diff --git a/docker-compose.yml b/docker-compose.yml
index be7370d6b..bc53c482b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -18,7 +18,7 @@ services:
postgres:
<< : *logging
<< : *restart_policy
- image: postgres:13-alpine
+ image: postgres:15-alpine
environment:
POSTGRES_DB: pysite
POSTGRES_PASSWORD: pysite
diff --git a/tests/bot/exts/filters/test_security.py b/tests/bot/exts/backend/test_security.py
index 007b7b1eb..c3985c609 100644
--- a/tests/bot/exts/filters/test_security.py
+++ b/tests/bot/exts/backend/test_security.py
@@ -2,7 +2,7 @@ import unittest
from discord.ext.commands import NoPrivateMessage
-from bot.exts.filters import security
+from bot.exts.backend import security
from tests.helpers import MockBot, MockContext
diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py
index 9f5143c01..65595e959 100644
--- a/tests/bot/exts/info/test_information.py
+++ b/tests/bot/exts/info/test_information.py
@@ -603,9 +603,9 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase):
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, ))
+ ("1 6 7 8", (6, 7, 8)),
+ ("10 first", (10,)),
+ ("first 10", (10,))
]
for raw_user_input, extracted_rule_numbers in test_cases:
@@ -614,7 +614,7 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase):
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, args=raw_user_input)
self.assertEqual(
self.ctx.send.call_args,
@@ -624,26 +624,26 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase):
async def test_return_correct_rule_numbers(self):
test_cases = [
- (("1", "2", "first"), {1, 2}),
- (("1", "hello", "2", "second"), {1}),
- (("second", "third", "unknown", "999"), {2, 3})
+ ("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:
with self.subTest(identifier=raw_user_input):
- 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, args=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),
+ ("", None),
+ ("hello 2 second", None),
+ ("hello 999", None),
]
for raw_user_input, expected_matched_rule_numbers in test_cases:
with self.subTest(identifier=raw_user_input):
- 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, args=raw_user_input)
embed = self.ctx.send.call_args.kwargs['embed']
self.assertEqual(information.DEFAULT_RULES_DESCRIPTION, embed.description)
self.assertEqual(expected_matched_rule_numbers, final_rule_numbers)