Skip to content

Add support for replies to help snippets#3538

Open
Saber0324 wants to merge 12 commits into
python-discord:mainfrom
Saber0324:add-reply
Open

Add support for replies to help snippets#3538
Saber0324 wants to merge 12 commits into
python-discord:mainfrom
Saber0324:add-reply

Conversation

@Saber0324

Copy link
Copy Markdown

PR for issue #3537

This implementation makes the bot reply to the same message that the caller replies, and if the command invoke isn't a reply, the bot maintains its current behavior, sending the snippet without replying to the caller.
If the caller replies to himself, the bot also maintains the no-reply behavior.

It is currently implemented for (all aliases of):

  • !learn
  • !kind
  • !pep
  • !src
  • !code
  • !res
  • !gif

Due to the use of LinePaginator.paginate in !rule command, I wasn't able to implement it for all of its variants, so I left it untouched for consistency. (I'll open an issue about it if the PR gets merged)
image

@jb3

jb3 commented Jul 6, 2026

Copy link
Copy Markdown
Member

For various reasons we probably don't want to add this functionality to !rule, so I wouldn't worry about that limitation.

It is not users responsibility to moderate others and the rule command serves as a reference rather than a tool for non-staff members.

Comment thread bot/exts/info/source.py Outdated
ShaharNaveh
ShaharNaveh previously approved these changes Jul 6, 2026

@ShaharNaveh ShaharNaveh left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I approve if it's worth anything

brass75
brass75 previously approved these changes Jul 6, 2026
Comment thread bot/utils/messages.py Outdated
…d include else in the try block for clarity.
@python-discord-policy-bot python-discord-policy-bot Bot dismissed brass75’s stale review July 9, 2026 22:06

Invalidated by push of a94def3

Comment thread bot/utils/messages.py
Comment on lines +289 to +302
async def get_reference_message(ctx: Context) -> Message | None:
"""Return a message reference if the reference exists and it does not refer to the author of the message."""
if ctx.message.reference is None:
return None
try:
referenced_message = ctx.message.reference.resolved
except (discord.Forbidden, discord.NotFound):
return None
else:
if referenced_message is None or isinstance(referenced_message, DeletedReferencedMessage):
return None
if referenced_message.author == ctx.author:
return None
return referenced_message

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry if wasn't very clear,
it should look something like this

Suggested change
async def get_reference_message(ctx: Context) -> Message | None:
"""Return a message reference if the reference exists and it does not refer to the author of the message."""
if ctx.message.reference is None:
return None
try:
referenced_message = ctx.message.reference.resolved
except (discord.Forbidden, discord.NotFound):
return None
else:
if referenced_message is None or isinstance(referenced_message, DeletedReferencedMessage):
return None
if referenced_message.author == ctx.author:
return None
return referenced_message
async def get_reference_message(ctx: Context) -> Message | None:
"""Return a message reference if the reference exists and it does not refer to the author of the message."""
if ctx.message.reference is None:
return None
try:
referenced_message = .resolved or .cached_message or await fetch_message(...)
except (discord.Forbidden, discord.NotFound):
return None
else:
if isinstance(referenced_message, DeletedReferencedMessage):
return None
if referenced_message.author == ctx.author:
return None
return referenced_message

NotFound and Forbidden won't ever be raised unless an API call is made.
Basically the idea is to check the data in resolved, if empty, check cached_message, if also empty finally make an api call.

…t, and now referenced_message can be taken from cache or fetched
Comment thread bot/utils/messages.py
except (discord.Forbidden, discord.NotFound):
return None
else:
if referenced_message is None or isinstance(referenced_message, DeletedReferencedMessage):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this looks gud now, just one minor nit: referenced_message can never be None here as if resolved or cached_message is None, we will fetch using the API, which will raise NotFound etc. if the message can't be fetched.

Comment thread bot/utils/messages.py Outdated
If the callers message does not have a reference, the bot messsage is sent without reply
"""
if message_reference := await get_reference_message(ctx):
return await message_reference.reply(embed=embed)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can fail if the referenced message is deleted between the fetch and send.

This is undesirable behavior since the bot will just fail to respond in that case. The command has been invoked, a reply being deleted shouldn't cause the bot to not reply either.

One of two options:

  • You'll need to construct a new reference with fail_if_not_exists set to False and provide that to .send()
  • try, except a failure, try again without the reference.

@Saber0324 Saber0324 Jul 10, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    reference = None
    if message_reference := await get_reference_message(ctx):
        reference = message_reference
    return await ctx.send(embed=embed, reference=reference)

Would this approach work? I tested the None case by commenting the line that sets the message_reference and it's sent as a normal message. This is the simplest solution I came across.

Actually, looking at how you reconstruct references, the first approach you mentioned is better, mine would fail anyways since an exception is raised.

    if message_reference := await get_reference_message(ctx):
        reference = message_reference.to_reference(fail_if_not_exists=False)
        return await ctx.send(embed=embed, reference=reference)
    return await ctx.send(embed=embed)

I don't like the duplication, but this avoids what you mentioned.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

I confirmed that this version works, is there anything else I could upgrade? ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants