Skip to content

Commit ae01fae

Browse files
committed
Chore: Fix the queues & split the error handlers & fix some errors
1 parent 0985807 commit ae01fae

11 files changed

Lines changed: 373 additions & 204 deletions

cogs/config_cog.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,17 @@ def __init__(self, bot: TheDiscordMathProblemBot):
4747
self.cache: problems_module.MathProblemCache = bot.cache
4848

4949
async def sync_check(
50-
self, inter: GuildCommandInteraction, cache_name: str, role: Role
50+
self, inter: GuildCommandInteraction, check_name: str, role: Role
5151
):
52-
if cache_name not in CHECKS:
52+
if check_name not in CHECKS:
5353
return await inter.send("This is not a valid check!")
5454

5555
# TODO: Refactor - don't use setattr
5656
data = await self.cache.get_guild_data(
5757
guild_id=inter.guild_id, default=GuildData.default(inter.guild_id)
5858
)
5959
try:
60-
check = getattr(data, cache_name) # Get the check
60+
check = getattr(data, check_name) # Get the check
6161
role_permissions = role.permissions # Cache the permissions
6262
check.permissions = [
6363
name

helpful_modules/actual_restarter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646

4747
def start():
48+
raise NotImplementedError("This function does not work as written. It should be rewritten to use os.execv()")
4849
print(f"Hello from my subprocess! My PID is {os.getpid()}")
4950
print(f"{main_script_path}")
5051

helpful_modules/base_on_error.py

Lines changed: 12 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -37,155 +37,31 @@
3737
from .custom_embeds import ErrorEmbed, SimpleEmbed, SuccessEmbed
3838
from .problems_module.errors import (
3939
LinearAlgebraUserInputErrorException,
40-
LockedCacheException,
40+
LockedCacheException
4141
)
42+
from error_handler.handle_known_errors import handle_known_error
43+
from error_handler.handle_unexpected_error import handle_unexpected_error
4244
from .the_documentation_file_loader import DocumentationFileLoader
4345

4446

45-
def get_git_revision_hash() -> str:
46-
"""A method that gets the git revision hash. Credit to https://stackoverflow.com/a/21901260 for the code :-)"""
47-
return subprocess.check_output(
48-
["git", "rev-parse", "HEAD"], encoding="ascii", errors="ignore"
49-
).strip()[
50-
:7
51-
] # [7:] is here because of the commit hash, the rest of this function is from stack overflow
47+
48+
49+
5250

5351

5452
async def base_on_error(
5553
inter: disnake.ApplicationCommandInteraction, error: BaseException | Exception
5654
):
5755
"""The base on_error event. Call this and use the dictionary as keyword arguments to print to the user"""
58-
print("OH NO AN ERROR OCCURED!!!!")
56+
print("OH NO AN ERROR OCCURRED!!!!")
5957
if isinstance(error, BaseException) and not isinstance(error, Exception):
6058
# Errors that do not inherit from Exception are not meant to be caught
6159
await inter.bot.close()
6260
if exc_info()[0] is not None:
6361
raise
6462
raise error
65-
cause = None
66-
if error.__context__ is not None:
67-
cause = error.__context__
68-
print(cause, error, inter)
69-
if isinstance(error, LockedCacheException):
70-
return {
71-
"content": "The bot's cache's lock is currently being held. Please try again later."
72-
}
73-
# print(isinstance(cause, LinearAlgebraUserInputErrorException))
74-
# print(type(cause))
75-
if isinstance(error, LinearAlgebraUserInputErrorException):
76-
return {"embed": ErrorEmbed(str(cause))}
77-
if isinstance(error, (OnCooldown, disnake.ext.commands.CommandOnCooldown)):
78-
# This is a cooldown exception
79-
content = f"This command is on cooldown; please retry **{disnake.utils.format_dt(disnake.utils.utcnow() + datetime.timedelta(seconds=error.retry_after), style='R')}**."
80-
return {"content": content, "delete_after": error.retry_after}
81-
if isinstance(error, (disnake.Forbidden,)):
82-
extra_content = """There was a 403 error. This means either
83-
1) You didn't give me enough permissions to function correctly, or
84-
2) There's a bug! If so, please report it!
85-
86-
The error traceback is below."""
87-
error_traceback = "\n".join(traceback.format_exception(error))
88-
paginator = PaginatorView.paginate(
89-
user_id=inter.author.id,
90-
text=extra_content + error_traceback,
91-
breaking_chars="\n",
92-
max_page_length=1900,
93-
special_color=disnake.Color.red(),
94-
)
95-
return {"embed": paginator.create_embed(), "view": paginator}
96-
97-
if isinstance(error, commands.NotOwner):
98-
return {"embed": ErrorEmbed("You are not the owner of this bot.")}
99-
if isinstance(error, disnake.ext.commands.errors.CheckFailure):
100-
return {"embed": ErrorEmbed(str(error))}
101-
102-
# Embed = ErrorEmbed(custom_title="⚠ Oh no! Error: " + str(type(error)), description=("Command raised an exception:" + str(error)))
103-
logging.error("Uh oh - an error occurred ", exc_info=exc_info())
104-
error_traceback = "\n".join(traceback.format_exception(error))
105-
print(
106-
"\n".join(traceback.format_exception(error)), # python 3.10 only!
107-
file=stderr,
108-
)
109-
110-
error_msg = """An error occurred!
111-
112-
Steps you should do:
113-
1) Please report this bug to me! (Either create a github issue, or report it in the support server)
114-
2) If you are a programmer, please suggest a fix by creating a Pull Request.
115-
3) Please don't use this command until it gets fixed in a later update!
116-
117-
The error traceback is shown below; this may be removed/DMed to the user in the future.
118-
119-
""" # TODO: update when my support server becomes public & think about providing the traceback to the user
120-
traceback_msg = disnake.utils.escape_markdown(error_traceback)
121-
additional_error = ""
122-
try:
123-
await log_error(error) # Log the error
124-
except Exception as log_error_exc:
125-
additional_error = (
126-
"""Additionally, while trying to log this error, the following exception occurred: \n"""
127-
+ disnake.utils.escape_markdown(
128-
"\n".join(traceback.format_exception(log_error_exc))
129-
)
130-
)
131-
132-
try:
133-
embed = disnake.Embed(
134-
colour=disnake.Colour.red(),
135-
description=error_msg + traceback_msg + additional_error,
136-
title="Oh, no! An error occurred!",
137-
)
138-
except (TypeError, NameError) as e:
139-
# send as plain text
140-
plain_text = (
141-
"""Oh no! An Exception occurred! And it couldn't be sent as an embed!```"""
142-
)
143-
plain_text += error_msg + traceback_msg + additional_error
144-
plain_text += f"```Time: {str(asctime())} Commit hash: {get_git_revision_hash()} The stack trace is shown for debugging purposes. The stack trace is also logged (and pushed), but should not contain identifying information (only code which is on github)"
145-
146-
plain_text += f"Error that occurred while attempting to send it as an embed:"
147-
plain_text += disnake.utils.escape_markdown(
148-
"".join(traceback.format_exception(e))
149-
)[: -(1650 - len(plain_text))]
150-
the_new_exception = deepcopy(e)
151-
the_new_exception.__cause__ = error
152-
if len(plain_text) > 2000:
153-
# uh oh
154-
raise RuntimeError(
155-
"An error occurred; could not send it as an embed nor as plain text!"
156-
) from the_new_exception
157-
158-
return {"content": plain_text}
159-
footer = f"Time: {str(asctime())} Commit hash: {get_git_revision_hash()} The stack trace is shown for debugging purposes. The stack trace is also logged (and pushed), but should not contain identifying information (only code which is on github)"
160-
embed.set_footer(text=footer)
161-
if len(embed.description) < 2048:
162-
return {"embed": embed}
163-
paginator = PaginatorView.paginate(
164-
user_id=inter.author.id,
165-
text=error_msg,
166-
breaking_chars="\n",
167-
max_page_length=1900,
168-
special_color=disnake.Color.red(),
169-
)
170-
paginator.add_pages(
171-
PaginatorView.break_into_pages(
172-
traceback_msg, max_page_length=1900, breaking_chars="\n"
173-
)
174-
)
175-
if additional_error:
176-
paginator.add_pages(
177-
PaginatorView.break_into_pages(
178-
additional_error, max_page_length=1900, breaking_chars="\n"
179-
)
180-
)
181-
accounted_for = len(error_msg) + len(traceback_msg) + len(additional_error)
182-
if len(embed.description) != accounted_for:
183-
paginator.add_pages(
184-
PaginatorView.break_into_pages(
185-
embed.description[accounted_for:],
186-
max_page_length=1900,
187-
breaking_chars="\n",
188-
)
189-
)
190-
first_page = paginator.create_embed()
191-
return {"embed": first_page, "view": paginator}
63+
known_error_result = handle_known_error(error)
64+
if known_error_result is not None:
65+
return known_error_result
66+
return handle_unexpected_error(error)
67+

helpful_modules/changelog.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import datetime
2121
import io
22+
import asyncio
2223
import json
2324
import typing as t
2425

@@ -27,7 +28,7 @@ class ChangeLogEntry:
2728
def __init__(
2829
self, *, patchNotes: t.List[str], old: str, new: str, date_released: str
2930
):
30-
self.patchNotes = "\n".join[patchNotes]
31+
self.patchNotes = "\n".join(patchNotes)
3132
self.patch_notes = patchNotes
3233
self.old_version = old
3334
self.new_version = new
@@ -41,15 +42,15 @@ def __init__(
4142
def to_dict(self) -> dict:
4243
return {
4344
"patch_notes": "\n".split(self.patchNotes),
44-
"old": self.old_verison,
45+
"old": self.old_version,
4546
"new": self.new_version,
4647
"date_released": self.date_released.totimestamp(
4748
tzinfo=datetime.timezone.utc
4849
),
4950
}
5051

5152
@classmethod
52-
def from_dict(cls, data: dict) -> "ChangelogEntry":
53+
def from_dict(cls, data: dict) -> "ChangeLogEntry":
5354
return cls(
5455
patchNotes=data["patch_notes"],
5556
old=data["old"],
@@ -66,7 +67,7 @@ def __init__(self, file_name: str):
6667
asyncio.run(self._open_file())
6768
except FileNotFoundError:
6869
raise ValueError("File not found.")
69-
self._changelogs: t.List[ChangelogEntry] = []
70+
self._changelogs: t.List[ChangeLogEntry] = []
7071

7172
async def _open_file(
7273
self,
@@ -88,7 +89,7 @@ def func(file):
8889
entries = json.load(file)
8990
changelogs = []
9091
for entry in entries.values():
91-
changelogs.append(ChangelogEntry.from_dict(entry))
92+
changelogs.append(ChangeLogEntry.from_dict(entry))
9293
return changelogs
9394

9495
self._changelogs = await self._open_file(func=func, mode="r")
@@ -100,7 +101,7 @@ def func(file: io.TextIOWrapper, data: dict):
100101

101102
return await self._open_file(func=func, mode="w", args=[new])
102103

103-
async def add_changelog(self, item: ChangelogEntry):
104+
async def add_changelog(self, item: ChangeLogEntry):
104105
data = await self.load_files()
105106
data.append(item.to_dict())
106107

@@ -113,6 +114,6 @@ def func(file, _data):
113114
async def create_changelog(self, data: dict):
114115
# TODO: finish
115116
try:
116-
return ChangelogEntry(*data)
117+
return ChangeLogEntry(**data)
117118
except BaseException as exc:
118119
raise RuntimeError("Could not convert it to a dictionary") from exc

helpful_modules/checks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ async def predicate(inter: disnake.ApplicationCommandInteraction):
272272
try:
273273
if int(it) >= max_num:
274274
return False
275-
except:
275+
except ValueError:
276276
pass
277277
for i in it.split():
278278
try:
@@ -319,9 +319,9 @@ async def predicate(inter: disnake.ApplicationCommandInteraction):
319319
extra_info={},
320320
)
321321
return True
322-
except:
323-
exit()
324-
322+
except Exception as e:
323+
traceback.print_exception(e)
324+
return True
325325
return commands.check(predicate)
326326

327327

helpful_modules/custom_bot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ async def on_connect(self):
655655
self.log.debug(
656656
"Deleting data from guilds the bot was kicked from while it was offline"
657657
)
658-
bot_guild_ids = [guild.id for guild in self.guilds]
658+
bot_guild_ids = set(guild.id for guild in self.guilds) # we make it a set because checking membership in a set is O(1)
659659
# The guild_ids of the guilds that the bot is in
660660
for guild_id in await self.cache.get_guilds():
661661
# Obtain all guilds the cache stores data (will need to be upgraded.)

helpful_modules/error_handler/__init__.py

Whitespace-only changes.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# This module is licensed under AGPLv3 (This includes everything in this file.)
2+
3+
# You can distribute any version of the Software created and distributed *before* 23:17:55.00 July 28, 2024 GMT-4
4+
# under the GNU General Public License version 3 or at your option, any later option.
5+
# But versions of the code created and/or distributed *on or after* that date must be distributed
6+
# under the GNU *Affero* General Public License, version 3, or, at your option, any later version.
7+
#
8+
# This file and this module are part of The Discord Math Problem Bot Repo
9+
#
10+
# This program is free software: you can redistribute it and/or modify
11+
# it under the terms of the GNU Affero General Public License as published by
12+
# the Free Software Foundation, either version 3 of the License, or
13+
# (at your option) any later version.
14+
#
15+
# This program is distributed in the hope that it will be useful,
16+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
# GNU General Public License for more details.
19+
#
20+
# You should have received a copy of the GNU Affero General Public License
21+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
#
23+
# Author: Samuel Guo (64931063+rf20008@users.noreply.github.com)
24+
import datetime
25+
26+
import disnake
27+
from disnake.ext import commands
28+
29+
30+
from ..threads_or_useful_funcs import get_error_cause
31+
from ..cooldowns import OnCooldown
32+
from ..custom_embeds import ErrorEmbed
33+
from ..problems_module.errors import (
34+
LinearAlgebraUserInputErrorException,
35+
LockedCacheException,
36+
)
37+
def handle_known_error(error: BaseException | Exception) -> dict[str, str | disnake.Embed | int] | None:
38+
cause = get_error_cause(error)
39+
if isinstance(error, LockedCacheException):
40+
return {
41+
"content": "The bot's cache's lock is currently held. Please try again later."
42+
}
43+
# print(isinstance(cause, LinearAlgebraUserInputErrorException))
44+
# print(type(cause))
45+
if isinstance(error, LinearAlgebraUserInputErrorException):
46+
return {"embed": ErrorEmbed(str(cause))}
47+
if isinstance(error, (OnCooldown, disnake.ext.commands.CommandOnCooldown)):
48+
# This is a cooldown exception
49+
content = f"This command is on cooldown; please retry **{disnake.utils.format_dt(disnake.utils.utcnow() + datetime.timedelta(seconds=error.retry_after), style='R')}**."
50+
return {"content": content, "delete_after": error.retry_after}
51+
if isinstance(error, commands.NotOwner):
52+
return {"embed": ErrorEmbed("You are not the owner of this bot.")}
53+
if isinstance(error, disnake.ext.commands.errors.CheckFailure):
54+
return {"embed": ErrorEmbed(str(error))}
55+
return None
56+

0 commit comments

Comments
 (0)