Skip to content

Commit d671998

Browse files
authored
Merge branch 'development' into development
2 parents 16d0a80 + d4ec13e commit d671998

File tree

11 files changed

+330
-183
lines changed

11 files changed

+330
-183
lines changed

CHANGELOG.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,25 @@ however, insignificant breaking changes do not guarantee a major version bump, s
1212
- `?alias make/create` as aliases to `?alias add`. This improves continuity between the bot and its command structure. ([PR #3195](https://github.com/kyb3r/modmail/pull/3195))
1313
- Loading the blocked list with the `?blocked` command takes a long time when the list is large. ([PR #3242](https://github.com/kyb3r/modmail/pull/3242))
1414
- Reply not being forwarded from DM. (PR [#3239](https://github.com/modmail-dev/modmail/pull/3239))
15-
16-
# [UNRELEASED]
15+
- Cleanup imports after removing/unloading a plugin. ([PR #3226](https://github.com/modmail-dev/Modmail/pull/3226))
16+
- Fixed a syntactic error in the close message when a thread is closed after a certain duration. ([PR #3233](https://github.com/modmail-dev/Modmail/pull/3233))
17+
- Removed an extra space in the help command title when the command has no parameters. ([PR #3271](https://github.com/modmail-dev/Modmail/pull/3271))
18+
- Corrected some incorrect config help descriptions. ([PR #3277](https://github.com/modmail-dev/Modmail/pull/3277))
1719

1820
### Added
19-
- New .env config option: `REGISTRY_PLUGINS_ONLY`, restricts to only allow adding registry plugins. ([PR #3247](https://github.com/modmail-dev/modmail/pull/3247))
21+
- `?log key <key>` to retrieve the log link and view a preview using a log key. ([PR #3196](https://github.com/modmail-dev/Modmail/pull/3196))
22+
- `REGISTRY_PLUGINS_ONLY`, environment variable, when set, restricts to only allow adding registry plugins. ([PR #3247](https://github.com/modmail-dev/modmail/pull/3247))
23+
- `DISCORD_LOG_LEVEL` environment variable to set the log level of discord.py. ([PR #3216](https://github.com/modmail-dev/Modmail/pull/3216))
2024

2125
### Changed
2226
- Repo moved to https://github.com/modmail-dev/modmail.
27+
- Guild icons in embed footers and author urls now have a fixed size of 128. ([PR #3261](https://github.com/modmail-dev/modmail/pull/3261))
28+
- Discord.py internal logging is now enabled by default. ([PR #3216](https://github.com/modmail-dev/Modmail/pull/3216))
29+
- The confirm-thread-creation dialog now uses buttons instead of reactions. ([PR #3273](https://github.com/modmail-dev/Modmail/pull/3273))
30+
- `?disable all` no longer overrides `?disable new`. ([PR #3278](https://github.com/modmail-dev/Modmail/pull/3278))
31+
32+
### Internal
33+
- Renamed `Bot.log_file_name` to `Bot.log_file_path`. Log files are now created at `temp/logs/modmail.log`. ([PR #3216](https://github.com/modmail-dev/Modmail/pull/3216))
2334

2435
# v4.0.2
2536

bot.py

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252

5353
logger = getLogger(__name__)
5454

55-
5655
temp_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "temp")
5756
if not os.path.exists(temp_dir):
5857
os.mkdir(temp_dir)
@@ -84,18 +83,25 @@ def __init__(self):
8483

8584
self.threads = ThreadManager(self)
8685

87-
self.log_file_name = os.path.join(temp_dir, f"{self.token.split('.')[0]}.log")
88-
self._configure_logging()
86+
log_dir = os.path.join(temp_dir, "logs")
87+
if not os.path.exists(log_dir):
88+
os.mkdir(log_dir)
89+
self.log_file_path = os.path.join(log_dir, "modmail.log")
90+
configure_logging(self)
8991

9092
self.plugin_db = PluginDatabaseClient(self) # Deprecated
9193
self.startup()
9294

93-
def get_guild_icon(self, guild: typing.Optional[discord.Guild]) -> str:
95+
def get_guild_icon(
96+
self, guild: typing.Optional[discord.Guild], *, size: typing.Optional[int] = None
97+
) -> str:
9498
if guild is None:
9599
guild = self.guild
96100
if guild.icon is None:
97101
return "https://cdn.discordapp.com/embed/avatars/0.png"
98-
return guild.icon.url
102+
if size is None:
103+
return guild.icon.url
104+
return guild.icon.with_size(size).url
99105

100106
def _resolve_snippet(self, name: str) -> typing.Optional[str]:
101107
"""
@@ -178,29 +184,6 @@ async def load_extensions(self):
178184
logger.exception("Failed to load %s.", cog)
179185
logger.line("debug")
180186

181-
def _configure_logging(self):
182-
level_text = self.config["log_level"].upper()
183-
logging_levels = {
184-
"CRITICAL": logging.CRITICAL,
185-
"ERROR": logging.ERROR,
186-
"WARNING": logging.WARNING,
187-
"INFO": logging.INFO,
188-
"DEBUG": logging.DEBUG,
189-
}
190-
logger.line()
191-
192-
log_level = logging_levels.get(level_text)
193-
if log_level is None:
194-
log_level = self.config.remove("log_level")
195-
logger.warning("Invalid logging level set: %s.", level_text)
196-
logger.warning("Using default logging level: INFO.")
197-
else:
198-
logger.info("Logging level: %s", level_text)
199-
200-
logger.info("Log file: %s", self.log_file_name)
201-
configure_logging(self.log_file_name, log_level)
202-
logger.debug("Successfully configured logging.")
203-
204187
@property
205188
def version(self):
206189
return parse_version(__version__)
@@ -912,7 +895,7 @@ async def process_dm_modmail(self, message: discord.Message) -> None:
912895
)
913896
embed.set_footer(
914897
text=self.config["disabled_new_thread_footer"],
915-
icon_url=self.get_guild_icon(guild=message.guild),
898+
icon_url=self.get_guild_icon(guild=message.guild, size=128),
916899
)
917900
logger.info("A new thread was blocked from %s due to disabled Modmail.", message.author)
918901
await self.add_reaction(message, blocked_emoji)
@@ -928,7 +911,7 @@ async def process_dm_modmail(self, message: discord.Message) -> None:
928911
)
929912
embed.set_footer(
930913
text=self.config["disabled_current_thread_footer"],
931-
icon_url=self.get_guild_icon(guild=message.guild),
914+
icon_url=self.get_guild_icon(guild=message.guild, size=128),
932915
)
933916
logger.info("A message was blocked from %s due to disabled Modmail.", message.author)
934917
await self.add_reaction(message, blocked_emoji)
@@ -1335,7 +1318,7 @@ async def handle_react_to_contact(self, payload):
13351318
)
13361319
embed.set_footer(
13371320
text=self.config["disabled_new_thread_footer"],
1338-
icon_url=self.get_guild_icon(guild=channel.guild),
1321+
icon_url=self.get_guild_icon(guild=channel.guild, size=128),
13391322
)
13401323
logger.info(
13411324
"A new thread using react to contact was blocked from %s due to disabled Modmail.",
@@ -1797,16 +1780,6 @@ def main():
17971780
)
17981781
sys.exit(0)
17991782

1800-
# Set up discord.py internal logging
1801-
if os.environ.get("LOG_DISCORD"):
1802-
logger.debug(f"Discord logging enabled: {os.environ['LOG_DISCORD'].upper()}")
1803-
d_logger = logging.getLogger("discord")
1804-
1805-
d_logger.setLevel(os.environ["LOG_DISCORD"].upper())
1806-
handler = logging.FileHandler(filename="discord.log", encoding="utf-8", mode="w")
1807-
handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s"))
1808-
d_logger.addHandler(handler)
1809-
18101783
bot = ModmailBot()
18111784
bot.run()
18121785

cogs/modmail.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,15 @@ async def snippet(self, ctx, *, name: str.lower = None):
160160
color=self.bot.error_color, description="You dont have any snippets at the moment."
161161
)
162162
embed.set_footer(text=f'Check "{self.bot.prefix}help snippet add" to add a snippet.')
163-
embed.set_author(name="Snippets", icon_url=self.bot.get_guild_icon(guild=ctx.guild))
163+
embed.set_author(name="Snippets", icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128))
164164
return await ctx.send(embed=embed)
165165

166166
embeds = []
167167

168168
for i, names in enumerate(zip_longest(*(iter(sorted(self.bot.snippets)),) * 15)):
169169
description = format_description(i, names)
170170
embed = discord.Embed(color=self.bot.main_color, description=description)
171-
embed.set_author(name="Snippets", icon_url=self.bot.get_guild_icon(guild=ctx.guild))
171+
embed.set_author(name="Snippets", icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128))
172172
embeds.append(embed)
173173

174174
session = EmbedPaginatorSession(ctx, *embeds)
@@ -444,11 +444,9 @@ async def move(self, ctx, *, arguments):
444444
async def send_scheduled_close_message(self, ctx, after, silent=False):
445445
human_delta = human_timedelta(after.dt)
446446

447-
silent = "*silently* " if silent else ""
448-
449447
embed = discord.Embed(
450448
title="Scheduled close",
451-
description=f"This thread will close {silent}{human_delta}.",
449+
description=f"This thread will{' silently' if silent else ''} close in {human_delta}.",
452450
color=self.bot.error_color,
453451
)
454452

@@ -1031,7 +1029,7 @@ async def anonadduser(self, ctx, *users_arg: Union[discord.Member, discord.Role,
10311029
name = tag
10321030
avatar_url = self.bot.config["anon_avatar_url"]
10331031
if avatar_url is None:
1034-
avatar_url = self.bot.get_guild_icon(guild=ctx.guild)
1032+
avatar_url = self.bot.get_guild_icon(guild=ctx.guild, size=128)
10351033
em.set_footer(text=name, icon_url=avatar_url)
10361034

10371035
for u in users:
@@ -1120,7 +1118,7 @@ async def anonremoveuser(self, ctx, *users_arg: Union[discord.Member, discord.Ro
11201118
name = tag
11211119
avatar_url = self.bot.config["anon_avatar_url"]
11221120
if avatar_url is None:
1123-
avatar_url = self.bot.get_guild_icon(guild=ctx.guild)
1121+
avatar_url = self.bot.get_guild_icon(guild=ctx.guild, size=128)
11241122
em.set_footer(text=name, icon_url=avatar_url)
11251123

11261124
for u in users:
@@ -1212,6 +1210,28 @@ async def logs_closed_by(self, ctx, *, user: User = None):
12121210
session = EmbedPaginatorSession(ctx, *embeds)
12131211
await session.run()
12141212

1213+
@logs.command(name="key", aliases=["id"])
1214+
@checks.has_permissions(PermissionLevel.SUPPORTER)
1215+
async def logs_key(self, ctx, key: str):
1216+
"""
1217+
Get the log link for the specified log key.
1218+
"""
1219+
icon_url = ctx.author.avatar.url
1220+
1221+
logs = await self.bot.api.find_log_entry(key)
1222+
1223+
if not logs:
1224+
embed = discord.Embed(
1225+
color=self.bot.error_color,
1226+
description=f"Log entry `{key}` not found.",
1227+
)
1228+
return await ctx.send(embed=embed)
1229+
1230+
embeds = self.format_log_embeds(logs, avatar_url=icon_url)
1231+
1232+
session = EmbedPaginatorSession(ctx, *embeds)
1233+
await session.run()
1234+
12151235
@logs.command(name="delete", aliases=["wipe"])
12161236
@checks.has_permissions(PermissionLevel.OWNER)
12171237
async def logs_delete(self, ctx, key_or_link: str):
@@ -2132,7 +2152,7 @@ async def disable_new(self, ctx):
21322152
description="Modmail will not create any new threads.",
21332153
color=self.bot.main_color,
21342154
)
2135-
if self.bot.config["dm_disabled"] < DMDisabled.NEW_THREADS:
2155+
if self.bot.config["dm_disabled"] != DMDisabled.NEW_THREADS:
21362156
self.bot.config["dm_disabled"] = DMDisabled.NEW_THREADS
21372157
await self.bot.config.update()
21382158

cogs/plugins.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,17 @@ async def load_plugin(self, plugin):
264264
logger.error("Plugin load failure: %s", plugin.ext_string, exc_info=True)
265265
raise InvalidPluginError("Cannot load extension, plugin invalid.") from exc
266266

267+
async def unload_plugin(self, plugin: Plugin) -> None:
268+
try:
269+
await self.bot.unload_extension(plugin.ext_string)
270+
except commands.ExtensionError as exc:
271+
raise exc
272+
273+
ext_parent = ".".join(plugin.ext_string.split(".")[:-1])
274+
for module in list(sys.modules.keys()):
275+
if module == ext_parent or module.startswith(ext_parent + "."):
276+
del sys.modules[module]
277+
267278
async def parse_user_input(self, ctx, plugin_name, check_version=False):
268279

269280
if not self.bot.config["enable_plugins"]:
@@ -378,7 +389,7 @@ async def plugins_add(self, ctx, *, plugin_name: str):
378389
logger.warning("Unable to download plugin %s.", plugin, exc_info=True)
379390

380391
embed = discord.Embed(
381-
description=f"Failed to download plugin, check logs for error.\n{type(e)}: {e}",
392+
description=f"Failed to download plugin, check logs for error.\n{type(e).__name__}: {e}",
382393
color=self.bot.error_color,
383394
)
384395

@@ -397,7 +408,7 @@ async def plugins_add(self, ctx, *, plugin_name: str):
397408
logger.warning("Unable to load plugin %s.", plugin, exc_info=True)
398409

399410
embed = discord.Embed(
400-
description=f"Failed to download plugin, check logs for error.\n{type(e)}: {e}",
411+
description=f"Failed to load plugin, check logs for error.\n{type(e).__name__}: {e}",
401412
color=self.bot.error_color,
402413
)
403414

@@ -438,7 +449,7 @@ async def plugins_remove(self, ctx, *, plugin_name: str):
438449

439450
if self.bot.config.get("enable_plugins"):
440451
try:
441-
await self.bot.unload_extension(plugin.ext_string)
452+
await self.unload_plugin(plugin)
442453
self.loaded_plugins.remove(plugin)
443454
except (commands.ExtensionNotLoaded, KeyError):
444455
logger.warning("Plugin was never loaded.")
@@ -480,9 +491,10 @@ async def update_plugin(self, ctx, plugin_name):
480491
await self.download_plugin(plugin, force=True)
481492
if self.bot.config.get("enable_plugins"):
482493
try:
483-
await self.bot.unload_extension(plugin.ext_string)
494+
await self.unload_plugin(plugin)
484495
except commands.ExtensionError:
485496
logger.warning("Plugin unload fail.", exc_info=True)
497+
486498
try:
487499
await self.load_plugin(plugin)
488500
except Exception:
@@ -529,17 +541,20 @@ async def plugins_reset(self, ctx):
529541
for ext in list(self.bot.extensions):
530542
if not ext.startswith("plugins."):
531543
continue
544+
logger.error("Unloading plugin: %s.", ext)
532545
try:
533-
logger.error("Unloading plugin: %s.", ext)
534-
await self.bot.unload_extension(ext)
535-
except Exception:
536-
logger.error("Failed to unload plugin: %s.", ext)
537-
else:
538-
if not self.loaded_plugins:
539-
continue
540546
plugin = next((p for p in self.loaded_plugins if p.ext_string == ext), None)
541547
if plugin:
548+
await self.unload_plugin(plugin)
542549
self.loaded_plugins.remove(plugin)
550+
else:
551+
await self.bot.unload_extension(ext)
552+
except Exception:
553+
logger.error("Failed to unload plugin: %s.", ext)
554+
555+
for module in list(sys.modules.keys()):
556+
if module.startswith("plugins."):
557+
del sys.modules[module]
543558

544559
self.bot.config["plugins"].clear()
545560
await self.bot.config.update()

0 commit comments

Comments
 (0)