Skip to content

Implement api endpoint on chain #1469

Implement api endpoint on chain

Implement api endpoint on chain #1469

Workflow file for this run

name: Bounty Tracker
on:
pull_request:
types: [closed]
issues:
types: [closed, labeled]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
track-bounty:
if: github.event.pull_request.merged == true || github.event_name == 'issues'
runs-on: ubuntu-latest
concurrency:
group: bounty-tracker-${{ github.event.pull_request.number || github.event.issue.number }}
steps:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: pip install requests
- name: Track Bounty
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.SOLFOUNDRY_TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.SOLFOUNDRY_TELEGRAM_CHAT_ID }}
GH_TOKEN: ${{ secrets.SOLFOUNDRY_GITHUB_PAT }}
EVENT_NAME: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_BODY: ${{ github.event.pull_request.body }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
ISSUE_ACTION: ${{ github.event.action }}
REPO: ${{ github.repository }}
run: |
python3 << 'PYEOF'
import os, re, requests, json
def send_telegram(msg, reply_markup=None):
token = os.environ.get("TELEGRAM_BOT_TOKEN")
chat_id = os.environ.get("TELEGRAM_CHAT_ID")
if not token or not chat_id:
print("Telegram not configured")
return
payload = {
"chat_id": chat_id,
"text": msg,
"parse_mode": "HTML",
"disable_web_page_preview": True
}
if reply_markup:
payload["reply_markup"] = json.dumps(reply_markup)
resp = requests.post(
f"https://api.telegram.org/bot{token}/sendMessage",
json=payload
)
print(f"Telegram send: {resp.status_code} {resp.text[:200]}")
def gh_api_get(path):
token = os.environ.get("GH_TOKEN", "")
resp = requests.get(
f"https://api.github.com/{path}",
headers={"Authorization": f"token {token}", "Accept": "application/vnd.github.v3+json"}
)
return resp.json() if resp.ok else None
event = os.environ["EVENT_NAME"]
repo = os.environ.get("REPO", "SolFoundry/solfoundry")
# ══════════════════════════════════════════════════════
# EVENT: Issue closed (bounty completed via merged PR)
# ══════════════════════════════════════════════════════
if event == "issues":
action = os.environ.get("ISSUE_ACTION", "")
issue_num = os.environ.get("ISSUE_NUMBER", "")
issue_title = os.environ.get("ISSUE_TITLE", "")
issue_body = os.environ.get("ISSUE_BODY", "")
if action != "closed":
print(f"Issue event action={action}, not closed — skipping")
exit(0)
# Check if it's a bounty issue
issue_data = gh_api_get(f"repos/{repo}/issues/{issue_num}")
if not issue_data:
print(f"Could not fetch issue #{issue_num}")
exit(0)
labels = [l["name"] for l in issue_data.get("labels", [])]
if "bounty" not in labels:
print(f"Issue #{issue_num} is not a bounty — skipping")
exit(0)
# Find the tier
tier = "unknown"
for t in ("tier-3", "tier-2", "tier-1"):
if t in labels:
tier = t
break
# Extract reward from issue body
reward = "unknown"
reward_match = re.search(r'(\d[\d,]*)\s*(?:\$FNDRY|FNDRY)', issue_body or "")
if reward_match:
reward = reward_match.group(1)
# Find which merged PR closed this issue
# Look at timeline events for the closing PR
timeline = gh_api_get(f"repos/{repo}/issues/{issue_num}/timeline?per_page=50")
closing_pr = None
if timeline:
for ev in timeline:
if isinstance(ev, dict) and ev.get("event") == "closed" and ev.get("commit_id"):
# Find the PR associated with this commit
commit_sha = ev["commit_id"]
# Search for PR by merge commit
prs = gh_api_get(f"repos/{repo}/commits/{commit_sha}/pulls")
if prs and len(prs) > 0:
closing_pr = prs[0]
break
if not closing_pr:
# Fallback: search recently merged PRs that reference this issue
recent_prs = gh_api_get(f"repos/{repo}/pulls?state=closed&sort=updated&direction=desc&per_page=20")
if recent_prs:
for pr in recent_prs:
if not pr.get("merged_at"):
continue
body = (pr.get("body") or "").lower()
if re.search(rf'(?:closes|fixes|resolves|issue|bounty)\s*:?\s*#{issue_num}\b', body, re.IGNORECASE):
closing_pr = pr
break
if closing_pr:
pr_num = closing_pr["number"]
pr_title = closing_pr["title"]
pr_author = closing_pr["user"]["login"]
pr_url = closing_pr["html_url"]
pr_body = closing_pr.get("body") or ""
# Extract wallet from PR body
wallet = "not found"
wallet_match = re.search(r'(?:wallet|address)[:\s]*`?([1-9A-HJ-NP-Za-km-z]{32,44})`?', pr_body, re.IGNORECASE)
if not wallet_match:
wallet_match = re.search(r'\b([1-9A-HJ-NP-Za-km-z]{32,44})\b', pr_body)
if wallet_match:
wallet = wallet_match.group(1)
# Check if this PR was already paid by the bot
pr_labels = [l["name"] for l in closing_pr.get("labels", [])]
if "paid" in pr_labels:
print(f"PR #{pr_num} already has 'paid' label — skipping duplicate notification")
sys.exit(0)
keyboard = {"inline_keyboard": [[{"text": "\U0001f440 View PR", "url": pr_url}]]}
msg = (
f"\U0001f389 <b>Bounty Completed!</b>\n\n"
f"\U0001f3af Issue #{issue_num}: {issue_title}\n"
f"\U0001f4b0 Reward: {reward} $FNDRY ({tier})\n\n"
f"\U0001f527 Merged PR #{pr_num}: {pr_title}\n"
f"\U0001f464 by @{pr_author}\n"
f"\U0001f4b3 Wallet: <code>{wallet}</code>\n\n"
f"Ready to send payout?"
)
send_telegram(msg, reply_markup=keyboard)
print(f"Bounty #{issue_num} completed by @{pr_author} via PR #{pr_num}")
else:
# Issue closed but couldn't find the PR — still notify
msg = (
f"\u2705 <b>Bounty Issue Closed</b>\n\n"
f"Issue #{issue_num}: {issue_title}\n"
f"Reward: {reward} $FNDRY ({tier})\n\n"
f"\u26a0\ufe0f Could not identify the merging PR — check manually."
)
send_telegram(msg)
print(f"Bounty #{issue_num} closed but no merging PR found")
# ══════════════════════════════════════════════════════
# EVENT: PR merged — only auto-close competing PRs
# (Telegram payout notification handled by issues event above
# to avoid double-send, since Closes #N fires both events)
# ══════════════════════════════════════════════════════
elif event == "pull_request":
pr_body = os.environ.get("PR_BODY", "")
pr_author = os.environ.get("PR_AUTHOR", "")
pr_number = os.environ.get("PR_NUMBER", "")
closes = re.findall(r'(?:closes|fixes|resolves|issue|bounty)\s*:?\s*#(\d+)', pr_body.lower())
# ── Auto-close competing PRs for the same bounty ──
if closes:
for issue_num in closes:
try:
open_prs = gh_api_get(f"repos/{repo}/pulls?state=open&per_page=100")
if not open_prs:
continue
for opr in open_prs:
opr_num = opr["number"]
if str(opr_num) == str(pr_number):
continue
opr_body = (opr.get("body", "") or "").lower()
opr_author = opr.get("user", {}).get("login", "unknown")
opr_closes = re.findall(r'(?:closes|fixes|resolves|issue|bounty)\s*:?\s*#(\d+)', opr_body)
if issue_num in opr_closes:
# Post comment and close
comment = (
f"\U0001f3ed **Bounty #{issue_num} completed** \u2014 "
f"PR #{pr_number} by @{pr_author} was merged first.\n\n"
f"Thank you for your submission @{opr_author}! "
f"Check out the other [open bounties]"
f"(https://github.com/{repo}/issues?q=is%3Aissue+is%3Aopen+label%3Abounty) "
f"for more opportunities.\n\n"
f"---\n*SolFoundry Bot \U0001f3ed*"
)
requests.post(
f"https://api.github.com/repos/{repo}/issues/{opr_num}/comments",
json={"body": comment},
headers={"Authorization": f"token {os.environ.get('GH_TOKEN', '')}",
"Accept": "application/vnd.github.v3+json"}
)
requests.patch(
f"https://api.github.com/repos/{repo}/pulls/{opr_num}",
json={"state": "closed"},
headers={"Authorization": f"token {os.environ.get('GH_TOKEN', '')}",
"Accept": "application/vnd.github.v3+json"}
)
print(f"Auto-closed competing PR #{opr_num} by @{opr_author} (bounty #{issue_num} already won)")
except Exception as e:
print(f"Error auto-closing competing PRs for #{issue_num}: {e}")
print("Bounty tracking complete")
PYEOF