|
2 | 2 | import datetime |
3 | 3 | import itertools |
4 | 4 | import json |
5 | | -import pathlib |
6 | 5 | import random |
7 | 6 | import re |
8 | 7 | from functools import cache |
|
14 | 13 | import rapidfuzz.process |
15 | 14 | from disnake.ext import commands, tasks |
16 | 15 |
|
17 | | -import monty.resources |
18 | 16 | from monty import constants |
19 | 17 | from monty.bot import Monty |
20 | 18 | from monty.log import get_logger |
|
25 | 23 | logger = get_logger(__name__) |
26 | 24 |
|
27 | 25 |
|
28 | | -RUFF_RULES = monty.resources.folder / "ruff_rules.json" |
| 26 | +RUFF_RULES = "https://raw.githubusercontent.com/onerandomusername/ruff-rules/refs/heads/main/rules.json" |
29 | 27 |
|
30 | 28 | RUFF_RULES_BASE_URL = "https://docs.astral.sh/ruff/rules" |
31 | 29 |
|
@@ -136,23 +134,22 @@ def cog_unload(self) -> None: |
136 | 134 | self.update_rules.cancel() |
137 | 135 |
|
138 | 136 | async def _fetch_rules(self) -> Any: |
139 | | - if isinstance(RUFF_RULES, pathlib.Path): |
140 | | - with open(RUFF_RULES, "r") as f: |
141 | | - return json.load(f) |
142 | 137 | async with self.bot.http_session.get(RUFF_RULES) as response: |
143 | | - if response.status == 200 and response.content_type == "application/json": |
144 | | - return await response.json() |
| 138 | + if response.status == 200: |
| 139 | + return json.loads(await response.text()) |
145 | 140 | return None |
146 | 141 |
|
147 | | - @tasks.loop(hours=1) |
| 142 | + @tasks.loop(minutes=10) |
148 | 143 | # @async_cached(cache=LRUMemoryCache(25, timeout=int(datetime.timedelta(hours=2).total_seconds()))) |
149 | 144 | async def update_rules(self) -> Optional[dict[str, Any]]: |
150 | 145 | """Fetch Ruff rules.""" |
151 | 146 | raw_rules = await self._fetch_rules() |
152 | | - new_rules = dict[str, Rule]() |
153 | 147 | if not raw_rules: |
154 | 148 | logger.error("Failed to fetch rules, something went wrong") |
| 149 | + self.last_fetched = utcnow() # don't try again for an hour |
155 | 150 | return |
| 151 | + |
| 152 | + new_rules = dict[str, Rule]() |
156 | 153 | for unparsed_rule in raw_rules: |
157 | 154 | parsed_rule = Rule(**unparsed_rule) |
158 | 155 | new_rules[parsed_rule.code] = parsed_rule |
@@ -279,8 +276,10 @@ async def _legacy_embed(self, inter: disnake.ApplicationCommandInteraction, rule |
279 | 276 | async def ruff_rule_autocomplete(self, inter: disnake.ApplicationCommandInteraction, option: str) -> dict[str, str]: |
280 | 277 | """Provide autocomplete for ruff rules.""" |
281 | 278 | # return dict(sorted([[code, code] for code, rule in self.rules.items()])[:25]) |
282 | | - option = option.upper().strip() |
| 279 | + if not self.rules: |
| 280 | + return {} |
283 | 281 |
|
| 282 | + option = option.upper().strip() |
284 | 283 | if not option: |
285 | 284 | return {rule.code_with_name: rule.code for rule in random.choices(list(self.rules.values()), k=12)} |
286 | 285 |
|
@@ -316,6 +315,19 @@ class Fake: |
316 | 315 |
|
317 | 316 | return matches |
318 | 317 |
|
| 318 | + def check_ruff_rules_loaded(self, inter: disnake.ApplicationCommandInteraction) -> bool: |
| 319 | + """A check for all commands in this cog.""" |
| 320 | + if not self.rules: |
| 321 | + raise commands.CommandError("Ruff rules have not been loaded yet, please try again later.") |
| 322 | + return True |
| 323 | + |
| 324 | + def cog_slash_command_check(self, inter: disnake.ApplicationCommandInteraction) -> bool: |
| 325 | + """A check for all commands in this cog.""" |
| 326 | + if inter.application_command.qualified_name.startswith("ruff rule"): |
| 327 | + return self.check_ruff_rules_loaded(inter) |
| 328 | + |
| 329 | + return True |
| 330 | + |
319 | 331 |
|
320 | 332 | def setup(bot: Monty) -> None: |
321 | 333 | """Load the Ruff cog.""" |
|
0 commit comments