feat: Add Breadcrumb Navigation Component (#346) #959
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Spam Guard | |
| # Handles T1/T2 bounty comment moderation: | |
| # - Deletes tiny/spam comments | |
| # - Redirects "claiming" attempts on T1/T2 with FCFS rules | |
| # - Auto-deletes repeat offenders | |
| # T3 claim approval is handled separately by claim-guard.yml | |
| on: | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| issues: write | |
| jobs: | |
| spam-guard: | |
| runs-on: ubuntu-latest | |
| # Skip T3 issues — those go to claim-guard.yml | |
| if: "!contains(join(github.event.issue.labels.*.name, ','), 'tier-3')" | |
| concurrency: | |
| group: spam-guard-${{ github.event.issue.number }}-${{ github.event.comment.user.login }} | |
| steps: | |
| - name: Moderate comments on T1/T2 bounties | |
| env: | |
| GH_TOKEN: ${{ secrets.SOLFOUNDRY_GITHUB_PAT }} | |
| ISSUE_LABELS: ${{ join(github.event.issue.labels.*.name, ',') }} | |
| ISSUE_TITLE: ${{ github.event.issue.title }} | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| COMMENT_USER: ${{ github.event.comment.user.login }} | |
| COMMENT_ID: ${{ github.event.comment.id }} | |
| ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| run: | | |
| python3 << 'PYEOF' | |
| import os, re, json, urllib.request, unicodedata | |
| labels = os.environ.get("ISSUE_LABELS", "") | |
| issue_title = os.environ.get("ISSUE_TITLE", "").lower() | |
| comment = os.environ.get("COMMENT_BODY", "") | |
| user = os.environ.get("COMMENT_USER", "") | |
| comment_id = os.environ.get("COMMENT_ID", "") | |
| issue = os.environ.get("ISSUE_NUMBER", "") | |
| # Skip content/social bounties | |
| content_keywords = ["x/twitter", "x post", "content", "tweet", "social media", "video", "article", "blog"] | |
| if any(kw in issue_title for kw in content_keywords): | |
| print(f"Content bounty #{issue} — skipping") | |
| exit(0) | |
| # Skip star-reward and content-bounty labeled issues | |
| label_names = [l.strip() for l in labels.split(",")] | |
| if any(lbl in ("star-reward", "content-bounty") for lbl in label_names): | |
| exit(0) | |
| repo = os.environ.get("GITHUB_REPOSITORY", "SolFoundry/solfoundry") | |
| token = os.environ.get("GH_TOKEN", "") | |
| if user in ["github-actions[bot]", "chronoeth-creator", "coderabbitai[bot]"]: | |
| exit(0) | |
| if "bounty" not in labels: | |
| print("Not a bounty issue - skipping") | |
| exit(0) | |
| def gh_post(path, data): | |
| url = f"https://api.github.com/{path}" | |
| req = urllib.request.Request(url, json.dumps(data).encode(), method="POST") | |
| req.add_header("Authorization", f"token {token}") | |
| req.add_header("Accept", "application/vnd.github.v3+json") | |
| req.add_header("Content-Type", "application/json") | |
| try: | |
| return urllib.request.urlopen(req) | |
| except Exception as e: | |
| print(f"POST failed: {e}") | |
| return None | |
| def gh_delete(path): | |
| url = f"https://api.github.com/{path}" | |
| req = urllib.request.Request(url, method="DELETE") | |
| req.add_header("Authorization", f"token {token}") | |
| req.add_header("Accept", "application/vnd.github.v3+json") | |
| try: | |
| return urllib.request.urlopen(req) | |
| except Exception as e: | |
| print(f"DELETE failed: {e}") | |
| return None | |
| def gh_get(path): | |
| url = f"https://api.github.com/{path}" | |
| req = urllib.request.Request(url) | |
| req.add_header("Authorization", f"token {token}") | |
| req.add_header("Accept", "application/vnd.github.v3+json") | |
| try: | |
| resp = urllib.request.urlopen(req) | |
| return json.loads(resp.read().decode()), resp.status | |
| except urllib.error.HTTPError as e: | |
| return None, e.code | |
| # Tiny comment filter | |
| if len(comment.strip()) < 5: | |
| print(f"Tiny comment from {user} on #{issue} — deleting") | |
| gh_delete(f"repos/{repo}/issues/comments/{comment_id}") | |
| exit(0) | |
| # Normalize unicode | |
| normalized = unicodedata.normalize('NFKC', comment).lower() | |
| confusables = str.maketrans('аеорсухіјёАЕОРСУХІ', 'aeopcyxijeAEOPCYXI') | |
| lower = normalized.translate(confusables) | |
| stripped = re.sub(r'[*_`~>\[\]()]', '', lower) | |
| # Repeat offender check | |
| comments_data = [] | |
| cpage = 1 | |
| while True: | |
| cpage_data, _ = gh_get(f"repos/{repo}/issues/{issue}/comments?per_page=100&page={cpage}") | |
| if not cpage_data: | |
| break | |
| comments_data.extend(cpage_data) | |
| if len(cpage_data) < 100: | |
| break | |
| cpage += 1 | |
| if comments_data: | |
| already_warned = False | |
| for c in comments_data: | |
| body_c = c.get("body", "") | |
| author = c["user"]["login"] | |
| if author in ["chronoeth-creator", "github-actions[bot]"] and f"@{user}" in body_c: | |
| if any(kw in body_c.lower() for kw in [ | |
| "open race", "fcfs", "no claiming needed", | |
| "reputation-gated", "posts have been removed", | |
| "auto-deleted", "just ship it" | |
| ]): | |
| already_warned = True | |
| break | |
| if already_warned: | |
| print(f"REPEAT OFFENDER: {user} already warned on #{issue} — auto-deleting") | |
| gh_delete(f"repos/{repo}/issues/comments/{comment_id}") | |
| exit(0) | |
| # Spam detection | |
| spam_patterns = [ | |
| r"(i am a .* developer|hire me|my portfolio|check my profile)", | |
| r"(dm me|contact me at|whatsapp|telegram.*@)", | |
| r"(upvote|follow me|subscribe|check out my)", | |
| r"(great (project|repo|work).*star|nice repo.*followed)", | |
| r"https?://\S+\.(click|xyz|top|buzz|gq|ml|tk|ga|cf)\b", | |
| ] | |
| if any(re.search(p, stripped) for p in spam_patterns): | |
| print(f"SPAM detected from {user} on #{issue} — deleting") | |
| gh_delete(f"repos/{repo}/issues/comments/{comment_id}") | |
| exit(0) | |
| # Claim detection on T1/T2 | |
| claim_re = r"(/attempt|claim(ing)?(\s+this)?|dibs|assigning myself|assign\s*(me|this)|i.?ll take this|i want to (take|work on) this|want to work on this|working on this|taking this|want to implement|i.?d like to (work on|take|build|implement) this|interested in (working on |taking )?this|please assign|let me work on this|i.?ll work on this|i.?ll build this|i.?ll implement this)" | |
| is_claim = bool(re.search(claim_re, stripped)) | |
| if not is_claim: | |
| print("Not a claim attempt — ignoring") | |
| exit(0) | |
| is_t1 = "tier-1" in labels | |
| is_t2 = "tier-2" in labels | |
| if is_t1: | |
| print(f"T1 claim from {user} on #{issue} — deleting + FCFS rules") | |
| gh_delete(f"repos/{repo}/issues/comments/{comment_id}") | |
| body = ( | |
| f"👋 Hey @{user} — **Tier 1 bounties are open race (FCFS)**. " | |
| f"No claiming needed!\n\n" | |
| f"### How it works:\n" | |
| f"1. Read the bounty spec in the issue description\n" | |
| f"2. Build your solution and submit a PR linking this issue (`Closes #{issue}`)\n" | |
| f"3. Include your **Solana wallet** in the PR body for payout\n" | |
| f"4. Our multi-LLM review pipeline scores your submission\n" | |
| f"5. **First PR to pass review wins** the bounty 🏆\n\n" | |
| f"Just ship it — your code speaks for itself.\n\n" | |
| f"*— SolFoundry Bot 🏭*" | |
| ) | |
| gh_post(f"repos/{repo}/issues/{issue}/comments", {"body": body}) | |
| elif is_t2: | |
| print(f"T2 claim from {user} on #{issue} — deleting + FCFS rules") | |
| gh_delete(f"repos/{repo}/issues/comments/{comment_id}") | |
| body = ( | |
| f"👋 Hey @{user} — **Tier 2 bounties are also FCFS** (no claiming needed). " | |
| f"First quality PR to pass review wins.\n\n" | |
| f"### How it works:\n" | |
| f"1. Read the bounty spec in the issue description\n" | |
| f"2. Build your solution and submit a PR (`Closes #{issue}`)\n" | |
| f"3. Include your **Solana wallet** in the PR body\n" | |
| f"4. Min AI review score: **7.0/10** (tests + docs required)\n" | |
| f"5. **First PR to pass wins** 🏆\n\n" | |
| f"⚠️ T2 requires 4+ merged T1 bounty PRs. The review pipeline will check eligibility.\n\n" | |
| f"Ship it!\n\n" | |
| f"*— SolFoundry Bot 🏭*" | |
| ) | |
| gh_post(f"repos/{repo}/issues/{issue}/comments", {"body": body}) | |
| PYEOF |