日刊記録sheetを自動生成するUserScript
開発動機
起床時にスマホで生成ボタンを毎回押さないといけない
色々とUIまわりの修正をさぼっているせいで、PCからだと生成できなくなっている
機能
バグ
いずれにせよAPIを叩く必要があるなら、scrapbox.Project.pagesの生成を待つメリットがあんまりないな
いや、とっているはずだよな。list("takker")を実行しているし……
なにかがおかしい
自projectのタスクリンクを取得出来ていない
2023-03-10
2023-01-24
11:59:56 テスト終了
11:41:25 できた
動くかな?
/icons/hr.icon
code:main.ts
/// <reference no-default-lib="true"/>
/// <reference lib="esnext"/>
/// <reference lib="dom"/>
import {
useStatusBar,
patch,
makeSocket,
disconnect,
Socket,
} from "../scrapbox-userscript-std/mod.ts";
import { delay as sleep } from "../deno_std%2Fasync/mod.ts";
import eachDayOfInterval from "../date-fns/eachDayOfInterval.ts";
import { addDays } from "../date-fns/addDays.ts";
import { lightFormat } from "../date-fns/lightFormat.ts";
import { readProgrammableTasks } from "../takker99%2Ftakker-scheduler/plan.ts";
import { toString, Task } from "../takker99%2Ftakker-scheduler/deps.ts";
import { toTitle, format } from "../takker99%2Ftakker-scheduler/diary.ts";
import { list, linkToTask } from "../takker99%2Ftakker-scheduler/workflow.ts";
import type { Scrapbox } from "../scrapbox-jp%2Ftypes/userscript.ts";
declare const scrapbox: Scrapbox;
const project = "takker-memex";
type Key = ${number}-${string}-${string};
/** 日付ごとに一意なkeyを生成する */
const toKey = (date: Date): Key => `${
date.getFullYear()
}-${${date.getMonth() + 1}.padStart(2, "0")}-${${date.getDate()}.padStart(2, "0")}`;
// 一回のみ実行する
// awaitはつけない
// - moduleのtop levelにあるので、awaitするとほかのmoduleの読み込みをblockしてしまう
(async () => {
if (scrapbox.Project.name !== project) return;
// scrapbox.Project.pagesが生成されるまで待つ
// 生成したpagesはcacheしておく
let pages = scrapbox.Project.pages;
await new Promise<void>((resolve) => {
const timer = setInterval(() => {
if (pages.length === 0) {
pages = scrapbox.Project.pages;
return;
}
clearInterval(timer);
resolve();
}, 2000);
});
const start = new Date(2023, 0, 24);
const now = new Date();
const interval = { start, end: now };
//console.debug(eachDayOfInterval(interval));
// 生成する振り返りページの日付リスト
const dates = eachDayOfInterval(interval).filter((date) => {
const title = toTitle(date);
const page = pages.find((page) => page.title === title);
return !page || !page.exists;
});
//console.debug("create the following diaries:", dates)
if (dates.length === 0) return;
const { render, dispose } = useStatusBar();
let socket: Socket | undefined;
try {
const tasks = new Map<Key, Task[]>();
/** projectにある全てのタスクリンクをtask lineに変換してtasksに格納する */
const crawlTaskLinks = async (project: string) => {
for await (const { task: taskLink } of list(project)) {
const task = linkToTask(taskLink);
if (!task) return;
const key = toKey(task.base);
tasks.set(key, [...(tasks.get(key) ?? []), task]);
}
};
render(
{ type: "spinner" },
{ type: "text", text: load tasks for ${dates.length} diary pages},
);
// タスクリンクを取得する
await Promise.all([
crawlTaskLinks("takker-memex"),
crawlTaskLinks("takker"),
// 並行してprogrammable tasksも取得する
(async () => {
for (const date of dates) {
const key = toKey(date);
for await (const tasks_ of readProgrammableTasks(date)) {
tasks.set(key, [...(tasks.get(key) ?? []), ...tasks_]);
}
}
})(),
]);
// ページを作る
socket = await makeSocket();
let counter = 0;
render(
{ type: "spinner" },
{ type: "text", text: create 0/${dates.length} diary pages...},
);
for (const date of dates) {
const title = toTitle(date);
const lines = tasks.get(toKey(date))?.map?.((task) => toString(task)) ?? [];
const dateTag = #${lightFormat(date, "yyyy-MM-dd")};
const datetimeTag = #${lightFormat(date, "yyyy-MM-dd HH:mm:ss")};
await patch(
project,
title,
(oldLines, { persistent }) => {
// 新規作成のみ
if (persistent) return;
if (newLines.some((line) => line.includes(dateTag))) return format(newLines);
},
{ socket },
);
counter++;
render(
{ type: "spinner" },
{ type: "text", text: create ${counter}/${dates.length} diary pages...},
);
}
render(
{ type: "check-circle" },
{ type: "text", text: created ${dates.length} diary pages.},
);
} catch(e: unknown) {
render(
{ type: "exclamation-triangle" },
{ type: "text", text: e instanceof Error ?
${e.name} ${e.message} :
Unknown error! (see developper console),
},
);
console.error(e);
} finally {
if (socket) await disconnect(socket);
await sleep(1000);
dispose();
}
})();