Skip to content

Conversation

@kzndotsh
Copy link
Contributor

@kzndotsh kzndotsh commented Jun 21, 2025

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change. If this change fixes any issues please put "Fixes #XX" in the description. Please also ensure to add the appropriate labels to the PR.

Guidelines

  • My code follows the style guidelines of this project (formatted with Ruff)

  • I have performed a self-review of my own code

  • I have commented my code, particularly in hard-to-understand areas

  • I have made corresponding changes to the documentation if needed

  • My changes generate no new warnings

  • I have tested this change

  • Any dependent changes have been merged and published in downstream modules

  • I have added all appropriate labels to this PR

  • [X ] I have followed all of these guidelines.

How Has This Been Tested? (if applicable)

Please describe how you tested your code. e.g describe what commands you ran, what arguments, and any config stuff (if applicable)

Screenshots (if applicable)

Please add screenshots to help explain your changes.

Additional Information

Please add any other information that is important to this PR.

Summary by Sourcery

Refactor the bookmark cog to support configurable add/remove reactions, streamline file attachments and embed composition, manage HTTP sessions, and improve error handling; update poll cog to handle channel fetch failures gracefully; and add bookmark emoji constants.

New Features:

  • Allow users to remove bookmarks by reacting with the remove emoji on the DM message.

Bug Fixes:

  • Improve poll cog to fetch missing channels and handle NotFound, Forbidden, and HTTP errors when accessing channels.

Enhancements:

  • Initialize and close an aiohttp session in the Bookmarks cog.
  • Validate and separate add/remove bookmark emojis before processing reactions.
  • Extract attachments, stickers, and embed images into DMs via a unified file-gathering method.
  • Enhance bookmark embeds with author info, reply references, attachments, stickers, footer, and timestamp.
  • Implement fallback notifications in channels when DMs fail due to privacy settings.
  • Add ADD_BOOKMARK and REMOVE_BOOKMARK emojis to project constants.

meatsnails and others added 23 commits June 9, 2025 18:48
…andling and support for attachments

Refactor the bookmarks cog to improve the handling of bookmark reactions.
Introduce aiohttp for asynchronous HTTP requests and manage session lifecycle.
Add support for handling attachments, stickers, and embeds in bookmarked messages,
ensuring that up to 10 files are attached to the bookmark message.
Improve error handling and logging for better traceability of issues.
These changes enhance the user experience by providing more comprehensive
bookmarking capabilities and ensuring robustness in various scenarios.
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jun 21, 2025

Reviewer's Guide

This refactor restructures the bookmark service by centralizing reaction handling, extracting bookmark operations into dedicated methods, introducing session management and robust error handling, and enhancing embed creation and file attachment logic.

Class diagram for refactored Bookmarks service

classDiagram
    class Bookmarks {
        - bot: Tux
        - add_bookmark_emojis
        - remove_bookmark_emojis
        - valid_emojis
        - session: aiohttp.ClientSession
        + __init__(bot: Tux)
        + cog_unload()
        + on_raw_reaction_add(payload: discord.RawReactionActionEvent)
        + add_bookmark(user: discord.User, message: discord.Message)
        + remove_bookmark(message: discord.Message)
        + _get_files_from_attachments(message: discord.Message, files: list[discord.File])
        + _get_files_from_stickers(message: discord.Message, files: list[discord.File])
        + _get_files_from_embeds(message: discord.Message, files: list[discord.File])
        + _get_files_from_message(message: discord.Message) -> list[discord.File]
        + _create_bookmark_embed(message: discord.Message) -> discord.Embed
    }
    Bookmarks --> "1" Tux
    Bookmarks --> "1" aiohttp.ClientSession
    Bookmarks --> "1" discord.RawReactionActionEvent
    Bookmarks --> "1" discord.User
    Bookmarks --> "1" discord.Message
    Bookmarks --> "*" discord.File
    Bookmarks --> "1" discord.Embed
Loading

File-Level Changes

Change Details Files
Initialize and clean up bookmark Cog resources
  • Store add/remove emoji lists and valid emojis in init
  • Create aiohttp session in init
  • Implement cog_unload to close the session
tux/cogs/services/bookmarks.py
Refactor on_raw_reaction_add listener for bookmarking
  • Filter out bot reactions and invalid emojis
  • Fetch user and channel with get/fallback to API
  • Unified error handling for fetch failures
  • Dispatch to add_bookmark or remove_bookmark based on emoji
tux/cogs/services/bookmarks.py
Extract add_bookmark and remove_bookmark operations
  • add_bookmark: build embed, gather files, send DM, add remove reaction, handle DM failures
  • remove_bookmark: delete bookmark message with error logging
tux/cogs/services/bookmarks.py
Consolidate file gathering into helper methods
  • Implement _get_files_from_attachments, _get_files_from_stickers, _get_files_from_embeds
  • Enforce a 10-file limit and log HTTP errors
  • Create unified _get_files_from_message to orchestrate these helpers
tux/cogs/services/bookmarks.py
Enhance bookmark embed construction
  • Truncate long content and set author/avatar
  • Include reply references, jump links, attachments, stickers
  • Add footer with channel/guild info and timestamp
tux/cogs/services/bookmarks.py
Improve poll reaction handler channel fetching
  • Fallback to fetch_channel when get_channel returns None
  • Handle NotFound, Forbidden, and HTTP exceptions
tux/cogs/utility/poll.py
Define bookmark emoji constants
  • Add ADD_BOOKMARK and REMOVE_BOOKMARK emojis in Constants
tux/utils/constants.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions
Copy link
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @kzndotsh - I've reviewed your changes - here's some feedback:

  • There’s duplicated channel/user fetching and validation logic in both the bookmark and poll listeners—consider extracting that into a shared helper to reduce repetition.
  • In add_bookmark, you call dm_message.add_reaction(self.remove_bookmark_emojis) but remove_bookmark_emojis is a string of emojis—if you only want to add one emoji this is fine, otherwise iterate over the collection or clarify your intent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- There’s duplicated channel/user fetching and validation logic in both the bookmark and poll listeners—consider extracting that into a shared helper to reduce repetition.
- In `add_bookmark`, you call `dm_message.add_reaction(self.remove_bookmark_emojis)` but `remove_bookmark_emojis` is a string of emojis—if you only want to add one emoji this is fine, otherwise iterate over the collection or clarify your intent.

## Individual Comments

### Comment 1
<location> `tux/cogs/services/bookmarks.py:22` </location>
<code_context>
+        self.add_bookmark_emojis = CONST.ADD_BOOKMARK
+        self.remove_bookmark_emojis = CONST.REMOVE_BOOKMARK
+        self.valid_emojis = self.add_bookmark_emojis + self.remove_bookmark_emojis
+        self.session = aiohttp.ClientSession()
+
+    async def cog_unload(self) -> None:
</code_context>

<issue_to_address>
Instantiating aiohttp.ClientSession in __init__ may cause warnings.

Move ClientSession creation to an async initialization method to prevent deprecation warnings and ensure compatibility with future aiohttp versions.

Suggested implementation:

```python
        self.session = None

```

```python
    async def cog_load(self) -> None:
        """Initializes resources for the cog, including the aiohttp session."""
        self.session = aiohttp.ClientSession()

    async def cog_unload(self) -> None:
        """Cleans up the cog, closing the aiohttp session."""
        if self.session:
            await self.session.close()

```
</issue_to_address>

### Comment 2
<location> `tux/cogs/utility/poll.py:79` </location>
<code_context>
         if channel is None:
-            logger.error(f"Channel with ID {payload.channel_id} not found.")
-            return
-        if isinstance(channel, discord.ForumChannel | discord.CategoryChannel | discord.abc.PrivateChannel):
-            logger.error(
-                f"Channel with ID {payload.channel_id} is not a compatible channel type. How the fuck did you get here?",
-            )
-            return
+            try:
+                channel = await self.bot.fetch_channel(payload.channel_id)
</code_context>

<issue_to_address>
Type check for channel is removed, which may allow unsupported channel types.

Without the type check, casting to TextChannel or Thread may be unsafe and could result in runtime errors if the channel is not of the expected type.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

self.add_bookmark_emojis = CONST.ADD_BOOKMARK
self.remove_bookmark_emojis = CONST.REMOVE_BOOKMARK
self.valid_emojis = self.add_bookmark_emojis + self.remove_bookmark_emojis
self.session = aiohttp.ClientSession()
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Instantiating aiohttp.ClientSession in init may cause warnings.

Move ClientSession creation to an async initialization method to prevent deprecation warnings and ensure compatibility with future aiohttp versions.

Suggested implementation:

        self.session = None
    async def cog_load(self) -> None:
        """Initializes resources for the cog, including the aiohttp session."""
        self.session = aiohttp.ClientSession()

    async def cog_unload(self) -> None:
        """Cleans up the cog, closing the aiohttp session."""
        if self.session:
            await self.session.close()

channel = self.bot.get_channel(payload.channel_id)
if channel is None:
logger.error(f"Channel with ID {payload.channel_id} not found.")
return
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Type check for channel is removed, which may allow unsupported channel types.

Without the type check, casting to TextChannel or Thread may be unsafe and could result in runtime errors if the channel is not of the expected type.

@codecov
Copy link

codecov bot commented Jun 21, 2025

Codecov Report

Attention: Patch coverage is 1.69492% with 116 lines in your changes missing coverage. Please review.

Project coverage is 8.92%. Comparing base (72845c0) to head (e1d0024).
Report is 1 commits behind head on main.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
tux/cogs/services/bookmarks.py 0.00% 106 Missing ⚠️
tux/cogs/utility/poll.py 0.00% 10 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main    #918      +/-   ##
========================================
+ Coverage   8.91%   8.92%   +0.01%     
========================================
  Files        121     121              
  Lines      10189   10269      +80     
  Branches    1236    1255      +19     
========================================
+ Hits         908     917       +9     
- Misses      9213    9278      +65     
- Partials      68      74       +6     
Flag Coverage Δ *Carryforward flag
database 0.30% <0.00%> (+<0.01%) ⬆️ Carriedforward from 67b53e2
integration 5.93% <1.69%> (-0.03%) ⬇️
unit 6.38% <1.69%> (-0.04%) ⬇️

*This pull request uses carry forward flags. Click here to find out more.

Components Coverage Δ
Core Bot Infrastructure 16.45% <ø> (ø)
Database Layer 0.00% <ø> (ø)
Bot Commands & Features 0.00% <0.00%> (ø)
Event & Error Handling ∅ <ø> (∅)
Utilities & Helpers ∅ <ø> (∅)
User Interface Components 0.00% <ø> (ø)
CLI Interface ∅ <ø> (∅)
External Service Wrappers ∅ <ø> (∅)

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@kzndotsh kzndotsh merged commit 1afca03 into main Jun 21, 2025
36 checks passed
@kzndotsh kzndotsh deleted the cherryl1k/main branch June 21, 2025 20:26
@sentry
Copy link

sentry bot commented Jun 21, 2025

Suspect Issues

This pull request was deployed and Sentry observed the following issues:

Did you find this useful? React with a 👍 or 👎

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.

4 participants