-
-
Save tetsuya-ki/71f7bb18a6573ed347b8feb058dd6998 to your computer and use it in GitHub Desktop.
GitHubTrafficGraph2Discord
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const WEBHOOK_URL = PropertiesService.getScriptProperties().getProperty("DISCORD_WEBHOOK_URL"); | |
const OWNER = PropertiesService.getScriptProperties().getProperty("OWNER"); | |
const REPO = PropertiesService.getScriptProperties().getProperty("REPO"); | |
const ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty("ACCESS_TOKEN"); | |
const SPREADSHEET_ID = PropertiesService.getScriptProperties().getProperty("SPREADSHEET_ID"); | |
const TEXT_LENGTH_MAX = 1950; | |
const NUMBAR = 6; | |
const SHEETNAME_WORK = 'WORK'; | |
// 曜日として表示したい名前(Sun)とかにしてもOK | |
const weekDay = { | |
0: '(日)', | |
1: '(月)', | |
2: '(火)', | |
3: '(水)', | |
4: '(木)', | |
5: '(金)', | |
6: '(土)', | |
}; | |
const repos = { | |
// 書き込み先がチャンネルの場合は、空白を記載 | |
'repository_name_0':'' | |
// スレッドに投稿する場合はスレッドIDを記載 | |
'repository_name_1':'discord_thread_id_1' | |
// 同じスレッドに投稿することも可能(当たり前か...) | |
, 'repository_name_2':'discord_thread_id_2' | |
, 'repository_name_3':'discord_thread_id_2' | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function _fetchTrafficByGitHubApi(repo, path){ | |
var url = `https://api.github.com/repos/${OWNER}/${repo}/traffic/${path}`; | |
var options = { | |
headers: { | |
'Authorization': `token ${ACCESS_TOKEN}` | |
, 'Accept': 'application/vnd.github+json' | |
, 'X-GitHub-Api-Version': '2022-11-28' | |
} | |
}; | |
var response = UrlFetchApp.fetch(url, options); | |
var json = response.getContentText(); | |
Logger.log(json); | |
return JSON.parse(json); | |
}gi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function getTrafficViewsAndWriteWork(repo) { | |
var parsed_data = _fetchTrafficByGitHubApi(repo, 'views'); | |
var checkDay = new Date(); | |
checkDay.setDate(checkDay.getDate() - 14); | |
checkDay = new Date(checkDay.toDateString()); | |
var checkEndDay = new Date(); | |
checkEndDay.setDate(checkEndDay.getDate() - 1); | |
checkEndDay = new Date(checkEndDay.toDateString()); | |
var headers = ['timestamp', 'count', 'uniques']; | |
var data = []; | |
var spreadsheet_data = []; | |
for (var i = 0; i < parsed_data.views.length; i++) { | |
const count = parsed_data.views[i].count; | |
const uniques = parsed_data.views[i].uniques; | |
const dt = new Date(parsed_data.views[i].timestamp); | |
var targetDay = new Date(dt.toDateString()); | |
if (checkEndDay.valueOf() <= targetDay.valueOf()){ | |
break; | |
} | |
// GitHubではデータなしの場合、その日付自体が存在しないため、補正する | |
if (targetDay.valueOf() !== checkDay.valueOf()) { | |
while(targetDay.valueOf() !== checkDay.valueOf()){ | |
var row = []; | |
// 日付準備 | |
const month = (checkDay.getMonth() + 1).toString().padStart(2, '0'); | |
const day = checkDay.getDate().toString().padStart(2, '0'); | |
const weekD = weekDay[checkDay.getDay()]; | |
const date_str = `${month}/${day}${weekD}`; | |
row.push(date_str); | |
row.push(0); | |
row.push(0); | |
data.push(row); | |
spreadsheet_data.push([0, 0]); | |
checkDay.setDate(checkDay.getDate() + 1); | |
if (checkEndDay.valueOf() <= checkDay.valueOf()){ | |
break; | |
} | |
} | |
} | |
var row = []; | |
// 日付準備 | |
const month = (checkDay.getMonth() + 1).toString().padStart(2, '0'); | |
const day = checkDay.getDate().toString().padStart(2, '0'); | |
const weekD = weekDay[checkDay.getDay()]; | |
const date_str = `${month}/${day}${weekD}`; | |
row.push(date_str); | |
row.push(count); | |
row.push(uniques); | |
data.push(row); | |
spreadsheet_data.push([count, uniques]); | |
checkDay.setDate(checkDay.getDate() + 1); | |
} | |
// 日付が足りない場合の処理 | |
for (i = data.length; i <= 14; i++){ | |
var row = []; | |
var checkDay = new Date(); | |
checkDay.setDate(checkDay.getDate() - 14 + i); | |
checkDay = new Date(checkDay.toDateString()); | |
// 日付準備 | |
const month = (checkDay.getMonth() + 1).toString().padStart(2, '0'); | |
const day = checkDay.getDate().toString().padStart(2, '0'); | |
const weekD = weekDay[checkDay.getDay()]; | |
const date_str = `${month}/${day}${weekD}`; | |
row.push(date_str); | |
row.push(0); | |
row.push(0); | |
data.push(row); | |
spreadsheet_data.push([0, 0]); | |
} | |
Logger.log('書き込みやる2'); | |
Logger.log(headers); | |
Logger.log(data); | |
_writeSpreadsheetWork(SPREADSHEET_ID, headers, data); | |
_writeRepoDays(SPREADSHEET_ID, repo, false, spreadsheet_data); | |
return {count:parsed_data.count, uniques:parsed_data.uniques}; | |
} | |
function getTrafficClonesAndWriteWork(repo) { | |
var parsed_data = _fetchTrafficByGitHubApi(repo, 'clones'); | |
var checkDay = new Date(); | |
checkDay.setDate(checkDay.getDate() - 14); | |
checkDay = new Date(checkDay.toDateString()); | |
var checkEndDay = new Date(); | |
checkEndDay.setDate(checkEndDay.getDate() - 1); | |
checkEndDay = new Date(checkEndDay.toDateString()); | |
var headers = ['timestamp', 'count', 'uniques']; | |
var data = []; | |
for (var i = 0; i < parsed_data.clones.length; i++) { | |
const count = parsed_data.clones[i].count; | |
const uniques = parsed_data.clones[i].uniques; | |
const dt = new Date(parsed_data.clones[i].timestamp); | |
var targetDay = new Date(dt.toDateString()); | |
if (checkEndDay.valueOf() <= targetDay.valueOf()){ | |
break; | |
} | |
// GitHubではデータなしの場合、その日付自体が存在しないため、補正する | |
if (targetDay.valueOf() !== checkDay.valueOf()) { | |
while(targetDay.valueOf() !== checkDay.valueOf()){ | |
var row = []; | |
// 日付準備 | |
const month = (checkDay.getMonth() + 1).toString().padStart(2, '0'); | |
const day = checkDay.getDate().toString().padStart(2, '0'); | |
const weekD = weekDay[checkDay.getDay()]; | |
const date_str = `${month}/${day}${weekD}`; | |
row.push(date_str); | |
row.push(0); | |
row.push(0); | |
data.push(row); | |
checkDay.setDate(checkDay.getDate() + 1); | |
if (checkEndDay.valueOf() <= checkDay.valueOf()){ | |
break; | |
} | |
} | |
} | |
var row = []; | |
// 日付準備 | |
const month = (checkDay.getMonth() + 1).toString().padStart(2, '0'); | |
const day = checkDay.getDate().toString().padStart(2, '0'); | |
const weekD = weekDay[checkDay.getDay()]; | |
const date_str = `${month}/${day}${weekD}`; | |
row.push(date_str); | |
row.push(count); | |
row.push(uniques); | |
data.push(row); | |
checkDay.setDate(checkDay.getDate() + 1); | |
} | |
// 日付が足りない場合の処理 | |
for (i = data.length; i <= 14; i++){ | |
var row = []; | |
var checkDay = new Date(); | |
checkDay.setDate(checkDay.getDate() - 14 + i); | |
checkDay = new Date(checkDay.toDateString()); | |
// 日付準備 | |
const month = (checkDay.getMonth() + 1).toString().padStart(2, '0'); | |
const day = checkDay.getDate().toString().padStart(2, '0'); | |
const weekD = weekDay[checkDay.getDay()]; | |
const date_str = `${month}/${day}${weekD}`; | |
row.push(date_str); | |
row.push(0); | |
row.push(0); | |
data.push(row); | |
} | |
Logger.log('書き込みやる'); | |
Logger.log(headers); | |
Logger.log(data); | |
_writeSpreadsheetWork(SPREADSHEET_ID, headers, data); | |
_writeRepoDays(SPREADSHEET_ID, repo, true, data); | |
return {count:parsed_data.count, uniques:parsed_data.uniques}; | |
} | |
function getTrafficPopularReferrers(repo) { | |
const path = 'popular/referrers'; | |
var parsed_data = _fetchTrafficByGitHubApi(repo, path); | |
var text = `### ${path}\n`; | |
var length = parsed_data.length; | |
if (parsed_data.length > NUMBAR){ | |
length = NUMBAR; | |
} else if (parsed_data.length == 0) { | |
text = ''; | |
} | |
for (var i = 0; i < length; i++) { | |
text += parsed_data[i].referrer; | |
text += `: count(${parsed_data[i].count}), uniques(${parsed_data[i].uniques})\n`; | |
} | |
return text; | |
} | |
function getTrafficPopularPaths(repo) { | |
const path = 'popular/paths'; | |
var parsed_data = _fetchTrafficByGitHubApi(repo, path); | |
var text = `### ${path}\n`; | |
var length = parsed_data.length; | |
if (parsed_data.length > NUMBAR){ | |
length = NUMBAR; | |
} else if (parsed_data.length == 0) { | |
text = ''; | |
} | |
for (var i = 0; i < length; i++) { | |
text += parsed_data[i].path; | |
text += `: count(${parsed_data[i].count}), uniques(${parsed_data[i].uniques})\n`; | |
} | |
return text; | |
} | |
function exportChart(repo, webhook_url) { | |
var text = `## traffic: ${repo}\n` | |
// clones | |
var clonesData = getTrafficClonesAndWriteWork(repo); | |
if (clonesData.uniques > 0){ | |
var chartClones = _createChartByWORK(SPREADSHEET_ID, `${repo} - clones`); | |
var imageDataClones = chartClones.getAs('image/png').copyBlob(); | |
} | |
// views | |
var viewsData = getTrafficViewsAndWriteWork(repo); | |
if (viewsData.uniques > 1){ | |
var chartViews = _createChartByWORK(SPREADSHEET_ID, `${repo} - views`); | |
var imageDataViews = chartViews.getAs('image/png').copyBlob(); | |
} | |
// others | |
var referrersText = getTrafficPopularReferrers(repo); | |
var pathsText = getTrafficPopularPaths(repo); | |
// summary書き込み | |
_writeRepoSummary(SPREADSHEET_ID, repo, [clonesData.count, clonesData.uniques, viewsData.count, viewsData.uniques]); | |
// 投稿文章作成 | |
text += `clones-> 総数:${clonesData.count}, ユニーク数:${clonesData.uniques}\n`; | |
text += `views-> 総数:${viewsData.count}, ユニーク数:${viewsData.uniques}\n`; | |
text += referrersText; | |
text += pathsText; | |
resultText = text; | |
if(text.length>TEXT_LENGTH_MAX){ | |
resultText = text.slice(0, TEXT_LENGTH_MAX); | |
resultText += '\n...(省略)...'; | |
} | |
// Discord投稿 | |
var payload = { | |
'content' : resultText | |
}; | |
if (viewsData.uniques > 1 && clonesData.uniques > 0) { | |
payload['file1'] = imageDataClones; | |
payload['file2'] = imageDataViews; | |
} else if (viewsData.uniques > 1 && clonesData.uniques == 0) { | |
payload['file1'] = imageDataViews; | |
} else if (viewsData.uniques == 1 && clonesData.uniques > 0) { | |
payload['file1'] = imageDataClones; | |
} | |
var params = { | |
method: "post", | |
payload: payload, | |
muteHttpExceptions: true | |
}; | |
UrlFetchApp.fetch(webhook_url, params); | |
} | |
function exportChartREPO() { | |
exportChart(REPO, WEBHOOK_URL); | |
} | |
function exportChartAll() { | |
if (repos == undefined || repos.length == 0) { | |
Logger.log('データなし。終了'); | |
return; | |
} | |
for (let [key, value] of Object.entries(repos)) { | |
if (value !== '') { | |
exportChart(key, `${WEBHOOK_URL}?thread_id=${value}`); | |
} else { | |
exportChart(key, `${WEBHOOK_URL}`); | |
} | |
Utilities.sleep(500);// 0.5[sec]お休み | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function _writeSpreadsheetWork(spreadsheet_id, headers, data){ | |
const activeSpreadSheet = SpreadsheetApp.openById(spreadsheet_id); | |
const sheet = activeSpreadSheet.getSheetByName(SHEETNAME_WORK); | |
sheet.getRange(1, 1, 1, headers.length).setValues([headers]); | |
sheet.getRange(2, 1, data.length, 3).setValues(data); | |
} | |
function _createChartByWORK(spreadsheet_id, name) { | |
const activeSpreadSheet = SpreadsheetApp.openById(spreadsheet_id); | |
const sheet = activeSpreadSheet.getSheetByName(SHEETNAME_WORK); | |
var chart = sheet.newChart() | |
.setChartType(Charts.ChartType.LINE) | |
.addRange(sheet.getRange("A1:C17")) | |
.setPosition(5, 5, 0, 0) | |
.setNumHeaders(1) | |
.setOption('title', `Traffic: ${name}`) | |
.setOption('hAxis', {title: 'Date'}) | |
.setOption('vAxes', {0: {title: 'count', textStyle: {color: 'blue'}, gridlines: {color: 'black'}, minorGridlines: {color: 'grey'}, visibleInLegend: true} | |
,1: {title: 'uniques', textStyle: {color: 'red'}, gridlines: {color: 'black'}, minorGridlines: {color: 'grey'}, visibleInLegend: true}}) | |
.setOption('series', {0: {targetAxisIndex: 0, dataLabel: 'value', dataLabelPlacement: 'center'} | |
, 1: {targetAxisIndex: 1, dataLabel: 'value', dataLabelPlacement: 'center'}}) | |
.build(); | |
// sheet.insertChart(chart); | |
return chart; | |
} | |
function _writeRepoSummary(spreadsheet_id, repo_name, data_list){ | |
const activeSpreadSheet = SpreadsheetApp.openById(spreadsheet_id); | |
const sheetName = `${repo_name}-summary`; | |
let sheet = activeSpreadSheet.getSheetByName(sheetName); | |
// 日付準備 | |
const checkDay = new Date(); | |
const month = (checkDay.getMonth() + 1).toString().padStart(2, '0'); | |
const day = checkDay.getDate().toString().padStart(2, '0'); | |
const weekD = weekDay[checkDay.getDay()]; | |
const hour = checkDay.getHours().toString().padStart(2, '0'); | |
const minute = checkDay.getMinutes().toString().padStart(2, '0'); | |
const date_str = `${month}/${day}${weekD} ${hour}:${minute}`; | |
data_list.unshift(date_str); | |
// 新規作成の場合 | |
if(sheet == null){ | |
sheet = activeSpreadSheet.insertSheet(sheetName); | |
const headers = ['取得日','clone-count','clone-uniques','view-count','view-uniques']; | |
const write_list = [headers, data_list]; | |
sheet.getRange(1,1,write_list.length,headers.length).setValues(write_list); | |
} else { | |
// すでに存在する場合は末尾に記載 | |
const start_row = sheet.getLastRow()+1; | |
sheet.getRange(start_row,1,1,data_list.length).setValues([data_list]); | |
} | |
} | |
function _writeRepoDays(spreadsheet_id, repo_name, clone_flg, data_list){ | |
const activeSpreadSheet = SpreadsheetApp.openById(spreadsheet_id); | |
const sheetName = `${repo_name}-days`; | |
const COLUMN_NUM = 4; | |
let sheet = activeSpreadSheet.getSheetByName(sheetName); | |
// 新規作成の場合 | |
if(sheet == null){ | |
sheet = activeSpreadSheet.insertSheet(sheetName); | |
if(clone_flg){ | |
const headers = ['日付','clone-count','clone-uniques','view-count','view-uniques']; | |
// ヘッダーの記載 | |
sheet.getRange(1,1,1,headers.length).setValues([headers]); | |
// データ記載 | |
data_list = data_list.slice(0, 14); | |
sheet.getRange(2,1,data_list.length,data_list[0].length).setValues(data_list); | |
} else { | |
// データ記載 | |
data_list = data_list.slice(-8); | |
data_list = data_list.slice(0, 7); | |
sheet.getRange(2,COLUMN_NUM,data_list.length,data_list[0].length).setValues(data_list); | |
} | |
} else { | |
// すでに存在する場合は末尾に記載 | |
if(clone_flg){ | |
let write_list = data_list.slice(-8); | |
write_list = write_list.slice(0, 7); | |
let lastRow = sheet.getRange(sheet.getMaxRows(), 1).getNextDataCell(SpreadsheetApp.Direction.UP).getRow(); | |
const finalRow = sheet.getRange(sheet.getMaxRows(), 1).getRow(); | |
const finalRowData = sheet.getRange(finalRow, 1).getValue(); | |
if (finalRowData !== ''){ | |
lastRow = finalRow; | |
} | |
// データ記載 | |
sheet.getRange(lastRow+1, 1, write_list.length, write_list[0].length).setValues(write_list); | |
} else { | |
let lastRow = sheet.getRange(sheet.getMaxRows(), COLUMN_NUM).getNextDataCell(SpreadsheetApp.Direction.UP).getRow(); | |
const finalRow = sheet.getRange(sheet.getMaxRows(), COLUMN_NUM).getRow(); | |
const finalRowData = sheet.getRange(finalRow, 1).getValue(); | |
if (finalRowData !== ''){ | |
lastRow = finalRow; | |
} | |
if (lastRow === 1) { | |
data_list = data_list.slice(0, 14); | |
sheet.getRange(lastRow+1, COLUMN_NUM, data_list.length, data_list[0].length).setValues(data_list); | |
} else { | |
data_list = data_list.slice(-8); | |
data_list = data_list.slice(0, 7); | |
// データ記載 | |
sheet.getRange(lastRow+1, COLUMN_NUM, data_list.length, data_list[0].length).setValues(data_list); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment