lib/backgroundFinder.js
37 |
/**
38 | * backgroundFinder module
39 | */
40 |
41 | module.exports = ({ dependencies: { extractRepoDetails, getUserDiscussionIssue, getUserCommentedIssues, getCommentsOnIssue, sentimentAnalyser, createDiscussionIssue } }) => {
42 | /**
43 | * backgroundFinder
44 | * @param {object} context - context argument.
45 | */
46 | const backgroundFinder = async(context) => {
47 | if (context.isBot) return
48 | const owner = context.payload.repository.owner.login
49 | const username = context.payload.comment.user.login
50 | context.log(`Analyis for ${username} has started`)
51 |
52 | /* -- Check if user is already found hostile -- */
53 | const userIssueResult = await getUserDiscussionIssue(context, { owner, username })
54 | const alreadyFoundHostile = userIssueResult.data.total_count >= 1
55 | if (alreadyFoundHostile) {
56 | context.log(`${username} has already been found hostile before.\nTerminating ${username}'s analysis.`)
57 | return
58 | }
59 | /* ^- -^ */
60 |
61 | context.log(`${username} has not been found hostile before. \nCollection of Public Comments starting.`)
62 | const issuesResult = await getUserCommentedIssues(context, { username })
63 | const { data: { items: issues } } = issuesResult
64 | const userToxicComments = []
65 | let userToxicCommentsLimit = 3
66 | let totalUserComments = 0
67 |
68 | for (let issue of issues) {
69 | // for each issue in search result
70 | const issueNum = issue.number
71 | const repoUrl = issue.repository_url
72 | context.log(`Fetching comments on ${issue.url}`)
73 | const { data: commentsOnIssue } = await getCommentsOnIssue(context, { issueNum, ...extractRepoDetails(repoUrl) })
74 |
75 | // we'll get 30 comments on issueNum issue
76 | const userCommentsOnIssue = [] // stores only the username's comments
77 | for (let comment of commentsOnIssue) {
78 | if (comment.user.type !== 'Bot' && comment.user.login === username) {
79 | userCommentsOnIssue.push(comment)
80 | }
81 | }
82 | totalUserComments += userCommentsOnIssue.length
83 |
84 | let commentBlob = userCommentsOnIssue.map(comment => comment.body).join('.\n')
85 | if (commentBlob.length <= 0) continue // empty commentBlob
86 |
87 | const toxicScore = await sentimentAnalyser(commentBlob.substr(0, 3000)) // perspective api limits the text size to 3000B
88 | const isToxic = toxicScore >= 0.6
89 | context.log('toxicScore is', toxicScore)
90 | if (isToxic) {
91 | userToxicCommentsLimit -= 1
92 | userToxicComments.push({
93 | link: issue.html_url,
94 | text: commentBlob,
95 | toxicScore
96 | })
97 | }
98 |
99 | if (userToxicCommentsLimit <= 0) {
100 | const { data: discussionIssueData } = await createDiscussionIssue(context, { username, owner, toxicComments: userToxicComments })
101 | context.log(`Discussion issue for ${username} has been created. Check out ${discussionIssueData.html_url}`)
102 | context.log(`${username}'s ${totalUserComments} comments were analysed`)
103 | break
104 | }
105 | }
106 | }
107 |
108 | return backgroundFinder
109 | }
110 |
111 |
112 |
113 |
114 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/docs/file/lib/getPublicComments.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/getPublicComments.js
37 |
const extractRepoDetails = require('./utils/extractRepoDetailsFromUrl')
38 | const filterUserComments = require('./utils/filterUserComments')
39 | const getUserCommentedIssues = require('./github-api/getUserCommentedIssues')
40 | const getCommentsOnIssue = require('./github-api/getCommentsOnIssue')
41 |
42 | module.exports = async(context, { username }) => {
43 | const issuesResult = await getUserCommentedIssues(context, { username })
44 | const { data: { total_count, items: issues } } = issuesResult
45 | console.log('total_count', total_count)
46 | const commentsResults = await getCommentsOnIssues(context, { issues })
47 | const userComments = getUserComments(commentsResults, username)
48 | return userComments
49 | }
50 |
51 | async function getCommentsOnIssues (context, { issues }) {
52 | const commentsPromises = issues
53 | .map(({ number, repository_url: url }) => getCommentsOnIssue(context, { issueNum: number, ...extractRepoDetails(url) }))
54 | return Promise.all(commentsPromises)
55 | }
56 |
57 | function getUserComments (commentsResults, username) {
58 | const userComments = []
59 | commentsResults.forEach(({ data: commentsData }) => {
60 | const userCommentsOnIssue = filterUserComments(commentsData, username)
61 | // userCommentsOnIssue is an array holding the wanted user's comment on current issue
62 | userComments.push(...userCommentsOnIssue) // flatten and store the array
63 | })
64 | // userComments hold all his comments on all issues
65 | return userComments
66 | }
67 |
68 |
69 |
70 |
71 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/docs/file/lib/github-api/createDiscussionIssue.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/github-api/createDiscussionIssue.js
37 |
module.exports = (context, { owner, username, toxicComments }) => {
38 | toxicComments.sort((a, b) => b.toxicScore - a.toxicScore) // sort in decreasing toxicScore order
39 | const toxicDataText = toxicComments.map(({ link, text, toxicScore }) => `
40 | 1. ${link} has a toxicity rating of **${toxicScore}** :
41 |
42 | > ${text}
43 | `).join('\n')
44 |
45 | return context.github.issues.create({
46 | owner,
47 | repo: 'maintainers-discussion',
48 | title: `${username}-discussion`,
49 | body: `Some comments from @${username} have been found to be toxic. Review the following comments and discuss whether or not you would like to allow this contributor to participate in your community.
50 |
51 | ${toxicDataText}`
52 | })
53 | }
54 |
55 |
56 |
57 |
58 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/docs/file/lib/github-api/getCommentsOnIssue.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/github-api/getCommentsOnIssue.js
37 |
module.exports = (context, { owner, repo, issueNum }) => {
38 | return context.github.issues.getComments({
39 | owner,
40 | repo,
41 | number: issueNum
42 | })
43 | }
44 |
45 |
46 |
47 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/docs/file/lib/github-api/getUserCommentedIssues.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/github-api/getUserCommentedIssues.js
37 |
module.exports = (context, { username }) => {
38 | return context.github.search.issues({
39 | q: `commenter:${username}`
40 | })
41 | }
42 |
43 |
44 |
45 |
46 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/docs/file/lib/github-api/getUserDiscussionIssue.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/github-api/getUserDiscussionIssue.js
37 |
module.exports = (context, { owner, username }) => {
38 | return context.github.search.issues({
39 | q: `repo:${owner}/maintainers-discussion is:issue is:open in:title ${username}-discussion`
40 | })
41 | }
42 |
43 |
44 |
45 |
46 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/docs/file/lib/utils/analyseSentiment.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/utils/analyseSentiment.js
37 |
module.exports = (perspectiveApiKey, { dependencies: { request } }) => {
38 | const BASE_URL = 'https://commentanalyzer.googleapis.com/v1alpha1'
39 | const API_URL = `${BASE_URL}/comments:analyze?key=${perspectiveApiKey}`
40 |
41 | const analyseSentiment = async (text) => {
42 | if (!text) return -1
43 | const textLength = text.length
44 | if (textLength < 1 || textLength > 3000) return -1 // perspective api text limit 3000B
45 | try {
46 | const response = await request.post(API_URL)
47 | .set('Content-Type', 'application/json')
48 | .send({
49 | comment: { text },
50 | languages: ['en'],
51 | requestedAttributes: { TOXICITY: {} }
52 | })
53 |
54 | const score = response.body.attributeScores.TOXICITY.summaryScore.value
55 | return score
56 | } catch (err) {
57 | console.log('err', err)
58 | return -1
59 | }
60 | }
61 |
62 | return analyseSentiment
63 | }
64 |
65 |
66 |
67 |
68 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/file/lib/utils/extractRepoDetailsFromUrl.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/utils/extractRepoDetailsFromUrl.js
37 |
module.exports = (repoUrl) => {
38 | const lastSlashIndex = repoUrl.lastIndexOf('/')
39 | const repo = repoUrl.substr(lastSlashIndex + 1)
40 | const repoRemovedUrl = repoUrl.slice(0, lastSlashIndex)
41 | const secondLastSlashIndex = repoRemovedUrl.lastIndexOf('/')
42 | const owner = repoRemovedUrl.substr(secondLastSlashIndex + 1)
43 | return { owner, repo }
44 | }
45 |
46 |
47 |
48 |
49 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/docs/file/lib/utils/filterUserComments.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/utils/filterUserComments.js
37 |
module.exports = (comments, username) => {
38 | const userComments = comments.filter(comment => comment.user.login === username)
39 | return userComments
40 | }
41 |
42 |
43 |
44 |
45 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/docs/file/lib/utils/getBinarySize.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lib/utils/getBinarySize.js
37 |
function getBinarySize (s) {
38 | return Buffer.byteLength(s, 'utf16')
39 | }
40 |
41 | module.exports = getBinarySize
42 |
43 |
44 |
45 |
46 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/docs/identifiers.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |