generated at
定期的にScrapboxのbackupをdownloadするScript
使い方
2. main.gsをコピペする
deployする必要があるのかはよくわからない……
わからんyosider

実装
Google Apps Scriptのタイマーを用いて、定期的に実行させる
前回実行した時刻と、backup fileの生成時刻とを照らし合わせて、新しくできたbackupをdownloadする
前回実行した時刻はPropertiesServiceに記録しておく
api/project-backup/:projectname/listを叩き、新しいbackup jsonがあればGoogle Driveにdownloadする
OneDriveなど、他のcloud storageでも使えるようにすると便利かもtakker

2021-01-31 23:26:34 とりあえず書きましたtakker
scrapboxでベタ打ちしただけで、テストもなんにもしていません
誰かテストしてくれ
2021-02-01 05:56:40 テスト完了!takker

実装したいこと
backupの最大数を制限する
現状だと、容量のある限りbackup filesが増えてしまう
最大数に到達したら、古いbackup fileから消すようにしたい
同じ名前のbackup fileがあったら上書きする
設定をjson fileから行う
backupするprojectやbackupの最大数など
connect.sidはファイルに保存していると危ないので、script propertiesで持っておく

メイン関数
この関数をタイマーで定期的に実行する
いくつかscript propertiesを用いる
PROJECTS : backupを取るprojectのリストをJSONに変換した文字列
CONNECT_SID : log in情報をつけてscrapbox APIを叩くのに必要な connect.sid
LAST_BACKUPED_LIST : 各projectから最後に取得したbackup fileの作成日時のリスト
なければ勝手に作られる
設定しやすいように、helper関数を用意しておきましたtakker
helper.gs(js)
function initScriptProperties() { const scriptProperties = PropertiesService.getScriptProperties(); scriptProperties.setProperty('PROJECTS', JSON.stringify(['your-project-name',])); scriptProperties.setProperty('CONNECT_SID', 'xxx'); }
LAST_BACKUPED_LIST を消すやつ
helper.gs(js)
function clearLastBackupTime(){ const scriptProperties = PropertiesService.getScriptProperties(); scriptProperties.deleteProperty('LAST_BACKUPED_LIST'); }
main.gs(js)
function main() { const scriptProperties = PropertiesService.getScriptProperties(); const projects = JSON.parse(scriptProperties.getProperty('PROJECTS')); // 複数のprojectを指定可能 const connect_sid = scriptProperties.getProperty('CONNECT_SID'); let lastbackupedList = JSON.parse(scriptProperties.getProperty('LAST_BACKUPED_LIST') || '[]'); const folder = DriveApp.getFolderById('xxx'); // お好みのfolderを選択してください lastbackupedList = projects.map((project, i) => downloadBackup({ project, connect_sid, folder, lastbackuped: lastbackupedList[i] || 0 })); scriptProperties.setProperty('LAST_BACKUPED_LIST', JSON.stringify(lastbackupedList)); }
?? operatorが使えないので、代わりに || を用いた
V8 engineを使っているのになんで ?? が使えないんだよtakker

指定のfolderに新しいbackup filesを保存する
cookie_sid がバケツリレーになっているのをなんとかできないかなtakker
直近1ファイルで十分な気がするyosider
保存するbackup fileの数を指定できるようにすればいいかtakker
main.gs(js)
const zero = n => String(n).padStart(2, '0'); function downloadBackup({ project, connect_sid, folder, lastbackuped }) { const backups = getBackupList({ project, connect_sid }); const backupIds = backups .flatMap(({ backuped, id }) => backuped > lastbackuped ? [id] : []) .slice(0, 10); // 直近10ファイルまで取得する if (backupIds.length === 0) { console.log('no backup files found.'); return lastbackuped; } const jsons = getBackupFiles({ project, connect_sid, fileIds: backupIds }); console.log('Save backup files...'); let saveNum = 0; jsons.forEach(json => { const exported = new Date(json.exported * 1000); const fileName = `${project}_${exported.getFullYear()}${zero(exported.getMonth() + 1)}${zero(exported.getDate())}${zero(exported.getHours())}${zero(exported.getMinutes())}${zero(exported.getSeconds())}.json`;
日付の文字列を作るところは、関数に切り出したほうが見やすいかもtakker

main.gs(js)
folder.createFile(fileName, JSON.stringify(json, null, 2), 'application/json');
MIME typeの指定はなくても動くみたい

main.gs(js)
saveNum++; console.log(`Saved: ${saveNum}/${jsons.length}`); }); // backupの最終更新日時を返す const newLastBackuped = Math.max(...jsons.map(({ exported }) => parseInt(exported))); console.log(`Finish saving. Last backuped date: ${new Date(newLastBackuped * 1000)}`);
読みやすいように、 YYYY-MM-DD hh:mm:ss の形式で最終更新日時を表示したい

main.gs(js)
return newLastBackuped; }

指定したprojectのbackup fileのlistを取得する
main.gs(js)
function getBackupList({ project, connect_sid }) { console.log(`Start fetching the backup list of /${project}...`); const response = UrlFetchApp .fetch(`https://scrapbox.io/api/project-backup/${project}/list`, { headers: { Cookie: `connect.sid=${connect_sid}` }, }); const json = JSON.parse(response.getContentText()); console.log(`Finish fetching.`, { backupNum: json.backups.length }); return json.backups; }

指定したbackup fileをdownloadする
main.gs(js)
function getBackupFiles({ project, fileIds, connect_sid }) { console.log(`Start fetching ${fileIds.length} backups from /${project}...`, fileIds); const responses = UrlFetchApp .fetchAll(fileIds.map(fileId => { return { url: `https://scrapbox.io/api/project-backup/${project}/${fileId}.json`, headers: { Cookie: `connect.sid=${connect_sid}` }, }; }) );
Promise.all()でfetchする代わりにUrlFetchApp.fetchAll()を使う
async \ await が使えないので結構不便

main.gs(js)
const jsons = responses.map(response => JSON.parse(response.getContentText())); console.log('Finish fetching.'); return jsons; }

time zoneはappsscript.jsonで各自の地域に変更しておいてください
appsscript.json
{ "timeZone": "Asia/Tokyo", "dependencies": { }, "exceptionLogging": "STACKDRIVER", "runtimeVersion": "V8" }

yuta0801さんに教えてもらったtakker