├── .env.sample ├── .github └── workflows │ ├── ci.yml │ ├── duplicator.yml │ └── udd.yml ├── .gitignore ├── .vscode └── settings.json ├── Procfile ├── README.md ├── deps.ts ├── index.ts └── runtime.txt /.env.sample: -------------------------------------------------------------------------------- 1 | SID="xxxxxxxxx" 2 | SOURCE_PROJECT_NAME="xxxxxxxx" 3 | DESTINATION_PROJECT_NAME="xxxxxxx" 4 | # "Ttrue"にすると、デフォルトで複製するようになります。 5 | SHOULD_DUPLICATE_BY_DEFAULT="False" -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: denoland/setup-deno@v1 11 | with: 12 | deno-version: "1.18.2" 13 | - name: Check fmt 14 | run: deno fmt --check 15 | - name: Run lint 16 | run: deno lint 17 | - name: Run type check 18 | run: deno cache *.ts 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/duplicator.yml: -------------------------------------------------------------------------------- 1 | name: Duplicater 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 21 * * *' # JST 6:00 7 | 8 | env: 9 | SID: ${{ secrets.SID }} 10 | PROJECT_NAME: ${{ secrets.PROJECT_NAME }} 11 | SOURCE_PROJECT_NAME: ${{ secrets.SOURCE_PROJECT_NAME }} 12 | DESTINATION_PROJECT_NAME: ${{ secrets.DESTINATION_PROJECT_NAME }} 13 | SHOULD_DUPLICATE_BY_DEFAULT: ${{ secrets.SHOULD_DUPLICATE_BY_DEFAULT }} 14 | 15 | jobs: 16 | build: 17 | permissions: 18 | contents: 'write' 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - uses: denoland/setup-deno@v1 23 | 24 | - name: Export from Scrapbox(/PROJECT_NAME to PROJECT_NAME.json) 25 | run: deno run --allow-net=scrapbox.io --allow-read=./ --allow-write --allow-env index.ts -------------------------------------------------------------------------------- /.github/workflows/udd.yml: -------------------------------------------------------------------------------- 1 | # from https://zenn.dev/kawarimidoll/articles/c68204d248c107#設定ファイル 2 | name: update-deno-dependencies 3 | 4 | on: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | udd: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: denoland/setup-deno@v1 14 | with: 15 | deno-version: 1.21.0 16 | - name: Update dependencies 17 | run: > 18 | deno run --allow-net --allow-read --allow-write=deps.ts 19 | --allow-run=deno https://deno.land/x/udd@0.7.2/main.ts deps.ts 20 | - name: Create Pull Request 21 | uses: peter-evans/create-pull-request@v3 22 | with: 23 | commit-message: ":arrow_up: update deno dependencies" 24 | title: Update Deno Dependencies 25 | body: > 26 | Automated updates by [deno-udd](https://github.com/hayd/deno-udd) 27 | and [create-pull-request](https://github.com/peter-evans/create-pull-request) 28 | GitHub action 29 | branch: update-deno-dependencies 30 | author: GitHub 31 | delete-branch: true 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.unstable": true 5 | } 6 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: deno run --allow-net=scrapbox.io --allow-read=./ --allow-env index.ts 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scrapbox Duplicator 2 | 3 | Scrapboxの非公開・公開プロジェクトを分けて運用する際に面倒な「ページの転送」を自動で行います。 4 | 5 | ## 目次 6 | 7 | - [仕組み](#仕組み) 8 | - [スタートガイド](#スタートガイド) 9 | - [必要なもの](#必要なもの) 10 | - [注意事項](#注意事項) 11 | - [その他](#その他) 12 | - [謝辞](#謝辞) 13 | 14 | ## 仕組み 15 | 16 | 以下の処理の定期実行によって、公開したいページのみ転送されたミラープロジェクトが作られます。 17 | 18 | 1. 転送元プロジェクトの内容をエクスポート 19 | 2. エクスポートされたjsonファイルから`[public.icon]`が含まれているページのみを抽出 20 | 3. 抽出されたページを転送先プロジェクトへインポート 21 | 22 | ## スタートガイド 23 | 24 | 以下のステップで実行可能です。 25 | 26 | 1. このリポジトリをForkする 27 | 2. Forkしたリポジトリに環境変数を設定する 28 | 29 | 30 | 以下の画像は環境変数の設定方法を示しています。 31 | [![Image from Gyazo](https://i.gyazo.com/cd8630a6fb125c6d7e627b290fbe79ce.png)](https://gyazo.com/cd8630a6fb125c6d7e627b290fbe79ce) 32 | 33 | 34 | 動作確認をすぐに行いたい場合は、以下の画像のように手動で起動することが可能です。 35 | [![Image from Gyazo](https://i.gyazo.com/e4762cda8e8566bb75d20a429c2f1cb1.png)](https://gyazo.com/e4762cda8e8566bb75d20a429c2f1cb1) 36 | 37 | 38 | ## 必要なもの 39 | 40 | 1. `SID` ScrapboxのSID(詳しくは[こちら](https://scrapbox.io/nishio/Scrapbox%E3%81%AEprivate%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AEAPI%E3%82%92%E5%8F%A9%E3%81%8F)) 41 | 2. `SOURCE_PROJECT_NAME` 転送元のプロジェクト名 42 | 3. `DESTINATION_PROJECT_NAME` 転送先のプロジェクト名 43 | 44 | ## 注意事項 45 | 46 | - まともにテストしていないので、**自己責任で使用してください**。使用前にプロジェクトのバックアップ取得をオススメします。 47 | - SIDは漏れた場合にリセットする手段が無さそうなので、気をつけて扱ってください。サブアカウントのSID等を使用する事をオススメします。(詳しくは[こちら](https://scrapbox.io/nishio/Scrapbox%E3%81%AEprivate%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AEAPI%E3%82%92%E5%8F%A9%E3%81%8F)) 48 | - export APIは使用回数に制限があるので、定期実行は一日2~3回程度が良いと思います。 49 | 50 | ## その他 51 | 52 | Scrapbox Duplicatorは定期実行のタイミングまで待たないと転送されません。好きなタイミングで公開したい場合は、[このUserScript](https://scrapbox.io/blu3mo-public/%E3%83%9A%E3%83%BC%E3%82%B8%E8%BB%A2%E9%80%81%E3%81%99%E3%82%8B%E6%8B%A1%E5%BC%B5script)を一緒に使う事をオススメします。 53 | 54 | ## 謝辞 55 | 56 | Scrapboxを開発しているNota, Inc. の皆さんに感謝します。 -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | import 'https://deno.land/x/dotenv@v3.2.0/load.ts'; 2 | export { assertString } from 'https://deno.land/x/unknownutil@v2.0.0/mod.ts'; 3 | export * from 'https://raw.githubusercontent.com/takker99/scrapbox-userscript-std/0.14.1/rest/page-data.ts'; 4 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { assertString, exportPages, importPages } from "./deps.ts"; 2 | 3 | const sid = Deno.env.get("SID"); 4 | const exportingProjectName = Deno.env.get("SOURCE_PROJECT_NAME"); //インポート元(本来はprivateプロジェクト) 5 | const importingProjectName = Deno.env.get("DESTINATION_PROJECT_NAME"); //インポート先(publicプロジェクト) 6 | const shouldDuplicateByDefault = 7 | Deno.env.get("SHOULD_DUPLICATE_BY_DEFAULT") === "True"; 8 | 9 | assertString(sid); 10 | assertString(exportingProjectName); 11 | assertString(importingProjectName); 12 | 13 | console.log(`Exporting a json file from "/${exportingProjectName}"...`); 14 | const result = await exportPages(exportingProjectName, { 15 | sid, 16 | metadata: true, 17 | }); 18 | if (!result.ok) { 19 | const error = new Error(); 20 | error.name = `${result.value.name} when exporting a json file`; 21 | error.message = result.value.message; 22 | throw error; 23 | } 24 | const { pages } = result.value; 25 | console.log(`Export ${pages.length}pages:`); 26 | for (const page of pages) { 27 | console.log(`\t${page.title}`); 28 | } 29 | 30 | const importingPages = pages.filter(({ lines }) => { 31 | if (lines.some((line) => line.text.includes("[private.icon]"))) { 32 | return false; 33 | } else if (lines.some((line) => line.text.includes("[public.icon]"))) { 34 | return true; 35 | } else { 36 | return shouldDuplicateByDefault; 37 | } 38 | }); 39 | 40 | if (importingPages.length === 0) { 41 | console.log("No page to be imported found."); 42 | } else { 43 | console.log( 44 | `Importing ${importingPages.length} pages to "/${importingProjectName}"...`, 45 | ); 46 | const result = await importPages(importingProjectName, { 47 | pages: importingPages, 48 | }, { 49 | sid, 50 | }); 51 | if (!result.ok) { 52 | const error = new Error(); 53 | error.name = `${result.value.name} when importing pages`; 54 | error.message = result.value.message; 55 | throw error; 56 | } 57 | console.log(result.value); 58 | } 59 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | v1.21.0 2 | --------------------------------------------------------------------------------