Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions bot/exts/help_channels/_channel.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Contains all logic to handle changes to posts in the help forum."""

import asyncio
import textwrap

import discord
Expand Down Expand Up @@ -52,6 +52,15 @@ async def _close_help_thread(closed_thread: discord.Thread, closed_on: _stats.Cl

poster = closed_thread.owner
cooldown_role = closed_thread.guild.get_role(constants.Roles.help_cooldown)

if poster is None:
# We can't include the owner ID/name here since the thread only contains None
log.info(
f"Failed to remove cooldown role for owner of thread ({closed_thread.id}). "
f"The user is likely no longer on the server."
)
return

await members.handle_role_change(poster, poster.remove_roles, cooldown_role)


Expand Down Expand Up @@ -80,10 +89,14 @@ async def send_opened_post_dm(thread: discord.Thread) -> None:
try:
message = await thread.fetch_message(thread.id)
except discord.HTTPException:
log.warning(f"Could not fetch message for thread {thread.name} ({thread.id})")
log.warning(f"Could not fetch message for thread {thread.id}")
Comment thread
HassanAbouelela marked this conversation as resolved.
return

formatted_message = textwrap.shorten(message.content, width=100, placeholder="...")
formatted_message = textwrap.shorten(message.content, width=100, placeholder="...").strip()
if formatted_message is None:
# This most likely means the initial message is only an image or similar
formatted_message = "No text content."

embed.add_field(name="Your message", value=formatted_message, inline=False)
embed.add_field(
name="Conversation",
Expand All @@ -109,11 +122,19 @@ async def help_thread_opened(opened_thread: discord.Thread, *, reopen: bool = Fa
await _close_help_thread(opened_thread, _stats.ClosingReason.CLEANUP)
return

# Discord sends the open event long before the thread is ready for actions in the API.
# This causes actions such as fetching the message, pinning message, etc to fail.
# We sleep here to try and delay our code enough so the thread is ready in the API.
await asyncio.sleep(2)
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.

Are we sure that 2 seconds will always be enough ? Isn't there another event we can subscribe to, like the starter message for example ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yea, theoretically we could wait_for an on_message event which has the same message_id as the just created thread. Since this would then mean the message was sent to us via the gateway, and is also cached in d.py


await send_opened_post_dm(opened_thread)

if opened_thread.starter_message:
# To cover the case where the user deletes their starter message before code execution reaches this line.
try:
await opened_thread.starter_message.pin()
except discord.HTTPException as e:
# Suppress if the message was not found, most likely deleted
if e.code != 10008:
raise e

await send_opened_post_message(opened_thread)

Expand Down
3 changes: 1 addition & 2 deletions bot/exts/help_channels/_cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,8 @@ async def on_thread_create(self, thread: discord.Thread) -> None:
if thread.parent_id != self.help_forum_channel_id:
return

await _channel.help_thread_opened(thread)

await self.post_with_disallowed_title_check(thread)
await _channel.help_thread_opened(thread)

@commands.Cog.listener()
async def on_thread_update(self, before: discord.Thread, after: discord.Thread) -> None:
Expand Down
100 changes: 43 additions & 57 deletions bot/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,71 +301,57 @@ async def paginate(
if str(reaction.emoji) == DELETE_EMOJI:
log.debug("Got delete reaction")
return await message.delete()

if reaction.emoji == FIRST_EMOJI:
await message.remove_reaction(reaction.emoji, user)
current_page = 0

log.debug(f"Got first page reaction - changing to page 1/{len(paginator.pages)}")
elif reaction.emoji in PAGINATION_EMOJI:
total_pages = len(paginator.pages)
try:
await message.remove_reaction(reaction.emoji, user)
except discord.HTTPException as e:
# Suppress if trying to act on an archived thread.
if e.code != 50083:
raise e

if reaction.emoji == FIRST_EMOJI:
current_page = 0
log.debug(f"Got first page reaction - changing to page 1/{total_pages}")
elif reaction.emoji == LAST_EMOJI:
current_page = len(paginator.pages) - 1
log.debug(f"Got last page reaction - changing to page {current_page + 1}/{total_pages}")
elif reaction.emoji == LEFT_EMOJI:
if current_page <= 0:
log.debug("Got previous page reaction, but we're on the first page - ignoring")
continue

current_page -= 1
log.debug(f"Got previous page reaction - changing to page {current_page + 1}/{total_pages}")
elif reaction.emoji == RIGHT_EMOJI:
if current_page >= len(paginator.pages) - 1:
log.debug("Got next page reaction, but we're on the last page - ignoring")
continue

current_page += 1
log.debug(f"Got next page reaction - changing to page {current_page + 1}/{total_pages}")

embed.description = paginator.pages[current_page]
if footer_text:
embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})")
else:
embed.set_footer(text=f"Page {current_page + 1}/{len(paginator.pages)}")
await message.edit(embed=embed)

if reaction.emoji == LAST_EMOJI:
await message.remove_reaction(reaction.emoji, user)
current_page = len(paginator.pages) - 1

log.debug(f"Got last page reaction - changing to page {current_page + 1}/{len(paginator.pages)}")

embed.description = paginator.pages[current_page]
if footer_text:
embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})")
else:
embed.set_footer(text=f"Page {current_page + 1}/{len(paginator.pages)}")
await message.edit(embed=embed)

if reaction.emoji == LEFT_EMOJI:
await message.remove_reaction(reaction.emoji, user)

if current_page <= 0:
log.debug("Got previous page reaction, but we're on the first page - ignoring")
continue

current_page -= 1
log.debug(f"Got previous page reaction - changing to page {current_page + 1}/{len(paginator.pages)}")

embed.description = paginator.pages[current_page]

if footer_text:
embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})")
else:
embed.set_footer(text=f"Page {current_page + 1}/{len(paginator.pages)}")

await message.edit(embed=embed)

if reaction.emoji == RIGHT_EMOJI:
await message.remove_reaction(reaction.emoji, user)

if current_page >= len(paginator.pages) - 1:
log.debug("Got next page reaction, but we're on the last page - ignoring")
continue

current_page += 1
log.debug(f"Got next page reaction - changing to page {current_page + 1}/{len(paginator.pages)}")

embed.description = paginator.pages[current_page]

if footer_text:
embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})")
else:
embed.set_footer(text=f"Page {current_page + 1}/{len(paginator.pages)}")

await message.edit(embed=embed)
try:
await message.edit(embed=embed)
except discord.HTTPException as e:
if e.code == 50083:
# Trying to act on an archived thread, just ignore and abort
break
else:
raise e
Comment thread
HassanAbouelela marked this conversation as resolved.

log.debug("Ending pagination and clearing reactions.")
with suppress(discord.NotFound):
await message.clear_reactions()
try:
await message.clear_reactions()
except discord.HTTPException as e:
# Suppress if trying to act on an archived thread.
if e.code != 50083:
raise e
6 changes: 6 additions & 0 deletions bot/utils/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,15 @@ async def wait_for_deletion(
await message.clear_reactions()
else:
await message.delete()

except discord.NotFound:
log.trace(f"wait_for_deletion: message {message.id} deleted prematurely.")

except discord.HTTPException:
if not isinstance(message.channel, discord.Thread):
# Threads might not be accessible by the time the timeout expires
raise


async def send_attachments(
message: discord.Message,
Expand Down