Skip to content

Add upvote functionality for posts and comments, and update README#12

Open
hemati wants to merge 3 commits into
Arindam200:mainfrom
hemati:upvote
Open

Add upvote functionality for posts and comments, and update README#12
hemati wants to merge 3 commits into
Arindam200:mainfrom
hemati:upvote

Conversation

@hemati

@hemati hemati commented Dec 24, 2025

Copy link
Copy Markdown
Contributor

This pull request adds new functionality for upvoting Reddit posts and comments, making it possible for users to programmatically upvote content through the server. The changes include the implementation of two new tools in the backend, updates to the documentation to reflect these capabilities, and examples demonstrating their usage.

New upvote functionality:

  • Added the upvote_post(post_id) and upvote_comment(comment_id) tool functions to server.py, allowing users to upvote Reddit posts and comments via the API. These functions handle input validation, error handling (including rate limits and archived content), and return detailed information about the upvoted item.

Documentation updates:

  • Updated the README.md to document the new upvote_post and upvote_comment tools in the list of available server tools.
  • Added example queries to the README.md showing how to use the new upvote features in natural language.

Summary by CodeRabbit

  • New Features

    • Added authenticated tools to upvote posts and comments, with input validation, archival/lock checks, rate‑limit handling, and detailed response metadata.
  • Documentation

    • Added examples showing how to use the new upvote tools.
  • Chores

    • Version control ignores updated to include IDE and VSCode editor folders.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 24, 2025

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds two authenticated upvote endpoints (upvote_post, upvote_comment) with validation, Reddit ID normalization, archival/lock checks, upvote execution, structured responses, README examples; updates .gitignore to re-add .idea/ and add .vscode/. Duplicate function blocks are present in server.py.

Changes

Cohort / File(s) Summary
Configuration
\.gitignore
Re-added \.idea/ and added \.vscode/ (VSCode generated files)
Documentation
README.md
Added public tool entries upvote_post(post_id) and upvote_comment(comment_id) plus example queries
API implementation
server.py
Added upvote_post(post_id: str) and upvote_comment(comment_id: str) endpoints (decorated with auth/write checks): validate input, normalize Reddit ID, fetch submission/comment, check archived/locked state, perform upvote, refresh object, return structured payload; enhanced error handling. Note: duplicated definitions of these functions appear in the file — reviewers should confirm and remove duplicates.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant Server as MCP Server
    participant Manager as MCP Manager
    participant Reddit as Reddit API

    Client->>Server: Request upvote_post(post_id) or upvote_comment(comment_id)
    Server->>Manager: init() / require write access
    Server->>Server: validate input, normalize/extract Reddit ID
    Server->>Reddit: fetch submission/comment by ID
    alt target not found
        Server-->>Client: 400 / ValueError (target not found)
    else target found
        Server->>Server: check archived/locked status
        alt not allowed (archived/locked)
            Server-->>Client: 400 / RuntimeError (cannot upvote)
        else allowed
            Server->>Reddit: perform upvote (vote)
            alt success
                Server->>Reddit: refresh object state
                Server-->>Client: 200 {id, author, subreddit, score, permalink, ...}
            else rate-limited / error
                Server-->>Client: 429 / RuntimeError (rate-limited or operational failure)
            end
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • Arindam200

Poem

🐰 I hopped in code with a twitch of my nose,
I nudged a post and made its blossom grow,
I tapped a comment, sent it a cheer,
Small hops, big smiles — upvotes appear! 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding upvote functionality for posts and comments, and updating the README with new tool documentation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
server.py (3)

2326-2414: Significant code duplication between upvote_post and upvote_comment.

Both functions share nearly identical structure:

  • Input validation pattern
  • ID extraction using _extract_reddit_id
  • Object existence verification
  • Archived status checking
  • Error handling (rate limits, generic errors)
  • Response structure

Consider refactoring to a shared helper function that accepts the object type and operation, reducing maintenance burden and improving consistency.

Example refactor approach

You could create a generic vote helper:

def _perform_vote(object_type: str, object_id: str, vote_direction: int) -> Dict[str, Any]:
    """
    Helper to perform vote operations.
    
    Args:
        object_type: 'submission' or 'comment'
        object_id: The ID to vote on
        vote_direction: 1 for upvote, -1 for downvote, 0 for clear
    """
    # Shared logic here
    pass

Then upvote_post and upvote_comment become thin wrappers that call this helper.

Also applies to: 2416-2508


2371-2371: Address static analysis warnings for logging consistency.

Ruff flags redundant exception objects in logging.exception calls. The exception is automatically included by logging.exception(), so explicitly passing it is unnecessary.

Recommended fix
-            logger.exception(f"Failed to access post {clean_post_id}: {e}")
+            logger.exception(f"Failed to access post {clean_post_id}")

Apply similar changes to all logger.exception() calls on Lines 2371, 2402, 2410, 2460, 2496, and 2504.

Also applies to: 2402-2402, 2410-2410, 2460-2460, 2496-2496, 2504-2504


2470-2471: Catching blind Exception could mask specific errors.

Line 2470 catches a broad Exception when checking submission status. Consider catching specific exceptions or at least logging the exception type for debugging.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c56363 and e968b44.

📒 Files selected for processing (3)
  • .gitignore
  • README.md
  • server.py
🧰 Additional context used
🪛 Ruff (0.14.10)
server.py

2343-2343: Avoid specifying long messages outside the exception class

(TRY003)


2347-2347: Avoid specifying long messages outside the exception class

(TRY003)


2371-2371: Redundant exception object included in logging.exception call

(TRY401)


2372-2372: Avoid specifying long messages outside the exception class

(TRY003)


2376-2376: Abstract raise to an inner function

(TRY301)


2376-2376: Avoid specifying long messages outside the exception class

(TRY003)


2402-2402: Redundant exception object included in logging.exception call

(TRY401)


2404-2406: Avoid specifying long messages outside the exception class

(TRY003)


2407-2407: Avoid specifying long messages outside the exception class

(TRY003)


2410-2410: Redundant exception object included in logging.exception call

(TRY401)


2413-2413: Avoid specifying long messages outside the exception class

(TRY003)


2433-2433: Avoid specifying long messages outside the exception class

(TRY003)


2437-2437: Avoid specifying long messages outside the exception class

(TRY003)


2460-2460: Redundant exception object included in logging.exception call

(TRY401)


2461-2461: Avoid specifying long messages outside the exception class

(TRY003)


2467-2467: Abstract raise to an inner function

(TRY301)


2467-2467: Avoid specifying long messages outside the exception class

(TRY003)


2470-2470: Do not catch blind exception: Exception

(BLE001)


2496-2496: Redundant exception object included in logging.exception call

(TRY401)


2498-2500: Avoid specifying long messages outside the exception class

(TRY003)


2501-2501: Avoid specifying long messages outside the exception class

(TRY003)


2504-2504: Redundant exception object included in logging.exception call

(TRY401)


2507-2507: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (5)
README.md (1)

107-108: LGTM! Documentation accurately reflects the new upvote functionality.

The new tools are correctly categorized under Authenticated Tools, and the example queries provide clear guidance for users.

Also applies to: 127-128

.gitignore (1)

13-15: LGTM! Appropriate IDE file exclusions.

Standard housekeeping to ignore IDE-specific configuration directories.

server.py (3)

2345-2347: Input validation looks correct.

The function properly validates that post_id is a non-empty string before proceeding.


2374-2376: Good safeguard against archived posts.

Correctly prevents upvoting archived posts, which would fail on Reddit's side.


2483-2483: Good practice truncating the comment body.

Truncating to 200 characters prevents excessively large response payloads.

Comment thread server.py
@hemati

hemati commented Dec 24, 2025

Copy link
Copy Markdown
Contributor Author

@Arindam200 Please check this PR

Removed unnecessary refresh calls after upvoting posts and comments.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 0

♻️ Duplicate comments (2)
server.py (2)

2475-2487: Returned score doesn't reflect the upvote.

After calling comment.upvote() on line 2475, the comment.score attribute (line 2486) remains cached and doesn't reflect the upvote that was just performed. PRAW doesn't automatically refresh object attributes after write operations.

Apply the same solution as suggested for upvote_post: either call comment.refresh() after upvoting, omit the score field, or document that the returned score is the pre-upvote value.

🔎 Proposed fix to refresh and return accurate data
         # Upvote the comment
         try:
             comment.upvote()
+            comment.refresh()
             logger.info(f"Comment upvoted successfully: {clean_comment_id}")

2380-2393: Returned score and upvote_ratio don't reflect the upvote.

After calling submission.upvote() on line 2380, the submission.score and submission.upvote_ratio attributes (lines 2391-2392) remain cached and don't reflect the upvote that was just performed. PRAW doesn't automatically refresh object attributes after write operations.

While the commit message indicates refresh calls were intentionally removed, returning stale values can mislead API consumers who expect to see the updated score.

Consider one of these approaches:

  1. Call submission.refresh() after upvoting to get current values
  2. Omit score and upvote_ratio from the response if they won't be accurate
  3. Document in the return value description that score/ratio reflect pre-upvote state
🔎 Proposed fix to refresh and return accurate data
         # Upvote the post
         try:
             submission.upvote()
+            submission.refresh()
             logger.info(f"Post upvoted successfully: {clean_post_id}")
🧹 Nitpick comments (1)
server.py (1)

2326-2507: Consider extracting common upvote logic.

The upvote_post and upvote_comment functions follow nearly identical patterns (validate input → extract ID → fetch object → verify → check archived → upvote → return result). Extracting shared logic into a helper function would reduce duplication and simplify maintenance.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3807656 and fe42fb6.

📒 Files selected for processing (1)
  • server.py
🧰 Additional context used
🪛 Ruff (0.14.10)
server.py

2343-2343: Avoid specifying long messages outside the exception class

(TRY003)


2347-2347: Avoid specifying long messages outside the exception class

(TRY003)


2371-2371: Redundant exception object included in logging.exception call

(TRY401)


2372-2372: Avoid specifying long messages outside the exception class

(TRY003)


2376-2376: Abstract raise to an inner function

(TRY301)


2376-2376: Avoid specifying long messages outside the exception class

(TRY003)


2402-2402: Redundant exception object included in logging.exception call

(TRY401)


2404-2406: Avoid specifying long messages outside the exception class

(TRY003)


2407-2407: Avoid specifying long messages outside the exception class

(TRY003)


2410-2410: Redundant exception object included in logging.exception call

(TRY401)


2413-2413: Avoid specifying long messages outside the exception class

(TRY003)


2433-2433: Avoid specifying long messages outside the exception class

(TRY003)


2437-2437: Avoid specifying long messages outside the exception class

(TRY003)


2460-2460: Redundant exception object included in logging.exception call

(TRY401)


2461-2461: Avoid specifying long messages outside the exception class

(TRY003)


2467-2467: Abstract raise to an inner function

(TRY301)


2467-2467: Avoid specifying long messages outside the exception class

(TRY003)


2470-2470: Do not catch blind exception: Exception

(BLE001)


2496-2496: Redundant exception object included in logging.exception call

(TRY401)


2498-2500: Avoid specifying long messages outside the exception class

(TRY003)


2501-2501: Avoid specifying long messages outside the exception class

(TRY003)


2504-2504: Redundant exception object included in logging.exception call

(TRY401)


2507-2507: Avoid specifying long messages outside the exception class

(TRY003)

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.

1 participant