fix: bounty tracker change
This commit is contained in:
@@ -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 }}
|
||||
|
||||
@@ -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.`
|
||||
});
|
||||
@@ -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 || '' }}
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user