Skip to content
This repository has been archived by the owner on Sep 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #627 from byteballet/automatically_derive_weight
Browse files Browse the repository at this point in the history
fix: derive weight automatically
  • Loading branch information
whilefoo authored Aug 26, 2023
2 parents 0c57588 + 3e40c9c commit fb1599b
Show file tree
Hide file tree
Showing 14 changed files with 66 additions and 73 deletions.
27 changes: 6 additions & 21 deletions .github/ubiquibot-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,19 @@ price-multiplier: 1.5
issue-creator-multiplier: 2
time-labels:
- name: "Time: <1 Hour"
weight: 0.125
value: 3600
- name: "Time: <2 Hours"
weight: 0.25
value: 7200
- name: "Time: <4 Hours"
weight: 0.5
value: 14400
- name: "Time: <1 Day"
weight: 1
value: 86400
- name: "Time: <1 Week"
weight: 2
value: 604800
priority-labels:
- name: "Priority: 0 (Normal)"
weight: 1
- name: "Priority: 1 (Medium)"
weight: 2
- name: "Priority: 2 (High)"
weight: 3
- name: "Priority: 3 (Urgent)"
weight: 4
- name: "Priority: 4 (Emergency)"
weight: 5
- name: "Priority: 1 (Normal)"
- name: "Priority: 2 (Medium)"
- name: "Priority: 3 (High)"
- name: "Priority: 4 (Urgent)"
- name: "Priority: 5 (Emergency)"
default-labels:
- "Time: <1 Hour"
- "Priority: 0 (Normal)"
- "Priority: 1 (Normal)"
payment-permit-max-price: 1000
comment-incentives: true
max-concurrent-bounties: 2
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,11 @@ To test the bot, you can:
`time-labels` are labels for marking the time limit of the bounty:

- `name` is a human-readable name
- `weight` is a number that will be used to calculate the bounty price
- `value` is number of seconds that corresponds to the time limit of the bounty

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

- `name` is a human-readable name
- `weight` is a number that will be used to calculate the bounty price

`command-settings` are setting to enable or disable a command

Expand Down
6 changes: 3 additions & 3 deletions src/handlers/assign/action.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getBotConfig, getBotContext, getLogger } from "../../bindings";
import { addCommentToIssue, closePullRequest, getOpenedPullRequestsForAnIssue } from "../../helpers";
import { addCommentToIssue, closePullRequest, getOpenedPullRequestsForAnIssue, calculateWeight, calculateDuration } from "../../helpers";
import { Payload, LabelItem } from "../../types";
import { deadLinePrefix } from "../shared";

Expand Down Expand Up @@ -49,9 +49,9 @@ export const commentWithAssignMessage = async (): Promise<void> => {
return;
}

const sorted = timeLabelsAssigned.sort((a, b) => a.weight - b.weight);
const sorted = timeLabelsAssigned.sort((a, b) => calculateWeight(a) - calculateWeight(b));
const targetTimeLabel = sorted[0];
const duration = targetTimeLabel.value;
const duration = calculateDuration(targetTimeLabel);
if (!duration) {
logger.debug(`Missing configure for timelabel: ${targetTimeLabel.name}`);
return;
Expand Down
6 changes: 3 additions & 3 deletions src/handlers/comment/handlers/assign.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addAssignees, getAssignedIssues, getAvailableOpenedPullRequests, getAllIssueComments } from "../../../helpers";
import { addAssignees, getAssignedIssues, getAvailableOpenedPullRequests, getAllIssueComments, calculateWeight, calculateDuration } from "../../../helpers";
import { getBotConfig, getBotContext, getLogger } from "../../../bindings";
import { Payload, LabelItem, Comment, IssueType, Issue } from "../../../types";
import { deadLinePrefix } from "../../shared";
Expand Down Expand Up @@ -81,9 +81,9 @@ export const assign = async (body: string) => {
return "Skipping `/start` since no time labels are set to calculate the timeline";
}

const sorted = timeLabelsAssigned.sort((a, b) => a.weight - b.weight);
const sorted = timeLabelsAssigned.sort((a, b) => calculateWeight(a) - calculateWeight(b));
const targetTimeLabel = sorted[0];
const duration = targetTimeLabel.value;
const duration = calculateDuration(targetTimeLabel);
if (!duration) {
logger.info(`Missing configure for time label: ${targetTimeLabel.name}`);
return "Skipping `/start` since configuration is missing for the following labels";
Expand Down
7 changes: 5 additions & 2 deletions src/handlers/comment/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getPayoutConfigByNetworkId,
getTokenSymbol,
getAllIssueAssignEvents,
calculateWeight,
} from "../../../helpers";
import { getBotConfig, getBotContext, getLogger } from "../../../bindings";
import { handleIssueClosed } from "../../payout";
Expand Down Expand Up @@ -97,8 +98,10 @@ export const issueCreatedCallback = async (): Promise<void> => {
const timeLabels = config.price.timeLabels.filter((item) => labels.map((i) => i.name).includes(item.name));
const priorityLabels = config.price.priorityLabels.filter((item) => labels.map((i) => i.name).includes(item.name));

const minTimeLabel = timeLabels.length > 0 ? timeLabels.reduce((a, b) => (a.weight < b.weight ? a : b)).name : config.price.defaultLabels[0];
const minPriorityLabel = priorityLabels.length > 0 ? priorityLabels.reduce((a, b) => (a.weight < b.weight ? a : b)).name : config.price.defaultLabels[1];
const minTimeLabel =
timeLabels.length > 0 ? timeLabels.reduce((a, b) => (calculateWeight(a) < calculateWeight(b) ? a : b)).name : config.price.defaultLabels[0];
const minPriorityLabel =
priorityLabels.length > 0 ? priorityLabels.reduce((a, b) => (calculateWeight(a) < calculateWeight(b) ? a : b)).name : config.price.defaultLabels[1];
if (!timeLabels.length) await addLabelToIssue(minTimeLabel);
if (!priorityLabels.length) await addLabelToIssue(minPriorityLabel);

Expand Down
6 changes: 3 additions & 3 deletions src/handlers/pricing/action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getBotConfig, getBotContext, getLogger } from "../../bindings";
import { GLOBAL_STRINGS } from "../../configs";
import { addCommentToIssue, addLabelToIssue, clearAllPriceLabelsOnIssue, createLabel, getLabel } from "../../helpers";
import { addCommentToIssue, addLabelToIssue, clearAllPriceLabelsOnIssue, createLabel, getLabel, calculateWeight } from "../../helpers";
import { Payload } from "../../types";
import { handleLabelsAccess } from "../access";
import { getTargetPriceLabel } from "../shared";
Expand Down Expand Up @@ -33,8 +33,8 @@ export const pricingLabelLogic = async (): Promise<void> => {
const timeLabels = config.price.timeLabels.filter((item) => labels.map((i) => i.name).includes(item.name));
const priorityLabels = config.price.priorityLabels.filter((item) => labels.map((i) => i.name).includes(item.name));

const minTimeLabel = timeLabels.length > 0 ? timeLabels.reduce((a, b) => (a.weight < b.weight ? a : b)).name : undefined;
const minPriorityLabel = priorityLabels.length > 0 ? priorityLabels.reduce((a, b) => (a.weight < b.weight ? a : b)).name : undefined;
const minTimeLabel = timeLabels.length > 0 ? timeLabels.reduce((a, b) => (calculateWeight(a) < calculateWeight(b) ? a : b)).name : undefined;
const minPriorityLabel = priorityLabels.length > 0 ? priorityLabels.reduce((a, b) => (calculateWeight(a) < calculateWeight(b) ? a : b)).name : undefined;

const targetPriceLabel = getTargetPriceLabel(minTimeLabel, minPriorityLabel);
if (targetPriceLabel) {
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/pricing/pre.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getBotConfig, getLogger } from "../../bindings";
import { createLabel, listLabelsForRepo } from "../../helpers";
import { calculateWeight, createLabel, listLabelsForRepo } from "../../helpers";
import { calculateBountyPrice } from "../shared";

/**
Expand All @@ -22,7 +22,7 @@ export const validatePriceLabels = async (): Promise<void> => {
const aiLabels: string[] = [];
for (const timeLabel of config.price.timeLabels) {
for (const priorityLabel of config.price.priorityLabels) {
const targetPrice = calculateBountyPrice(timeLabel.weight, priorityLabel.weight, config.price.baseMultiplier);
const targetPrice = calculateBountyPrice(calculateWeight(timeLabel), calculateWeight(priorityLabel), config.price.baseMultiplier);
const targetPriceLabel = `Price: ${targetPrice} USD`;
aiLabels.push(targetPriceLabel);
}
Expand Down
5 changes: 3 additions & 2 deletions src/handlers/shared/pricing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getBotConfig } from "../../bindings";
import { calculateWeight } from "../../helpers";

export const calculateBountyPrice = (timeValue: number, priorityValue: number, baseValue?: number): number => {
const botConfig = getBotConfig();
Expand All @@ -12,8 +13,8 @@ export const getTargetPriceLabel = (timeLabel: string | undefined, priorityLabel
const botConfig = getBotConfig();
let targetPriceLabel: string | undefined = undefined;
if (timeLabel && priorityLabel) {
const timeWeight = botConfig.price.timeLabels.find((item) => item.name === timeLabel)?.weight;
const priorityWeight = botConfig.price.priorityLabels.find((item) => item.name === priorityLabel)?.weight;
const timeWeight = calculateWeight(botConfig.price.timeLabels.find((item) => item.name === timeLabel));
const priorityWeight = calculateWeight(botConfig.price.priorityLabels.find((item) => item.name === priorityLabel));
if (timeWeight && priorityWeight) {
const bountyPrice = calculateBountyPrice(timeWeight, priorityWeight);
targetPriceLabel = `Price: ${bountyPrice} USD`;
Expand Down
6 changes: 3 additions & 3 deletions src/handlers/wildcard/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getMaxIssueNumber, upsertIssue, upsertUser } from "../../adapters/supabase";
import { getBotConfig, getLogger } from "../../bindings";
import { listIssuesForRepo, getUser } from "../../helpers";
import { listIssuesForRepo, getUser, calculateWeight } from "../../helpers";
import { Issue, IssueType, User, UserProfile } from "../../types";
import { getTargetPriceLabel } from "../shared";

Expand All @@ -24,8 +24,8 @@ export const bountyInfo = (

const isBounty = timeLabels.length > 0 && priorityLabels.length > 0;

const minTimeLabel = timeLabels.length > 0 ? timeLabels.reduce((a, b) => (a.weight < b.weight ? a : b)).name : undefined;
const minPriorityLabel = priorityLabels.length > 0 ? priorityLabels.reduce((a, b) => (a.weight < b.weight ? a : b)).name : undefined;
const minTimeLabel = timeLabels.length > 0 ? timeLabels.reduce((a, b) => (calculateWeight(a) < calculateWeight(b) ? a : b)).name : undefined;
const minPriorityLabel = priorityLabels.length > 0 ? priorityLabels.reduce((a, b) => (calculateWeight(a) < calculateWeight(b) ? a : b)).name : undefined;

const priceLabel = getTargetPriceLabel(minTimeLabel, minPriorityLabel);

Expand Down
5 changes: 3 additions & 2 deletions src/helpers/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { COLORS } from "../configs";
import { calculateBountyPrice } from "../handlers";
import { Label, Payload } from "../types";
import { deleteLabel } from "./issue";
import { calculateWeight } from "../helpers";

export const listLabelsForRepo = async (per_page?: number, page?: number): Promise<Label[]> => {
const context = getBotContext();
Expand Down Expand Up @@ -67,11 +68,11 @@ export const updateLabelsFromBaseRate = async (owner: string, repo: string, cont

for (const timeLabel of config.price.timeLabels) {
for (const priorityLabel of config.price.priorityLabels) {
const targetPrice = calculateBountyPrice(timeLabel.weight, priorityLabel.weight, config.price.baseMultiplier);
const targetPrice = calculateBountyPrice(calculateWeight(timeLabel), calculateWeight(priorityLabel), config.price.baseMultiplier);
const targetPriceLabel = `Price: ${targetPrice} USD`;
newLabels.push(targetPriceLabel);

const previousTargetPrice = calculateBountyPrice(timeLabel.weight, priorityLabel.weight, previousBaseRate);
const previousTargetPrice = calculateBountyPrice(calculateWeight(timeLabel), calculateWeight(priorityLabel), previousBaseRate);
const previousTargetPriceLabel = `Price: ${previousTargetPrice} USD`;
previousLabels.push(previousTargetPriceLabel);
}
Expand Down
26 changes: 25 additions & 1 deletion src/helpers/shared.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getBotContext } from "../bindings";
import { Payload, UserType } from "../types";
import { LabelItem, Payload, UserType } from "../types";

const contextNamesToSkip = ["workflow_run"];

Expand All @@ -19,3 +19,27 @@ export const shouldSkip = (): { skip: boolean; reason: string } => {
};

export const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));

export const calculateWeight = (label: LabelItem | undefined): number => {
if (!label) return 0;
const matches = label.name.match(/\d+/);
const number = matches && matches.length > 0 ? parseInt(matches[0]) || 0 : 0;
if (label.name.toLowerCase().includes("priority")) return number;
if (label.name.toLowerCase().includes("hour")) return number * 0.125;
if (label.name.toLowerCase().includes("day")) return 1 + (number - 1) * 0.25;
if (label.name.toLowerCase().includes("week")) return number + 1;
if (label.name.toLowerCase().includes("month")) return 5 + (number - 1) * 8;
return 0;
};

export const calculateDuration = (label: LabelItem): number => {
if (!label) return 0;
const matches = label.name.match(/\d+/);
if (label.name.toLowerCase().includes("priority")) return 0;
const number = matches && matches.length > 0 ? parseInt(matches[0]) || 0 : 0;
if (label.name.toLowerCase().includes("hour")) return number * 3600;
if (label.name.toLowerCase().includes("day")) return number * 86400;
if (label.name.toLowerCase().includes("week")) return number * 604800;
if (label.name.toLowerCase().includes("month")) return number * 2592000;
return 0;
};
2 changes: 0 additions & 2 deletions src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { Level } from "../adapters/supabase";

const LabelItemSchema = Type.Object({
name: Type.String(),
weight: Type.Number(),
value: Type.Optional(Type.Number()),
});
export type LabelItem = Static<typeof LabelItemSchema>;

Expand Down
2 changes: 0 additions & 2 deletions src/utils/private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ export const getConfigSuperset = async (context: Context, type: "org" | "repo",

export interface WideLabel {
name: string;
weight: number;
value?: number | undefined;
}

export interface CommentIncentives {
Expand Down
35 changes: 10 additions & 25 deletions ubiquibot-config-default.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,36 @@
"default-labels": [],
"time-labels": [
{
"name": "Time: <1 Hour",
"weight": 0.125,
"value": 3600
"name": "Time: <1 Hour"
},
{
"name": "Time: <1 Day",
"weight": 1,
"value": 86400
"name": "Time: <1 Day"
},
{
"name": "Time: <1 Week",
"weight": 2,
"value": 604800
"name": "Time: <1 Week"
},
{
"name": "Time: <2 Weeks",
"weight": 3,
"value": 1209600
"name": "Time: <2 Weeks"
},
{
"name": "Time: <1 Month",
"weight": 4,
"value": 2592000
"name": "Time: <1 Month"
}
],
"priority-labels": [
{
"name": "Priority: 0 (Normal)",
"weight": 1
"name": "Priority: 1 (Normal)"
},
{
"name": "Priority: 1 (Medium)",
"weight": 2
"name": "Priority: 2 (Medium)"
},
{
"name": "Priority: 2 (High)",
"weight": 3
"name": "Priority: 3 (High)"
},
{
"name": "Priority: 3 (Urgent)",
"weight": 4
"name": "Priority: 4 (Urgent)"
},
{
"name": "Priority: 4 (Emergency)",
"weight": 5
"name": "Priority: 5 (Emergency)"
}
],
"command-settings": [
Expand Down

0 comments on commit fb1599b

Please sign in to comment.