Skip to content

Commit 82faceb

Browse files
authored
Merge branch 'development' into users/lorenzo132/arged-responses
2 parents 041b23e + 5d4175e commit 82faceb

6 files changed

Lines changed: 178 additions & 98 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66
This project mostly adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
77
however, insignificant breaking changes do not guarantee a major version bump, see the reasoning [here](https://github.com/modmail-dev/modmail/issues/319). If you're a plugin developer, note the "BREAKING" section.
88

9+
# Unreleased
10+
11+
### Fixed
12+
* Confirm thread creation (react to contact) no longer leaves a thread stuck in a "not ready" cache state when the recipient has DMs disabled. The bot now catches `discord.Forbidden` when sending the confirmation prompt, cancels the thread, and clears the cache entry immediately instead of requiring a bot restart. (#3442)
13+
914
# v4.2.1
1015

1116
### Added

cogs/plugins.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ async def initial_load_plugins(self):
153153
logger.info("Migrated legacy plugin name: %s, now %s.", plugin_name, str(plugin))
154154
self.bot.config["plugins"].append(str(plugin))
155155

156+
if self.bot.config.get("registry_plugins_only") and not plugin_name in self.registry:
157+
self.bot.config["plugins"].remove(plugin_name)
158+
logger.info(
159+
"Loading of plugin %s was skipped and the plugin was removed because it is not present in the registry.",
160+
plugin_name,
161+
)
162+
continue
163+
156164
try:
157165
await self.download_plugin(plugin)
158166
await self.load_plugin(plugin)
@@ -281,7 +289,7 @@ async def unload_plugin(self, plugin: Plugin) -> None:
281289
if module == ext_parent or module.startswith(ext_parent + "."):
282290
del sys.modules[module]
283291

284-
async def parse_user_input(self, ctx, plugin_name, check_version=False):
292+
async def parse_user_input(self, ctx, plugin_name, check_version=False, check_registry=True):
285293
if not self.bot.config["enable_plugins"]:
286294
embed = discord.Embed(
287295
description="Plugins are disabled, enable them by setting `ENABLE_PLUGINS=true`",
@@ -318,7 +326,7 @@ async def parse_user_input(self, ctx, plugin_name, check_version=False):
318326
plugin = Plugin(user, repo, plugin_name, branch)
319327

320328
else:
321-
if self.bot.config.get("registry_plugins_only"):
329+
if check_registry and self.bot.config.get("registry_plugins_only"):
322330
embed = discord.Embed(
323331
description="This plugin is not in the registry. To install this plugin, "
324332
"you must set `REGISTRY_PLUGINS_ONLY=no` or remove this key in your .env file.",
@@ -337,7 +345,7 @@ async def parse_user_input(self, ctx, plugin_name, check_version=False):
337345
)
338346
await ctx.send(embed=embed)
339347
return
340-
return plugin
348+
return plugin
341349

342350
@commands.group(aliases=["plugin"], invoke_without_command=True)
343351
@checks.has_permissions(PermissionLevel.OWNER)
@@ -446,7 +454,7 @@ async def plugins_remove(self, ctx, *, plugin_name: str):
446454
`plugin_name` can be the name of the plugin found in `{prefix}plugin registry`, or a direct reference
447455
to a GitHub hosted plugin (in the format `user/repo/name[@branch]`) or `@local/name` for local plugins.
448456
"""
449-
plugin = await self.parse_user_input(ctx, plugin_name)
457+
plugin = await self.parse_user_input(ctx, plugin_name, check_registry=False)
450458
if plugin is None:
451459
return
452460

core/clients.py

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -662,39 +662,59 @@ async def append_log(
662662
channel_id: str = "",
663663
type_: str = "thread_message",
664664
) -> dict:
665-
channel_id = str(channel_id) or str(message.channel.id)
666-
message_id = str(message_id) or str(message.id)
667-
668-
content = message.content or ""
669-
if forwarded := extract_forwarded_content(message):
670-
if content:
671-
content += "\n" + forwarded
672-
else:
673-
content = forwarded
674-
675-
data = {
676-
"timestamp": str(message.created_at),
677-
"message_id": message_id,
678-
"author": {
679-
"id": str(message.author.id),
680-
"name": message.author.name,
681-
"discriminator": message.author.discriminator,
682-
"avatar_url": message.author.display_avatar.url if message.author.display_avatar else None,
683-
"mod": not isinstance(message.channel, DMChannel),
684-
},
685-
"content": content,
686-
"type": type_,
687-
"attachments": [
688-
{
689-
"id": a.id,
690-
"filename": a.filename,
691-
"is_image": a.width is not None,
692-
"size": a.size,
693-
"url": a.url,
694-
}
695-
for a in message.attachments
696-
],
697-
}
665+
channel_id = str(channel_id) or (str(message.channel.id) if message else "")
666+
message_id = str(message_id) or (str(message.id) if message else "")
667+
668+
if message:
669+
content = message.content or ""
670+
if forwarded := extract_forwarded_content(message):
671+
if content:
672+
content += "\n" + forwarded
673+
else:
674+
content = forwarded
675+
676+
data = {
677+
"timestamp": str(message.created_at),
678+
"message_id": message_id,
679+
"author": {
680+
"id": str(message.author.id),
681+
"name": message.author.name,
682+
"discriminator": message.author.discriminator,
683+
"avatar_url": (
684+
message.author.display_avatar.url if message.author.display_avatar else None
685+
),
686+
"mod": not isinstance(message.channel, DMChannel),
687+
},
688+
"content": content,
689+
"type": type_,
690+
"attachments": [
691+
{
692+
"id": a.id,
693+
"filename": a.filename,
694+
"is_image": a.width is not None,
695+
"size": a.size,
696+
"url": a.url,
697+
}
698+
for a in message.attachments
699+
],
700+
}
701+
else:
702+
# Fallback for when message is None but we still want to log something (e.g. system note)
703+
# This requires at least some manual data to be useful.
704+
data = {
705+
"timestamp": str(discord.utils.utcnow()),
706+
"message_id": message_id or "0",
707+
"author": {
708+
"id": "0",
709+
"name": "System",
710+
"discriminator": "0000",
711+
"avatar_url": None,
712+
"mod": True,
713+
},
714+
"content": "System Message (No Content)",
715+
"type": type_,
716+
"attachments": [],
717+
}
698718

699719
return await self.logs.find_one_and_update(
700720
{"channel_id": channel_id},

core/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,10 @@ def __init__(self, message):
438438
self._message = message
439439

440440
def __getattr__(self, name: str):
441+
if self._message is None:
442+
# If we're wrapping None, we can't delegate attributes.
443+
# This mimics behavior where the attribute doesn't exist.
444+
raise AttributeError(f"'DummyMessage' object has no attribute '{name}' (wrapped message is None)")
441445
return getattr(self._message, name)
442446

443447
def __bool__(self):

0 commit comments

Comments
 (0)