Skip to content

Instantly share code, notes, and snippets.

@geektutu
Last active December 28, 2022 13:51
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save geektutu/f379d87767787979507a0e4a20da64ba to your computer and use it in GitHub Desktop.
Save geektutu/f379d87767787979507a0e4a20da64ba to your computer and use it in GitHub Desktop.
模仿disqus的 SEE ALSO ON <blog> 功能,为静态博客添加“显示本站其他文章评论”的功能。 https://geektutu.com/post/blog-experience-7.html
/**
* Generate Comments JSON Data
*/
const https = require('https');
const fs = require('fs');
const github = {
client_id: '<your-client-id>',
client_secret: '<your-client-secret>',
repo: '<your-github-repo>',
owner: '<your-github-name>'
}
const PREFIX = `/repos/${github.owner}/${github.repo}/`
const AUTH = `&client_id=${github.client_id}&client_secret=${github.client_secret}`
const PAGING = '&sort=created&direction=desc&per_page=100'
class Comments {
constructor() {
this.comments = []
this.issueMap = []
this.obj = {}
}
deltaDate(old) {
let hours = (Date.now() - new Date(old)) / 1000 / 3600
let years = Math.floor(hours / 24 / 365)
if (years) {
return `${years}年前`
}
let months = Math.floor(hours / 24 / 30)
if (months) {
return `${months}月前`
}
let days = Math.floor(hours / 24)
if (days) {
return `${days}天前`
}
hours = Math.floor(hours)
return `${hours}小时前`
}
async parse() {
this.comments = await this.get(`issues/comments?${PAGING}`)
console.log(`comments.length: ${this.comments.length}`)
await this.writeComments()
}
async fetchIssue(issueUrl) {
let issueApi = issueUrl.slice(issueUrl.indexOf(PREFIX) + PREFIX.length)
if (!this.issueMap[issueApi]) {
let issue = await this.get(issueApi + '?')
if (!issue.labels.find(label => label.name === 'Gitalk')) {
return
}
issue.post = issue.labels.find(label => label.name.startsWith("/")).name
issue.title = issue.title.split('|')[0].trim()
this.issueMap[issueApi] = issue
}
return this.issueMap[issueApi]
}
async writeComments() {
let simpleComments = {}
for (const comment of this.comments) {
let issue = await this.fetchIssue(comment.issue_url)
if (!issue) {
continue
}
if (issue.user.login === comment.user.login) {
continue
}
if (simpleComments[issue.post]) {
continue
}
simpleComments[issue.post] = {
title: issue.title,
url: issue.post,
count: issue.comments,
user: comment.user.login,
icon: comment.user.avatar_url,
date: this.deltaDate(comment.created_at),
body: comment.body.replace(/</g, " ").replace(/>/g, " ").replace(/\s+/g, " ").trim()
}
}
let obj = Object.keys(simpleComments).map(key => simpleComments[key])
fs.writeFileSync("comments.json", JSON.stringify(obj), { encoding: 'utf-8' });
console.log(`write ${obj.length} success!`)
}
get(api) {
let options = {
hostname: 'api.github.com',
path: `${PREFIX}${api}${AUTH}`,
headers: { 'User-Agent': 'Node Https Client' }
};
console.log(`GET ${options.path}`)
return new Promise((resolve, reject) => {
const req = https.get(options, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => resolve(JSON.parse(data)));
});
req.on('error', (e) => reject(e));
req.end();
});
}
}
(async () => {
client = new Comments()
await client.parse()
})();
<div id="gitalk-related"></div>
<style>
#gitalk-related.dis-wrapper {
font-size: 14px;
margin: 70px 0;
overflow: hidden;
padding-bottom: 10px;
}
#gitalk-related * {
margin: 0;
padding: 0;
box-sizing: border-box;
text-decoration: none;
transform: none;
-webkit-transform: none;
}
#gitalk-related .dis-divide {
border-top: 2px solid #e7e9ee;
width: 100%;
float: left;
padding: 5px 10px;
margin-top: 20px;
text-align: center;
}
#gitalk-related .dis-item {
width: 50%;
float: left;
padding: 0px 10px;
margin-top: 20px;
height: 108px;
overflow: hidden;
}
#gitalk-related .dis-item-title {
font-size: 16px;
color: #6190e8;
font-weight: 500;
max-height: 3em;
line-height: 1.5;
overflow: hidden;
}
#gitalk-related .dis-item-des {
font-size: 0.85em;
}
#gitalk-related .dis-divide a,
#gitalk-related .dis-item-des,
#gitalk-related .dis-item-user {
color: rgba(30, 55, 70, .4);
}
#gitalk-related .dis-item-img {
width: 30px;
height: 30px;
margin-top: 3px;
}
#gitalk-related .dis-item-content {
display: flex;
}
#gitalk-related .dis-item-content>p {
padding-left: 10px;
color: #333;
line-height: 1.4em;
max-height: 4.2em;
overflow: hidden;
}
</style>
<script>
window.addEventListener('load', function () {
function render(comments) {
var template = '<a href="${comment.url}?utm_source=gitalk" class="dis-item-url"><h3 class="dis-item-title">${comment.title}</h3>' +
'<p class="dis-item-des">${comment.count} 评论 ● ${comment.date}</p>' +
'<div class="dis-item-content"><img class="dis-item-img" src="${comment.icon}" alt="icon"><p><b><span class="dis-item-user">${comment.user}</span></b>&nbsp;——&nbsp;${comment.body}</p></div>' +
'</a>'
var wrapper = get('#gitalk-related');
comments = shuffle(comments);
comments.slice(0, 4).forEach(function (c) {
var div = document.createElement('div');
div.classList.add('dis-item');
div.innerHTML = template.replace("${comment.url}", c.url)
.replace("${comment.title}", c.title)
.replace("${comment.count}", c.count)
.replace("${comment.date}", c.date)
.replace("${comment.icon}", c.icon)
.replace("${comment.user}", c.user)
.replace("${comment.body}", c.body)
wrapper.appendChild(div)
})
var p = document.createElement('p')
p.innerHTML = '<a href="https://geektutu.com/post/blog-experience-7.html">Gitalk Plus</a>';
p.classList.add('dis-divide');
wrapper.appendChild(p);
wrapper.classList.add('dis-wrapper')
}
function shuffle(a) {
for (var i = a.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function get(str) { return document.querySelector(str) }
fetch('/comments.json').then(function (r) { return r.json() }).then(render).catch(function (e) { })
})
</script>
@geektutu
Copy link
Author

效果

Gitalk-plus

@geektutu
Copy link
Author

@hutusi
Copy link

hutusi commented Oct 8, 2020

report 一个bug:

github 的 获取 issues/comments api 会将 PR 的 comments 也获取出来,我在运行时发生了错误,发现是将这个 PR 给带出来了: hutusi/hutusi.github.com#56

issue.post = issue.labels.find(label => label.name.startsWith("/")).name

这段如果找不到 / 开头的 label 会抛异常。

@geektutu
Copy link
Author

@hutusi Fixed, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment