diff --git a/src/parser/content-evaluator-module.ts b/src/parser/content-evaluator-module.ts index 46de9194..88c643b5 100644 --- a/src/parser/content-evaluator-module.ts +++ b/src/parser/content-evaluator-module.ts @@ -121,13 +121,13 @@ export class ContentEvaluatorModule implements Module { const currentComment = commentsWithScore[i]; if (!this._fixedRelevances[currentComment.type]) { if (currentComment.type & CommentKind.PULL) { - if (currentComment?.diff_hunk) { + if (currentComment?.diffHunk) { //Eval PR comment with diff_hunk, all other PR comments get relevance:1 by default reviewCommentsToEvaluate.push({ id: currentComment.id, comment: currentComment.content, - diff_hunk: currentComment.diff_hunk, + diffHunk: currentComment.diffHunk, }); } } else { diff --git a/src/parser/data-purge-module.ts b/src/parser/data-purge-module.ts index befdf69e..bec1315e 100644 --- a/src/parser/data-purge-module.ts +++ b/src/parser/data-purge-module.ts @@ -41,7 +41,7 @@ export class DataPurgeModule implements Module { content: newContent, url: comment.html_url, type: comment.type, - diff_hunk: reviewComment?.pull_request_review_id ? reviewComment?.diff_hunk : undefined, + diffHunk: reviewComment?.pull_request_review_id ? reviewComment?.diff_hunk : undefined, }, ]; } diff --git a/src/parser/processor.ts b/src/parser/processor.ts index 972befdd..f1527bf8 100644 --- a/src/parser/processor.ts +++ b/src/parser/processor.ts @@ -94,7 +94,7 @@ export interface GithubCommentScore { content: string; url: string; type: CommentKind | CommentAssociation; - diff_hunk?: string; + diffHunk?: string; score?: { formatting?: { content: Record; diff --git a/src/types/content-evaluator-module-type.ts b/src/types/content-evaluator-module-type.ts index 57956800..2b59aecd 100644 --- a/src/types/content-evaluator-module-type.ts +++ b/src/types/content-evaluator-module-type.ts @@ -2,7 +2,7 @@ import { Type, Static } from "@sinclair/typebox"; export type CommentToEvaluate = { id: number; comment: string }; -export type ReviewCommentToEvaluate = { id: number; comment: string; diff_hunk: string }; +export type ReviewCommentToEvaluate = { id: number; comment: string; diffHunk: string }; export const openAiRelevanceResponseSchema = Type.Record(Type.String(), Type.Number({ minimum: 0, maximum: 1 })); diff --git a/tests/__mocks__/results/content-evaluator-results.json b/tests/__mocks__/results/content-evaluator-results.json index f9a25bc4..3c5e63da 100644 --- a/tests/__mocks__/results/content-evaluator-results.json +++ b/tests/__mocks__/results/content-evaluator-results.json @@ -288,7 +288,7 @@ "content": "I think in this case it makes sense because this function will return `\"\"` if nothing is found. If I return `undefined` or `null` it will be interpreted in HTML as a string and will display the text null or undefined.\r\nI changed the code to always return `content` so it forces it to be properly initialized which should avoid mistakes.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570010044", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -313,7 +313,7 @@ "content": "It is supposed to represent a comment. Would you prefer a descriptive action such as `COMMENTED`?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570011467", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -338,7 +338,7 @@ "content": "True, just picked this up from the previous codebase. https://rpc.gnosischain.com should do it for both then?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570014142", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -359,7 +359,7 @@ "content": "Seems convoluted to me and I don't know what problem that solved, but sure can do. Because either way this will yield an empty string or populated string?\r\nEither way changed to a `const string[]` to minimize mistakes then!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570187141", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -384,7 +384,7 @@ "content": "Changed `COMMENT` to `COMMENTED`, added jsdoc description for each value\r\nhttps://github.com/ubiquibot/conversation-rewards/blob/559c13c9a7d548519a6434e1b8393fd0d5c8f3db/src/issue-activity.ts#L21", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570188450", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -409,7 +409,7 @@ "content": "Simply makes the docs linking the code within the comment, for convenience, and looks like this\r\n\"image\"\r\nAlso be careful you linked a random GitHub account within your comment", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573413974", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -430,7 +430,7 @@ "content": "In the case of a string it would coerce the value and you would get \"null\" as a string so I get your point indeed", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573416633", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -451,7 +451,7 @@ "content": "`observe` would help to chose what we want to include and ignore? We surely can add more properties as it goes. The purpose of this section is to attribute different multipliers based on the type of comment, mimicking the old [codebase](https://github.com/ubiquibot/comment-incentives/blob/525fcdccc8f0d9032a41cdaed3b4fbc6514d44b6/src/handlers/issue/comment-scoring-by-contribution-style.ts)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573420718", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -480,7 +480,7 @@ "content": "Not very useful indeed. I am working on the tests on a separate PR to avoid cluttering that one. I didn't cover all the tests cases yet!\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731420", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -501,7 +501,7 @@ "content": "See https://github.com/ubiquibot/conversation-rewards/pull/14 for the soon up to date mocks", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731649", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -522,7 +522,7 @@ "content": "Good point, also symbol should not be DAI but ETH isn't it?\r\n`pay.ubq.fi` seems to be using https://rpc.mevblocker.io for its RPC which seems to have low latency, a good privacy and score according to Chainlist as well.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573733603", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -547,7 +547,7 @@ "content": "Had changed it everywhere else but here, now it should be fine", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573737049", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -568,7 +568,7 @@ "content": "This matches any whitespace character (spaces, tabs, line breaks) so basically I make the produced HTML one liner. This helps to collect it from the CLI, makes the string shorter and more compact.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574114225", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -589,7 +589,7 @@ "content": "By definition `dump` was just outputting results to stdout, but I could make it return the result as well. Since the console log is what we want to make sure works properly to be reused by other workflows, I thought it would make sense to test it that way because potentially if we forget other logs somewhere that would mess up the output for the next Action step.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574622869", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -614,7 +614,7 @@ "content": "Un-nested them one level. I think that the `buildXrow` should just be contained by that function because its purpose only serves inside of it.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574702577", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -639,7 +639,7 @@ "content": "I assumed BigInt only handles integers, and we manipulate a lot of floating decimal values for our operations (mostly everything has a floating point)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574724902", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -660,7 +660,7 @@ "content": "There is one permit generated per participating user, last run:\r\nhttps://github.com/Meniole/test/issues/1#issuecomment-2069443938", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574767984", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -681,7 +681,7 @@ "content": "I made it according to the previous version:\r\n- Issue Specification means the item when opening an issue\r\n- Issue Task means the item that fixed the issue\r\nExample with the current comments:\r\nhttps://github.com/ubiquibot/comment-incentives/issues/31#issuecomment-1998609326", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575659438", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -710,7 +710,7 @@ "content": "Wouldn't that lead to precision loss in JS and after the division we could end up with 42.000000001 results? That's also what `DecimalJs` fixes in this case.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575675704", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -735,7 +735,7 @@ "content": "That is correct. I understand now why you're confused:\r\nThe value for the task would be\r\n`\"ISSUE|ISSUER|TASK\"`\r\nbecause it is inside the `issue`\r\nThe value for the Specification would be\r\n`\"REVIEW|ISSUER|SPECIFICATION\"`\r\nbecause it is inside the `pull`\r\nI display `Issue: Specification` and `Issue: Task` just to stick to our current display format. Maybe instead I could display `Review: Task` instead?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576567583", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -760,7 +760,7 @@ "content": "I don't think is it relevant in this scenario because we are not manipulating big numbers. Decimaljs allows for non-precision loss on the operations because there is a lot of multiply / divide with floating points which can result in very long decimals due to Js nature.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577477616", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -781,7 +781,7 @@ "content": "@whilefoo you are correct, these were wrongly used. I corrected `TASK` and replaced it by `SPECIFICATION` so we have:\r\n`\"ISSUE|ISSUER|SPECIFICATION\"` and `\"REVIEW|ISSUER|TASK\"`\r\nI used `ISSUER` for this because technically the issuer is not necessarily the assignee in both cases.\r\nFix within next PR:\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14/commits/90e150f76073c9cfa16a251f8c1ae48a75aa5408", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578162024", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1120,7 +1120,7 @@ "content": "\"Comment\" implication isn't clear to me", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569964797", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1141,7 +1141,7 @@ "content": "I've always been anti empty string for variable declarations. It's led to too many problems with other less experienced developers. I wish for a linter rule to flag these as a consequence. \nConsider initializing an empty array as a buffer and then joining it at the end.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569970517", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1162,7 +1162,7 @@ "content": "Our RPCs don't work anymore", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569971792", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -1183,7 +1183,7 @@ "content": "Yes consider changing them to verbs \n`ISSUER`\n`COMMENTED`\nI'm not 100% the best way to name all of them I have to look at the full list", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570133378", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1208,7 +1208,7 @@ "content": "`[].join(\"\");` yields an empty string as well if there's nothing in the array.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570159594", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1233,7 +1233,7 @@ "content": "Perhaps it will make the config more expressive if you add other properties?\nFor example \n```yml\nobserve:\n views:\n - ISSUE\n - PULL\n actors:\n - ISSUER \n - COLLABORATOR \n actions: \n - COMMENTED\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570588757", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1262,7 +1262,7 @@ "content": "What is this @link syntax", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570589892", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -1283,7 +1283,7 @@ "content": "Just noticed the bit wise operators 1337 code", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570591425", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", + "diffHunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", "score": { "formatting": { "content": { @@ -1304,7 +1304,7 @@ "content": "It might seem convoluted but I think that it condenses the logic to coerce everything into a string as expected. This is a suggestion to be proactive when we set up the empty string linter one day. \nFor example if you `push` any element into the array (compared to `+=`) when you join they should render as expected. \n```js \n[ null, null ].join(\"\"); // \"\"\nnull += null; // 0 I think?\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570596007", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1333,7 +1333,7 @@ "content": "I am aware of its purpose. I am proposing to make the config more expressive so that its more intuitive to work with.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573720820", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1354,7 +1354,7 @@ "content": "Will you use array syntax?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573722731", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1375,7 +1375,7 @@ "content": "This regex appears to convert repeating spaces i.e. `\" \"` to a single space `\" \"` what is the purpose of this?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723262", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1400,7 +1400,7 @@ "content": "This doesn't seem right. Network ID 1 is mainnet. The RPC clearly is for gnosis chain. Perhaps try and find a nice mainnet RPC?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723774", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1421,7 +1421,7 @@ "content": "Seems like not a very useful mock.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724498", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1442,7 +1442,7 @@ "content": "Same here", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724853", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1463,7 +1463,7 @@ "content": "The token address indeed represents DAI on mainnet.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086028", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1484,7 +1484,7 @@ "content": "@gentlementlegen rfc", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086293", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1505,7 +1505,7 @@ "content": "Why not do it in the industry standard way? All values are denominated in `1e18` without floating point. \nAt the end, for rendering, we just need to divide by `1e18`", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575555444", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1530,7 +1530,7 @@ "content": "I'm not sure if I understand but task reward is if you completed the task (if you are an assignee when the issue is completed) and specification reward is if you write the specification (you are the original issue author)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575556553", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1551,7 +1551,7 @@ "content": "Spoke a bit on this in dms. @whilefoo my intent with referring to GitHub Issues as \"tasks\" was to emphasize that its a funded, and recognized \"real project\" so I want to refer to them as \"tasks\" instead of just \"issues.\"\r\nSince we are dealing so heavily with the GitHub API, I figured that there is benefit to being specific about what words we are using. Sure we can load in all the GitHub issues on a repository, but not all of them will be funded and recognized. \r\nWe are only primarily concerned with working with \"tasks\" aka funded GitHub issues.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576666170", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1572,7 +1572,7 @@ "content": "Possibly. I personally haven't had that issue but either way my suggestion is to keep everything internally without a decimal and then only convert it upon rendering.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577225638", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1647,7 +1647,7 @@ "content": "```suggestion\r\n return result;\r\n```\r\nresolve is unnecessary here and some other places I saw it too", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574427305", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", "score": { "formatting": { "content": { @@ -1676,7 +1676,7 @@ "content": "I'm not sure if it's a good idea to rely on logs for testing, is not possible to use output from the processor?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574441918", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -1697,7 +1697,7 @@ "content": "why so many nested functions? it becomes hard to read", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574458540", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -1718,7 +1718,7 @@ "content": "is there a reason we are using Decimal.js instead of native BigInt?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574460099", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1739,7 +1739,7 @@ "content": "Shouldn't task be issue opening item and specification is pull request opening item? At least that's how I understand from the code.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574487172", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1760,7 +1760,7 @@ "content": "I assume this is a permit for task assignee, but where are other permits or is that not done yet?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574492061", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -1781,7 +1781,7 @@ "content": "gotcha, I thought this html is for all users", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575851470", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -1802,7 +1802,7 @@ "content": "I can see from the example that it works correctly but when reading the code I get confused\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/12/files#diff-03f6b1f98f9f1df2651b65c46241c43e65d9544f7ea1de71e94255e757b3f2f0R108\r\nas I understand the if statement will be true if the comment is the first/opening comment of the issue or pull request so then it checks if it's issue then it will be TASK otherwise (if it's PR) it will be SPECIFICATION?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576482609", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1823,7 +1823,7 @@ "content": "Ok so we need it for precise floating point calculations, in that case BigInt is not applicable.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578040543", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1844,7 +1844,7 @@ "content": "why is it `ISSUER` which is meant for author of issue/review. wouldn't it make more sense to use `ASIGNEE`?\r\nisn't specification the first comment of the issue so how is it inside PR?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578050965", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1869,7 +1869,7 @@ "content": "this makes more sense!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1579556333", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { diff --git a/tests/__mocks__/results/data-purge-result.json b/tests/__mocks__/results/data-purge-result.json index 81890f7d..224c03d5 100644 --- a/tests/__mocks__/results/data-purge-result.json +++ b/tests/__mocks__/results/data-purge-result.json @@ -78,154 +78,154 @@ "content": "I think in this case it makes sense because this function will return `\"\"` if nothing is found. If I return `undefined` or `null` it will be interpreted in HTML as a string and will display the text null or undefined.\r\nI changed the code to always return `content` so it forces it to be properly initialized which should avoid mistakes.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570010044", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" }, { "id": 1570011467, "content": "It is supposed to represent a comment. Would you prefer a descriptive action such as `COMMENTED`?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570011467", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:" + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:" }, { "id": 1570014142, "content": "True, just picked this up from the previous codebase. https://rpc.gnosischain.com should do it for both then?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570014142", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\"," + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\"," }, { "id": 1570187141, "content": "Seems convoluted to me and I don't know what problem that solved, but sure can do. Because either way this will yield an empty string or populated string?\r\nEither way changed to a `const string[]` to minimize mistakes then!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570187141", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" }, { "id": 1570188450, "content": "Changed `COMMENT` to `COMMENTED`, added jsdoc description for each value\r\nhttps://github.com/ubiquibot/conversation-rewards/blob/559c13c9a7d548519a6434e1b8393fd0d5c8f3db/src/issue-activity.ts#L21", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570188450", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:" + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:" }, { "id": 1573413974, "content": "Simply makes the docs linking the code within the comment, for convenience, and looks like this\r\n\"image\"\r\nAlso be careful you linked a random GitHub account within your comment", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573413974", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}" + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}" }, { "id": 1573416633, "content": "In the case of a string it would coerce the value and you would get \"null\" as a string so I get your point indeed", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573416633", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" }, { "id": 1573420718, "content": "`observe` would help to chose what we want to include and ignore? We surely can add more properties as it goes. The purpose of this section is to attribute different multipliers based on the type of comment, mimicking the old [codebase](https://github.com/ubiquibot/comment-incentives/blob/525fcdccc8f0d9032a41cdaed3b4fbc6514d44b6/src/handlers/issue/comment-scoring-by-contribution-style.ts)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573420718", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:" + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:" }, { "id": 1573731420, "content": "Not very useful indeed. I am working on the tests on a separate PR to avoid cluttering that one. I didn't cover all the tests cases yet!\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731420", "type": "PULL_AUTHOR", - "diff_hunk": "" + "diffHunk": "" }, { "id": 1573731649, "content": "See https://github.com/ubiquibot/conversation-rewards/pull/14 for the soon up to date mocks", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731649", "type": "PULL_AUTHOR", - "diff_hunk": "" + "diffHunk": "" }, { "id": 1573733603, "content": "Good point, also symbol should not be DAI but ETH isn't it?\r\n`pay.ubq.fi` seems to be using https://rpc.mevblocker.io for its RPC which seems to have low latency, a good privacy and score according to Chainlist as well.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573733603", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ }," + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ }," }, { "id": 1573737049, "content": "Had changed it everywhere else but here, now it should be fine", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573737049", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" }, { "id": 1574114225, "content": "This matches any whitespace character (spaces, tabs, line breaks) so basically I make the produced HTML one liner. This helps to collect it from the CLI, makes the string shorter and more compact.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574114225", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")" + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")" }, { "id": 1574622869, "content": "By definition `dump` was just outputting results to stdout, but I could make it return the result as well. Since the console log is what we want to make sure works properly to be reused by other workflows, I thought it would make sense to test it that way because potentially if we forget other logs somewhere that would mess up the output for the next Action step.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574622869", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));" + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));" }, { "id": 1574702577, "content": "Un-nested them one level. I think that the `buildXrow` should just be contained by that function because its purpose only serves inside of it.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574702577", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(" }, { "id": 1574724902, "content": "I assumed BigInt only handles integers, and we manipulate a lot of floating decimal values for our operations (mostly everything has a floating point)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574724902", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" }, { "id": 1574767984, "content": "There is one permit generated per participating user, last run:\r\nhttps://github.com/Meniole/test/issues/1#issuecomment-2069443938", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574767984", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ " + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ " }, { "id": 1575659438, "content": "I made it according to the previous version:\r\n- Issue Specification means the item when opening an issue\r\n- Issue Task means the item that fixed the issue\r\nExample with the current comments:\r\nhttps://github.com/ubiquibot/comment-incentives/issues/31#issuecomment-1998609326", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575659438", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," }, { "id": 1575675704, "content": "Wouldn't that lead to precision loss in JS and after the division we could end up with 42.000000001 results? That's also what `DecimalJs` fixes in this case.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575675704", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" }, { "id": 1576567583, "content": "That is correct. I understand now why you're confused:\r\nThe value for the task would be\r\n`\"ISSUE|ISSUER|TASK\"`\r\nbecause it is inside the `issue`\r\nThe value for the Specification would be\r\n`\"REVIEW|ISSUER|SPECIFICATION\"`\r\nbecause it is inside the `pull`\r\nI display `Issue: Specification` and `Issue: Task` just to stick to our current display format. Maybe instead I could display `Review: Task` instead?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576567583", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," }, { "id": 1577477616, "content": "I don't think is it relevant in this scenario because we are not manipulating big numbers. Decimaljs allows for non-precision loss on the operations because there is a lot of multiply / divide with floating points which can result in very long decimals due to Js nature.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577477616", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" }, { "id": 1578162024, "content": "@whilefoo you are correct, these were wrongly used. I corrected `TASK` and replaced it by `SPECIFICATION` so we have:\r\n`\"ISSUE|ISSUER|SPECIFICATION\"` and `\"REVIEW|ISSUER|TASK\"`\r\nI used `ISSUER` for this because technically the issuer is not necessarily the assignee in both cases.\r\nFix within next PR:\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14/commits/90e150f76073c9cfa16a251f8c1ae48a75aa5408", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578162024", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," }, { "id": 2056635516, @@ -310,147 +310,147 @@ "content": "\"Comment\" implication isn't clear to me", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569964797", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:" + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:" }, { "id": 1569970517, "content": "I've always been anti empty string for variable declarations. It's led to too many problems with other less experienced developers. I wish for a linter rule to flag these as a consequence. \nConsider initializing an empty array as a buffer and then joining it at the end.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569970517", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" }, { "id": 1569971792, "content": "Our RPCs don't work anymore", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569971792", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\"," + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\"," }, { "id": 1570133378, "content": "Yes consider changing them to verbs \n`ISSUER`\n`COMMENTED`\nI'm not 100% the best way to name all of them I have to look at the full list", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570133378", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:" + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:" }, { "id": 1570159594, "content": "`[].join(\"\");` yields an empty string as well if there's nothing in the array.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570159594", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" }, { "id": 1570588757, "content": "Perhaps it will make the config more expressive if you add other properties?\nFor example \n```yml\nobserve:\n views:\n - ISSUE\n - PULL\n actors:\n - ISSUER \n - COLLABORATOR \n actions: \n - COMMENTED\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570588757", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:" + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:" }, { "id": 1570589892, "content": "What is this @link syntax", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570589892", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}" + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}" }, { "id": 1570591425, "content": "Just noticed the bit wise operators 1337 code", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570591425", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;" + "diffHunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;" }, { "id": 1570596007, "content": "It might seem convoluted but I think that it condenses the logic to coerce everything into a string as expected. This is a suggestion to be proactive when we set up the empty string linter one day. \nFor example if you `push` any element into the array (compared to `+=`) when you join they should render as expected. \n```js \n[ null, null ].join(\"\"); // \"\"\nnull += null; // 0 I think?\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570596007", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" }, { "id": 1573720820, "content": "I am aware of its purpose. I am proposing to make the config more expressive so that its more intuitive to work with.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573720820", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:" + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:" }, { "id": 1573722731, "content": "Will you use array syntax?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573722731", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";" }, { "id": 1573723262, "content": "This regex appears to convert repeating spaces i.e. `\" \"` to a single space `\" \"` what is the purpose of this?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723262", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")" + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")" }, { "id": 1573723774, "content": "This doesn't seem right. Network ID 1 is mainnet. The RPC clearly is for gnosis chain. Perhaps try and find a nice mainnet RPC?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723774", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ }," + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ }," }, { "id": 1573724498, "content": "Seems like not a very useful mock.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724498", "type": "PULL_COLLABORATOR", - "diff_hunk": "" + "diffHunk": "" }, { "id": 1573724853, "content": "Same here", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724853", "type": "PULL_COLLABORATOR", - "diff_hunk": "" + "diffHunk": "" }, { "id": 1574086028, "content": "The token address indeed represents DAI on mainnet.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086028", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ }," + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ }," }, { "id": 1574086293, "content": "@gentlementlegen rfc", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086293", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")" + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")" }, { "id": 1575555444, "content": "Why not do it in the industry standard way? All values are denominated in `1e18` without floating point. \nAt the end, for rendering, we just need to divide by `1e18`", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575555444", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" }, { "id": 1575556553, "content": "I'm not sure if I understand but task reward is if you completed the task (if you are an assignee when the issue is completed) and specification reward is if you write the specification (you are the original issue author)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575556553", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," }, { "id": 1576666170, "content": "Spoke a bit on this in dms. @whilefoo my intent with referring to GitHub Issues as \"tasks\" was to emphasize that its a funded, and recognized \"real project\" so I want to refer to them as \"tasks\" instead of just \"issues.\"\r\nSince we are dealing so heavily with the GitHub API, I figured that there is benefit to being specific about what words we are using. Sure we can load in all the GitHub issues on a repository, but not all of them will be funded and recognized. \r\nWe are only primarily concerned with working with \"tasks\" aka funded GitHub issues.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576666170", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," }, { "id": 1577225638, "content": "Possibly. I personally haven't had that issue but either way my suggestion is to keep everything internally without a decimal and then only convert it upon rendering.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577225638", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" }, { "id": 2063712447, @@ -475,77 +475,77 @@ "content": "```suggestion\r\n return result;\r\n```\r\nresolve is unnecessary here and some other places I saw it too", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574427305", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);" }, { "id": 1574441918, "content": "I'm not sure if it's a good idea to rely on logs for testing, is not possible to use output from the processor?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574441918", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));" + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));" }, { "id": 1574458540, "content": "why so many nested functions? it becomes hard to read", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574458540", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(" }, { "id": 1574460099, "content": "is there a reason we are using Decimal.js instead of native BigInt?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574460099", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" }, { "id": 1574487172, "content": "Shouldn't task be issue opening item and specification is pull request opening item? At least that's how I understand from the code.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574487172", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," }, { "id": 1574492061, "content": "I assume this is a permit for task assignee, but where are other permits or is that not done yet?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574492061", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ " + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ " }, { "id": 1575851470, "content": "gotcha, I thought this html is for all users", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575851470", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ " + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ " }, { "id": 1576482609, "content": "I can see from the example that it works correctly but when reading the code I get confused\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/12/files#diff-03f6b1f98f9f1df2651b65c46241c43e65d9544f7ea1de71e94255e757b3f2f0R108\r\nas I understand the if statement will be true if the comment is the first/opening comment of the issue or pull request so then it checks if it's issue then it will be TASK otherwise (if it's PR) it will be SPECIFICATION?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576482609", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," }, { "id": 1578040543, "content": "Ok so we need it for precise floating point calculations, in that case BigInt is not applicable.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578040543", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))" }, { "id": 1578050965, "content": "why is it `ISSUER` which is meant for author of issue/review. wouldn't it make more sense to use `ASIGNEE`?\r\nisn't specification the first comment of the issue so how is it inside PR?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578050965", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," }, { "id": 1579556333, "content": "this makes more sense!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1579556333", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000," } ] } diff --git a/tests/__mocks__/results/formatting-evaluator-results.json b/tests/__mocks__/results/formatting-evaluator-results.json index a35a4a1a..77855420 100644 --- a/tests/__mocks__/results/formatting-evaluator-results.json +++ b/tests/__mocks__/results/formatting-evaluator-results.json @@ -277,7 +277,7 @@ "content": "I think in this case it makes sense because this function will return `\"\"` if nothing is found. If I return `undefined` or `null` it will be interpreted in HTML as a string and will display the text null or undefined.\r\nI changed the code to always return `content` so it forces it to be properly initialized which should avoid mistakes.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570010044", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -301,7 +301,7 @@ "content": "It is supposed to represent a comment. Would you prefer a descriptive action such as `COMMENTED`?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570011467", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -325,7 +325,7 @@ "content": "True, just picked this up from the previous codebase. https://rpc.gnosischain.com should do it for both then?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570014142", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -345,7 +345,7 @@ "content": "Seems convoluted to me and I don't know what problem that solved, but sure can do. Because either way this will yield an empty string or populated string?\r\nEither way changed to a `const string[]` to minimize mistakes then!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570187141", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -369,7 +369,7 @@ "content": "Changed `COMMENT` to `COMMENTED`, added jsdoc description for each value\r\nhttps://github.com/ubiquibot/conversation-rewards/blob/559c13c9a7d548519a6434e1b8393fd0d5c8f3db/src/issue-activity.ts#L21", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570188450", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -393,7 +393,7 @@ "content": "Simply makes the docs linking the code within the comment, for convenience, and looks like this\r\n\"image\"\r\nAlso be careful you linked a random GitHub account within your comment", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573413974", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -413,7 +413,7 @@ "content": "In the case of a string it would coerce the value and you would get \"null\" as a string so I get your point indeed", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573416633", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -433,7 +433,7 @@ "content": "`observe` would help to chose what we want to include and ignore? We surely can add more properties as it goes. The purpose of this section is to attribute different multipliers based on the type of comment, mimicking the old [codebase](https://github.com/ubiquibot/comment-incentives/blob/525fcdccc8f0d9032a41cdaed3b4fbc6514d44b6/src/handlers/issue/comment-scoring-by-contribution-style.ts)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573420718", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -461,7 +461,7 @@ "content": "Not very useful indeed. I am working on the tests on a separate PR to avoid cluttering that one. I didn't cover all the tests cases yet!\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731420", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -481,7 +481,7 @@ "content": "See https://github.com/ubiquibot/conversation-rewards/pull/14 for the soon up to date mocks", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731649", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -501,7 +501,7 @@ "content": "Good point, also symbol should not be DAI but ETH isn't it?\r\n`pay.ubq.fi` seems to be using https://rpc.mevblocker.io for its RPC which seems to have low latency, a good privacy and score according to Chainlist as well.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573733603", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -525,7 +525,7 @@ "content": "Had changed it everywhere else but here, now it should be fine", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573737049", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -545,7 +545,7 @@ "content": "This matches any whitespace character (spaces, tabs, line breaks) so basically I make the produced HTML one liner. This helps to collect it from the CLI, makes the string shorter and more compact.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574114225", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -565,7 +565,7 @@ "content": "By definition `dump` was just outputting results to stdout, but I could make it return the result as well. Since the console log is what we want to make sure works properly to be reused by other workflows, I thought it would make sense to test it that way because potentially if we forget other logs somewhere that would mess up the output for the next Action step.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574622869", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -589,7 +589,7 @@ "content": "Un-nested them one level. I think that the `buildXrow` should just be contained by that function because its purpose only serves inside of it.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574702577", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -613,7 +613,7 @@ "content": "I assumed BigInt only handles integers, and we manipulate a lot of floating decimal values for our operations (mostly everything has a floating point)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574724902", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -633,7 +633,7 @@ "content": "There is one permit generated per participating user, last run:\r\nhttps://github.com/Meniole/test/issues/1#issuecomment-2069443938", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574767984", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -653,7 +653,7 @@ "content": "I made it according to the previous version:\r\n- Issue Specification means the item when opening an issue\r\n- Issue Task means the item that fixed the issue\r\nExample with the current comments:\r\nhttps://github.com/ubiquibot/comment-incentives/issues/31#issuecomment-1998609326", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575659438", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -681,7 +681,7 @@ "content": "Wouldn't that lead to precision loss in JS and after the division we could end up with 42.000000001 results? That's also what `DecimalJs` fixes in this case.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575675704", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -705,7 +705,7 @@ "content": "That is correct. I understand now why you're confused:\r\nThe value for the task would be\r\n`\"ISSUE|ISSUER|TASK\"`\r\nbecause it is inside the `issue`\r\nThe value for the Specification would be\r\n`\"REVIEW|ISSUER|SPECIFICATION\"`\r\nbecause it is inside the `pull`\r\nI display `Issue: Specification` and `Issue: Task` just to stick to our current display format. Maybe instead I could display `Review: Task` instead?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576567583", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -729,7 +729,7 @@ "content": "I don't think is it relevant in this scenario because we are not manipulating big numbers. Decimaljs allows for non-precision loss on the operations because there is a lot of multiply / divide with floating points which can result in very long decimals due to Js nature.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577477616", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -749,7 +749,7 @@ "content": "@whilefoo you are correct, these were wrongly used. I corrected `TASK` and replaced it by `SPECIFICATION` so we have:\r\n`\"ISSUE|ISSUER|SPECIFICATION\"` and `\"REVIEW|ISSUER|TASK\"`\r\nI used `ISSUER` for this because technically the issuer is not necessarily the assignee in both cases.\r\nFix within next PR:\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14/commits/90e150f76073c9cfa16a251f8c1ae48a75aa5408", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578162024", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1075,7 +1075,7 @@ "content": "\"Comment\" implication isn't clear to me", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569964797", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1095,7 +1095,7 @@ "content": "I've always been anti empty string for variable declarations. It's led to too many problems with other less experienced developers. I wish for a linter rule to flag these as a consequence. \nConsider initializing an empty array as a buffer and then joining it at the end.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569970517", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1115,7 +1115,7 @@ "content": "Our RPCs don't work anymore", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569971792", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -1135,7 +1135,7 @@ "content": "Yes consider changing them to verbs \n`ISSUER`\n`COMMENTED`\nI'm not 100% the best way to name all of them I have to look at the full list", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570133378", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1159,7 +1159,7 @@ "content": "`[].join(\"\");` yields an empty string as well if there's nothing in the array.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570159594", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1183,7 +1183,7 @@ "content": "Perhaps it will make the config more expressive if you add other properties?\nFor example \n```yml\nobserve:\n views:\n - ISSUE\n - PULL\n actors:\n - ISSUER \n - COLLABORATOR \n actions: \n - COMMENTED\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570588757", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1211,7 +1211,7 @@ "content": "What is this @link syntax", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570589892", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -1231,7 +1231,7 @@ "content": "Just noticed the bit wise operators 1337 code", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570591425", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", + "diffHunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", "score": { "formatting": { "content": { @@ -1251,7 +1251,7 @@ "content": "It might seem convoluted but I think that it condenses the logic to coerce everything into a string as expected. This is a suggestion to be proactive when we set up the empty string linter one day. \nFor example if you `push` any element into the array (compared to `+=`) when you join they should render as expected. \n```js \n[ null, null ].join(\"\"); // \"\"\nnull += null; // 0 I think?\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570596007", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1279,7 +1279,7 @@ "content": "I am aware of its purpose. I am proposing to make the config more expressive so that its more intuitive to work with.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573720820", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1299,7 +1299,7 @@ "content": "Will you use array syntax?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573722731", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1319,7 +1319,7 @@ "content": "This regex appears to convert repeating spaces i.e. `\" \"` to a single space `\" \"` what is the purpose of this?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723262", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1343,7 +1343,7 @@ "content": "This doesn't seem right. Network ID 1 is mainnet. The RPC clearly is for gnosis chain. Perhaps try and find a nice mainnet RPC?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723774", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1363,7 +1363,7 @@ "content": "Seems like not a very useful mock.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724498", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1383,7 +1383,7 @@ "content": "Same here", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724853", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1403,7 +1403,7 @@ "content": "The token address indeed represents DAI on mainnet.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086028", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1423,7 +1423,7 @@ "content": "@gentlementlegen rfc", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086293", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1443,7 +1443,7 @@ "content": "Why not do it in the industry standard way? All values are denominated in `1e18` without floating point. \nAt the end, for rendering, we just need to divide by `1e18`", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575555444", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1467,7 +1467,7 @@ "content": "I'm not sure if I understand but task reward is if you completed the task (if you are an assignee when the issue is completed) and specification reward is if you write the specification (you are the original issue author)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575556553", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1487,7 +1487,7 @@ "content": "Spoke a bit on this in dms. @whilefoo my intent with referring to GitHub Issues as \"tasks\" was to emphasize that its a funded, and recognized \"real project\" so I want to refer to them as \"tasks\" instead of just \"issues.\"\r\nSince we are dealing so heavily with the GitHub API, I figured that there is benefit to being specific about what words we are using. Sure we can load in all the GitHub issues on a repository, but not all of them will be funded and recognized. \r\nWe are only primarily concerned with working with \"tasks\" aka funded GitHub issues.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576666170", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1507,7 +1507,7 @@ "content": "Possibly. I personally haven't had that issue but either way my suggestion is to keep everything internally without a decimal and then only convert it upon rendering.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577225638", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1579,7 +1579,7 @@ "content": "```suggestion\r\n return result;\r\n```\r\nresolve is unnecessary here and some other places I saw it too", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574427305", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", "score": { "formatting": { "content": { @@ -1607,7 +1607,7 @@ "content": "I'm not sure if it's a good idea to rely on logs for testing, is not possible to use output from the processor?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574441918", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -1627,7 +1627,7 @@ "content": "why so many nested functions? it becomes hard to read", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574458540", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -1647,7 +1647,7 @@ "content": "is there a reason we are using Decimal.js instead of native BigInt?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574460099", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1667,7 +1667,7 @@ "content": "Shouldn't task be issue opening item and specification is pull request opening item? At least that's how I understand from the code.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574487172", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1687,7 +1687,7 @@ "content": "I assume this is a permit for task assignee, but where are other permits or is that not done yet?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574492061", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -1707,7 +1707,7 @@ "content": "gotcha, I thought this html is for all users", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575851470", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -1727,7 +1727,7 @@ "content": "I can see from the example that it works correctly but when reading the code I get confused\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/12/files#diff-03f6b1f98f9f1df2651b65c46241c43e65d9544f7ea1de71e94255e757b3f2f0R108\r\nas I understand the if statement will be true if the comment is the first/opening comment of the issue or pull request so then it checks if it's issue then it will be TASK otherwise (if it's PR) it will be SPECIFICATION?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576482609", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1747,7 +1747,7 @@ "content": "Ok so we need it for precise floating point calculations, in that case BigInt is not applicable.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578040543", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1767,7 +1767,7 @@ "content": "why is it `ISSUER` which is meant for author of issue/review. wouldn't it make more sense to use `ASIGNEE`?\r\nisn't specification the first comment of the issue so how is it inside PR?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578050965", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1791,7 +1791,7 @@ "content": "this makes more sense!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1579556333", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { diff --git a/tests/__mocks__/results/github-comment-results.json b/tests/__mocks__/results/github-comment-results.json index 52b773cd..d5fee104 100644 --- a/tests/__mocks__/results/github-comment-results.json +++ b/tests/__mocks__/results/github-comment-results.json @@ -288,7 +288,7 @@ "content": "I think in this case it makes sense because this function will return `\"\"` if nothing is found. If I return `undefined` or `null` it will be interpreted in HTML as a string and will display the text null or undefined.\r\nI changed the code to always return `content` so it forces it to be properly initialized which should avoid mistakes.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570010044", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -313,7 +313,7 @@ "content": "It is supposed to represent a comment. Would you prefer a descriptive action such as `COMMENTED`?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570011467", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -338,7 +338,7 @@ "content": "True, just picked this up from the previous codebase. https://rpc.gnosischain.com should do it for both then?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570014142", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -359,7 +359,7 @@ "content": "Seems convoluted to me and I don't know what problem that solved, but sure can do. Because either way this will yield an empty string or populated string?\r\nEither way changed to a `const string[]` to minimize mistakes then!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570187141", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -384,7 +384,7 @@ "content": "Changed `COMMENT` to `COMMENTED`, added jsdoc description for each value\r\nhttps://github.com/ubiquibot/conversation-rewards/blob/559c13c9a7d548519a6434e1b8393fd0d5c8f3db/src/issue-activity.ts#L21", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570188450", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -409,7 +409,7 @@ "content": "Simply makes the docs linking the code within the comment, for convenience, and looks like this\r\n\"image\"\r\nAlso be careful you linked a random GitHub account within your comment", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573413974", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -430,7 +430,7 @@ "content": "In the case of a string it would coerce the value and you would get \"null\" as a string so I get your point indeed", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573416633", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -451,7 +451,7 @@ "content": "`observe` would help to chose what we want to include and ignore? We surely can add more properties as it goes. The purpose of this section is to attribute different multipliers based on the type of comment, mimicking the old [codebase](https://github.com/ubiquibot/comment-incentives/blob/525fcdccc8f0d9032a41cdaed3b4fbc6514d44b6/src/handlers/issue/comment-scoring-by-contribution-style.ts)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573420718", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -480,7 +480,7 @@ "content": "Not very useful indeed. I am working on the tests on a separate PR to avoid cluttering that one. I didn't cover all the tests cases yet!\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731420", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -501,7 +501,7 @@ "content": "See https://github.com/ubiquibot/conversation-rewards/pull/14 for the soon up to date mocks", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731649", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -522,7 +522,7 @@ "content": "Good point, also symbol should not be DAI but ETH isn't it?\r\n`pay.ubq.fi` seems to be using https://rpc.mevblocker.io for its RPC which seems to have low latency, a good privacy and score according to Chainlist as well.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573733603", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -547,7 +547,7 @@ "content": "Had changed it everywhere else but here, now it should be fine", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573737049", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -568,7 +568,7 @@ "content": "This matches any whitespace character (spaces, tabs, line breaks) so basically I make the produced HTML one liner. This helps to collect it from the CLI, makes the string shorter and more compact.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574114225", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -589,7 +589,7 @@ "content": "By definition `dump` was just outputting results to stdout, but I could make it return the result as well. Since the console log is what we want to make sure works properly to be reused by other workflows, I thought it would make sense to test it that way because potentially if we forget other logs somewhere that would mess up the output for the next Action step.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574622869", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -614,7 +614,7 @@ "content": "Un-nested them one level. I think that the `buildXrow` should just be contained by that function because its purpose only serves inside of it.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574702577", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -639,7 +639,7 @@ "content": "I assumed BigInt only handles integers, and we manipulate a lot of floating decimal values for our operations (mostly everything has a floating point)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574724902", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -660,7 +660,7 @@ "content": "There is one permit generated per participating user, last run:\r\nhttps://github.com/Meniole/test/issues/1#issuecomment-2069443938", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574767984", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -681,7 +681,7 @@ "content": "I made it according to the previous version:\r\n- Issue Specification means the item when opening an issue\r\n- Issue Task means the item that fixed the issue\r\nExample with the current comments:\r\nhttps://github.com/ubiquibot/comment-incentives/issues/31#issuecomment-1998609326", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575659438", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -710,7 +710,7 @@ "content": "Wouldn't that lead to precision loss in JS and after the division we could end up with 42.000000001 results? That's also what `DecimalJs` fixes in this case.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575675704", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -735,7 +735,7 @@ "content": "That is correct. I understand now why you're confused:\r\nThe value for the task would be\r\n`\"ISSUE|ISSUER|TASK\"`\r\nbecause it is inside the `issue`\r\nThe value for the Specification would be\r\n`\"REVIEW|ISSUER|SPECIFICATION\"`\r\nbecause it is inside the `pull`\r\nI display `Issue: Specification` and `Issue: Task` just to stick to our current display format. Maybe instead I could display `Review: Task` instead?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576567583", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -760,7 +760,7 @@ "content": "I don't think is it relevant in this scenario because we are not manipulating big numbers. Decimaljs allows for non-precision loss on the operations because there is a lot of multiply / divide with floating points which can result in very long decimals due to Js nature.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577477616", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -781,7 +781,7 @@ "content": "@whilefoo you are correct, these were wrongly used. I corrected `TASK` and replaced it by `SPECIFICATION` so we have:\r\n`\"ISSUE|ISSUER|SPECIFICATION\"` and `\"REVIEW|ISSUER|TASK\"`\r\nI used `ISSUER` for this because technically the issuer is not necessarily the assignee in both cases.\r\nFix within next PR:\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14/commits/90e150f76073c9cfa16a251f8c1ae48a75aa5408", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578162024", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1121,7 +1121,7 @@ "content": "\"Comment\" implication isn't clear to me", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569964797", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1142,7 +1142,7 @@ "content": "I've always been anti empty string for variable declarations. It's led to too many problems with other less experienced developers. I wish for a linter rule to flag these as a consequence. \nConsider initializing an empty array as a buffer and then joining it at the end.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569970517", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1163,7 +1163,7 @@ "content": "Our RPCs don't work anymore", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569971792", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -1184,7 +1184,7 @@ "content": "Yes consider changing them to verbs \n`ISSUER`\n`COMMENTED`\nI'm not 100% the best way to name all of them I have to look at the full list", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570133378", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1209,7 +1209,7 @@ "content": "`[].join(\"\");` yields an empty string as well if there's nothing in the array.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570159594", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1234,7 +1234,7 @@ "content": "Perhaps it will make the config more expressive if you add other properties?\nFor example \n```yml\nobserve:\n views:\n - ISSUE\n - PULL\n actors:\n - ISSUER \n - COLLABORATOR \n actions: \n - COMMENTED\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570588757", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1263,7 +1263,7 @@ "content": "What is this @link syntax", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570589892", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -1284,7 +1284,7 @@ "content": "Just noticed the bit wise operators 1337 code", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570591425", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", + "diffHunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", "score": { "formatting": { "content": { @@ -1305,7 +1305,7 @@ "content": "It might seem convoluted but I think that it condenses the logic to coerce everything into a string as expected. This is a suggestion to be proactive when we set up the empty string linter one day. \nFor example if you `push` any element into the array (compared to `+=`) when you join they should render as expected. \n```js \n[ null, null ].join(\"\"); // \"\"\nnull += null; // 0 I think?\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570596007", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1334,7 +1334,7 @@ "content": "I am aware of its purpose. I am proposing to make the config more expressive so that its more intuitive to work with.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573720820", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1355,7 +1355,7 @@ "content": "Will you use array syntax?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573722731", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1376,7 +1376,7 @@ "content": "This regex appears to convert repeating spaces i.e. `\" \"` to a single space `\" \"` what is the purpose of this?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723262", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1401,7 +1401,7 @@ "content": "This doesn't seem right. Network ID 1 is mainnet. The RPC clearly is for gnosis chain. Perhaps try and find a nice mainnet RPC?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723774", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1422,7 +1422,7 @@ "content": "Seems like not a very useful mock.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724498", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1443,7 +1443,7 @@ "content": "Same here", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724853", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1464,7 +1464,7 @@ "content": "The token address indeed represents DAI on mainnet.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086028", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1485,7 +1485,7 @@ "content": "@gentlementlegen rfc", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086293", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1506,7 +1506,7 @@ "content": "Why not do it in the industry standard way? All values are denominated in `1e18` without floating point. \nAt the end, for rendering, we just need to divide by `1e18`", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575555444", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1531,7 +1531,7 @@ "content": "I'm not sure if I understand but task reward is if you completed the task (if you are an assignee when the issue is completed) and specification reward is if you write the specification (you are the original issue author)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575556553", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1552,7 +1552,7 @@ "content": "Spoke a bit on this in dms. @whilefoo my intent with referring to GitHub Issues as \"tasks\" was to emphasize that its a funded, and recognized \"real project\" so I want to refer to them as \"tasks\" instead of just \"issues.\"\r\nSince we are dealing so heavily with the GitHub API, I figured that there is benefit to being specific about what words we are using. Sure we can load in all the GitHub issues on a repository, but not all of them will be funded and recognized. \r\nWe are only primarily concerned with working with \"tasks\" aka funded GitHub issues.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576666170", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1573,7 +1573,7 @@ "content": "Possibly. I personally haven't had that issue but either way my suggestion is to keep everything internally without a decimal and then only convert it upon rendering.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577225638", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1650,7 +1650,7 @@ "content": "```suggestion\r\n return result;\r\n```\r\nresolve is unnecessary here and some other places I saw it too", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574427305", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", "score": { "formatting": { "content": { @@ -1679,7 +1679,7 @@ "content": "I'm not sure if it's a good idea to rely on logs for testing, is not possible to use output from the processor?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574441918", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -1700,7 +1700,7 @@ "content": "why so many nested functions? it becomes hard to read", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574458540", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -1721,7 +1721,7 @@ "content": "is there a reason we are using Decimal.js instead of native BigInt?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574460099", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1742,7 +1742,7 @@ "content": "Shouldn't task be issue opening item and specification is pull request opening item? At least that's how I understand from the code.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574487172", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1763,7 +1763,7 @@ "content": "I assume this is a permit for task assignee, but where are other permits or is that not done yet?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574492061", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -1784,7 +1784,7 @@ "content": "gotcha, I thought this html is for all users", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575851470", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -1805,7 +1805,7 @@ "content": "I can see from the example that it works correctly but when reading the code I get confused\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/12/files#diff-03f6b1f98f9f1df2651b65c46241c43e65d9544f7ea1de71e94255e757b3f2f0R108\r\nas I understand the if statement will be true if the comment is the first/opening comment of the issue or pull request so then it checks if it's issue then it will be TASK otherwise (if it's PR) it will be SPECIFICATION?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576482609", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1826,7 +1826,7 @@ "content": "Ok so we need it for precise floating point calculations, in that case BigInt is not applicable.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578040543", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1847,7 +1847,7 @@ "content": "why is it `ISSUER` which is meant for author of issue/review. wouldn't it make more sense to use `ASIGNEE`?\r\nisn't specification the first comment of the issue so how is it inside PR?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578050965", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1872,7 +1872,7 @@ "content": "this makes more sense!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1579556333", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { diff --git a/tests/__mocks__/results/output.html b/tests/__mocks__/results/output.html index dca09ebf..b51fcbcf 100644 --- a/tests/__mocks__/results/output.html +++ b/tests/__mocks__/results/output.html @@ -291,7 +291,7 @@ "content": "I think in this case it makes sense because this function will return `\"\"` if nothing is found. If I return `undefined` or `null` it will be interpreted in HTML as a string and will display the text null or undefined.\r\nI changed the code to always return `content` so it forces it to be properly initialized which should avoid mistakes.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570010044", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -316,7 +316,7 @@ "content": "It is supposed to represent a comment. Would you prefer a descriptive action such as `COMMENTED`?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570011467", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -341,7 +341,7 @@ "content": "True, just picked this up from the previous codebase. https://rpc.gnosischain.com should do it for both then?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570014142", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -362,7 +362,7 @@ "content": "Seems convoluted to me and I don't know what problem that solved, but sure can do. Because either way this will yield an empty string or populated string?\r\nEither way changed to a `const string[]` to minimize mistakes then!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570187141", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -387,7 +387,7 @@ "content": "Changed `COMMENT` to `COMMENTED`, added jsdoc description for each value\r\nhttps://github.com/ubiquibot/conversation-rewards/blob/559c13c9a7d548519a6434e1b8393fd0d5c8f3db/src/issue-activity.ts#L21", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570188450", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -412,7 +412,7 @@ "content": "Simply makes the docs linking the code within the comment, for convenience, and looks like this\r\n<img width=\"598\" alt=\"image\" src=\"https://github.com/ubiquibot/conversation-rewards/assets/9807008/76a60610-f3e4-4886-a9bd-c34a4c67a198\">\r\nAlso be careful you linked a random GitHub account within your comment", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573413974", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -433,7 +433,7 @@ "content": "In the case of a string it would coerce the value and you would get \"null\" as a string so I get your point indeed", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573416633", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -454,7 +454,7 @@ "content": "`observe` would help to chose what we want to include and ignore? We surely can add more properties as it goes. The purpose of this section is to attribute different multipliers based on the type of comment, mimicking the old [codebase](https://github.com/ubiquibot/comment-incentives/blob/525fcdccc8f0d9032a41cdaed3b4fbc6514d44b6/src/handlers/issue/comment-scoring-by-contribution-style.ts)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573420718", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -483,7 +483,7 @@ "content": "Not very useful indeed. I am working on the tests on a separate PR to avoid cluttering that one. I didn't cover all the tests cases yet!\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731420", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -504,7 +504,7 @@ "content": "See https://github.com/ubiquibot/conversation-rewards/pull/14 for the soon up to date mocks", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731649", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -525,7 +525,7 @@ "content": "Good point, also symbol should not be DAI but ETH isn't it?\r\n`pay.ubq.fi` seems to be using https://rpc.mevblocker.io for its RPC which seems to have low latency, a good privacy and score according to Chainlist as well.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573733603", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -550,7 +550,7 @@ "content": "Had changed it everywhere else but here, now it should be fine", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573737049", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -571,7 +571,7 @@ "content": "This matches any whitespace character (spaces, tabs, line breaks) so basically I make the produced HTML one liner. This helps to collect it from the CLI, makes the string shorter and more compact.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574114225", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">\n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ </a>\n+ </h3>\n+ <h6>\n+ @${username}\n+ </h6>\n+ </b>\n+ </summary>\n+ <h6>Contributions Overview</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>View</th>\n+ <th>Contribution</th>\n+ <th>Count</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createContributionRows()}\n+ </tbody>\n+ </table>\n+ <h6>Conversation Incentives</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>Comment</th>\n+ <th>Formatting</th>\n+ <th>Relevance</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createIncentiveRows()}\n+ </tbody>\n+ </table>\n+ </details>\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">\n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ </a>\n+ </h3>\n+ <h6>\n+ @${username}\n+ </h6>\n+ </b>\n+ </summary>\n+ <h6>Contributions Overview</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>View</th>\n+ <th>Contribution</th>\n+ <th>Count</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createContributionRows()}\n+ </tbody>\n+ </table>\n+ <h6>Conversation Incentives</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>Comment</th>\n+ <th>Formatting</th>\n+ <th>Relevance</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createIncentiveRows()}\n+ </tbody>\n+ </table>\n+ </details>\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -592,7 +592,7 @@ "content": "By definition `dump` was just outputting results to stdout, but I could make it return the result as well. Since the console log is what we want to make sure works properly to be reused by other workflows, I thought it would make sense to test it that way because potentially if we forget other logs somewhere that would mess up the output for the next Action step.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574622869", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -617,7 +617,7 @@ "content": "Un-nested them one level. I think that the `buildXrow` should just be contained by that function because its purpose only serves inside of it.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574702577", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -642,7 +642,7 @@ "content": "I assumed BigInt only handles integers, and we manipulate a lot of floating decimal values for our operations (mostly everything has a floating point)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574724902", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -663,7 +663,7 @@ "content": "There is one permit generated per participating user, last run:\r\nhttps://github.com/Meniole/test/issues/1#issuecomment-2069443938", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574767984", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">", "score": { "formatting": { "content": { @@ -684,7 +684,7 @@ "content": "I made it according to the previous version:\r\n- Issue Specification means the item when opening an issue\r\n- Issue Task means the item that fixed the issue\r\nExample with the current comments:\r\nhttps://github.com/ubiquibot/comment-incentives/issues/31#issuecomment-1998609326", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575659438", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -713,7 +713,7 @@ "content": "Wouldn't that lead to precision loss in JS and after the division we could end up with 42.000000001 results? That's also what `DecimalJs` fixes in this case.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575675704", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -738,7 +738,7 @@ "content": "That is correct. I understand now why you're confused:\r\nThe value for the task would be\r\n`\"ISSUE|ISSUER|TASK\"`\r\nbecause it is inside the `issue`\r\nThe value for the Specification would be\r\n`\"REVIEW|ISSUER|SPECIFICATION\"`\r\nbecause it is inside the `pull`\r\nI display `Issue: Specification` and `Issue: Task` just to stick to our current display format. Maybe instead I could display `Review: Task` instead?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576567583", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -763,7 +763,7 @@ "content": "I don't think is it relevant in this scenario because we are not manipulating big numbers. Decimaljs allows for non-precision loss on the operations because there is a lot of multiply / divide with floating points which can result in very long decimals due to Js nature.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577477616", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -784,7 +784,7 @@ "content": "@whilefoo you are correct, these were wrongly used. I corrected `TASK` and replaced it by `SPECIFICATION` so we have:\r\n`\"ISSUE|ISSUER|SPECIFICATION\"` and `\"REVIEW|ISSUER|TASK\"`\r\nI used `ISSUER` for this because technically the issuer is not necessarily the assignee in both cases.\r\nFix within next PR:\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14/commits/90e150f76073c9cfa16a251f8c1ae48a75aa5408", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578162024", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1124,7 +1124,7 @@ "content": "\"Comment\" implication isn't clear to me", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569964797", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1145,7 +1145,7 @@ "content": "I've always been anti empty string for variable declarations. It's led to too many problems with other less experienced developers. I wish for a linter rule to flag these as a consequence. \nConsider initializing an empty array as a buffer and then joining it at the end.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569970517", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1166,7 +1166,7 @@ "content": "Our RPCs don't work anymore", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569971792", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -1187,7 +1187,7 @@ "content": "Yes consider changing them to verbs \n`ISSUER`\n`COMMENTED`\nI'm not 100% the best way to name all of them I have to look at the full list", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570133378", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1212,7 +1212,7 @@ "content": "`[].join(\"\");` yields an empty string as well if there's nothing in the array.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570159594", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1237,7 +1237,7 @@ "content": "Perhaps it will make the config more expressive if you add other properties?\nFor example \n```yml\nobserve:\n views:\n - ISSUE\n - PULL\n actors:\n - ISSUER \n - COLLABORATOR \n actions: \n - COMMENTED\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570588757", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1266,7 +1266,7 @@ "content": "What is this @link syntax", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570589892", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -1287,7 +1287,7 @@ "content": "Just noticed the bit wise operators 1337 code", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570591425", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", + "diffHunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", "score": { "formatting": { "content": { @@ -1308,7 +1308,7 @@ "content": "It might seem convoluted but I think that it condenses the logic to coerce everything into a string as expected. This is a suggestion to be proactive when we set up the empty string linter one day. \nFor example if you `push` any element into the array (compared to `+=`) when you join they should render as expected. \n```js \n[ null, null ].join(\"\"); // \"\"\nnull += null; // 0 I think?\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570596007", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1337,7 +1337,7 @@ "content": "I am aware of its purpose. I am proposing to make the config more expressive so that its more intuitive to work with.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573720820", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1358,7 +1358,7 @@ "content": "Will you use array syntax?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573722731", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1379,7 +1379,7 @@ "content": "This regex appears to convert repeating spaces i.e. `\" \"` to a single space `\" \"` what is the purpose of this?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723262", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">\n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ </a>\n+ </h3>\n+ <h6>\n+ @${username}\n+ </h6>\n+ </b>\n+ </summary>\n+ <h6>Contributions Overview</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>View</th>\n+ <th>Contribution</th>\n+ <th>Count</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createContributionRows()}\n+ </tbody>\n+ </table>\n+ <h6>Conversation Incentives</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>Comment</th>\n+ <th>Formatting</th>\n+ <th>Relevance</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createIncentiveRows()}\n+ </tbody>\n+ </table>\n+ </details>\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">\n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ </a>\n+ </h3>\n+ <h6>\n+ @${username}\n+ </h6>\n+ </b>\n+ </summary>\n+ <h6>Contributions Overview</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>View</th>\n+ <th>Contribution</th>\n+ <th>Count</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createContributionRows()}\n+ </tbody>\n+ </table>\n+ <h6>Conversation Incentives</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>Comment</th>\n+ <th>Formatting</th>\n+ <th>Relevance</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createIncentiveRows()}\n+ </tbody>\n+ </table>\n+ </details>\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1404,7 +1404,7 @@ "content": "This doesn't seem right. Network ID 1 is mainnet. The RPC clearly is for gnosis chain. Perhaps try and find a nice mainnet RPC?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723774", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1425,7 +1425,7 @@ "content": "Seems like not a very useful mock.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724498", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1446,7 +1446,7 @@ "content": "Same here", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724853", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1467,7 +1467,7 @@ "content": "The token address indeed represents DAI on mainnet.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086028", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record<string, { rpc: string; token: string; symbol: string }> = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1488,7 +1488,7 @@ "content": "@gentlementlegen rfc", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086293", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">\n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ </a>\n+ </h3>\n+ <h6>\n+ @${username}\n+ </h6>\n+ </b>\n+ </summary>\n+ <h6>Contributions Overview</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>View</th>\n+ <th>Contribution</th>\n+ <th>Count</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createContributionRows()}\n+ </tbody>\n+ </table>\n+ <h6>Conversation Incentives</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>Comment</th>\n+ <th>Formatting</th>\n+ <th>Relevance</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createIncentiveRows()}\n+ </tbody>\n+ </table>\n+ </details>\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">\n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ </a>\n+ </h3>\n+ <h6>\n+ @${username}\n+ </h6>\n+ </b>\n+ </summary>\n+ <h6>Contributions Overview</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>View</th>\n+ <th>Contribution</th>\n+ <th>Count</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createContributionRows()}\n+ </tbody>\n+ </table>\n+ <h6>Conversation Incentives</h6>\n+ <table>\n+ <thead>\n+ <tr>\n+ <th>Comment</th>\n+ <th>Formatting</th>\n+ <th>Relevance</th>\n+ <th>Reward</th>\n+ </tr>\n+ </thead>\n+ <tbody>\n+ ${createIncentiveRows()}\n+ </tbody>\n+ </table>\n+ </details>\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1509,7 +1509,7 @@ "content": "Why not do it in the industry standard way? All values are denominated in `1e18` without floating point. \nAt the end, for rendering, we just need to divide by `1e18`", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575555444", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1534,7 +1534,7 @@ "content": "I'm not sure if I understand but task reward is if you completed the task (if you are an assignee when the issue is completed) and specification reward is if you write the specification (you are the original issue author)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575556553", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1555,7 +1555,7 @@ "content": "Spoke a bit on this in dms. @whilefoo my intent with referring to GitHub Issues as \"tasks\" was to emphasize that its a funded, and recognized \"real project\" so I want to refer to them as \"tasks\" instead of just \"issues.\"\r\nSince we are dealing so heavily with the GitHub API, I figured that there is benefit to being specific about what words we are using. Sure we can load in all the GitHub issues on a repository, but not all of them will be funded and recognized. \r\nWe are only primarily concerned with working with \"tasks\" aka funded GitHub issues.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576666170", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1576,7 +1576,7 @@ "content": "Possibly. I personally haven't had that issue but either way my suggestion is to keep everything internally without a decimal and then only convert it upon rendering.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577225638", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1653,7 +1653,7 @@ "content": "```suggestion\r\n return result;\r\n```\r\nresolve is unnecessary here and some other places I saw it too", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574427305", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", "score": { "formatting": { "content": { @@ -1682,7 +1682,7 @@ "content": "I'm not sure if it's a good idea to rely on logs for testing, is not possible to use output from the processor?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574441918", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -1703,7 +1703,7 @@ "content": "why so many nested functions? it becomes hard to read", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574458540", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -1724,7 +1724,7 @@ "content": "is there a reason we are using Decimal.js instead of native BigInt?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574460099", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1745,7 +1745,7 @@ "content": "Shouldn't task be issue opening item and specification is pull request opening item? At least that's how I understand from the code.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574487172", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1766,7 +1766,7 @@ "content": "I assume this is a permit for task assignee, but where are other permits or is that not done yet?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574492061", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">", "score": { "formatting": { "content": { @@ -1787,7 +1787,7 @@ "content": "gotcha, I thought this html is for all users", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575851470", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \"&#13;\");\n+ return `\n+ <tr>\n+ <td>\n+ <h6>\n+ <a href=\"${commentScore.url}\" target=\"_blank\" rel=\"noopener\">${commentScore.content.replace(/(.{64})..+/, \"$1…\")}</a>\n+ </h6>\n+ </td>\n+ <td>\n+ <details>\n+ <summary>\n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ </summary>\n+ <pre>${formatting}</pre>\n+ </details>\n+ </td>\n+ <td>${commentScore.score?.relevance || \"-\"}</td>\n+ <td>${commentScore.score?.reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+ <details>\n+ <summary>\n+ <b>\n+ <h3>\n+ <a href=\"${result.permitUrl}\" target=\"_blank\" rel=\"noopener\">", "score": { "formatting": { "content": { @@ -1808,7 +1808,7 @@ "content": "I can see from the example that it works correctly but when reading the code I get confused\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/12/files#diff-03f6b1f98f9f1df2651b65c46241c43e65d9544f7ea1de71e94255e757b3f2f0R108\r\nas I understand the if statement will be true if the comment is the first/opening comment of the issue or pull request so then it checks if it's issue then it will be TASK otherwise (if it's PR) it will be SPECIFICATION?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576482609", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1829,7 +1829,7 @@ "content": "Ok so we need it for precise floating point calculations, in that case BigInt is not applicable.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578040543", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ <tr>\n+ <td>${view}</td>\n+ <td>${contribution}</td>\n+ <td>${count}</td>\n+ <td>${reward || \"-\"}</td>\n+ </tr>`;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1850,7 +1850,7 @@ "content": "why is it `ISSUER` which is meant for author of issue/review. wouldn't it make more sense to use `ASIGNEE`?\r\nisn't specification the first comment of the issue so how is it inside PR?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578050965", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1875,7 +1875,7 @@ "content": "this makes more sense!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1579556333", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { diff --git a/tests/__mocks__/results/permit-generation-results.json b/tests/__mocks__/results/permit-generation-results.json index 74417ac6..0d340c73 100644 --- a/tests/__mocks__/results/permit-generation-results.json +++ b/tests/__mocks__/results/permit-generation-results.json @@ -288,7 +288,7 @@ "content": "I think in this case it makes sense because this function will return `\"\"` if nothing is found. If I return `undefined` or `null` it will be interpreted in HTML as a string and will display the text null or undefined.\r\nI changed the code to always return `content` so it forces it to be properly initialized which should avoid mistakes.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570010044", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -313,7 +313,7 @@ "content": "It is supposed to represent a comment. Would you prefer a descriptive action such as `COMMENTED`?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570011467", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -338,7 +338,7 @@ "content": "True, just picked this up from the previous codebase. https://rpc.gnosischain.com should do it for both then?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570014142", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -359,7 +359,7 @@ "content": "Seems convoluted to me and I don't know what problem that solved, but sure can do. Because either way this will yield an empty string or populated string?\r\nEither way changed to a `const string[]` to minimize mistakes then!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570187141", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -384,7 +384,7 @@ "content": "Changed `COMMENT` to `COMMENTED`, added jsdoc description for each value\r\nhttps://github.com/ubiquibot/conversation-rewards/blob/559c13c9a7d548519a6434e1b8393fd0d5c8f3db/src/issue-activity.ts#L21", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570188450", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -409,7 +409,7 @@ "content": "Simply makes the docs linking the code within the comment, for convenience, and looks like this\r\n\"image\"\r\nAlso be careful you linked a random GitHub account within your comment", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573413974", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -430,7 +430,7 @@ "content": "In the case of a string it would coerce the value and you would get \"null\" as a string so I get your point indeed", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573416633", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -451,7 +451,7 @@ "content": "`observe` would help to chose what we want to include and ignore? We surely can add more properties as it goes. The purpose of this section is to attribute different multipliers based on the type of comment, mimicking the old [codebase](https://github.com/ubiquibot/comment-incentives/blob/525fcdccc8f0d9032a41cdaed3b4fbc6514d44b6/src/handlers/issue/comment-scoring-by-contribution-style.ts)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573420718", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -480,7 +480,7 @@ "content": "Not very useful indeed. I am working on the tests on a separate PR to avoid cluttering that one. I didn't cover all the tests cases yet!\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731420", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -501,7 +501,7 @@ "content": "See https://github.com/ubiquibot/conversation-rewards/pull/14 for the soon up to date mocks", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573731649", "type": "PULL_AUTHOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -522,7 +522,7 @@ "content": "Good point, also symbol should not be DAI but ETH isn't it?\r\n`pay.ubq.fi` seems to be using https://rpc.mevblocker.io for its RPC which seems to have low latency, a good privacy and score according to Chainlist as well.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573733603", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -547,7 +547,7 @@ "content": "Had changed it everywhere else but here, now it should be fine", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573737049", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -568,7 +568,7 @@ "content": "This matches any whitespace character (spaces, tabs, line breaks) so basically I make the produced HTML one liner. This helps to collect it from the CLI, makes the string shorter and more compact.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574114225", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -589,7 +589,7 @@ "content": "By definition `dump` was just outputting results to stdout, but I could make it return the result as well. Since the console log is what we want to make sure works properly to be reused by other workflows, I thought it would make sense to test it that way because potentially if we forget other logs somewhere that would mess up the output for the next Action step.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574622869", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -614,7 +614,7 @@ "content": "Un-nested them one level. I think that the `buildXrow` should just be contained by that function because its purpose only serves inside of it.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574702577", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -639,7 +639,7 @@ "content": "I assumed BigInt only handles integers, and we manipulate a lot of floating decimal values for our operations (mostly everything has a floating point)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574724902", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -660,7 +660,7 @@ "content": "There is one permit generated per participating user, last run:\r\nhttps://github.com/Meniole/test/issues/1#issuecomment-2069443938", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574767984", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -681,7 +681,7 @@ "content": "I made it according to the previous version:\r\n- Issue Specification means the item when opening an issue\r\n- Issue Task means the item that fixed the issue\r\nExample with the current comments:\r\nhttps://github.com/ubiquibot/comment-incentives/issues/31#issuecomment-1998609326", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575659438", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -710,7 +710,7 @@ "content": "Wouldn't that lead to precision loss in JS and after the division we could end up with 42.000000001 results? That's also what `DecimalJs` fixes in this case.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575675704", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -735,7 +735,7 @@ "content": "That is correct. I understand now why you're confused:\r\nThe value for the task would be\r\n`\"ISSUE|ISSUER|TASK\"`\r\nbecause it is inside the `issue`\r\nThe value for the Specification would be\r\n`\"REVIEW|ISSUER|SPECIFICATION\"`\r\nbecause it is inside the `pull`\r\nI display `Issue: Specification` and `Issue: Task` just to stick to our current display format. Maybe instead I could display `Review: Task` instead?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576567583", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -760,7 +760,7 @@ "content": "I don't think is it relevant in this scenario because we are not manipulating big numbers. Decimaljs allows for non-precision loss on the operations because there is a lot of multiply / divide with floating points which can result in very long decimals due to Js nature.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577477616", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -781,7 +781,7 @@ "content": "@whilefoo you are correct, these were wrongly used. I corrected `TASK` and replaced it by `SPECIFICATION` so we have:\r\n`\"ISSUE|ISSUER|SPECIFICATION\"` and `\"REVIEW|ISSUER|TASK\"`\r\nI used `ISSUER` for this because technically the issuer is not necessarily the assignee in both cases.\r\nFix within next PR:\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/14/commits/90e150f76073c9cfa16a251f8c1ae48a75aa5408", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578162024", "type": "PULL_AUTHOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1120,7 +1120,7 @@ "content": "\"Comment\" implication isn't clear to me", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569964797", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1141,7 +1141,7 @@ "content": "I've always been anti empty string for variable declarations. It's led to too many problems with other less experienced developers. I wish for a linter rule to flag these as a consequence. \nConsider initializing an empty array as a buffer and then joining it at the end.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569970517", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1162,7 +1162,7 @@ "content": "Our RPCs don't work anymore", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1569971792", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc-bot.ubq.fi/v1/mainnet\",", "score": { "formatting": { "content": { @@ -1183,7 +1183,7 @@ "content": "Yes consider changing them to verbs \n`ISSUER`\n`COMMENTED`\nI'm not 100% the best way to name all of them I have to look at the full list", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570133378", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENT]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENT]:", "score": { "formatting": { "content": { @@ -1208,7 +1208,7 @@ "content": "`[].join(\"\");` yields an empty string as well if there's nothing in the array.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570159594", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1233,7 +1233,7 @@ "content": "Perhaps it will make the config more expressive if you add other properties?\nFor example \n```yml\nobserve:\n views:\n - ISSUE\n - PULL\n actors:\n - ISSUER \n - COLLABORATOR \n actions: \n - COMMENTED\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570588757", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1262,7 +1262,7 @@ "content": "What is this @link syntax", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570589892", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}", "score": { "formatting": { "content": { @@ -1283,7 +1283,7 @@ "content": "Just noticed the bit wise operators 1337 code", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570591425", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", + "diffHunk": "@@ -73,12 +103,17 @@ export class IssueActivity {\n self: GitHubPullRequest | GitHubIssue | null\n ) {\n let ret = 0;\n- ret |= \"pull_request_review_id\" in comment ? CommentType.REVIEW : CommentType.ISSUE;\n+ ret |= \"pull_request_review_id\" in comment || \"draft\" in comment ? CommentType.REVIEW : CommentType.ISSUE;", "score": { "formatting": { "content": { @@ -1304,7 +1304,7 @@ "content": "It might seem convoluted but I think that it condenses the logic to coerce everything into a string as expected. This is a suggestion to be proactive when we set up the empty string linter one day. \nFor example if you `push` any element into the array (compared to `+=`) when you join they should render as expected. \n```js \n[ null, null ].join(\"\"); // \"\"\nnull += null; // 0 I think?\n```", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1570596007", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1333,7 +1333,7 @@ "content": "I am aware of its purpose. I am proposing to make the config more expressive so that its more intuitive to work with.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573720820", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", + "diffHunk": "@@ -27,29 +27,39 @@ formattingEvaluator:\n td: 1\n hr: 0\n multipliers:\n- - type: [ISSUE, ISSUER]:\n+ - type: [ISSUE, ISSUER, TASK]:\n+ formattingMultiplier: 1\n+ wordValue: 0.1\n+ - type: [ISSUE, ISSUER, COMMENTED]:\n formattingMultiplier: 1\n wordValue: 0.2\n- - type: [ISSUE, ASSIGNEE]:\n+ - type: [ISSUE, ASSIGNEE, COMMENTED]:\n formattingMultiplier: 0\n wordValue: 0\n- - type: [ISSUE, COLLABORATOR]:\n+ - type: [ISSUE, COLLABORATOR, COMMENTED]:", "score": { "formatting": { "content": { @@ -1354,7 +1354,7 @@ "content": "Will you use array syntax?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573722731", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", + "diffHunk": "@@ -0,0 +1,212 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";", "score": { "formatting": { "content": { @@ -1375,7 +1375,7 @@ "content": "This regex appears to convert repeating spaces i.e. `\" \"` to a single space `\" \"` what is the purpose of this?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723262", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1400,7 +1400,7 @@ "content": "This doesn't seem right. Network ID 1 is mainnet. The RPC clearly is for gnosis chain. Perhaps try and find a nice mainnet RPC?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573723774", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1421,7 +1421,7 @@ "content": "Seems like not a very useful mock.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724498", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1442,7 +1442,7 @@ "content": "Same here", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1573724853", "type": "PULL_COLLABORATOR", - "diff_hunk": "", + "diffHunk": "", "score": { "formatting": { "content": { @@ -1463,7 +1463,7 @@ "content": "The token address indeed represents DAI on mainnet.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086028", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", + "diffHunk": "@@ -0,0 +1,22 @@\n+// available tokens for payouts\n+export const PAYMENT_TOKEN_PER_NETWORK: Record = {\n+ \"1\": {\n+ rpc: \"https://rpc.gnosischain.com\",\n+ token: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n+ symbol: \"DAI\",\n+ },", "score": { "formatting": { "content": { @@ -1484,7 +1484,7 @@ "content": "@gentlementlegen rfc", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574086293", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", + "diffHunk": "@@ -0,0 +1,216 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ let body = \"\";\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ body += result[key].evaluationCommentHtml;\n+ }\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ \n+ [ ${result.total} ${getPayoutConfigByNetworkId(program.opts().evmNetworkId).symbol} ]\n+ \n+

\n+
\n+ @${username}\n+
\n+
\n+
\n+
Contributions Overview
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createContributionRows()}\n+ \n+
ViewContributionCountReward
\n+
Conversation Incentives
\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ ${createIncentiveRows()}\n+ \n+
CommentFormattingRelevanceReward
\n+
\n+ `\n+ .replace(/\\s+/g, \" \")", "score": { "formatting": { "content": { @@ -1505,7 +1505,7 @@ "content": "Why not do it in the industry standard way? All values are denominated in `1e18` without floating point. \nAt the end, for rendering, we just need to divide by `1e18`", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575555444", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1530,7 +1530,7 @@ "content": "I'm not sure if I understand but task reward is if you completed the task (if you are an assignee when the issue is completed) and specification reward is if you write the specification (you are the original issue author)", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575556553", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1551,7 +1551,7 @@ "content": "Spoke a bit on this in dms. @whilefoo my intent with referring to GitHub Issues as \"tasks\" was to emphasize that its a funded, and recognized \"real project\" so I want to refer to them as \"tasks\" instead of just \"issues.\"\r\nSince we are dealing so heavily with the GitHub API, I figured that there is benefit to being specific about what words we are using. Sure we can load in all the GitHub issues on a repository, but not all of them will be funded and recognized. \r\nWe are only primarily concerned with working with \"tasks\" aka funded GitHub issues.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576666170", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1572,7 +1572,7 @@ "content": "Possibly. I personally haven't had that issue but either way my suggestion is to keep everything internally without a decimal and then only convert it upon rendering.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1577225638", "type": "PULL_COLLABORATOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1648,7 +1648,7 @@ "content": "```suggestion\r\n return result;\r\n```\r\nresolve is unnecessary here and some other places I saw it too", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574427305", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);", "score": { "formatting": { "content": { @@ -1677,7 +1677,7 @@ "content": "I'm not sure if it's a good idea to rely on logs for testing, is not possible to use output from the processor?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574441918", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", + "diffHunk": "@@ -0,0 +1,41 @@\n+import { parseGitHubUrl } from \"../src/start\";\n+import { IssueActivity } from \"../src/issue-activity\";\n+import { Processor } from \"../src/parser/processor\";\n+import { UserExtractorModule } from \"../src/parser/user-extractor-module\";\n+import { server } from \"./__mocks__/node\";\n+import { DataPurgeModule } from \"../src/parser/data-purge-module\";\n+import userCommentResults from \"./__mocks__/results/user-comment-results.json\";\n+import dataPurgeResults from \"./__mocks__/results/data-purge-result.json\";\n+\n+const issueUrl = process.env.TEST_ISSUE_URL || \"https://github.com/ubiquibot/comment-incentives/issues/22\";\n+\n+beforeAll(() => server.listen());\n+afterEach(() => server.resetHandlers());\n+afterAll(() => server.close());\n+\n+describe(\"Modules tests\", () => {\n+ const issue = parseGitHubUrl(issueUrl);\n+ const activity = new IssueActivity(issue);\n+\n+ beforeAll(async () => {\n+ await activity.init();\n+ });\n+\n+ it(\"Should extract users from comments\", async () => {\n+ const logSpy = jest.spyOn(console, \"log\");\n+ const processor = new Processor();\n+ processor[\"_transformers\"] = [new UserExtractorModule()];\n+ await processor.run(activity);\n+ processor.dump();\n+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(userCommentResults, undefined, 2));", "score": { "formatting": { "content": { @@ -1698,7 +1698,7 @@ "content": "why so many nested functions? it becomes hard to read", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574458540", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(", "score": { "formatting": { "content": { @@ -1719,7 +1719,7 @@ "content": "is there a reason we are using Decimal.js instead of native BigInt?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574460099", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1740,7 +1740,7 @@ "content": "Shouldn't task be issue opening item and specification is pull request opening item? At least that's how I understand from the code.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574487172", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1761,7 +1761,7 @@ "content": "I assume this is a permit for task assignee, but where are other permits or is that not done yet?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1574492061", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -1782,7 +1782,7 @@ "content": "gotcha, I thought this html is for all users", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1575851470", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ function createIncentiveRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function buildIncentiveRow(commentScore: GithubCommentScore) {\n+ // Properly escape carriage returns for HTML rendering\n+ const formatting = stringify(commentScore.score?.formatting?.content).replace(/[\\n\\r]/g, \" \");\n+ return `\n+ \n+ \n+
\n+ ${commentScore.content.replace(/(.{64})..+/, \"$1…\")}\n+
\n+ \n+ \n+
\n+ \n+ ${Object.values(commentScore.score?.formatting?.content || {}).reduce((acc, curr) => {\n+ return acc.add(curr.score * curr.count);\n+ }, new Decimal(0))}\n+ \n+
${formatting}
\n+
\n+ \n+ ${commentScore.score?.relevance || \"-\"}\n+ ${commentScore.score?.reward || \"-\"}\n+ `;\n+ }\n+\n+ for (const issueComment of sorted.issues.comments) {\n+ content.push(buildIncentiveRow(issueComment));\n+ }\n+ for (const reviewComment of sorted.reviews) {\n+ content.push(buildIncentiveRow(reviewComment));\n+ }\n+ return content.join(\"\");\n+ }\n+\n+ return `\n+
\n+ \n+ \n+

\n+ ", "score": { "formatting": { "content": { @@ -1803,7 +1803,7 @@ "content": "I can see from the example that it works correctly but when reading the code I get confused\r\nhttps://github.com/ubiquibot/conversation-rewards/pull/12/files#diff-03f6b1f98f9f1df2651b65c46241c43e65d9544f7ea1de71e94255e757b3f2f0R108\r\nas I understand the if statement will be true if the comment is the first/opening comment of the issue or pull request so then it checks if it's issue then it will be TASK otherwise (if it's PR) it will be SPECIFICATION?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1576482609", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1824,7 +1824,7 @@ "content": "Ok so we need it for precise floating point calculations, in that case BigInt is not applicable.", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578040543", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", + "diffHunk": "@@ -0,0 +1,217 @@\n+import { Value } from \"@sinclair/typebox/value\";\n+import Decimal from \"decimal.js\";\n+import * as fs from \"fs\";\n+import { stringify } from \"yaml\";\n+import configuration from \"../configuration/config-reader\";\n+import githubCommentConfig, { GithubCommentConfiguration } from \"../configuration/github-comment-config\";\n+import { getOctokitInstance } from \"../get-authentication-token\";\n+import { CommentType, IssueActivity } from \"../issue-activity\";\n+import { parseGitHubUrl } from \"../start\";\n+import { getPayoutConfigByNetworkId } from \"../types/payout\";\n+import program from \"./command-line\";\n+import { GithubCommentScore, Module, Result } from \"./processor\";\n+\n+/**\n+ * Posts a GitHub comment according to the given results.\n+ */\n+export class GithubCommentModule implements Module {\n+ private readonly _configuration: GithubCommentConfiguration = configuration.githubComment;\n+ private readonly _debugFilePath = \"./output.html\";\n+\n+ async transform(data: Readonly, result: Result): Promise {\n+ const bodyArray: (string | undefined)[] = [];\n+\n+ for (const [key, value] of Object.entries(result)) {\n+ result[key].evaluationCommentHtml = this._generateHtml(key, value);\n+ bodyArray.push(result[key].evaluationCommentHtml);\n+ }\n+ const body = bodyArray.join(\"\");\n+ if (this._configuration.debug) {\n+ fs.writeFileSync(this._debugFilePath, body);\n+ }\n+ if (this._configuration.post) {\n+ try {\n+ const octokit = getOctokitInstance();\n+ const { owner, repo, issue_number } = parseGitHubUrl(program.opts().issue);\n+\n+ await octokit.issues.createComment({\n+ body,\n+ repo,\n+ owner,\n+ issue_number,\n+ });\n+ } catch (e) {\n+ console.error(`Could not post GitHub comment: ${e}`);\n+ }\n+ }\n+ return Promise.resolve(result);\n+ }\n+\n+ get enabled(): boolean {\n+ if (!Value.Check(githubCommentConfig, this._configuration)) {\n+ console.warn(\"Invalid configuration detected for GithubContentModule, disabling.\");\n+ return false;\n+ }\n+ return true;\n+ }\n+\n+ _generateHtml(username: string, result: Result[0]) {\n+ const sorted = result.comments?.reduce<{\n+ issues: { task: GithubCommentScore | null; comments: GithubCommentScore[] };\n+ reviews: GithubCommentScore[];\n+ }>(\n+ (acc, curr) => {\n+ if (curr.type & CommentType.ISSUE) {\n+ if (curr.type & CommentType.TASK) {\n+ acc.issues.task = curr;\n+ } else {\n+ acc.issues.comments.push(curr);\n+ }\n+ } else if (curr.type & CommentType.REVIEW) {\n+ acc.reviews.push(curr);\n+ }\n+ return acc;\n+ },\n+ { issues: { task: null, comments: [] }, reviews: [] }\n+ );\n+\n+ function createContributionRows() {\n+ const content: string[] = [];\n+\n+ if (!sorted) {\n+ return content.join(\"\");\n+ }\n+\n+ function generateContributionRow(\n+ view: string,\n+ contribution: string,\n+ count: number,\n+ reward: number | Decimal | undefined\n+ ) {\n+ return `\n+ \n+ ${view}\n+ ${contribution}\n+ ${count}\n+ ${reward || \"-\"}\n+ `;\n+ }\n+\n+ if (result.task?.reward) {\n+ content.push(generateContributionRow(\"Issue\", \"Task\", 1, result.task.reward));\n+ }\n+ if (sorted.issues.task) {\n+ content.push(generateContributionRow(\"Issue\", \"Specification\", 1, sorted.issues.task.score?.reward));\n+ }\n+ if (sorted.issues.comments.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Issue\",\n+ \"Comment\",\n+ sorted.issues.comments.length,\n+ sorted.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))\n+ )\n+ );\n+ }\n+ if (sorted.reviews.length) {\n+ content.push(\n+ generateContributionRow(\n+ \"Review\",\n+ \"Comment\",\n+ sorted.reviews.length,\n+ sorted.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))", "score": { "formatting": { "content": { @@ -1845,7 +1845,7 @@ "content": "why is it `ISSUER` which is meant for author of issue/review. wouldn't it make more sense to use `ASIGNEE`?\r\nisn't specification the first comment of the issue so how is it inside PR?", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1578050965", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": { @@ -1870,7 +1870,7 @@ "content": "this makes more sense!", "url": "https://github.com/ubiquibot/conversation-rewards/pull/12#discussion_r1579556333", "type": "PULL_CONTRIBUTOR", - "diff_hunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", + "diffHunk": "@@ -19,12 +19,42 @@ import {\n } from \"./start\";\n \n export enum CommentType {\n+ /**\n+ * Review related item\n+ */\n REVIEW = 0b1,\n+ /**\n+ * Issue related item\n+ */\n ISSUE = 0b10,\n+ /**\n+ * User assigned to the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ASSIGNEE = 0b100,\n+ /**\n+ * The author of the {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n ISSUER = 0b1000,\n+ /**\n+ * A user that is part of the organization or owner of the repo\n+ */\n COLLABORATOR = 0b10000,\n+ /**\n+ * A user that is NOT part of the organization nor owner of the repo\n+ */\n CONTRIBUTOR = 0b100000,\n+ /**\n+ * A user comment action on a {@link CommentType.ISSUE} or {@link CommentType.REVIEW}\n+ */\n+ COMMENTED = 0b1000000,\n+ /**\n+ * Pull request opening item\n+ */\n+ TASK = 0b10000000,", "score": { "formatting": { "content": {