Skip to content

feat: GitHub activity feed (#51)#110

Merged
BillChirico merged 6 commits intomainfrom
feat/github-feed
Feb 27, 2026
Merged

feat: GitHub activity feed (#51)#110
BillChirico merged 6 commits intomainfrom
feat/github-feed

Conversation

@BillChirico
Copy link
Collaborator

Summary

Implements the GitHub activity feed requested in #51.

What's included

Migration

  • migrations/005_github-feed.cjs: github_feed_state table to track per-guild, per-repo last seen event ID for dedup

Module — src/modules/githubFeed.js

  • startGithubFeed(client) / stopGithubFeed() — interval-based polling
  • Fetches events via gh api repos/{owner}/{repo}/events (shell exec, 30s timeout)
  • Filters to configured event types: PR, Issue, Release, Push
  • Deduplicates against last_event_id in DB (upsert on each poll)
  • Rich embeds per event type:
    • PR: green (opened), purple (merged), red (closed) — title, author, +/- lines
    • Issue: blue (opened), red (closed) — title, labels, assignee
    • Release: gold — version, notes preview (200 char)
    • Push: gray — branch, commit count, first 3 commit messages
  • Uses safeSend for all channel messages

Command — src/commands/github.js

  • /github feed add owner/repo — admin only
  • /github feed remove owner/repo — admin only
  • /github feed list — everyone
  • /github feed channel #channel — admin only
  • Config gate: github.feed.enabled

Config

Added to config.json (disabled by default):

"github": {
  "feed": {
    "enabled": false,
    "channelId": null,
    "repos": [],
    "events": ["pr", "issue", "release", "push"],
    "pollIntervalMinutes": 5
  }
}

Wired into src/index.js

  • startGithubFeed(client) called on startup (after DB ready)
  • stopGithubFeed() called on graceful shutdown

Tests

47 new tests covering:

  • Embed builders (PR/Issue/Release/Push — colors, fields, edge cases)
  • fetchRepoEvents (gh CLI mock, empty output, errors)
  • buildEmbed dispatch + enabled event type filtering
  • Feed start/stop, dedup, skip-disabled-guild
  • All command subcommands (add/remove/list/channel)
  • Admin gating (non-admins blocked for mutating commands)
  • isValidRepo validation

Closes #51

Copilot AI review requested due to automatic review settings February 27, 2026 15:11
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 27, 2026

Warning

Rate limit exceeded

@BillChirico has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 42 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8a0eec0 and 00fdb72.

📒 Files selected for processing (8)
  • config.json
  • migrations/005_github-feed.cjs
  • src/api/routes/moderation.js
  • src/commands/github.js
  • src/index.js
  • src/modules/githubFeed.js
  • tests/commands/github.test.js
  • tests/modules/githubFeed.test.js
📝 Walkthrough

Walkthrough

Adds GitHub activity feed integration to the Discord bot, including configuration schema, database migration for polling state tracking, command handler for managing tracked repositories, polling engine with embed builders for multiple event types, and comprehensive test coverage for both command and module functionality.

Changes

Cohort / File(s) Summary
Configuration & Database
config.json, migrations/005_github-feed.cjs
Adds github.feed configuration block (enabled, channelId, repos, events, pollIntervalMinutes) and "github" command permission. Creates github_feed_state table to track per-guild-repo polling state with deduplication tracking.
GitHub Feed Implementation
src/modules/githubFeed.js, src/commands/github.js
Introduces GitHub feed module with event fetching via gh CLI, embed builders for PR/Issue/Release/Push events, and polling logic with deduplication. Adds /github command with subcommands to add/remove/list/set-channel for managed repositories.
Bot Integration
src/index.js
Integrates GitHub feed startup/shutdown calls into bot lifecycle (startGithubFeed on startup, stopGithubFeed on graceful shutdown).
Moderation API
src/api/routes/moderation.js
Changes NaN validation in GET /cases/:caseNumber from isNaN() to Number.isNaN().
Test Coverage
tests/commands/github.test.js, tests/modules/githubFeed.test.js
Comprehensive test suites for GitHub command (validation, permission checks, CRUD operations) and GitHub feed module (event fetching, embed building, polling lifecycle, deduplication).

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: GitHub activity feed (#51)' clearly and specifically identifies the main feature being added.
Description check ✅ Passed The description is detailed and comprehensively covers the changes made, relating directly to the pull request changeset.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #51: config section, polling engine, embeds for PR/Issue/Release/Push, deduplication via DB, and admin commands.
Out of Scope Changes check ✅ Passed All changes are scoped to the GitHub feed feature; the only tangential change is a NaN check fix in moderation.js which is unrelated but minimal.
Docstring Coverage ✅ Passed Docstring coverage is 95.24% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/github-feed

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.

@claude
Copy link

claude bot commented Feb 27, 2026

Claude finished @BillChirico's task in 3m 22s —— View job


Review — PR #110: GitHub Activity Feed

  • Read AGENTS.md and project conventions
  • Read all changed files (8 files, 1610 additions)
  • Analyze code for issues
  • Post inline comments for specific issues
  • Post summary and submit review verdict

Result: 2 documentation issues remaining (code is clean)

The code is solid after 5 fix commits addressing all prior critical issues from previous review rounds (BigInt event ID comparison, --paginate removal, setConfigValue usage, _firstPoll separation, isValidRepo regex hardening, "github": "everyone" permission, dead pool null-check, config clone mutation, DB cleanup try/catch, misleading interval comment).

🟡 Warning (2)

# Severity File Issue
1 🟡 config.json:165-172 Missing README.md documentation for github.feed config section and gh CLI prerequisite
2 🟡 src/modules/githubFeed.js, src/commands/github.js, migrations/005_github-feed.cjs Missing AGENTS.md Key Files and Database Tables entries
Prompt to fix all issues
Fix the following documentation issues on branch feat/github-feed:

1. In AGENTS.md, add to the Key Files table (after the existing entries):
   - `src/commands/github.js` | GitHub feed management slash command (`/github feed add/remove/list/channel`)
   - `src/modules/githubFeed.js` | GitHub activity feed polling engine — interval-based, with embed builders for PR/Issue/Release/Push events
   - `migrations/005_github-feed.cjs` | Migration for `github_feed_state` table

2. In AGENTS.md, add to the Database Tables section:
   - `github_feed_state` | Per-guild, per-repo polling state for GitHub event deduplication (guild_id, repo, last_event_id, last_poll_at)

3. In README.md:
   a. Add `gh` CLI to the Prerequisites section: "GitHub CLI (gh) — required for the GitHub activity feed feature. Must be installed and authenticated (gh auth login)."
   b. Add a "GitHub Feed" subsection to the Configuration section documenting the github.feed config block:
      - enabled (boolean, default false) — enable/disable the feed
      - channelId (string|null) — Discord channel ID to post events to
      - repos (string[]) — list of repos to track in owner/repo format
      - events (string[]) — event types to post: pr, issue, release, push

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Review Summary — 9 issues found

🔴 Critical (3)

  1. --paginate fetches all pages unnecessarily (src/modules/githubFeed.js:34): --paginate with -q '.[0:10]' fetches ALL pages (up to 300 events) and applies the jq filter per-page, producing far more than 10 events. Multiple concatenated JSON arrays will also break JSON.parse(). Remove --paginate.
  2. String comparison for event ID dedup is broken (src/modules/githubFeed.js:273): e.id > lastEventId uses string comparison on numeric IDs. '9' > '10' is true in JS. Use Number(e.id) > Number(lastEventId).
  3. Same string comparison bug for newest ID tracking (src/modules/githubFeed.js:304): Same fix needed.

🟡 Warning (5)

  1. Direct DB writes bypass setConfigValue() (src/commands/github.js:150-158): handleAdd writes directly to guild_config table instead of using setConfigValue(), bypassing cache invalidation and change listeners. Also mutates config clone via repos.push().
  2. Same direct DB write in handleRemove (src/commands/github.js:186-193): Same pattern issue.
  3. Same direct DB write in handleChannel (src/commands/github.js:242-249): Also redundantly calls getPool().
  4. Missing github in permissions.allowedCommands (config.json): All other commands have an entry; the new github command does not.
  5. setInterval timing is fixed, not dynamic (src/modules/githubFeed.js:375-390): Despite the comment, setInterval frequency never changes at runtime. Use self-rescheduling setTimeout instead.
  6. Monkey-patching timer ID with _firstPoll (src/modules/githubFeed.js:395): Fragile — use a separate module-level variable.

🔵 Nitpick (1)

  1. isValidRepo doesn't validate characters (src/commands/github.js:65-71): Accepts ../../etc as valid. Add a regex for GitHub-allowed characters.

📝 Documentation

  • AGENTS.md Key Files table should include src/commands/github.js, src/modules/githubFeed.js, and migrations/005_github-feed.cjs.

@greptile-apps
Copy link

greptile-apps bot commented Feb 27, 2026

Greptile Summary

This PR implements a well-architected GitHub activity feed feature that polls repositories via the gh CLI and posts rich embeds to Discord channels.

Key strengths:

  • Previous review feedback properly addressed (uses setConfigValue() for all config writes, removed unused pollIntervalMinutes, clarified interval behavior)
  • Clean architecture: polling module, slash commands, migration, and comprehensive tests (47 new tests)
  • Proper error handling with re-entrancy guard, BigInt event ID comparison for accurate deduplication, and first-run spam prevention (only posts latest event)
  • Follows all coding conventions: Winston logging, node: imports, proper safeSend usage, admin permission gating

Implementation details:

  • 5-minute fixed poll interval with graceful startup/shutdown lifecycle
  • Four event types supported: PR (green/purple/red), Issue (blue/red), Release (gold), Push (gray)
  • Per-guild, per-repo state tracking in github_feed_state table
  • Command structure: /github feed add|remove|list|channel with appropriate permission checks

Dependencies:

  • Requires gh CLI to be installed and authenticated (runtime dependency)

Confidence Score: 5/5

  • Safe to merge - well-tested, follows established patterns, addresses all previous review feedback
  • Code quality is excellent with proper error handling, defensive programming (re-entrancy guard, null checks), correct use of BigInt for numeric comparisons, and comprehensive test coverage. Previous architectural concerns have been resolved (setConfigValue adoption, config cleanup). The feature is disabled by default and requires explicit configuration.
  • No files require special attention

Important Files Changed

Filename Overview
src/modules/githubFeed.js New GitHub feed polling module with event filtering, deduplication, and rich embeds. Well-structured with proper error handling, re-entrancy guard, and BigInt comparison for event IDs.
src/commands/github.js GitHub feed management commands with proper admin gating and config persistence via setConfigValue(). Clean validation and error handling throughout.
migrations/005_github-feed.cjs Simple, correct migration adding github_feed_state table with appropriate indexes and constraints for per-guild, per-repo state tracking.
src/index.js Properly wired GitHub feed into startup (after DB ready) and graceful shutdown sequence. Follows existing patterns for background service lifecycle.

Sequence Diagram

sequenceDiagram
    participant Bot as Discord Bot
    participant Feed as githubFeed Module
    participant GH as GitHub CLI
    participant DB as PostgreSQL
    participant Discord as Discord Channel

    Bot->>Feed: startGithubFeed(client)
    Note over Feed: Set 5-minute interval
    
    loop Every 5 minutes
        Feed->>Feed: pollAllFeeds()
        Note over Feed: Re-entrancy guard active
        
        loop For each guild
            Feed->>DB: SELECT last_event_id
            DB-->>Feed: Previous event ID
            
            loop For each repo
                Feed->>GH: gh api repos/{owner}/{repo}/events
                GH-->>Feed: Recent events (up to 10)
                
                Feed->>Feed: Filter new events (BigInt comparison)
                
                alt New events found
                    loop For each new event
                        Feed->>Feed: buildEmbed(event, enabledTypes)
                        alt Event type enabled
                            Feed->>Discord: safeSend(embed)
                            Discord-->>Feed: Message sent
                        end
                    end
                    Feed->>DB: UPSERT last_event_id, last_poll_at
                else No new events
                    Feed->>DB: UPDATE last_poll_at
                end
            end
        end
    end
    
    Bot->>Feed: stopGithubFeed()
    Note over Feed: Clear interval & timeout
Loading

Last reviewed commit: 00fdb72

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

9 files reviewed, 7 comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a GitHub activity feed feature that polls configured repos and posts Discord embeds for selected GitHub event types, with per-guild configuration and DB-backed dedup state.

Changes:

  • Introduces github_feed_state migration and a new src/modules/githubFeed.js polling + embed builder module.
  • Adds /github feed ... command group for managing tracked repos and target channel.
  • Wires feed start/stop into bot startup and graceful shutdown; adds config defaults and accompanying tests.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/modules/githubFeed.js Implements polling, event fetching via gh, dedup state updates, and embed builders.
src/commands/github.js Adds /github feed subcommands to manage tracked repos/channel.
src/index.js Starts/stops the GitHub feed with bot lifecycle.
migrations/005_github-feed.cjs Adds github_feed_state table for per-guild/per-repo dedup persistence.
config.json Adds github.feed configuration section (disabled by default).
tests/modules/githubFeed.test.js Tests embed builders, fetch logic, feed start/stop, and dedup behavior.
tests/commands/github.test.js Tests /github feed subcommands and admin gating.
pnpm-lock.yaml Locks dependency graph updates (incl. @anthropic-ai/sdk version alignment).
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 27, 2026
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Review Summary — 9 issues found

🔴 Critical (4)

  1. handleAdd writes to non-existent guild_config table (src/commands/github.js:147-161): The config module uses a config table, not guild_config. This INSERT will fail at runtime. Also bypasses setConfigValue() cache/listeners and mutates config clone.
  2. handleRemove same guild_config issue (src/commands/github.js:183-203): Same non-existent table + bypassed config module.
  3. handleChannel same guild_config issue (src/commands/github.js:236-253): Same problem, also redundantly calls getPool().
  4. Test asserts stale CLI arguments (tests/modules/githubFeed.test.js:117-122): Commit 81ece78 changed fetchRepoEvents to use ?per_page=30 and --jq, but the test still asserts --paginate and -q. This test will fail.

🟡 Warning (5)

  1. getPool() throws, never returns null (src/commands/github.js:102-106): Dead code — getPool() throws when pool is null, so the if (!pool) branch is unreachable. Wrap in try/catch.
  2. isValidRepo doesn't validate characters (src/commands/github.js:62-68): Accepts ../../etc as valid. Add regex for GitHub-allowed characters.
  3. setInterval timing is fixed, not dynamic (src/modules/githubFeed.js:374-390): Despite the comment, interval frequency never changes. Use self-rescheduling setTimeout.
  4. Monkey-patching timer with _firstPoll (src/modules/githubFeed.js:395): Fragile — use a separate module-level variable.
  5. Missing github in permissions.allowedCommands (config.json): All other commands have an entry; the new github command does not.

📝 Documentation

  • AGENTS.md Key Files table should include src/commands/github.js, src/modules/githubFeed.js, and migrations/005_github-feed.cjs.
  • README.md should document the github.feed config section and note the gh CLI dependency.

Copilot AI review requested due to automatic review settings February 27, 2026 15:35
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

4 issues found (3 Warning, 2 Nitpick)

🟡 Warning (3)

  1. Dead pollIntervalMinutes config (config.json:170): Never read by startGithubFeed(). Remove it or wire it up.
  2. Unhandled getPool() throw in handleRemove (src/commands/github.js:158): Throws when DB is uninitialized; wrap in try/catch for a user-friendly error.
  3. No error handling on DB cleanup DELETE (src/commands/github.js:173-177): Failed DELETE after successful setConfigValue gives user a generic error. Wrap in try/catch.

🔵 Nitpick (2)
4. Number() for event ID comparison (src/modules/githubFeed.js:276,307): Will lose precision when IDs exceed MAX_SAFE_INTEGER. Use BigInt.
5. Missing numeric dedup edge case test (tests/modules/githubFeed.test.js:525): Add test with last_event_id: '9' and new event '10'.

📝 Documentation (required per AGENTS.md)

  • AGENTS.md Key Files table should include src/commands/github.js, src/modules/githubFeed.js, and migrations/005_github-feed.cjs.
  • README.md should document the github.feed config section and note the gh CLI dependency.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

- migrations/005_github-feed.cjs: github_feed_state table for per-guild/repo dedup
- src/modules/githubFeed.js: polling module with rich embeds (PR/Issue/Release/Push)
- src/commands/github.js: /github feed add/remove/list/channel slash command
- config.json: github.feed defaults (enabled: false)
- src/index.js: wire startGithubFeed / stopGithubFeed on ready/shutdown
- tests: 47 new tests covering embeds, dedup, commands, admin gating

Closes #51
…or config writes, fix _firstPoll monkey-patch
- Spread repos array before push to avoid mutating getConfig() clone in-place
- Fix misleading comment in startGithubFeed that claimed interval was read
  from guild config (it's hardcoded to 5 minutes)
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Review Summary — 5 issues remaining

Previous review rounds fixed most critical issues (numeric event ID comparison, --paginate removal, setConfigValue usage, _firstPoll separation, isValidRepo regex, github: "everyone" permission, dead pool null-check removal, config clone mutation). Good progress across 4 fix commits.

🟡 Warning (4)

  1. handleRemove DB cleanup not error-handled (src/commands/github.js:173-178): If DELETE fails after setConfigValue succeeds, user gets a generic error even though the repo was already removed from config. Wrap in try/catch.
  2. getPool() throw unhandled in handleRemove (src/commands/github.js:158): getPool() throws when DB is uninitialized. Wrap in try/catch for a user-friendly error.
  3. pollIntervalMinutes dead config (config.json:171): Never read by startGithubFeed(). Remove it or wire it up to avoid user confusion.
  4. Missing documentation (required per AGENTS.md): Key Files table, Database Tables section, and README.md config reference should include the new GitHub feed files and github_feed_state table.

🔵 Nitpick (1)
5. Missing numeric dedup edge case test (tests/modules/githubFeed.test.js:525): Add test with last_event_id: '9' and new event '10' to guard the Number() comparison.

🤖 Prompt to fix all issues
Fix the following issues on branch feat/github-feed:

1. In src/commands/github.js:
   a. Line 10: add `warn as logWarn` to the logger import
   b. Line 158: wrap `getPool()` in try/catch — on error, call `safeEditReply(interaction, { content: '❌ Database is not available.' })` and return
   c. Lines 173-178: wrap the DELETE query in a try/catch. On error, call `logWarn('GitHub feed: failed to clean up state row', { guildId: interaction.guildId, repo, error: err.message })` but still proceed to send the success reply

2. In config.json:
   Line 171: remove the `"pollIntervalMinutes": 5` line (and fix trailing comma on previous line)

3. In AGENTS.md:
   a. Add to Key Files table:
      - `src/commands/github.js` | GitHub feed management slash command
      - `src/modules/githubFeed.js` | GitHub activity feed polling engine — interval-based, with embed builders for PR/Issue/Release/Push
      - `migrations/005_github-feed.cjs` | Migration for `github_feed_state` table
   b. Add to Database Tables section:
      - `github_feed_state` | Per-guild, per-repo polling state for GitHub event deduplication

4. In tests/modules/githubFeed.test.js:
   After the dedup test at line 524, add a new test:
   `it('should correctly dedup with numeric ordering (9 < 10)')` that sets
   last_event_id to '9', returns an event with id '10', and asserts safeSend was called

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 27, 2026
Copilot AI review requested due to automatic review settings February 27, 2026 16:36
@BillChirico BillChirico merged commit 768c858 into main Feb 27, 2026
13 of 18 checks passed
@BillChirico BillChirico deleted the feat/github-feed branch February 27, 2026 16:38
"repos": [],
"events": ["pr", "issue", "release", "push"]
}
},
Copy link

Choose a reason for hiding this comment

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

🟡 Warning: Missing README.md documentation for github.feed config section

Per AGENTS.md (line 180): "if you add a new config section or key, document it in README.md's config reference". The README has a ⚙️ Configuration section documenting all other config blocks (ai, triage, welcome, moderation, etc.) but github.feed is missing.

Also: the gh CLI is a runtime prerequisite that should be listed in README's Prerequisites section (currently only lists Node.js 22+, Discord bot token, and PostgreSQL).

@@ -0,0 +1,400 @@
/**
Copy link

Choose a reason for hiding this comment

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

🟡 Warning: Missing AGENTS.md Key Files and Database Tables entries

Per AGENTS.md (lines 183-186):

  • "Added a new command → update Key Files table"
  • "Added a new module → update Key Files table, document config section"

Add to Key Files table:
| src/commands/github.js | GitHub feed management slash command |
| src/modules/githubFeed.js | GitHub activity feed polling engine with embed builders |
| migrations/005_github-feed.cjs | Migration for github_feed_state table |

Add to Database Tables section:
| github_feed_state | Per-guild, per-repo polling state for GitHub event deduplication |

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

2 documentation issues remaining (code is clean). 1) Missing README.md documentation: Per AGENTS.md line 180, the github.feed config section must be documented in README config reference, and gh CLI must be listed in Prerequisites. 2) Missing AGENTS.md updates: Per AGENTS.md lines 183-186, new Key Files entries needed for src/commands/github.js, src/modules/githubFeed.js, migrations/005_github-feed.cjs, and new Database Tables entry for github_feed_state.

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.

GitHub activity feed — auto-post repo updates to a Discord channel

2 participants