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

September 2023 Update Adjustments #818

Open
wants to merge 9 commits into
base: development
Choose a base branch
from
16 changes: 0 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,22 +224,6 @@ Bounty bot is built using the [probot](https://probot.github.io/) framework so i
├── <a href="https://github.com/ubiquity/ubiquibot/tree/development/src/utils">utils</a> A set of utility functions
</pre>

## Default Config Notes (`ubiquibot-config-default.ts`)

We can't use a `jsonc` file due to limitations with Netlify. Here is a snippet of some values with notes next to them.

```jsonc
{
"payment-permit-max-price": 9007199254740991, // Number.MAX_SAFE_INTEGER
"max-concurrent-assigns": 9007199254740991, // Number.MAX_SAFE_INTEGER
"comment-element-pricing": {
/* https://github.com/syntax-tree/mdast#nodes */
"strong": 0 // Also includes italics, unfortunately https://github.com/syntax-tree/mdast#strong
/* https://github.com/syntax-tree/mdast#gfm */
}
}
```

## Supabase Cron Job (`logs-cleaner`)

##### Dashboard > Project > Database > Extensions
Expand Down
30 changes: 27 additions & 3 deletions src/configs/ubiquibot-config-default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export const DefaultConfig: MergedConfig = {
evmNetworkId: 100,
priceMultiplier: 1,
issueCreatorMultiplier: 2,
paymentPermitMaxPrice: 9007199254740991,
maxConcurrentAssigns: 9007199254740991,
paymentPermitMaxPrice: Number.MAX_SAFE_INTEGER,
maxConcurrentAssigns: Number.MAX_SAFE_INTEGER,
assistivePricing: false,
disableAnalytics: false,
commentIncentives: false,
Expand Down Expand Up @@ -87,7 +87,31 @@ export const DefaultConfig: MergedConfig = {
],
incentives: {
comment: {
elements: {},
elements: {
h1: 0,
h2: 0,
h3: 0,
h4: 0,
h5: 0,
h6: 0,
a: 0,
ul: 0,
li: 0,
p: 0,
img: 0,
code: 0,
table: 0,
td: 0,
tr: 0,
br: 0,
blockquote: 0,
em: 0,
strong: 0,
hr: 0,
del: 0,
pre: 0,
ol: 0,
},
totals: {
word: 0,
},
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/comment/handlers/ask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const ask = async (body: string) => {
}

if (!issue) {
return `This command can only be used on issues`;
return `This command can only be used on issues on pull requests`;
whilefoo marked this conversation as resolved.
Show resolved Hide resolved
}

const chatHistory: CreateChatCompletionRequestMessage[] = [];
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 @@ -288,7 +288,7 @@ export const userCommands = (): UserCommands[] => {
},
{
id: IssueCommentCommands.ASK,
description: `Ask a technical question to the Ubiquity AI. \n example usage: "/ask How do I do X?"`,
description: `Ask a technical question to the UbiquiBot. \n example usage: "/ask How do I do X?"`,
whilefoo marked this conversation as resolved.
Show resolved Hide resolved
handler: ask,
callback: commandCallback,
},
Expand Down
7 changes: 5 additions & 2 deletions src/handlers/comment/handlers/payout.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getBotContext, getLogger } from "../../../bindings";
import { getBotConfig, getBotContext, getLogger } from "../../../bindings";
import { Payload } from "../../../types";
import { IssueCommentCommands } from "../commands";
import {
Expand All @@ -14,6 +14,9 @@ import { GLOBAL_STRINGS } from "../../../configs";

export const payout = async (body: string) => {
const { payload: _payload } = getBotContext();
const {
payout: { permitBaseUrl },
} = getBotConfig();
const logger = getLogger();
if (body != IssueCommentCommands.PAYOUT && body.replace(/`/g, "") != IssueCommentCommands.PAYOUT) {
logger.info(`Skipping to payout. body: ${body}`);
Expand All @@ -39,7 +42,7 @@ export const payout = async (body: string) => {
return `Permit generation failed due to internal GitHub Error`;
}

const hasPosted = IssueComments.find((e) => e.user.type === "Bot" && e.body.includes("https://pay.ubq.fi?claim"));
const hasPosted = IssueComments.find((e) => e.user.type === "Bot" && e.body.includes(permitBaseUrl));
if (hasPosted) {
logger.info(`Permit already generated for ${payload.issue?.number}`);
return;
Expand Down
64 changes: 46 additions & 18 deletions src/handlers/comment/handlers/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,51 @@ export const tableComment = ({
days?: number;
}) => {
return `
<code>
<table>
${
isBountyStale
? `<tr><td>Warning!</td> <td>This task was created over ${days} days ago. Please confirm that this issue specification is accurate before starting.</td></tr>`
: ``
}
<tr>
<td>Deadline</td>
<td>${deadline}</td>
</tr>
<tr>
<td>Registered Wallet</td>
<td>${wallet}</td>
</tr>
${multiplier ? `<tr><td>Payment Multiplier</td><td>${multiplier}</td></tr>` : ``}
${reason ? `<tr><td>Multiplier Reason</td><td>${reason}</td></tr>` : ``}
${bounty ? `<tr><td>Total Bounty</td><td>${bounty}</td></tr>` : ``}
</table></code>`;
${
isBountyStale
? `
<tr>
<td>Warning!</td>
<td>This task was created over ${days} days ago. Please confirm that this issue specification is accurate before starting.</td>
</tr>`
: ``
}
<tr>
<td>Deadline</td>
<td>${deadline}</td>
</tr>
<tr>
<td>Registered Wallet</td>
<td>${wallet}</td>
</tr>
${
multiplier
? `
<tr>
<td>Payment Multiplier</td>
<td>${multiplier}</td>
</tr>`
: ``
}
${
reason
? `
<tr>
<td>Multiplier Reason</td>
<td>${reason}</td>
</tr>`
: ``
}
${
bounty
? `
<tr>
<td>Total Bounty</td>
<td>${bounty}</td>
</tr>`
: ``
}
</table>
`;
};
4 changes: 2 additions & 2 deletions src/handlers/payout/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ export const incentivesCalculation = async (): Promise<IncentivesCalculationResu
const comments = await getAllIssueComments(issue.number);

const wasReopened = await wasIssueReopened(issue.number);
const claimUrlRegex = new RegExp(`\\((${permitBaseUrl}\\?claim=\\S+)\\)`);
const permitCommentIdx = comments.findIndex((e) => e.user.type === UserType.Bot && e.body.match(claimUrlRegex));
const claimUrlRegex = new RegExp(`\\((${permitBaseUrl}\\S+)\\)`);
const permitCommentIdx = comments.findIndex((e) => e.user.type === UserType.Bot && e.body.match(claimUrlRegex) && e.body.includes("Task Assignee Reward"));
Copy link
Member

Choose a reason for hiding this comment

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

Maybe it makes sense to call this Assignee Reward because task is implicit

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We shouldn't change it because it will break backwards compatibility. We need to implement comment metadata first and then we can change format


if (wasReopened && permitCommentIdx !== -1) {
const permitComment = comments[permitCommentIdx];
Expand Down
45 changes: 27 additions & 18 deletions src/handlers/payout/post.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getWalletAddress } from "../../adapters/supabase";
import { getBotContext, getLogger } from "../../bindings";
import { getBotConfig, getBotContext, getLogger } from "../../bindings";
import { getAllIssueComments, getAllPullRequestReviews, getIssueDescription, parseComments } from "../../helpers";
import { getLatestPullRequest, gitLinkedPrParser } from "../../helpers/parser";
import { Incentives, MarkdownItem, Payload, UserType } from "../../types";
Expand All @@ -12,7 +12,7 @@ import { BigNumber } from "ethers";
export interface CreatorCommentResult {
title: string;
account?: string | undefined;
amountInETH?: Decimal | undefined;
rewardInTokens?: Decimal | undefined;
userId?: string | undefined;
tokenSymbol?: string | undefined;
node_id?: string | undefined;
Expand All @@ -29,11 +29,14 @@ export const calculateIssueConversationReward = async (calculateIncentives: Ince
const logger = getLogger();

const context = getBotContext();
const {
payout: { permitBaseUrl },
} = getBotConfig();
const payload = context.payload as Payload;
const issue = payload.issue;

const permitComments = calculateIncentives.comments.filter(
(content) => content.body.includes(title) && content.body.includes("https://pay.ubq.fi?claim=") && content.user.type == UserType.Bot
(content) => content.body.includes(title) && content.body.includes(permitBaseUrl) && content.user.type == UserType.Bot
);
if (permitComments.length > 0) {
logger.info(`incentivizeComments: skip to generate a permit url because it has been already posted`);
Expand Down Expand Up @@ -105,6 +108,9 @@ export const calculateIssueConversationReward = async (calculateIncentives: Ince
export const calculateIssueCreatorReward = async (incentivesCalculation: IncentivesCalculationResult): Promise<RewardsResponse> => {
const title = `Task Creator`;
const logger = getLogger();
const {
payout: { permitBaseUrl },
} = getBotConfig();

const issueDetailed = bountyInfo(incentivesCalculation.issue);
if (!issueDetailed.isBounty) {
Expand All @@ -114,7 +120,7 @@ export const calculateIssueCreatorReward = async (incentivesCalculation: Incenti

const comments = await getAllIssueComments(incentivesCalculation.issue.number);
const permitComments = comments.filter(
(content) => content.body.includes(title) && content.body.includes("https://pay.ubq.fi?claim=") && content.user.type == UserType.Bot
(content) => content.body.includes(title) && content.body.includes(permitBaseUrl) && content.user.type == UserType.Bot
);
if (permitComments.length > 0) {
logger.info(`incentivizeCreatorComment: skip to generate a permit url because it has been already posted`);
Expand All @@ -130,14 +136,14 @@ export const calculateIssueCreatorReward = async (incentivesCalculation: Incenti

const description = await getIssueDescription(incentivesCalculation.issue.number, "html");
if (!description) {
logger.info(`Skipping to generate a permit url because issue description is empty. description: ${description}`);
return { error: `Skipping to generate a permit url because issue description is empty. description: ${description}` };
logger.info(`Skipping issue creator reward because issue description is empty. description: ${description}`);
return { error: `Skipping issue creator reward because issue description is empty. description: ${description}` };
}
logger.info(`Getting the issue description done. description: ${description}`);
const creator = incentivesCalculation.issue.user;
if (creator.type === UserType.Bot || creator.login === incentivesCalculation.issue.assignee) {
logger.info("Issue creator assigneed himself or Bot created this issue.");
return { error: "Issue creator assigneed himself or Bot created this issue." };
if (creator.type === UserType.Bot) {
logger.info("Skipping issue creator reward because Bot created this issue.");
return { error: "Skipping issue creator reward because Bot created this issue." };
}

const result = await generatePermitForComments(
Expand All @@ -148,8 +154,8 @@ export const calculateIssueCreatorReward = async (incentivesCalculation: Incenti
incentivesCalculation.paymentPermitMaxPrice
);

if (!result || !result.account || !result.amountInETH) {
throw new Error("Failed to generate permit for issue creator because of missing account or amountInETH");
if (!result || !result.account || !result.rewardInTokens) {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think that's as expressive as inBigNumber etc it implies 1e18 format

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it's not in 1e18 format, but with decimals like 5.235

throw new Error("Failed to generate permit for issue creator because of missing account or rewardInTokens");
}

return {
Expand All @@ -159,7 +165,7 @@ export const calculateIssueCreatorReward = async (incentivesCalculation: Incenti
username: creator.login,
reward: [
{
priceInEth: result?.amountInETH ?? new Decimal(0),
priceInEth: result?.rewardInTokens ?? new Decimal(0),
account: result?.account,
userId: "",
user: "",
Expand All @@ -172,6 +178,9 @@ export const calculateIssueCreatorReward = async (incentivesCalculation: Incenti
export const calculatePullRequestReviewsReward = async (incentivesCalculation: IncentivesCalculationResult): Promise<RewardsResponse> => {
const logger = getLogger();
const context = getBotContext();
const {
payout: { permitBaseUrl },
} = getBotConfig();
const title = "Reviewer";

const linkedPullRequest = await gitLinkedPrParser({
Expand All @@ -189,7 +198,7 @@ export const calculatePullRequestReviewsReward = async (incentivesCalculation: I

const comments = await getAllIssueComments(incentivesCalculation.issue.number);
const permitComments = comments.filter(
(content) => content.body.includes(title) && content.body.includes("https://pay.ubq.fi?claim=") && content.user.type == UserType.Bot
(content) => content.body.includes(title) && content.body.includes(permitBaseUrl) && content.user.type == UserType.Bot
);
if (permitComments.length > 0) {
logger.info(`calculatePullRequestReviewsReward: skip to generate a permit url because it has been already posted`);
Expand Down Expand Up @@ -278,7 +287,7 @@ const generatePermitForComments = async (
multiplier: number,
incentives: Incentives,
paymentPermitMaxPrice: number
): Promise<undefined | { account: string; amountInETH: Decimal }> => {
): Promise<undefined | { account: string; rewardInTokens: Decimal }> => {
const logger = getLogger();
const commentsByNode = await parseComments(comments, ItemsToExclude);
const rewardValue = calculateRewardValue(commentsByNode, incentives);
Expand All @@ -288,15 +297,15 @@ const generatePermitForComments = async (
}
logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`);
const account = await getWalletAddress(user);
const amountInETH = rewardValue.mul(multiplier);
if (amountInETH.gt(paymentPermitMaxPrice)) {
const rewardInTokens = rewardValue.mul(multiplier);
if (rewardInTokens.gt(paymentPermitMaxPrice)) {
logger.info(`Skipping issue creator reward for user ${user} because reward is higher than payment permit max price`);
return;
}
if (account) {
return { account, amountInETH };
return { account, rewardInTokens };
} else {
return { account: "0x", amountInETH: new Decimal(0) };
return { account: "0x", rewardInTokens: new Decimal(0) };
}
};
/**
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/push/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getBotContext, getLogger } from "../../bindings";
import { createCommitComment, getFileContent } from "../../helpers";
import { CommitsPayload, PushPayload, WideConfigSchema } from "../../types";
import { CommitsPayload, PushPayload, ConfigSchema } from "../../types";
whilefoo marked this conversation as resolved.
Show resolved Hide resolved
import { parseYAML } from "../../utils/private";
import { updateBaseRate } from "./update-base";
import { validate } from "../../utils/ajv";
Expand Down Expand Up @@ -87,7 +87,7 @@ export const validateConfigChange = async () => {
if (configFileContent) {
const decodedConfig = Buffer.from(configFileContent, "base64").toString();
const config = parseYAML(decodedConfig);
const { valid, error } = validate(WideConfigSchema, config);
const { valid, error } = validate(ConfigSchema, config);
if (!valid) {
await createCommitComment(`@${payload.sender.login} Config validation failed! ${error}`, commitSha, BASE_RATE_FILE);
}
Expand Down
10 changes: 5 additions & 5 deletions src/helpers/gpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import OpenAI from "openai";
import { CreateChatCompletionRequestMessage } from "openai/resources/chat";
import { ErrorDiff } from "../utils/helpers";

export const sysMsg = `You are the UbiquityAI, designed to provide accurate technical answers. \n
export const sysMsg = `You are the UbiquiBot, designed to provide accurate technical answers. \n
Whenever appropriate, format your response using GitHub Flavored Markdown. Utilize tables, lists, and code blocks for clear and organized answers. \n
Do not make up answers. If you are unsure, say so. \n
Original Context exists only to provide you with additional information to the current question, use it to formulate answers. \n
Expand All @@ -14,7 +14,7 @@ All replies MUST end with "\n\n <!--- { 'UbiquityAI': 'answer' } ---> ".\n
`;

export const gptContextTemplate = `
You are the UbiquityAI, designed to review and analyze pull requests.
You are the UbiquiBot, designed to review and analyze pull requests.
You have been provided with the spec of the issue and all linked issues or pull requests.
Using this full context, Reply in pure JSON format, with the following structure omitting irrelvant information pertaining to the specification.
You MUST provide the following structure, but you may add additional information if you deem it relevant.
Expand Down Expand Up @@ -118,17 +118,17 @@ export const decideContextGPT = async (
{
role: "system",
content: "This issue/Pr context: \n" + JSON.stringify(streamlined),
name: "UbiquityAI",
name: "UbiquiBot",
} as CreateChatCompletionRequestMessage,
{
role: "system",
content: "Linked issue(s) context: \n" + JSON.stringify(linkedIssueStreamlined),
name: "UbiquityAI",
name: "UbiquiBot",
} as CreateChatCompletionRequestMessage,
{
role: "system",
content: "Linked Pr(s) context: \n" + JSON.stringify(linkedPRStreamlined),
name: "UbiquityAI",
name: "UbiquiBot",
} as CreateChatCompletionRequestMessage
);

Expand Down
Loading
Loading