Handle various file related issues (#76)

* Create state files if they don't exist yet

* Add notifications helper to message bot managers

* Inform bot managers about errors if possible

* Handle JSONDecodeErrors including empty files
This commit is contained in:
TSRBerry 2023-10-09 22:56:13 +02:00 committed by GitHub
parent 31b9aeb436
commit 9669556a39
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 171 additions and 14 deletions

View file

@ -8,6 +8,8 @@ import discord
from discord.ext import commands from discord.ext import commands
from discord.ext.commands import CommandError, Context from discord.ext.commands import CommandError, Context
from robocop_ng.helpers.notifications import report_critical_error
if len(sys.argv[1:]) != 1: if len(sys.argv[1:]) != 1:
sys.stderr.write("usage: <state_dir>") sys.stderr.write("usage: <state_dir>")
sys.exit(1) sys.exit(1)
@ -63,6 +65,9 @@ for wanted_json_idx in range(len(wanted_jsons)):
wanted_jsons[wanted_json_idx] = os.path.join( wanted_jsons[wanted_json_idx] = os.path.join(
state_dir, wanted_jsons[wanted_json_idx] state_dir, wanted_jsons[wanted_json_idx]
) )
if not os.path.isfile(wanted_jsons[wanted_json_idx]):
with open(wanted_jsons[wanted_json_idx], "w") as file:
file.write("{}")
intents = discord.Intents.all() intents = discord.Intents.all()
intents.typing = False intents.typing = False
@ -137,6 +142,25 @@ async def on_command(ctx):
async def on_error(event: str, *args, **kwargs): async def on_error(event: str, *args, **kwargs):
log.exception(f"Error on {event}:") log.exception(f"Error on {event}:")
exception = sys.exception()
is_report_allowed = any(
[
not isinstance(exception, x)
for x in [
discord.RateLimited,
discord.GatewayNotFound,
discord.InteractionResponded,
discord.LoginFailure,
]
]
)
if exception is not None and is_report_allowed:
await report_critical_error(
bot,
exception,
additional_info={"Event": event, "args": args, "kwargs": kwargs},
)
@bot.event @bot.event
async def on_command_error(ctx: Context, error: CommandError): async def on_command_error(ctx: Context, error: CommandError):

View file

@ -2,6 +2,8 @@ import json
import os import os
from typing import Union from typing import Union
from robocop_ng.helpers.notifications import report_critical_error
def get_disabled_ids_path(bot) -> str: def get_disabled_ids_path(bot) -> str:
return os.path.join(bot.state_dir, "data/disabled_ids.json") return os.path.join(bot.state_dir, "data/disabled_ids.json")
@ -22,7 +24,19 @@ def is_ro_section_valid(ro_section: dict[str, str]) -> bool:
def get_disabled_ids(bot) -> dict[str, dict[str, Union[str, dict[str, str]]]]: def get_disabled_ids(bot) -> dict[str, dict[str, Union[str, dict[str, str]]]]:
if os.path.isfile(get_disabled_ids_path(bot)): if os.path.isfile(get_disabled_ids_path(bot)):
with open(get_disabled_ids_path(bot), "r") as f: with open(get_disabled_ids_path(bot), "r") as f:
disabled_ids = json.load(f) try:
disabled_ids = json.load(f)
except json.JSONDecodeError as e:
content = f.read()
report_critical_error(
bot,
e,
additional_info={
"file": {"length": len(content), "content": content}
},
)
return {}
# Migration code # Migration code
if "app_id" in disabled_ids.keys(): if "app_id" in disabled_ids.keys():
old_disabled_ids = disabled_ids.copy() old_disabled_ids = disabled_ids.copy()

View file

@ -2,6 +2,8 @@ import json
import os import os
from typing import Optional, Union from typing import Optional, Union
from robocop_ng.helpers.notifications import report_critical_error
def get_macros_path(bot): def get_macros_path(bot):
return os.path.join(bot.state_dir, "data/macros.json") return os.path.join(bot.state_dir, "data/macros.json")
@ -10,7 +12,18 @@ def get_macros_path(bot):
def get_macros_dict(bot) -> dict[str, dict[str, Union[list[str], str]]]: def get_macros_dict(bot) -> dict[str, dict[str, Union[list[str], str]]]:
if os.path.isfile(get_macros_path(bot)): if os.path.isfile(get_macros_path(bot)):
with open(get_macros_path(bot), "r") as f: with open(get_macros_path(bot), "r") as f:
macros = json.load(f) try:
macros = json.load(f)
except json.JSONDecodeError as e:
content = f.read()
report_critical_error(
bot,
e,
additional_info={
"file": {"length": len(content), "content": content}
},
)
return {}
# Migration code # Migration code
if "aliases" not in macros.keys(): if "aliases" not in macros.keys():

View file

@ -0,0 +1,53 @@
import json
from typing import Optional, Union
from discord import Message, MessageReference, PartialMessage
MessageReferenceTypes = Union[Message, MessageReference, PartialMessage]
async def notify_management(
bot, message: str, reference_message: Optional[MessageReferenceTypes] = None
):
log_channel = await bot.get_channel_safe(bot.config.botlog_channel)
bot_manager_role = log_channel.guild.get_role(bot.config.bot_manager_role_id)
notification_message = f"{bot_manager_role.mention}:\n"
if reference_message is not None and reference_message.channel != log_channel:
notification_message += f"Message reference: {reference_message.jump_url}\n"
notification_message += message
return await log_channel.send(notification_message)
else:
notification_message += message
return await log_channel.send(
notification_message,
reference=reference_message,
mention_author=False,
)
async def report_critical_error(
bot,
error: BaseException,
reference_message: Optional[MessageReferenceTypes] = None,
additional_info: Optional[dict] = None,
):
message = "⛔ A critical error occurred!"
if additional_info is not None:
message += f"""
```json
{json.dumps(additional_info)}
```"""
message += f"""
Exception:
```
{error}
```"""
return await notify_management(bot, message, reference_message)

View file

@ -1,14 +1,28 @@
import json import json
import os import os
from robocop_ng.helpers.notifications import report_critical_error
def get_restrictions_path(bot): def get_restrictions_path(bot):
return os.path.join(bot.state_dir, "data/restrictions.json") return os.path.join(bot.state_dir, "data/restrictions.json")
def get_restrictions(bot): def get_restrictions(bot):
with open(get_restrictions_path(bot), "r") as f: if os.path.isfile(get_restrictions_path(bot)):
return json.load(f) with open(get_restrictions_path(bot), "r") as f:
try:
return json.load(f)
except json.JSONDecodeError as e:
content = f.read()
report_critical_error(
bot,
e,
additional_info={
"file": {"length": len(content), "content": content}
},
)
return {}
def set_restrictions(bot, contents): def set_restrictions(bot, contents):
@ -18,11 +32,10 @@ def set_restrictions(bot, contents):
def get_user_restrictions(bot, uid): def get_user_restrictions(bot, uid):
uid = str(uid) uid = str(uid)
with open(get_restrictions_path(bot), "r") as f: rsts = get_restrictions(bot)
rsts = json.load(f) if uid in rsts:
if uid in rsts: return rsts[uid]
return rsts[uid] return []
return []
def add_restriction(bot, uid, rst): def add_restriction(bot, uid, rst):

View file

@ -2,14 +2,28 @@ import json
import math import math
import os import os
from robocop_ng.helpers.notifications import report_critical_error
def get_crontab_path(bot): def get_crontab_path(bot):
return os.path.join(bot.state_dir, "data/robocronptab.json") return os.path.join(bot.state_dir, "data/robocronptab.json")
def get_crontab(bot): def get_crontab(bot):
with open(get_crontab_path(bot), "r") as f: if os.path.isfile(get_crontab_path(bot)):
return json.load(f) with open(get_crontab_path(bot), "r") as f:
try:
return json.load(f)
except json.JSONDecodeError as e:
content = f.read()
report_critical_error(
bot,
e,
additional_info={
"file": {"length": len(content), "content": content}
},
)
return {}
def set_crontab(bot, contents): def set_crontab(bot, contents):

View file

@ -2,6 +2,8 @@ import json
import os.path import os.path
import os import os
from robocop_ng.helpers.notifications import report_critical_error
def get_persistent_roles_path(bot): def get_persistent_roles_path(bot):
return os.path.join(bot.state_dir, "data/persistent_roles.json") return os.path.join(bot.state_dir, "data/persistent_roles.json")
@ -10,7 +12,17 @@ def get_persistent_roles_path(bot):
def get_persistent_roles(bot) -> dict[str, list[str]]: def get_persistent_roles(bot) -> dict[str, list[str]]:
if os.path.isfile(get_persistent_roles_path(bot)): if os.path.isfile(get_persistent_roles_path(bot)):
with open(get_persistent_roles_path(bot), "r") as f: with open(get_persistent_roles_path(bot), "r") as f:
return json.load(f) try:
return json.load(f)
except json.JSONDecodeError as e:
content = f.read()
report_critical_error(
bot,
e,
additional_info={
"file": {"length": len(content), "content": content}
},
)
return {} return {}

View file

@ -2,6 +2,8 @@ import json
import os import os
import time import time
from robocop_ng.helpers.notifications import report_critical_error
userlog_event_types = { userlog_event_types = {
"warns": "Warn", "warns": "Warn",
"bans": "Ban", "bans": "Ban",
@ -16,8 +18,20 @@ def get_userlog_path(bot):
def get_userlog(bot): def get_userlog(bot):
with open(get_userlog_path(bot), "r") as f: if os.path.isfile(get_userlog_path(bot)):
return json.load(f) with open(get_userlog_path(bot), "r") as f:
try:
return json.load(f)
except json.JSONDecodeError as e:
content = f.read()
report_critical_error(
bot,
e,
additional_info={
"file": {"length": len(content), "content": content}
},
)
return {}
def set_userlog(bot, contents): def set_userlog(bot, contents):