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

feat: selectivly enable incentives #781

Closed
wants to merge 12 commits into from
42 changes: 41 additions & 1 deletion .github/ubiquibot-config.yml
Original file line number Diff line number Diff line change
@@ -1 +1,41 @@
price-multiplier: 1.5
---
evm-network-id: 100
price-multiplier: 1.5
assistive-pricing: true
time-labels:
- name: "Time: <1 Hour"
- name: "Time: <1 Day"
- name: "Time: <1 Week"
- name: "Time: <2 Weeks"
- name: "Time: <1 Month"
priority-labels:
- name: "Priority: 0 (Normal)"
- name: "Priority: 1 (Medium)"
- name: "Priority: 2 (High)"
- name: "Priority: 3 (Urgent)"
- name: "Priority: 4 (Emergency)"
default-labels:
- "Time: <1 Hour"
- "Priority: 1 (Normal)"
payment-permit-max-price: 1000
comment-incentives: true
promotion-comment: "<h6>If you've enjoyed your experience in the DevPool, we'd appreciate your support. Follow <a href='https://github.com/ubiquity'>Ubiquity on GitHub</a> and star <a href='https://github.com/ubiquity/devpool-directory'>this repo</a>. Your endorsement means the world to us and helps us grow!</h6><h6>We are excited to announce that the DevPool and UbiquiBot are now available to partners! Our ideal collaborators are globally distributed crypto-native organizations, who actively work on open source on GitHub, and excel in research & development. If you can introduce us to the repository maintainers in these types of companies, we have a special bonus in store for you!</h6>"
register-wallet-with-verification: false
command-settings:
- name: start
enabled: true
- name: stop
enabled: true
- name: wallet
enabled: true
- name: payout
enabled: true
- name: multiplier
enabled: true
- name: query
enabled: true
- name: allow
enabled: true
- name: autopay
enabled: true
private-key-encrypted: "5Awi3hP6k78nINwmjadju8SJPZ1SeHBOhoaNYjdRm2_1VAF3OlzVVIv6iFrZ2CqsJ-R2h9clb4CyXRhK7kRXucrev2sfalqRa33El_LiSQFlZBQ4WwYtk22np1BOUeDQLwW3dx8mse_05c3b1rMTQA"
48 changes: 26 additions & 22 deletions src/handlers/payout/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
generatePermit2Signature,
getAllIssueComments,
getAllPullRequestReviews,
getIncentivizedUsers,
getIssueDescription,
getTokenSymbol,
parseComments,
Expand Down Expand Up @@ -70,13 +71,12 @@ export const incentivizeComments = async () => {
logger.info("incentivizeComments: skipping payment permit generation because `assignee` is `undefined`.");
return;
}

const issueComments = await getAllIssueComments(issue.number, "full");
logger.info(`Getting the issue comments done. comments: ${JSON.stringify(issueComments)}`);
const issueCommentsByUser: Record<string, { id: string; comments: string[] }> = {};
for (const issueComment of issueComments) {
const user = issueComment.user;
if (user.type == UserType.Bot || user.login == assignee) continue;
if (user.type == UserType.Bot) continue;
const commands = commentParser(issueComment.body);
if (commands.length > 0) {
logger.info(`Skipping to parse the comment because it contains commands. comment: ${JSON.stringify(issueComment)}`);
Expand All @@ -98,31 +98,35 @@ export const incentivizeComments = async () => {

// The mapping between gh handle and comment with a permit url
const reward: Record<string, string> = {};

const users = await getIncentivizedUsers(issue.number);
// The mapping between gh handle and amount in ETH
const fallbackReward: Record<string, Decimal> = {};
let comment = `#### Conversation Rewards\n`;
for (const user of Object.keys(issueCommentsByUser)) {
const commentsByUser = issueCommentsByUser[user];
const commentsByNode = await parseComments(commentsByUser.comments, ItemsToExclude);
const rewardValue = calculateRewardValue(commentsByNode, incentives);
if (rewardValue.equals(0)) {
logger.info(`Skipping to generate a permit url because the reward value is 0. user: ${user}`);
continue;
}
logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`);
const account = await getWalletAddress(user);
const amountInETH = rewardValue.mul(baseMultiplier);
if (amountInETH.gt(paymentPermitMaxPrice)) {
logger.info(`Skipping comment reward for user ${user} because reward is higher than payment permit max price`);
continue;
}
if (account) {
const { payoutUrl } = await generatePermit2Signature(account, amountInETH, issue.node_id, commentsByUser.id, "ISSUE_COMMENTER");
comment = `${comment}### [ **${user}: [ CLAIM ${amountInETH} ${tokenSymbol.toUpperCase()} ]** ](${payoutUrl})\n`;
reward[user] = payoutUrl;
if (users.users.includes(user) || users.enable) {
const commentsByUser = issueCommentsByUser[user];
const commentsByNode = await parseComments(commentsByUser.comments, ItemsToExclude);
const rewardValue = calculateRewardValue(commentsByNode, incentives);
if (rewardValue.equals(0)) {
logger.info(`Skipping to generate a permit url because the reward value is 0. user: ${user}`);
continue;
}
logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`);
const account = await getWalletAddress(user);
const amountInETH = rewardValue.mul(baseMultiplier);
if (amountInETH.gt(paymentPermitMaxPrice)) {
logger.info(`Skipping comment reward for user ${user} because reward is higher than payment permit max price`);
continue;
}
if (account) {
const { payoutUrl } = await generatePermit2Signature(account, amountInETH, issue.node_id, commentsByUser.id, "ISSUE_COMMENTER");
comment = `${comment}### [ **${user}: [ CLAIM ${amountInETH} ${tokenSymbol.toUpperCase()} ]** ](${payoutUrl})\n`;
reward[user] = payoutUrl;
} else {
fallbackReward[user] = amountInETH;
}
} else {
fallbackReward[user] = amountInETH;
continue;
}
BeanieMen marked this conversation as resolved.
Show resolved Hide resolved
}

Expand Down
16 changes: 16 additions & 0 deletions src/helpers/issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,22 @@ export const listAllIssuesForRepo = async (state: "open" | "closed" | "all" = "o
return issuesArr;
};

export const getIncentivizedUsers = async (issue_number: number) => {
const comments = await getAllIssueComments(issue_number);
const incentiveComment = comments.filter((comment) => comment.body.startsWith("/comment-incentives"));
const parts = incentiveComment[0].body.split("");
BeanieMen marked this conversation as resolved.
Show resolved Hide resolved
BeanieMen marked this conversation as resolved.
Show resolved Hide resolved
parts.shift();
const users: { enable: boolean; users: string[] } = { enable: false, users: [] };
for (const part of parts) {
if (part.startsWith("@")) {
users.users.push(part.substring(1));
} else if (part == "true") {
BeanieMen marked this conversation as resolved.
Show resolved Hide resolved
users.enable = part == "true";
}
}
return users;
};

export const addCommentToIssue = async (msg: string, issue_number: number) => {
const context = getBotContext();
const logger = getLogger();
Expand Down
Loading