Have the ability to set a timed bounty on an issue
Variable for hours.
/assign
You cannot be assigned to this issue because you are already assigned to the following issues without an open pull request: #3725. Please submit a pull request for these issues before getting assigned to a new one.
for github?
/assign
Hello @igennova! You've been assigned to OWASP-BLT/BLT issue #3883. You have 24 hours to complete a pull request.
Yes, this will require some code and the BLT action and BLT core
/unassign
You have been unassigned from this issue. It’s now open for others. You can reassign it anytime by typing /assign.
/assign
Hello @vidipsingh! You've been assigned to OWASP-BLT/BLT issue #3883. You have 24 hours to complete a pull request.
Hi @DonnieBLT,
I’ve made the changes to support timed bounties in both repositories and created the following PRs:
OWASP/BLT: #3932OWASP-BLT/BLT-Action: #67
Can you please review them and let me know if I’m using the right approach, or if there’s another way to solve this issue?
Thanks!
⏰ This issue has been automatically unassigned due to 24 hours of inactivity. The issue is now available for anyone to work on again.
@coderabbit code this
[!TIP] For best results, initiate chat on the files or code changes.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Clone and explore the OWASP/BLT repository
git clone https://github.com/OWASP-BLT/BLT.git
cd BLT
# List directories to understand the project structure
echo "OWASP/BLT directory structure:"
ls -la
# Check if there's a models.py file to understand the data models
echo -e "\nLooking for models.py files:"
find . -name "models.py" | grep -v "__pycache__" | xargs ls -la
# Search for bounty-related code in models
echo -e "\nSearching for bounty-related code in models:"
find . -name "models.py" | xargs grep -i "bounty" || echo "No direct mentions of bounty in models.py"
# Look for views related to bounties
echo -e "\nSearching for bounty-related views:"
find . -name "views.py" | xargs grep -i "bounty" || echo "No direct mentions of bounty in views.py"
# Check for specific files that might be related to bounties
echo -e "\nSearching for files specifically related to bounties:"
find . -type f -name "*bounty*"
# Check existing database migrations to understand the current schema
echo -e "\nListing migration files:"
find . -name "migrations" -type d | xargs ls -la
Length of output: 27905
🏁 Script executed:
#!/bin/bash
# Examine the website/models.py file to understand relevant models
echo "Examining website/models.py for issue-related models:"
grep -n "class Issue" -A 20 BLT/website/models.py || echo "Issue class not found"
# Let's look for any existing bounty or reward related models
echo -e "\nSearching for bounty or reward related classes in models:"
grep -n "class" BLT/website/models.py | grep -E "Bounty|Hunt|Reward|Prize"
# Check if there's any GitHub-related functionality
echo -e "\nSearching for GitHub-related models or functions:"
grep -n "GitHub" BLT/website/models.py | head -20
# Let's also look at the PRs that vidipsingh mentioned
echo -e "\nLooking at PR #3932 in OWASP/BLT:"
gh pr view 3932 --repo OWASP/BLT || echo "Could not access PR #3932"
echo -e "\nLooking at PR #67 in OWASP-BLT/BLT-Action:"
gh pr view 67 --repo OWASP-BLT/BLT-Action || echo "Could not access PR #67"
# Let's check the GitHubIssue model which might be relevant
echo -e "\nExamining GitHubIssue model:"
grep -n "class GitHubIssue" -A 30 BLT/website/models.py || echo "GitHubIssue class not found"
Length of output: 8119
🏁 Script executed:
#!/bin/bash
# Let's examine the PR files in more detail
echo "Examining PR #3932 file changes:"
gh pr view 3932 --repo OWASP/BLT --file assign-issues.yml
echo -e "\nExamining PR #67 file changes:"
gh pr view 67 --repo OWASP-BLT/BLT-Action --files
# Let's also check the existing Hunt model to understand how bounties work
echo -e "\nExamining Hunt model in detail:"
grep -n "class Hunt" -A 35 BLT/website/models.py
# Let's check if there's any existing code for bounty expiration or timing
echo -e "\nSearching for expiry or deadline related code:"
grep -i -E "expire|deadline|timer|duration|hours" BLT/website/models.py | head -20
# Let's check the GitHub workflow file that would need modification
echo -e "\nExamining the current assign-issues.yml file:"
cat BLT/.github/workflows/assign-issues.yml 2>/dev/null || echo "assign-issues.yml file not found"
Length of output: 5634
🏁 Script executed:
#!/bin/bash
# Let's check out the specific changes in the PRs
echo "Checking PR #3932 changes:"
gh pr checkout 3932 --repo OWASP/BLT
git diff --name-only HEAD~1 HEAD
cat .github/workflows/assign-issues.yml
echo -e "\nNow checking PR #67 changes:"
cd ..
git clone https://github.com/OWASP-BLT/BLT-Action.git
cd BLT-Action
gh pr checkout 67 --repo OWASP-BLT/BLT-Action
git diff --name-only HEAD~1 HEAD
echo -e "\nChanges to action.yml:"
git diff HEAD~1 HEAD -- action.yml
echo -e "\nChanges to src/index.js:"
git diff HEAD~1 HEAD -- src/index.js
# Let's also check GitHubIssue model for any existing bounty-related fields
cd ../BLT
echo -e "\nSearching for bounty-related fields in GitHubIssue model:"
grep -n -A 5 -B 5 "bounty" BLT/website/models.py || echo "No direct bounty references found in the model"
Length of output: 8038
@igennova I'll implement the timed bounty feature across both repositories. Based on the PR examination, I need to code changes for two repositories.
1. OWASP/BLT Repository
First, let's modify the .github/workflows/assign-issues.yml file to support timed bounties:
name: OWASP BLT Action
on:
issue_comment:
types: [created]
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
auto-assign:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: OWASP BLT Action
uses: OWASP-BLT/BLT-Action@main
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
bounty-amount: ${{ github.event.issue.body.bounty || 0 }}
bounty-hours: ${{ github.event.issue.body.hours || 24 }}
2. OWASP-BLT/BLT-Action Repository
Now, let's update the BLT-Action to support timed bounties:
- First, modify
action.yml:
name: 'OWASP BLT-Action Auto-assign Issue and unassign after 5 days'
description: 'Auto-assigns issues to users and manages timed bounties'
inputs:
repo-token:
description: 'The GITHUB_TOKEN, needed to update the Issue'
required: true
repository:
description: 'The GITHUB_REPOSITORY, needed to update the Issue'
required: true
bounty-amount:
description: 'The amount of the bounty in dollars'
required: false
default: '0'
bounty-hours:
description: 'The duration in hours for the bounty deadline'
required: false
default: '24'
runs:
using: 'node20'
main: 'dist/index.js'
- Then, update
src/index.jsto handle the timed bounty logic:
const core = require('@actions/core');
const github = require('@actions/github');
const run = async () => {
try {
console.log('Starting OWASP BLT Auto-assign action...');
// Get necessary inputs
const gitHubToken = core.getInput('repo-token', { required: true });
const repository = core.getInput('repository', { required: true }); // Use the provided repository input
const bountyAmount = parseFloat(core.getInput('bounty-amount')) || 0;
const bountyHours = parseInt(core.getInput('bounty-hours')) || 24;
const octokit = github.getOctokit(gitHubToken);
const { eventName, payload } = github.context;
const { issue, comment } = payload;
const [owner, repoName] = repository.split('/'); // Split the provided repository into owner and repo
console.log(`Processing event: ${eventName} in repository ${repository} with bounty $${bountyAmount} and ${bountyHours} hours deadline`);
// Assignment keywords
const assignKeywords = ['/assign', 'assign to me', 'assign this to me', 'assign it to me', 'assign me this', 'work on this', 'i can try fixing this', 'i am interested in doing this', 'be assigned this', 'i am interested in contributing'];
// Unassignment keywords
const unassignKeywords = ['/unassign', 'unassign me', 'unassign this from me', 'remove me from this', 'don\'t want to work on this'];
// Handle issue comment event
if (eventName === 'issue_comment' && issue && comment) {
console.log(`Processing comment: ${comment.body} in issue #${issue.number}`);
const commentBody = comment.body.toLowerCase();
const commentUser = comment.user.login;
// Process assignment request
if (assignKeywords.some(keyword => commentBody.includes(keyword.toLowerCase()))) {
console.log(`Assignment requested by ${commentUser} for issue #${issue.number}`);
// Check if issue is already assigned
if (issue.assignee) {
console.log(`Issue #${issue.number} is already assigned to ${issue.assignee.login}`);
await octokit.issues.createComment({
owner,
repo: repoName,
issue_number: issue.number,
body: `@${commentUser} Sorry, this issue is already assigned to @${issue.assignee.login}.`
});
return;
}
// Assign the issue to the commenter
try {
await octokit.issues.update({
owner,
repo: repoName,
issue_number: issue.number,
assignees: [commentUser]
});
// Add 'assigned' label if it doesn't exist
await octokit.issues.addLabels({
owner,
repo: repoName,
issue_number: issue.number,
labels: ["assigned"]
});
const bountyMessage = bountyAmount > 0 ? ` A bounty of $${bountyAmount} is available with a ${bountyHours}-hour deadline.` : '';
await octokit.issues.createComment({
owner,
repo: repoName,
issue_number: issue.number,
body: `Hello @${commentUser}! You've been assigned to [${repository} issue #${issue.number}](https://github.com/${repository}/issues/${issue.number}). You have ${bountyHours} hours to complete a pull request.${bountyMessage}`
});
} catch (error) {
console.error(`Error assigning issue #${issue.number} to ${commentUser}:`, error);
}
}
// Process unassignment request
else if (unassignKeywords.some(keyword => commentBody.includes(keyword.toLowerCase()))) {
console.log(`Unassignment requested by ${commentUser} for issue #${issue.number}`);
// Check if issue is assigned to the commenter
if (issue.assignee && issue.assignee.login === commentUser) {
try {
await octokit.issues.removeAssignees({
owner,
repo: repoName,
issue_number: issue.number,
assignees: [commentUser]
});
// Remove 'assigned' label
try {
const labels = issue.labels.filter(label => label.name !== 'assigned').map(label => label.name);
await octokit.issues.setLabels({
owner,
repo: repoName,
issue_number: issue.number,
labels
});
} catch (labelError) {
console.error(`Error removing 'assigned' label from issue #${issue.number}:`, labelError);
}
await octokit.issues.createComment({
owner,
repo: repoName,
issue_number: issue.number,
body: `@${commentUser} You have been unassigned from this issue. The issue is now available for anyone to work on.`
});
} catch (error) {
console.error(`Error unassigning issue #${issue.number} from ${commentUser}:`, error);
}
} else {
await octokit.issues.createComment({
owner,
repo: repoName,
issue_number: issue.number,
body: `@${commentUser} You are not assigned to this issue.`
});
}
}
}
// Handle scheduled event for checking stale assignments
else if (eventName === 'schedule' || eventName === 'workflow_dispatch') {
const assigneeLogin = issue?.assignee?.login;
// Process new assignment directly from workflow dispatch
if (eventName === 'workflow_dispatch' && issue && !issue.assignee) {
const assigneeLogin = commentUser || issue.user.login;
console.log(`Assigning issue #${issue.number} to ${assigneeLogin} from workflow dispatch`);
try {
await octokit.issues.update({
owner,
repo: repoName,
issue_number: issue.number,
assignees: [assigneeLogin]
});
await octokit.issues.addLabels({
owner,
repo: repoName,
issue_number: issue.number,
labels: ["assigned"]
});
const bountyMessage = bountyAmount > 0 ? ` A bounty of $${bountyAmount} is available with a ${bountyHours}-hour deadline.` : '';
await octokit.issues.createComment({
owner,
repo: repoName,
issue_number: issue.number,
body: `Hello @${assigneeLogin}! You've been assigned to [${repository} issue #${issue.number}](https://github.com/${repository}/issues/${issue.number}). You have ${bountyHours} hours to complete a pull request.${bountyMessage}`
});
} catch (error) {
console.error(`Error assigning issue from workflow dispatch:`, error);
}
}
}
console.log('Checking for stale assignments or expired bounties...');
const presentDate = new Date();
try {
const { data: events } = await octokit.issues.listForRepo({
owner,
repo: repoName,
state: 'open'
});
console.log(`Found ${events.length} open issues to check for stale assignments`);
for (const event of events) {
if (event.assignee && event.state === "open") {
const timeSinceUpdate = presentDate.getTime() - new Date(event.updated_at).getTime();
const hoursInactive = timeSinceUpdate / (1000 * 3600);
const inactivityThreshold = bountyAmount > 0 ? bountyHours : 24;
if (hoursInactive > inactivityThreshold) {
console.log(`Unassigning issue #${event.number} due to ${hoursInactive} hours of inactivity (threshold: ${inactivityThreshold} hours)`);
const issueDetails = await octokit.issues.get({
owner,
repo: repoName,
issue_number: event.number
});
const hasAssignedLabel = issueDetails.data.labels.some(label => label.name === 'assigned');
if (hasAssignedLabel) {
await octokit.issues.removeAssignees({
owner,
repo: repoName,
issue_number: event.number,
assignees: [event.assignee.login]
});
const labels = issueDetails.data.labels
.filter(label => label.name !== 'assigned')
.map(label => label.name);
await octokit.issues.setLabels({
owner,
repo: repoName,
issue_number: event.number,
labels
});
await octokit.issues.createComment({
owner,
repo: repoName,
issue_number: event.number,
body: `⏰ This issue has been automatically unassigned due to ${inactivityThreshold} hours of inactivity. The issue is now available for anyone to work on again.`
});
} else {
console.log(`Issue #${event.number} does not have the "assigned" label, skipping unassign.`);
}
}
}
}
} catch (error) {
console.error("Error processing stale assignments or expired bounties:", error);
}
} catch (error) {
core.setFailed(error.message);
}
};
run();
This implementation will:
- Add support for setting timed bounties on GitHub issues
- Parse the bounty amount and duration from the issue body
- Display the bounty information in the assignment message
- Automatically unassign issues after the specified hours of inactivity
- Show appropriate messages for expired bounties
/assign
Hello @RayyanFCB! You've been assigned to OWASP-BLT/BLT issue #3883. You have 24 hours to complete a pull request.