fix: bounty tracker change

This commit is contained in:
Timothy
2026-03-18 14:49:21 -07:00
parent c7fdc92594
commit 651d6850a1
8 changed files with 57 additions and 217 deletions
+15 -3
View File
@@ -5,11 +5,19 @@ on:
pull_request_target:
types: [closed]
workflow_dispatch:
inputs:
pr_number:
description: "PR number to process (for missed bounties)"
required: true
type: number
jobs:
bounty-notify:
if: >
github.event.pull_request.merged == true &&
contains(join(github.event.pull_request.labels.*.name, ','), 'bounty:')
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.merged == true &&
contains(join(github.event.pull_request.labels.*.name, ','), 'bounty:'))
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
@@ -25,6 +33,9 @@ jobs:
with:
bun-version: latest
- name: Install mongodb dependency
run: bun add mongodb
- name: Award XP and notify Discord
run: bun run scripts/bounty-tracker.ts notify
env:
@@ -32,6 +43,7 @@ jobs:
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_BOUNTY_WEBHOOK_URL }}
MONGODB_URI: ${{ secrets.MONGODB_URI }}
LURKR_API_KEY: ${{ secrets.LURKR_API_KEY }}
LURKR_GUILD_ID: ${{ secrets.LURKR_GUILD_ID }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_NUMBER: ${{ inputs.pr_number || github.event.pull_request.number }}
-126
View File
@@ -1,126 +0,0 @@
name: Link Discord account
description: Auto-creates a PR to add contributor to contributors.yml when a link-discord issue is opened
on:
issues:
types: [opened]
jobs:
link-discord:
if: contains(github.event.issue.labels.*.name, 'link-discord')
runs-on: ubuntu-latest
timeout-minutes: 2
permissions:
contents: write
issues: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Parse issue and update contributors.yml
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const issue = context.payload.issue;
const githubUsername = issue.user.login;
// Parse the issue body for form fields
const body = issue.body || '';
// Extract Discord ID — look for the numeric value after the "Discord User ID" heading
const discordMatch = body.match(/### Discord User ID\s*\n\s*(\d{17,20})/);
if (!discordMatch) {
await github.rest.issues.createComment({
...context.repo,
issue_number: issue.number,
body: `Could not find a valid Discord ID in the issue body. Please make sure you entered a numeric ID (17-20 digits), not a username.\n\nExample: \`123456789012345678\``
});
await github.rest.issues.update({
...context.repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'not_planned'
});
return;
}
const discordId = discordMatch[1];
// Extract display name (optional)
const nameMatch = body.match(/### Display Name \(optional\)\s*\n\s*(.+)/);
const displayName = nameMatch ? nameMatch[1].trim() : '';
// Check if user already exists
const yml = fs.readFileSync('contributors.yml', 'utf-8');
if (yml.includes(`github: ${githubUsername}`)) {
await github.rest.issues.createComment({
...context.repo,
issue_number: issue.number,
body: `@${githubUsername} is already in \`contributors.yml\`. If you need to update your Discord ID, please edit the file directly via PR.`
});
await github.rest.issues.update({
...context.repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'completed'
});
return;
}
// Append entry to contributors.yml
let entry = ` - github: ${githubUsername}\n discord: "${discordId}"`;
if (displayName && displayName !== '_No response_') {
entry += `\n name: ${displayName}`;
}
entry += '\n';
const updated = yml.trimEnd() + '\n' + entry;
fs.writeFileSync('contributors.yml', updated);
// Set outputs for commit step
core.exportVariable('GITHUB_USERNAME', githubUsername);
core.exportVariable('DISCORD_ID', discordId);
core.exportVariable('ISSUE_NUMBER', issue.number.toString());
- name: Create PR
run: |
# Check if there are changes
if git diff --quiet contributors.yml; then
echo "No changes to contributors.yml"
exit 0
fi
BRANCH="docs/link-discord-${GITHUB_USERNAME}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
git add contributors.yml
git commit -m "docs: link @${GITHUB_USERNAME} to Discord"
git push origin "$BRANCH"
gh pr create \
--title "docs: link @${GITHUB_USERNAME} to Discord" \
--body "Adds @${GITHUB_USERNAME} (Discord \`${DISCORD_ID}\`) to \`contributors.yml\` for bounty XP tracking.
Closes #${ISSUE_NUMBER}" \
--base main \
--head "$BRANCH" \
--label "link-discord"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify on issue
uses: actions/github-script@v7
with:
script: |
const username = process.env.GITHUB_USERNAME;
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
await github.rest.issues.createComment({
...context.repo,
issue_number: issueNumber,
body: `A PR has been created to link your account. A maintainer will merge it shortly — once merged, you'll receive XP and Discord pings when your bounty PRs are merged.`
});
+4
View File
@@ -28,6 +28,9 @@ jobs:
with:
bun-version: latest
- name: Install mongodb dependency
run: bun add mongodb
- name: Post leaderboard to Discord
run: bun run scripts/bounty-tracker.ts leaderboard
env:
@@ -35,6 +38,7 @@ jobs:
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_BOUNTY_WEBHOOK_URL }}
MONGODB_URI: ${{ secrets.MONGODB_URI }}
LURKR_API_KEY: ${{ secrets.LURKR_API_KEY }}
LURKR_GUILD_ID: ${{ secrets.LURKR_GUILD_ID }}
SINCE_DATE: ${{ github.event.inputs.since_date || '' }}
-27
View File
@@ -1,27 +0,0 @@
# Identity mapping: GitHub username -> Discord ID
#
# This file links GitHub accounts to Discord accounts for the
# Integration Bounty Program. When a bounty PR is merged, the
# GitHub Action uses this file to ping the contributor on Discord.
#
# HOW TO ADD YOURSELF:
# Open a "Link Discord Account" issue:
# https://github.com/aden-hive/hive/issues/new?template=link-discord.yml
# A GitHub Action will automatically add your entry here.
#
# To find your Discord ID:
# 1. Open Discord Settings > Advanced > Enable Developer Mode
# 2. Right-click your name > Copy User ID
#
# Format:
# - github: your-github-username
# discord: "your-discord-id" # quotes required (it's a number)
# name: Your Display Name # optional
contributors:
# - github: example-user
# discord: "123456789012345678"
# name: Example User
- github: TimothyZhang7
discord: "408460790061072384"
name: Timothy@Aden
+4 -4
View File
@@ -157,7 +157,7 @@ All bounty types open in parallel. Contributors self-select. Daily progress upda
PR merged with bounty:* label
→ GitHub Action runs bounty-tracker.ts
→ Calculates points from label
→ Resolves GitHub → Discord ID via contributors.yml
→ Resolves GitHub → Discord ID via MongoDB (hive.contributors)
→ Pushes XP to Lurkr API
→ Posts notification to #integrations-announcements
```
@@ -166,7 +166,7 @@ See the [Setup Guide](setup-guide.md) for full configuration (Lurkr, webhooks, s
### Identity Linking
Contributors link GitHub ↔ Discord by opening a [Link Discord Account](https://github.com/aden-hive/hive/issues/new?template=link-discord.yml) issue. A GitHub Action auto-adds them to `contributors.yml` and closes the issue.
Contributors link GitHub ↔ Discord by running `/link-github` in Discord. The bot verifies ownership via a public gist, then stores the mapping in MongoDB.
Without this link, bounties are still tracked but Lurkr can't push XP to your Discord account.
@@ -181,7 +181,7 @@ Without this link, bounties are still tracked but Lurkr can't push XP to your Di
| Agent Builder role | Lurkr bot | Auto-assigned at level 5 |
| OSS Contributor role | Lurkr bot | Auto-assigned at level 15 |
| Core Contributor role | Maintainer | Manual (involves money) |
| Identity linking | contributors.yml | PR-based, reviewed by maintainers |
| Identity linking | Discord bot → MongoDB | `/link-github` command with gist verification |
## Guides
@@ -203,4 +203,4 @@ Without this link, bounties are still tracked but Lurkr can't push XP to your Di
- `.github/workflows/weekly-leaderboard.yml` — Monday leaderboard post
- `scripts/bounty-tracker.ts` — Point calculation, Lurkr API, Discord formatting
- `scripts/setup-bounty-labels.sh` — One-time label setup
- `contributors.yml` — GitHub ↔ Discord identity mapping
- MongoDB `hive.contributors` — GitHub ↔ Discord identity mapping (managed by Discord bot)
+2 -4
View File
@@ -6,9 +6,7 @@ Earn XP, Discord roles, and eventually real money by contributing to the Aden ag
### 1. Link your GitHub and Discord
Open a [Link Discord Account](https://github.com/aden-hive/hive/issues/new?template=link-discord.yml) issue — just paste your Discord ID and submit. A GitHub Action will automatically add you to `contributors.yml` and close the issue.
To find your Discord ID: Discord Settings > Advanced > Enable **Developer Mode**, then right-click your name > **Copy User ID**.
Run `/link-github your-github-username` in Discord. The bot will give you a verification code — create a public gist with that code, then run `/verify`. Done.
Without this link, Lurkr can't push XP to your Discord account.
@@ -154,7 +152,7 @@ A: Yes. Most services have free tiers. The bounty issue links to where you get t
A: Contribute consistently across different bounty types for 4+ weeks. Maintainers will nominate you.
**Q: What if I haven't linked my Discord yet?**
A: You'll still get credit in GitHub, but no Lurkr XP or Discord roles. Add yourself to `contributors.yml`.
A: You'll still get credit in GitHub, but no Lurkr XP or Discord roles. Run `/link-github` in Discord.
## Quick Reference
+3 -2
View File
@@ -104,6 +104,7 @@ Repo Settings > Secrets and variables > Actions:
| `DISCORD_BOUNTY_WEBHOOK_URL` | Webhook URL from Step 5 |
| `LURKR_API_KEY` | Lurkr API key from Step 4f |
| `LURKR_GUILD_ID` | Your Discord server ID\* |
| `MONGODB_URI` | MongoDB connection string |
\*Enable Developer Mode in Discord, right-click server name > Copy Server ID.
@@ -146,12 +147,12 @@ powerbi, redis
- [ ] All 3 GitHub secrets added
- [ ] Both workflows enabled (`bounty-completed.yml`, `weekly-leaderboard.yml`)
- [ ] Test PR + merge triggers Discord notification
- [ ] `contributors.yml` exists at repo root
- [ ] MongoDB `hive.contributors` collection accessible
## Troubleshooting
**No Discord message:** Check `DISCORD_BOUNTY_WEBHOOK_URL` secret and Action logs.
**Lurkr XP not awarded:** Confirm API key is Read/Write, contributor is in `contributors.yml`, check Action logs for `Lurkr XP push failed`.
**Lurkr XP not awarded:** Confirm API key is Read/Write, contributor has run `/link-github` in Discord, check Action logs for `Lurkr XP push failed`.
**Role not assigned:** Verify role rewards in the Lurkr dashboard or via `/config set`. Lurkr's role must be above the roles it assigns in server hierarchy.
+29 -51
View File
@@ -12,13 +12,13 @@
* GITHUB_REPOSITORY_OWNER — e.g. "adenhq"
* GITHUB_REPOSITORY_NAME — e.g. "hive"
* DISCORD_WEBHOOK_URL — Discord webhook for #integrations-announcements
* MONGODB_URI — MongoDB connection string (contributors collection)
* LURKR_API_KEY — Lurkr Read/Write API key (for XP push)
* LURKR_GUILD_ID — Discord server ID where Lurkr is installed
* PR_NUMBER — (notify mode) The merged PR number
*/
import { readFileSync } from "fs";
import { join } from "path";
import { MongoClient } from "mongodb";
// ---------------------------------------------------------------------------
// Types
@@ -151,59 +151,37 @@ async function getMergedBountyPRs(
}
// ---------------------------------------------------------------------------
// Identity resolution
// Identity resolution (MongoDB-backed)
// ---------------------------------------------------------------------------
// Parse contributors.yml without a YAML dependency.
// The format is simple enough to parse with regex:
// contributors:
// - github: jane-doe
// discord: "123456789012345678"
// name: Jane Doe
function parseContributorsYaml(raw: string): Contributor[] {
const contributors: Contributor[] = [];
let current: Partial<Contributor> | null = null;
for (const line of raw.split("\n")) {
const trimmed = line.trim();
if (trimmed.startsWith("- github:")) {
if (current?.github && current?.discord) {
contributors.push(current as Contributor);
}
current = { github: trimmed.replace("- github:", "").trim() };
} else if (trimmed.startsWith("discord:") && current) {
current.discord = trimmed.replace("discord:", "").trim().replace(/^["']|["']$/g, "");
} else if (trimmed.startsWith("name:") && current) {
current.name = trimmed.replace("name:", "").trim();
}
}
// Don't forget the last entry
if (current?.github && current?.discord) {
contributors.push(current as Contributor);
}
return contributors;
}
function loadContributors(): Map<string, Contributor> {
async function loadContributors(): Promise<Map<string, Contributor>> {
const map = new Map<string, Contributor>();
try {
// Resolve path relative to the script location (scripts/ dir → repo root)
const scriptDir = new URL(".", import.meta.url).pathname;
const raw = readFileSync(
join(scriptDir, "..", "contributors.yml"),
"utf-8"
);
const entries = parseContributorsYaml(raw);
const uri = process.env.MONGODB_URI;
if (!uri) {
console.warn("Warning: MONGODB_URI not set, contributor lookups disabled");
return map;
}
for (const c of entries) {
map.set(c.github.toLowerCase(), c);
const client = new MongoClient(uri);
try {
await client.connect();
const db = client.db("hive");
const docs = await db.collection("contributors").find().toArray();
for (const doc of docs) {
map.set((doc.github as string).toLowerCase(), {
github: (doc.githubDisplay as string) ?? (doc.github as string),
discord: doc.discord as string,
name: doc.name as string | undefined,
});
}
} catch {
console.warn("Warning: could not load contributors.yml");
console.log(`Loaded ${map.size} contributors from MongoDB`);
} catch (err) {
console.warn(`Warning: could not load contributors from MongoDB: ${err}`);
} finally {
await client.close();
}
return map;
@@ -295,7 +273,7 @@ function formatBountyNotification(bounty: BountyResult): string {
msg += `PR: ${bounty.pr.html_url}\n`;
if (!bounty.discordId) {
msg += `\n_\u{1F517} @${bounty.contributor}: link your Discord in \`contributors.yml\` to get pinged!_`;
msg += `\n_\u{1F517} @${bounty.contributor}: use \`/link-github\` in Discord to get pinged!_`;
}
return msg;
@@ -464,7 +442,7 @@ async function main() {
process.exit(1);
}
const contributors = loadContributors();
const contributors = await loadContributors();
if (mode === "notify") {
// Single bounty notification