Skip to content

Commit

Permalink
refactor: bounties to tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
0x4007 committed Sep 25, 2023
1 parent e93ff70 commit 840620d
Show file tree
Hide file tree
Showing 18 changed files with 56 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: "Bounty Proposal"
name: "Task Proposal"
description: Have a suggestion for how to improve UbiquiBot? Let us know!
title: "Bounty Proposal: "
title: "Task Proposal: "

body:
- type: markdown
attributes:
value: |
## Feature Request Form
Thank you for taking the time to file a feature request.
If you register your wallet address, you will be eligible for compensation if this is accepted!
Thank you for taking the time to file a feature request.
If you register your wallet address, you will be eligible for compensation if this is accepted!
Please let us know how we can improve the bot.
- type: textarea
Expand All @@ -17,14 +17,14 @@ body:
description: Please let us know what inspired you to write this proposal. Backlinking to specific comments on GitHub, and leaving a remark about how the bot should have interacted with it is usually sufficient context.
validations:
required: false

- type: textarea
attributes:
label: Describe the solution
description: A clear description of what you want to happen. Add any considered drawbacks.
validations:
required: true

- type: textarea
attributes:
label: Remarks
Expand Down
2 changes: 1 addition & 1 deletion .github/ubiquibot-config.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
priceMultiplier: 1.5
# newContributorGreeting:
# enabled: true
# header: "Thank you for contributing to UbiquiBot! Please be sure to set your wallet address before completing your first bounty so that the automatic payout upon task completion will work for you."
# header: "Thank you for contributing to UbiquiBot! Please be sure to set your wallet address before completing your first task so that the automatic payout upon task completion will work for you."
# helpMenu: true
# footer: "###### Also please star this repository and [@ubiquity/devpool-directory](https://github.com/ubiquity/devpool-directory/) to show your support. It helps a lot!"
4 changes: 2 additions & 2 deletions .github/workflows/bot.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Calculate Bounty Based on Events
name: Calculate Task Price Based on Events
on:
push:
issues:
Expand All @@ -19,7 +19,7 @@ jobs:
if: >-
github.event.pull_request.payload.sender.type != 'Bot' && github.repository != 'ubiquity/ubiquibot'
runs-on: ubuntu-latest
name: Calculate Bounty with UbiquiBot
name: Calculate Task Price with UbiquiBot
steps:
# To use this repository's private action,
# you must check out the repository
Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Instead, it is recommended to make a copy of the `.env.example` file and replace

1. Go to the [UbiquiBot App Marketplace](https://github.com/marketplace/ubiquibot)
2. Choose a plan and install UbiquiBot on your repository
3. Congratulations! You can now use the UbiquiBot to manage your bounties.
3. Congratulations! You can now use the UbiquiBot to manage your tasks.

To test the bot, you can:

Expand All @@ -61,12 +61,12 @@ To test the bot, you can:

`evm-network-id` is ID of the EVM-compatible network that will be used for payouts.

`price-multiplier` is a base number that will be used to calculate bounty price based on the following formula: `price = price-multiplier * time-label-weight * priority-label-weight * 100`
`price-multiplier` is a base number that will be used to calculate task price based on the following formula: `price = price-multiplier * time-label-weight * priority-label-weight * 100`

`time-labels` are labels for marking the time limit of the task:

- `name` is a human-readable name
- `value` is number of seconds that corresponds to the time limit of the bounty
- `value` is number of seconds that corresponds to the time limit of the task

`priority-labels` are labels for marking the priority of the task:

Expand All @@ -83,7 +83,7 @@ To test the bot, you can:

`disable-analytics` can be `true` or `false` that disables or enables weekly analytics collection by Ubiquity.

`payment-permit-max-price` sets the max amount for automatic payout of bounties when the issue is closed.
`payment-permit-max-price` sets the max amount for automatic payout of tasks when the issue is closed.

`comment-incentives` can be `true` or `false` that enable or disable comment incentives. These are payments generated for comments in the issue by contributors, excluding the assignee.

Expand All @@ -98,7 +98,7 @@ To test the bot, you can:
- `totals`:
- `word` defines reward for each word in the comment

`max-concurrent-assigns` is the maximum number of bounties that can be assigned to a bounty hunter at once. This excludes bounties with delayed or approved pull request reviews.
`max-concurrent-assigns` is the maximum number of tasks that can be assigned to an assignee at once. This excludes tasks with delayed or approved pull request reviews.

`register-wallet-with-verification` can be `true` or `false`. If enabled, it requires a signed message to set wallet address. This prevents users from setting wallet address from centralized exchanges, which would make payments impossible to claim.

Expand Down Expand Up @@ -150,13 +150,13 @@ You can, for example:

## How it works

Bounty bot is built using the [probot](https://probot.github.io/) framework so initially the bot is a github app. But thanks to the [probot/adapter-github-actions](https://github.com/probot/adapter-github-actions) you can also use the bot as a github action.
Task bot is built using the [probot](https://probot.github.io/) framework so initially the bot is a github app. But thanks to the [probot/adapter-github-actions](https://github.com/probot/adapter-github-actions) you can also use the bot as a github action.

You can use the bounty bot as a [github app](https://github.com/marketplace/ubiquibot).
You can use the task bot as a [github app](https://github.com/marketplace/ubiquibot).

When using as a github app the flow is the following:

1. Bounty bot is added to a repository as a github app
1. Task bot is added to a repository as a github app
2. You run the bot "backend" (for example on your local machine)
3. Some event happens in a repository and the bot should react somehow (for example: on adding a time label to an issue the bot should add a price label)
4. Event details are sent to your deployed bot instance (to a webhook URL that was set in github app's settings)
Expand Down Expand Up @@ -197,7 +197,7 @@ Make sure you have your local instance of ubiquibot running.

## Architecture Overview

Bounty bot is built using the [probot](https://probot.github.io/) framework so initially the bot is a github app
Task bot is built using the [probot](https://probot.github.io/) framework so initially the bot is a github app

<pre>
&lt;root&gt;
Expand Down
2 changes: 1 addition & 1 deletion src/configs/strings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const GLOBAL_STRINGS = {
unassignComment: "Releasing the bounty back to dev pool because the allocated duration already ended!",
unassignComment: "Releasing the task due to lack of updates.",
askUpdate: "Do you have any updates",
assignCommandDisabledComment: "The `/assign` command is disabled for this repository.",
skipPriceLabelGenerationComment: "Pricing is disabled on parent issues.",
Expand Down
2 changes: 1 addition & 1 deletion src/configs/ubiquibot-config-default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const DefaultConfig: MergedConfig = {
newContributorGreeting: {
enabled: false,
header:
"Thank you for contributing! Please be sure to set your wallet address before completing your first bounty so that the automatic payout upon task completion will work for you.",
"Thank you for contributing! Please be sure to set your wallet address before completing your first task so that the automatic payout upon task completion will work for you.",
helpMenu: true,
footer:
"###### Also please star this repository and [@ubiquity/devpool-directory](https://github.com/ubiquity/devpool-directory/) to show your support. It helps a lot!",
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/comment/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export enum IssueCommentCommands {
STOP = "/stop", // unassign to default
WALLET = "/wallet", // register wallet address
PAYOUT = "/payout", // request permit payout
MULTIPLIER = "/multiplier", // set bounty multiplier (for treasury)
MULTIPLIER = "/multiplier", // set task multiplier (for contributor)
QUERY = "/query",
ASK = "/ask", // ask GPT a question
// Access Controls
Expand Down
8 changes: 4 additions & 4 deletions src/handlers/comment/handlers/assign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const assign = async (body: string) => {

const id = organization?.id || repository?.id; // repository?.id as fallback

const staleBounty = config.assign.staleTaskTime;
const staleTask = config.assign.staleTaskTime;

logger.info(`Received '/start' command from user: ${payload.sender.login}, body: ${body}`);
const issue = (_payload as Payload).issue;
Expand Down Expand Up @@ -116,9 +116,9 @@ export const assign = async (body: string) => {
let staleToDays: number | undefined;
let isTaskStale = false;

if (staleBounty !== 0) {
if (staleTask !== 0) {
days = Math.floor((new Date().getTime() - new Date(issue.created_at).getTime()) / (1000 * 60 * 60 * 24));
staleToDays = Math.floor(staleBounty / (1000 * 60 * 60 * 24));
staleToDays = Math.floor(staleTask / (1000 * 60 * 60 * 24));
isTaskStale = days >= staleToDays;
}

Expand Down Expand Up @@ -146,7 +146,7 @@ const getMultiplierInfoToDisplay = async (senderLogin: string, org_id: string, i
_multiplierToDisplay = multiplier;
_reasonToDisplay = reason;
} else {
// default mode: normal bounty hunter with default multiplier 1 and no reason
// default mode: normal contributor with default multiplier 1 and no reason
// nothing to show about multiplier
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/comment/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export const userCommands = (): UserCommands[] => {
},
{
id: IssueCommentCommands.MULTIPLIER,
description: `Set the bounty payout multiplier for a specific contributor, and provide a reason for why.\n\te.g. '/wallet @user 0.5 "Multiplier reason"'`,
description: `Set the task payout multiplier for a specific contributor, and provide a reason for why.\n\te.g. '/wallet @user 0.5 "Multiplier reason"'`,
handler: multiplier,
callback: commandCallback,
},
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/comment/handlers/multiplier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const multiplier = async (body: string) => {
if (taskMultiplier > 1) {
return `Successfully changed the payout multiplier for @${username} to ${taskMultiplier}. The reason ${
reason ? `provided is "${reason}"` : "is not provided"
}. This feature is designed to limit the contributor's compensation for any bounty on the current repository due to other compensation structures (i.e. salary.) are you sure you want to use a bounty multiplier above 1?`;
}. This feature is designed to limit the contributor's compensation for any task on the current repository due to other compensation structures (i.e. salary.) are you sure you want to use a price multiplier above 1?`;
} else {
return `Successfully changed the payout multiplier for @${username} to ${taskMultiplier}. The reason ${
reason ? `provided is "${reason}"` : "is not provided"
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/comment/handlers/unassign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const unassign = async (body: string) => {
issue_number,
assignees.map((i) => i.login)
);
return `You have been unassigned from the bounty @${payload.sender.login}`;
return `You have been unassigned from the task @${payload.sender.login}`;
}
return;
};
12 changes: 6 additions & 6 deletions src/handlers/payout/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,11 @@ export const calculateIssueAssigneeReward = async (incentivesCalculation: Incent
incentivesCalculation.multiplier
);
if (priceInEth.gt(incentivesCalculation.permitMaxPrice)) {
logger.info("Skipping to proceed the payment because bounty payout is higher than permitMaxPrice.");
return { error: `Permit generation disabled because issue's bounty is higher than ${incentivesCalculation.permitMaxPrice}` };
logger.info("Skipping to proceed the payment because task payout is higher than permitMaxPrice.");
return { error: `Permit generation disabled because issue's task is higher than ${incentivesCalculation.permitMaxPrice}` };
}

// if bounty hunter has any penalty then deduct it from the bounty
// if contributor has any penalty then deduct it from the task
const penaltyAmount = await getPenalty(
assigneeLogin,
incentivesCalculation.payload.repository.full_name,
Expand All @@ -283,7 +283,7 @@ export const calculateIssueAssigneeReward = async (incentivesCalculation: Incent
incentivesCalculation.evmNetworkId.toString(),
taskAmount
);
const msg = `Permit generation disabled because bounty amount after penalty is 0.`;
const msg = `Permit generation disabled because task amount after penalty is 0.`;
logger.info(msg);
return { error: msg };
}
Expand Down Expand Up @@ -337,8 +337,8 @@ export const handleIssueClosed = async (
incentivesCalculation.multiplier
);
if (priceInEth.gt(incentivesCalculation.permitMaxPrice)) {
logger.info("Skipping to proceed the payment because bounty payout is higher than permitMaxPrice");
return { error: `Permit generation skipped since issue's bounty is higher than ${incentivesCalculation.permitMaxPrice}` };
logger.info("Skipping to proceed the payment because task payout is higher than permitMaxPrice");
return { error: `Permit generation skipped since issue's task is higher than ${incentivesCalculation.permitMaxPrice}` };
}

// COMMENTERS REWARD HANDLER
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/processors.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GithubEvent, Handler, ActionHandler } from "../types";
import { closePullRequestForAnIssue, commentWithAssignMessage } from "./assign";
import { pricingLabelLogic, validatePriceLabels } from "./pricing";
import { checkBountiesToUnassign, collectAnalytics, checkWeeklyUpdate } from "./wildcard";
import { checkTasksToUnassign, collectAnalytics, checkWeeklyUpdate } from "./wildcard";
import { nullHandler } from "./shared";
import { handleComment, issueClosedCallback, issueCreatedCallback, issueReopenedCallback } from "./comment";
import { checkPullRequests } from "./assign/auto";
Expand Down Expand Up @@ -75,4 +75,4 @@ export const processors: Record<string, Handler> = {
/**
* @dev The handlers which will run on every event hooked
*/
export const wildcardProcessors: ActionHandler[] = [checkBountiesToUnassign, collectAnalytics, checkWeeklyUpdate];
export const wildcardProcessors: ActionHandler[] = [checkTasksToUnassign, collectAnalytics, checkWeeklyUpdate];
2 changes: 1 addition & 1 deletion src/handlers/shared/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "./handler";
export * from "./pricing";

export const deadLinePrefix = "The time limit for this bounty is on";
export const deadLinePrefix = "The time limit for this task is on";
16 changes: 8 additions & 8 deletions src/handlers/wildcard/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Issue, IssueType, User, UserProfile } from "../../types";
import { getTargetPriceLabel } from "../shared";

/**
* Checks the issue whether it's a bounty for hunters or an issue for not
* Checks the issue whether it's a task for hunters or an issue for not
* @param issue - The issue object
* @returns If bounty - true, If issue - false
* @returns If task - true, If issue - false
*/
export const taskInfo = (
issue: Issue
Expand Down Expand Up @@ -61,10 +61,10 @@ export const collectAnalytics = async (): Promise<void> => {
// need to skip checking the closed issues already stored in the db and filter them by doing a sanitation checks.
// sanitation checks would be basically checking labels of the issue
// whether the issue has both `priority` label and `timeline` label
const bounties = issues.filter((issue) => taskInfo(issue as Issue).isTask);
const tasks = issues.filter((issue) => taskInfo(issue as Issue).isTask);

// collect assignees from both type of issues (opened/closed)
const assignees = bounties.filter((task) => task.assignee).map((task) => task.assignee as User);
const assignees = tasks.filter((task) => task.assignee).map((task) => task.assignee as User);

// remove duplication by assignee
const tmp = assignees.map((i) => i.login);
Expand All @@ -85,11 +85,11 @@ export const collectAnalytics = async (): Promise<void> => {

await Promise.all(userProfilesToUpsert.map((i) => upsertUser(i)));

// No need to update the record for the bounties already closed
const bountiesToUpsert = bounties.filter((task) => (task.state === IssueType.CLOSED ? task.number > maximumIssueNumber : true));
logger.info(`Upserting bounties: ${bountiesToUpsert.map((i) => i.title).toString()}`);
// No need to update the record for the tasks already closed
const tasksToUpsert = tasks.filter((task) => (task.state === IssueType.CLOSED ? task.number > maximumIssueNumber : true));
logger.info(`Upserting tasks: ${tasksToUpsert.map((i) => i.title).toString()}`);
await Promise.all(
bountiesToUpsert.map((i) => {
tasksToUpsert.map((i) => {
const additions = taskInfo(i as Issue);
if (additions.timelabel && additions.priorityLabel && additions.priceLabel)
return upsertIssue(i as Issue, {
Expand Down
14 changes: 7 additions & 7 deletions src/handlers/wildcard/unassign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import { Comment, Issue, IssueType, Payload, UserType } from "../../types";
import { deadLinePrefix } from "../shared";

/**
* @dev Check out the bounties which haven't been completed within the initial timeline
* and try to release the bounty back to dev pool
* @dev Check out the tasks which haven't been completed within the initial timeline
* and try to release the task back to dev pool
*/
export const checkBountiesToUnassign = async () => {
export const checkTasksToUnassign = async () => {
const logger = getLogger();
logger.info(`Getting all the issues...`);

Expand All @@ -26,9 +26,9 @@ export const checkBountiesToUnassign = async () => {

const assigned_issues = issues_opened.filter((issue) => issue.assignee);

// Checking the bounties in parallel
// Checking the tasks in parallel
const res = await Promise.all(assigned_issues.map(async (issue) => checkTaskToUnassign(issue as Issue)));
logger.info(`Checking expired bounties done! total: ${res.length}, unassigned: ${res.filter((i) => i).length}`);
logger.info(`Checking expired tasks done! total: ${res.length}, unassigned: ${res.filter((i) => i).length}`);
};

const checkTaskToUnassign = async (issue: Issue): Promise<boolean> => {
Expand All @@ -38,7 +38,7 @@ const checkTaskToUnassign = async (issue: Issue): Promise<boolean> => {
const {
unassign: { followUpTime, disqualifyTime },
} = getBotConfig();
logger.info(`Checking the bounty to unassign, issue_number: ${issue.number}`);
logger.info(`Checking the task to unassign, issue_number: ${issue.number}`);
const { unassignComment, askUpdate } = GLOBAL_STRINGS;
const assignees = issue.assignees.map((i) => i.login);
const comments = await getAllIssueComments(issue.number);
Expand Down Expand Up @@ -82,7 +82,7 @@ const checkTaskToUnassign = async (issue: Issue): Promise<boolean> => {
);
} else {
await addCommentToIssue(
`${askUpdate} @${assignees[0]}? If you would like to release the bounty back to the DevPool, please comment \`/stop\` \nLast activity time: ${lastActivity}`,
`${askUpdate} @${assignees[0]}? If you would like to release the task back to the DevPool, please comment \`/stop\` \nLast activity time: ${lastActivity}`,
issue.number
);
}
Expand Down
6 changes: 3 additions & 3 deletions src/handlers/wildcard/weekly/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const processEvents = (JSONList: any[]): SummaryType => {
let openedIssues = 0;
let closedIssues = 0;
let comments = 0;
let bountiesUSD = 0;
let tasksUSD = 0;
let openedPRs = 0;
let closedPRs = 0;
let mergedPRs = 0;
Expand All @@ -110,7 +110,7 @@ const processEvents = (JSONList: any[]): SummaryType => {
.match(/\b\d+\b/)
.join("")
);
bountiesUSD += taskUSD;
tasksUSD += taskUSD;
}
});
break;
Expand Down Expand Up @@ -156,7 +156,7 @@ const processEvents = (JSONList: any[]): SummaryType => {
`<code>new issues: ${openedIssues}</code>\n` +
`<code>issues resolved: ${closedIssues}</code>\n` +
`<code>total user interactions count: ${comments}</code>\n` +
`<code>bounties given: ${bountiesUSD} USD</code>\n` +
`<code>tasks given: ${tasksUSD} USD</code>\n` +
`<code>new pulls: ${openedPRs}</code>\n` +
`<code>closed pulls: ${closedPRs}</code>\n` +
`<code>merged pulls: ${mergedPRs}</code>\n` +
Expand Down
Loading

0 comments on commit 840620d

Please sign in to comment.