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

Default labels #506

Merged
merged 6 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/ubiquibot-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ priority-labels:
weight: 4
- name: "Priority: 4 (Emergency)"
weight: 5
default-labels:
- "Time: <1 Hour"
- "Priority: 0 (Normal)"
- "Test"
auto-pay-mode: true
analytics-mode: true
max-concurrent-bounties: 2
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,37 @@ To test the bot, you can:
2. Add a time label, ex: `Time: <1 Day`
3. At this point the bot should add a price label.

## Configuration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You did a great job here thanks.


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

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

`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

`default-labels` are labels that are applied when an issue is created without any time or priority labels.

`auto-pay-mode` can be `true` or `false` that enables or disables automatic payout of bounties when the issue is closed.

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

`incentive-mode` can be `true` or `false` that enables or disables comment incentives. These are comments in the issue by either the creator of the bounty or other users.

`issue-creator-multiplier` is a number that defines a base multiplier for calculating incentive reward for the creator of the issue.

`comment-element-pricing` defines how much is a part of the comment worth. For example `text: 0.1` means that any text in the comment will be multiplied by 0.1

`max-concurrent-bounties` is the maximum number of bounties that can be assigned to a bounty hunter at once. This excludes bounties with pending pull request reviews.

## How to run locally

1. Create a new project at [Supabase](https://supabase.com/). Add `Project URL` and `API Key` to the `.env` file:
Expand Down
2 changes: 2 additions & 0 deletions src/bindings/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const loadConfig = async (context: Context): Promise<BotConfig> => {
incentiveMode,
networkId,
issueCreatorMultiplier,
defaultLabels,
promotionComment,
} = await getWideConfig(context);

Expand All @@ -37,6 +38,7 @@ export const loadConfig = async (context: Context): Promise<BotConfig> => {
timeLabels,
priorityLabels,
commentElementPricing,
defaultLabels,
},
comments: {
promotionComment: promotionComment,
Expand Down
1 change: 1 addition & 0 deletions src/configs/price.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,5 @@ export const DefaultPriceConfig: PriceConfig = {
[MarkdownItem.Code]: 5,
[MarkdownItem.Image]: 5,
},
defaultLabels: [],
};
16 changes: 8 additions & 8 deletions src/handlers/comment/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { unassign } from "./unassign";
import { registerWallet } from "./wallet";
import { setAccess } from "./set-access";
import { multiplier } from "./multiplier";
import { addCommentToIssue, createLabel, addLabelToIssue } from "../../../helpers";
import { addCommentToIssue, createLabel, addLabelToIssue, getLabel } from "../../../helpers";
import { getBotContext } from "../../../bindings";
import { handleIssueClosed } from "../../payout";

Expand Down Expand Up @@ -63,16 +63,16 @@ export const issueCreatedCallback = async (): Promise<void> => {
if (!issue) return;
const labels = issue.labels;
try {
const timeLabelConfigs = config.price.timeLabels.sort((label1, label2) => label1.weight - label2.weight);
const priorityLabelConfigs = config.price.priorityLabels.sort((label1, label2) => label1.weight - label2.weight);
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));

if (timeLabels.length === 0 && timeLabelConfigs.length > 0) await createLabel(timeLabelConfigs[0].name);
if (priorityLabels.length === 0 && priorityLabelConfigs.length > 0) await createLabel(priorityLabelConfigs[0].name);
await addLabelToIssue(timeLabelConfigs[0].name);
await addLabelToIssue(priorityLabelConfigs[0].name);
return;
if (timeLabels.length === 0 && priorityLabels.length === 0) {
for (const label of config.price.defaultLabels) {
const exists = await getLabel(label);
if (!exists) await createLabel(label);
await addLabelToIssue(label);
}
}
} catch (err: unknown) {
return await addCommentToIssue(`Error: ${err}`, issue.number);
}
Expand Down
1 change: 1 addition & 0 deletions src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const PriceConfigSchema = Type.Object({
timeLabels: Type.Array(LabelItemSchema),
priorityLabels: Type.Array(LabelItemSchema),
commentElementPricing: CommentElementPricingSchema,
defaultLabels: Type.Array(Type.String()),
});
export type PriceConfig = Static<typeof PriceConfigSchema>;

Expand Down
10 changes: 10 additions & 0 deletions src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,13 @@ export const getBountyHunterMax = (parsedRepo: WideRepoConfig | undefined, parse
return 2;
}
};

export const getDefaultLabels = (parsedRepo: WideRepoConfig | undefined, parsedOrg: WideOrgConfig | undefined): string[] => {
if (parsedRepo && parsedRepo["default-labels"]) {
return parsedRepo["default-labels"];
} else if (parsedOrg && parsedOrg["default-labels"]) {
return parsedOrg["default-labels"];
} else {
return [];
}
};
3 changes: 3 additions & 0 deletions src/utils/private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getPriorityLabels,
getTimeLabels,
getCommentItemPrice,
getDefaultLabels,
getPromotionComment,
} from "./helpers";

Expand Down Expand Up @@ -59,6 +60,7 @@ export interface WideConfig {
"incentive-mode"?: boolean;
"max-concurrent-bounties"?: number;
"comment-element-pricing"?: Record<string, number>;
"default-labels"?: string[];
}

export type WideRepoConfig = WideConfig;
Expand Down Expand Up @@ -139,6 +141,7 @@ export const getWideConfig = async (context: Context) => {
bountyHunterMax: getBountyHunterMax(parsedRepo, parsedOrg),
incentiveMode: getIncentiveMode(parsedRepo, parsedOrg),
commentElementPricing: getCommentItemPrice(parsedRepo, parsedOrg),
defaultLabels: getDefaultLabels(parsedRepo, parsedOrg),
promotionComment: getPromotionComment(parsedRepo, parsedOrg),
};

Expand Down