-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathupdate_repo_views_counter.js
More file actions
134 lines (111 loc) · 3.45 KB
/
update_repo_views_counter.js
File metadata and controls
134 lines (111 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
const fs = require('fs');
const path = require('path');
const REPO = process.env.REPO;
const GITHUB_TOKEN = process.env.TRAFFIC_TOKEN;
const METRICS_FILE = 'metrics.json';
if (!GITHUB_TOKEN || !REPO) {
console.error('Error: TRAFFIC_TOKEN and REPO environment variables must be set.');
process.exit(1);
}
if (typeof fetch !== 'function') {
console.error('Error: global fetch is not available. Use Node.js 20 or later.');
process.exit(1);
}
async function getLast14DaysTraffic() {
const response = await fetch(`https://api.github.com/repos/${REPO}/traffic/views`, {
headers: {
Accept: 'application/vnd.github+json',
Authorization: `Bearer ${GITHUB_TOKEN}`,
'User-Agent': 'visitor-counter'
}
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(
`Failed to fetch traffic data: ${response.status} ${response.statusText}\n${errorText}`
);
}
const data = await response.json();
return data.views.map((item) => ({
date: item.timestamp.slice(0, 10),
count: item.count,
uniques: item.uniques
}));
}
function readMetrics() {
if (!fs.existsSync(METRICS_FILE)) {
return [];
}
try {
const raw = fs.readFileSync(METRICS_FILE, 'utf-8');
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch {
console.error('metrics.json is not valid JSON. Starting fresh.');
return [];
}
}
function writeMetrics(metrics) {
fs.writeFileSync(METRICS_FILE, JSON.stringify(metrics, null, 2));
console.log(`metrics.json updated with ${metrics.length} days`);
}
function mergeMetrics(existing, fetched) {
const byDate = new Map();
for (const entry of existing) {
byDate.set(entry.date, entry);
}
for (const entry of fetched) {
byDate.set(entry.date, entry);
}
return [...byDate.values()].sort((left, right) => left.date.localeCompare(right.date));
}
function calculateTotalViews(metrics) {
return metrics.reduce((sum, entry) => sum + entry.count, 0);
}
function findMarkdownFiles(dir) {
let results = [];
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
results = results.concat(findMarkdownFiles(fullPath));
continue;
}
if (entry.isFile() && entry.name.endsWith('.md')) {
results.push(fullPath);
}
}
return results;
}
function updateMarkdownBadges(totalViews) {
const refreshDate = new Date().toISOString().split('T')[0];
const badgeRegex = /<!-- START BADGE -->[\s\S]*?<!-- END BADGE -->/g;
const badgeBlock = `<!-- START BADGE -->
<div align="center">
<img src="https://img.shields.io/badge/Total%20views-${totalViews}-limegreen" alt="Total views">
<p>Refresh Date: ${refreshDate}</p>
</div>
<!-- END BADGE -->`;
for (const file of findMarkdownFiles('.')) {
const content = fs.readFileSync(file, 'utf-8');
if (!badgeRegex.test(content)) {
continue;
}
const updated = content.replace(badgeRegex, badgeBlock);
fs.writeFileSync(file, updated);
console.log(`Updated badge in ${file}`);
}
}
(async () => {
try {
const fetched = await getLast14DaysTraffic();
const existing = readMetrics();
const merged = mergeMetrics(existing, fetched);
writeMetrics(merged);
const totalViews = calculateTotalViews(merged);
updateMarkdownBadges(totalViews);
} catch (error) {
console.error(error);
process.exit(1);
}
})();