diff --git a/assets/radar-rules.js b/assets/radar-rules.js index a66587686a..ff82fb4846 100644 --- a/assets/radar-rules.js +++ b/assets/radar-rules.js @@ -227,6 +227,12 @@ source: '/:user', target: '/github/starred_repos/:user', }, + { + title: '仓库 Contributors', + docs: 'https://docs.rsshub.app/programming.html#github', + source: ['/:user/:repo/graphs/contributors', '/:user/:repo'], + target: '/github/contributors/:user/:repo', + }, ], }, 'zhihu.com': { diff --git a/docs/en/programming.md b/docs/en/programming.md index d8890c8904..005d0539dc 100644 --- a/docs/en/programming.md +++ b/docs/en/programming.md @@ -77,6 +77,10 @@ GitHub provides some official RSS feeds: +### Repo Contributors + + + ## GitLab ### Explore diff --git a/docs/programming.md b/docs/programming.md index 4b8a08c58b..3a9f60c993 100644 --- a/docs/programming.md +++ b/docs/programming.md @@ -127,6 +127,10 @@ GitHub 官方也提供了一些 RSS: +### 仓库 Contirbutors + + + ## GitLab ### Explore diff --git a/lib/router.js b/lib/router.js index 1051724506..ffdd62e26e 100644 --- a/lib/router.js +++ b/lib/router.js @@ -275,6 +275,8 @@ router.get('/github/search/:query/:sort?/:order?', require('./routes/github/sear router.get('/github/branches/:user/:repo', require('./routes/github/branches')); router.get('/github/file/:user/:repo/:branch/:filepath+', require('./routes/github/file')); router.get('/github/starred_repos/:user', require('./routes/github/starred_repos')); +router.get('/github/contributors/:user/:repo/:anon?', require('./routes/github/contributors')); + // f-droid router.get('/fdroid/apprelease/:app', require('./routes/fdroid/apprelease')); diff --git a/lib/routes/github/contributors.js b/lib/routes/github/contributors.js new file mode 100644 index 0000000000..dfbff149e8 --- /dev/null +++ b/lib/routes/github/contributors.js @@ -0,0 +1,85 @@ +const got = require('@/utils/got'); +const config = require('@/config').value; + +module.exports = async (ctx) => { + const user = ctx.params.user; + const repo = ctx.params.repo; + const anon = ctx.params.anon; + + const host = `https://github.com/${user}/${repo}`; + const url = `https://api.github.com/repos/${user}/${repo}/contributors?` + (anon ? 'anon=1' : ''); + + const headers = {}; + if (config.github && config.github.access_token) { + headers.Authorization = `token ${config.github.access_token}`; + } + + const response = await got({ + method: 'get', + url, + headers, + }); + let data = response.data; + + try { + const last_page_link = response.headers.link.split(',').find(function(elem) { + return elem.includes('"last"'); + }); + const url_base = last_page_link.match(/<(.*)page=\d*/)[1]; + const page_count = Number(last_page_link.match(/page=(\d*)/)[1]); + + for (let page = 2; page <= page_count; page++) { + // eslint-disable-next-line no-await-in-loop + const response = await got({ + method: 'get', + url: `${url_base}page=${page}`, + headers, + }); + data = data.concat(response.data); + } + } catch (err) { + if (!(err instanceof TypeError)) { + throw err; + } + } + + let title; + let description; + let link; + let guid; + + const items = []; + let index = 0; + + data.forEach(function(item) { + const time = new Date(); + time.setMinutes(time.getMinutes() - data.length + index); + index++; + + if (item.type === 'Anonymous') { + title = `Contributor: ${item.name}`; + description = `

Anonymous contributor

Name: ${item.name}

E-mail: ${item.email}

Contributions: ${item.contributions}

`; + link = ''; + guid = `anon-${item.name}`; + } else { + title = `Contributor: ${item.login}`; + description = `

${item.login}

Contributions: ${item.contributions}

`; + link = item.html_url; + guid = item.id; + } + + items.push({ + title: title, + description: description, + guid: guid, + link: link, + pubDate: time, + }); + }), + (ctx.state.data = { + title: `${user}/${repo} Contributors`, + link: `${host}/graphs/contributors`, + description: `New contributors for ${user}/${repo}`, + item: items, + }); +};