generated at
scrapbox2slack
main.gs(js)
const global=this; function subscribe(settings) { } (() => { var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a2, b2) => { for (var prop in b2 || (b2 = {})) if (__hasOwnProp.call(b2, prop)) __defNormalProp(a2, prop, b2[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b2)) { if (__propIsEnum.call(b2, prop)) __defNormalProp(a2, prop, b2[prop]); } return a2; }; var __spreadProps = (a2, b2) => __defProps(a2, __getOwnPropDescs(b2)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; const postToSlack = (params) => { const temp = []; for (const _a of params) { const _b = _a, { blocks: blocks1 } = _b, rest = __objRest(_b, ["blocks"]); if (blocks1.length > 50) { temp.push(__spreadValues({ blocks: blocks1.slice(0, 50 - 1) }, rest), __spreadValues({ blocks: blocks1.slice(50 - 1) }, rest)); } temp.push(__spreadValues({ blocks: blocks1 }, rest)); } const count = Math.floor(temp.length / 100) + 1; for (let i = 0; i < count; i++) { try { UrlFetchApp.fetchAll(temp.slice(i * 100, (i + 1) * 100).map(({ url, blocks, description }) => ({ url, method: "post", headers: { "Content-Type": "application/json" }, payload: JSON.stringify({ text: description, blocks }) }))); } catch (e) { console.error(e); } } }; var b = (e) => ({ type: "title", text: e.rows[0].text }); var y = (e) => { let { rows: [t, ...o] } = e, { indent: r = 0, text: n = "" } = t != null ? t : {}, c = n.replace(/^\s*code:/, ""); return { indent: r, type: "codeBlock", fileName: c, content: o.map((i) => i.text.substring(r + 1)).join(` `) }; }; var s = (e, { parseOnNested: t, parseOnQuoted: o, patterns: r }) => (n, c, i) => { var p, l, m, u, f, N; if (!t && c.nested) return (p = i == null ? void 0 : i()) !== null && p !== void 0 ? p : []; if (!o && c.quoted) return (l = i == null ? void 0 : i()) !== null && l !== void 0 ? l : []; for (let D of r) { let g = D.exec(n); if (g === null) continue; let Z = n.substring(0, g.index), A = n.substring(g.index + ((u = (m = g[0]) === null || m === void 0 ? void 0 : m.length) !== null && u !== void 0 ? u : 0)), J = e((f = g[0]) !== null && f !== void 0 ? f : "", c); return [ ...d(Z, c), ...J, ...d(A, c) ]; } return (N = i == null ? void 0 : i()) !== null && N !== void 0 ? N : []; }; var a = (e) => [ { type: "plain", raw: e, text: e } ], T = s(a, { parseOnNested: true, parseOnQuoted: true, patterns: [ /^()(.*)()$/ ] }); var K = /^>.*$/, V = (e, t) => t.context === "table" ? a(e, t) : [ { type: "quote", raw: e, nodes: d(e.substring(1), __spreadProps(__spreadValues({}, t), { quoted: true })) } ], O = s(V, { parseOnNested: false, parseOnQuoted: false, patterns: [ K ] }); var X = /^\? .+$/, Y = (e, t) => t.context === "table" ? a(e, t) : [ { type: "helpfeel", raw: e, text: e.substring(2) } ], I = s(Y, { parseOnNested: false, parseOnQuoted: false, patterns: [ X ] }); var w = /\[\[https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)\]\]/i, ee = /\[\[https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}\]\]/, te = (e, t) => { if (t.context === "table") return a(e, t); let o = e.substring(2, e.length - 2), r = /^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}$/.test(o); return [ { type: "strongImage", raw: e, src: r ? `${o}/thumb/1000` : o } ]; }, R = s(te, { parseOnNested: false, parseOnQuoted: true, patterns: [ w, ee ] }); var oe = /\[[^[\]]*\.icon(?:\*[1-9]\d*)?\]/; function h(e) { return (t, o) => { if (e === "strongIcon" && o.context === "table") return a(t, o); let r = e === "icon" ? t.substring(1, t.length - 1) : t.substring(2, t.length - 2), n = r.lastIndexOf(".icon"), c = r.substring(0, n), i = c.startsWith("/") ? "root" : "relative", p = r.substring(n + 5, r.length), l = p.startsWith("*") ? parseInt(p.substring(1), 10) : 1; return new Array(l).fill({}).map(() => ({ path: c, pathType: i, type: e, raw: t })); }; } var re = h("icon"), E = s(re, { parseOnNested: false, parseOnQuoted: true, patterns: [ oe ] }); var ne = /\[\[[^[\]]*\.icon(?:\*\d+)?\]\]/, se = h("strongIcon"), k = s(se, { parseOnNested: false, parseOnQuoted: true, patterns: [ ne ] }); var ae = /\[\[(?:[^[]|\[[^[]).*?\]*\]\]/, ce = (e, t) => t.context === "table" ? a(e, t) : [ { type: "strong", raw: e, nodes: d(e.substring(2, e.length - 2), __spreadProps(__spreadValues({}, t), { nested: true })) } ], $ = s(ce, { parseOnNested: false, parseOnQuoted: true, patterns: [ ae ] }); var ie = /\[\$ .+? \]/, de = /\[\$ [^\]]+\]/, pe = (e, t) => t.context === "table" ? a(e, t) : [ { type: "formula", raw: e, formula: e.substring(3, e.length - (e.endsWith(" ]") ? 2 : 1)) } ], S = s(pe, { parseOnNested: false, parseOnQuoted: true, patterns: [ ie, de ] }); var le = /\[[!"#%&'()*+,\-./{|}<>_~]+ (?:\[[^[\]]+\]|[^\]])+\]/, me = (e, t) => { if (t.context === "table") return a(e, t); let o = e.indexOf(" "), r = e.substring(1, o), n = e.substring(o + 1, e.length - 1), c = new Set(r); if (c.has("*")) { let i = r.split("*").length - 1; c.delete("*"), c.add(`*-${Math.min(i, 10)}`); } return [ { type: "decoration", raw: e, rawDecos: r, decos: Array.from(c), nodes: d(n, __spreadProps(__spreadValues({}, t), { nested: true })) } ]; }, z = s(me, { parseOnNested: false, parseOnQuoted: true, patterns: [ le ] }); var ue = /`.*?`/, ge = (e, t) => t.context === "table" ? a(e, t) : [ { type: "code", raw: e, text: e.substring(1, e.length - 1) } ], Q = s(ge, { parseOnNested: false, parseOnQuoted: true, patterns: [ ue ] }); var fe = /^[$%] .+$/, Ne = (e, t) => { var o; if (t.context === "table") return a(e, t); let r = (o = e[0]) !== null && o !== void 0 ? o : "", n = e.substring(2); return [ { type: "commandLine", raw: e, symbol: r, text: n } ]; }, _ = s(Ne, { parseOnNested: false, parseOnQuoted: false, patterns: [ fe ] }); var he = /\[\s+\]/, xe = (e, t) => t.context === "table" ? a(e, t) : [ { type: "blank", raw: e, text: e.substring(1, e.length - 1) } ], F = s(xe, { parseOnNested: false, parseOnQuoted: true, patterns: [ he ] }); var ve = /\[https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)(?:\?[^\]\s]+)?(?:\s+https?:\/\/[^\s\]]+)?\]/i, Pe = /\[https?:\/\/[^\s\]]+\s+https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)(?:\?[^\]\s]+)?\]/i, be = /\[https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}(?:\/raw)?(?:\s+https?:\/\/[^\s\]]+)?\]/, ye = /\[https?:\/\/[^\s\]]+\s+https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}(?:\/raw)?\]/, Te = (e) => /^https?:\/\/[^\s\]]+\.(png|jpe?g|gif|svg)(\?[^\]\s]+)?$/i.test(e) || Oe(e), Oe = (e) => /^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}(\/raw)?$/.test(e), Ie = (e, t) => { if (t.context === "table") return a(e, t); let o = e.search(/\s/), r = o !== -1 ? e.substring(1, o) : e.substring(1, e.length - 1), n = o !== -1 ? e.substring(o, e.length - 1).trimLeft() : "", [c, i] = Te(n) ? [ n, r ] : [ r, n ]; return [ { type: "image", raw: e, src: /^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}$/.test(c) ? `${c}/thumb/1000` : c, link: i } ]; }, C = s(Ie, { parseOnNested: true, parseOnQuoted: true, patterns: [ ve, Pe, be, ye ] }); var Re = /\[https?:\/\/[^\s\]]+\s+[^\]]*[^\s]\]/, Ee = /\[[^[\]]*[^\s]\s+https?:\/\/[^\s\]]+\]/, ke = /\[https?:\/\/[^\s\]]+\]/, $e = /https?:\/\/[^\s]+/, Se = (e, t) => { if (t.context === "table") return a(e, t); let o = e.startsWith("[") && e.endsWith("]") ? e.substring(1, e.length - 1) : e, r = /^https?:\/\/[^\s\]]/.test(o), n = (r ? /^https?:\/\/[^\s\]]+/ : /https?:\/\/[^\s\]]+$/).exec(o); if ((n == null ? void 0 : n[0]) === void 0) return []; let c = r ? o.substring(n[0].length) : o.substring(0, n.index - 1); return [ { type: "link", raw: e, pathType: "absolute", href: n[0], content: c.trim() } ]; }, L = s(Se, { parseOnNested: true, parseOnQuoted: true, patterns: [ Re, Ee, ke, $e ] }); var W = /\[([^\]]*[^\s])\s+([NS]\d+(?:\.\d+)?,[EW]\d+(?:\.\d+)?(?:,Z\d+)?)\]/, B = /\[([NS]\d+(?:\.\d+)?,[EW]\d+(?:\.\d+)?(?:,Z\d+)?)(?:\s+([^\]]*[^\s]))?\]/, ze = (e) => { let [t = "", o = "", r = ""] = e.split(","), n = parseFloat(t.replace(/^N/, "").replace(/^S/, "-")), c = parseFloat(o.replace(/^E/, "").replace(/^W/, "-")), i = /^Z\d+$/.test(r) ? parseInt(r.replace(/^Z/, ""), 10) : 14; return { latitude: n, longitude: c, zoom: i }; }, Qe = (e, t) => { var o; if (t.context === "table") return a(e, t); let r = (o = e.match(W)) !== null && o !== void 0 ? o : e.match(B); if (r === null) return []; let n = e.startsWith("[N") || e.startsWith("[S"), [, c = "", i = ""] = n ? r : [ r[0], r[2], r[1] ], { latitude: p, longitude: l, zoom: m } = ze(c), u = i !== "" ? `https://www.google.com/maps/place/${encodeURIComponent(i)}/@${p},${l},${m}z` : `https://www.google.com/maps/@${p},${l},${m}z`; return [ { type: "googleMap", raw: e, latitude: p, longitude: l, zoom: m, place: i, url: u } ]; }, G = s(Qe, { parseOnNested: false, parseOnQuoted: true, patterns: [ W, B ] }); var _e = /\[\/?[^[\]]+\]/, Fe = (e) => { let t = e.substring(1, e.length - 1); return [ { type: "link", raw: e, pathType: t.startsWith("/") ? "root" : "relative", href: t, content: "" } ]; }, H = s(Fe, { parseOnNested: true, parseOnQuoted: true, patterns: [ _e ] }); var Ce = /(?:^|\s)#\S+/, Le = (e, t) => { if (t.context === "table") return a(e, t); if (e.startsWith("#")) return [ { type: "hashTag", raw: e, href: e.substring(1) } ]; let o = e.substring(0, 1), r = e.substring(1); return [ ...a(o, t), { type: "hashTag", raw: r, href: r.substring(1) } ]; }, M = s(Le, { parseOnNested: true, parseOnQuoted: true, patterns: [ Ce ] }); var We = (e, t, o) => { var r; return e === "" ? [] : (r = o == null ? void 0 : o()) !== null && r !== void 0 ? r : []; }, Be = (...e) => (t, o) => e.reduceRight((r, n) => () => n(t, o, r), () => T(t, o))(), d = Be(We, O, I, Q, _, S, F, z, R, k, $, C, L, E, G, H, M); var U = (e) => { let { rows: [t, ...o] } = e, { indent: r = 0, text: n = "" } = t != null ? t : {}, c = n.replace(/^\s*table:/, ""); return { indent: r, type: "table", fileName: c, cells: o.map((i) => i.text.substring(r + 1)).map((i) => i.split(" ").map((p) => d(p, { nested: false, quoted: false, context: "table" }))) }; }; var q = (e) => { let { indent: t, text: o } = e.rows[0]; return { indent: t, type: "line", nodes: d(o.substring(t), { nested: false, quoted: false, context: "line" }) }; }; var x = (e) => { switch (e.type) { case "title": return b(e); case "codeBlock": return y(e); case "table": return U(e); case "line": return q(e); } }; var v = (e) => e.split(` `).map((t) => { var o, r, n; return { indent: (n = (r = (o = /^\s+/.exec(t)) === null || o === void 0 ? void 0 : o[0]) === null || r === void 0 ? void 0 : r.length) !== null && n !== void 0 ? n : 0, text: t }; }); var Ge = (e, t) => { var o, r; return (e.type === "codeBlock" || e.type === "table") && t.indent > ((r = (o = e.rows[0]) === null || o === void 0 ? void 0 : o.indent) !== null && r !== void 0 ? r : 0); }, j = (e, t) => { let o = e[e.length - 1]; return o !== void 0 && Ge(o, t) ? (o.rows.push(t), e) : (e.push({ type: /^\s*code:/.test(t.text) ? "codeBlock" : /^\s*table:/.test(t.text) ? "table" : "line", rows: [ t ] }), e); }, P = (e, t) => { var o; if ((o = t.hasTitle) !== null && o !== void 0 ? o : true) { let [r, ...n] = e; return r === void 0 ? [] : [ { type: "title", rows: [ r ] }, ...n.reduce(j, []) ]; } return e.reduce(j, []); }; var He = (e, t) => { var o; let r = v(e); return P(r, { hasTitle: (o = t == null ? void 0 : t.hasTitle) !== null && o !== void 0 ? o : true }).map(x); }; const sb2mrkdwn = (text, project) => { const blocks = He(text, { hasTitle: false }); return blocks.flatMap((block) => { const data = convertSb2Md(block, project); return data ? [ data ] : []; }); }; const convertSb2Md = (block, project) => { switch (block.type) { case "title": return; case "codeBlock": return { type: "section", text: { type: "mrkdwn", text: `\`${block.fileName}\` \`\`\`${block.content}\`\`\`` } }; case "table": return { type: "section", text: { type: "mrkdwn", text: `\`table:${block.fileName}\`` } }; case "line": return convertLine(block, project); } }; const convertLine = (line, project) => { const objects = line.nodes.flatMap((node) => convertNode(node, project)); if (objects.length === 1 && objects[0] && objects[0].type === "image" && !/^https:\/\/scrapbox\.io\/api\/pages\/.+\/.+\/icon$/.test(objects[0].image_url)) { return objects[0]; } const elements = line.indent > 0 ? [ { type: "plain_text", text: `${"\u3000".repeat(line.indent - 1)}\u25CF` } ] : []; let chunk; for (const object of objects) { if (object.type !== "mrkdwn") { if (chunk) elements.push(chunk); chunk = void 0; elements.push(object); continue; } chunk = { type: "mrkdwn", text: `${chunk ? chunk.text : ""}${object.text}` }; } if (chunk) elements.push(chunk); if (elements.length === 0) { elements.push({ type: "plain_text", text: " " }); } return { type: "context", elements }; }; const convertNode = (node1, project) => { switch (node1.type) { case "quote": { const objects = node1.nodes.flatMap((node) => convertNode(node, project)); if (objects.length === 0) objects.push({ type: "plain_text", text: " " }); switch (objects[0].type) { case "plain_text": case "mrkdwn": return [ { type: "mrkdwn", text: `>${objects[0].text}` }, ...objects.slice(1) ]; default: break; } return objects; } case "image": case "strongImage": return [ { type: "image", image_url: node1.src, alt_text: "image" } ]; case "icon": case "strongIcon": { const path = node1.pathType === "relative" ? `/${project}/${node1.path}` : node1.path; return [ { type: "image", image_url: `https://scrapbox.io/api/pages${path}/icon`, alt_text: node1.path } ]; } case "formula": return [ { type: "mrkdwn", text: ` \`${node1.formula}\` ` } ]; case "helpfeel": return [ { type: "mrkdwn", text: ` \`? ${node1.text}\` ` } ]; case "commandLine": return [ { type: "mrkdwn", text: ` \`${node1.symbol} ${node1.text}\` ` } ]; case "code": return [ { type: "mrkdwn", text: ` \`${node1.text}\` ` } ]; case "decoration": { const hasStrong = node1.decos.some((deco) => /\*-/.test(deco[0])); const hasItalic = node1.decos.includes("/"); const hasStrike = node1.decos.includes("-"); return node1.nodes.flatMap((node) => convertNode(node, project)).map((object) => { if (object.type !== "plain_text" || !/^\s*$/.test(object.text) || !/^<https?:\/\/.+\|.+>$/.test(object.text)) { return object; } let text = object.text; if (hasStrong) text = `*${text}*`; if (hasItalic) text = `_${text}_`; if (hasStrike) text = `~${text}~`; return { type: "mrkdwn", text: ` ${text} ` }; }); } case "strong": return node1.nodes.flatMap((node) => convertNode(node, project)).map((object) => { if (object.type !== "plain_text" || !/^\s*$/.test(object.text) || !/^<https?:\/\/.+\|.+>$/.test(object.text)) { return object; } return { type: "mrkdwn", text: `*${object.text}*` }; }); case "googleMap": return [ { type: "mrkdwn", text: `<${node1.url}|${node1.place}>` } ]; case "link": { switch (node1.pathType) { case "root": return [ { type: "mrkdwn", text: `<https://scrapbox.io${node1.href}|${node1.href}>` } ]; case "relative": return [ { type: "mrkdwn", text: `<https://scrapbox.io/${project}/${node1.href}|${node1.href}>` } ]; case "absolute": break; } return [ { type: "mrkdwn", text: `<${node1.href}|${node1.content || node1.href}>` } ]; } case "hashTag": return [ { type: "mrkdwn", text: `<https://scrapbox.io/${project}/${node1.href}|#${node1.href}>` } ]; case "blank": case "plain": return [ { type: "mrkdwn", text: node1.text } ]; } }; const toBlocks = (project, title, lineBlocks) => { const id = lineBlocks.length > 0 ? lineBlocks[0][0].id : void 0; return [ { type: "section", text: { type: "mrkdwn", text: `*<https://scrapbox.io/${project}/${encodeURIComponent(title)}${id !== void 0 ? `#${id}` : ""}|${title}>*` } }, ...lineBlocks.flatMap((lines) => [ ...sb2mrkdwn(lines.map((line) => line.text).join("\n"), project), { type: "divider" } ]) ]; }; function requiredArgs(required, args) { if (args.length < required) { throw new TypeError(required + " argument" + (required > 1 ? "s" : "") + " required, but only " + args.length + " present"); } } function toDate(argument) { requiredArgs(1, arguments); const argStr = Object.prototype.toString.call(argument); if (argument instanceof Date || typeof argument === "object" && argStr === "[object Date]") { return new Date(argument.getTime()); } else if (typeof argument === "number" || argStr === "[object Number]") { return new Date(argument); } else { if ((typeof argument === "string" || argStr === "[object String]") && typeof console !== "undefined") { console.warn("Starting with v2.0.0-beta.1 date-fns doesn't accept strings as date arguments. Please use `parseISO` to parse strings. See: https://git.io/fjule"); console.warn(new Error().stack); } return new Date(NaN); } } function addLeadingZeros(number, targetLength) { var sign = number < 0 ? "-" : ""; var output = Math.abs(number).toString(); while (output.length < targetLength) { output = "0" + output; } return sign + output; } const formatters = { y(date, token) { const signedYear = date.getUTCFullYear(); const year = signedYear > 0 ? signedYear : 1 - signedYear; return addLeadingZeros(token === "yy" ? year % 100 : year, token.length); }, M(date, token) { const month = date.getUTCMonth(); return token === "M" ? String(month + 1) : addLeadingZeros(month + 1, 2); }, d(date, token) { return addLeadingZeros(date.getUTCDate(), token.length); }, a(date, token) { const dayPeriodEnumValue = date.getUTCHours() / 12 >= 1 ? "pm" : "am"; switch (token) { case "a": case "aa": return dayPeriodEnumValue.toUpperCase(); case "aaa": return dayPeriodEnumValue; case "aaaaa": return dayPeriodEnumValue[0]; case "aaaa": default: return dayPeriodEnumValue === "am" ? "a.m." : "p.m."; } }, h(date, token) { return addLeadingZeros(date.getUTCHours() % 12 || 12, token.length); }, H(date, token) { return addLeadingZeros(date.getUTCHours(), token.length); }, m(date, token) { return addLeadingZeros(date.getUTCMinutes(), token.length); }, s(date, token) { return addLeadingZeros(date.getUTCSeconds(), token.length); }, S(date, token) { const numberOfDigits = token.length; const milliseconds = date.getUTCMilliseconds(); const fractionalSeconds = Math.floor(milliseconds * Math.pow(10, numberOfDigits - 3)); return addLeadingZeros(fractionalSeconds, token.length); } }; function getTimezoneOffsetInMilliseconds(date) { const utcDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds())); utcDate.setUTCFullYear(date.getFullYear()); return date.getTime() - utcDate.getTime(); } function isValid(dirtyDate) { requiredArgs(1, arguments); var date = toDate(dirtyDate); return !isNaN(date); } function toInteger(dirtyNumber) { if (dirtyNumber === null || dirtyNumber === true || dirtyNumber === false) { return NaN; } const number = Number(dirtyNumber); if (isNaN(number)) { return number; } return number < 0 ? Math.ceil(number) : Math.floor(number); } function addMilliseconds(dirtyDate, dirtyAmount) { requiredArgs(2, arguments); const timestamp = toDate(dirtyDate).getTime(); const amount = toInteger(dirtyAmount); return new Date(timestamp + amount); } function subMilliseconds(dirtyDate, dirtyAmount) { requiredArgs(2, arguments); const amount = toInteger(dirtyAmount); return addMilliseconds(dirtyDate, -amount); } const formattingTokensRegExp = /(\w)\1*|''|'(''|[^'])+('|$)|./g; const escapedStringRegExp = /^'([^]*?)'?$/; const doubleQuoteRegExp = /''/g; const unescapedLatinCharacterRegExp = /[a-zA-Z]/; function lightFormat(dirtyDate, formatStr) { requiredArgs(2, arguments); const originalDate = toDate(dirtyDate); if (!isValid(originalDate)) { throw new RangeError("Invalid time value"); } const timezoneOffset = getTimezoneOffsetInMilliseconds(originalDate); const utcDate = subMilliseconds(originalDate, timezoneOffset); const tokens = formatStr.match(formattingTokensRegExp); if (!tokens) return ""; const result = tokens.map((substring) => { if (substring === "''") { return "'"; } const firstCharacter = substring[0]; if (firstCharacter === "'") { return cleanEscapedString(substring); } const formatter = formatters[firstCharacter]; if (formatter) { return formatter(utcDate, substring); } if (firstCharacter.match(unescapedLatinCharacterRegExp)) { throw new RangeError("Format string contains an unescaped latin alphabet character `" + firstCharacter + "`"); } return substring; }).join(""); return result; } function cleanEscapedString(input) { const matches = input.match(escapedStringRegExp); if (!matches) { return input; } return matches[1].replace(doubleQuoteRegExp, "'"); } function* getPages(pages, { sid }) { console.log(`[getPages()] Start fetching ${pages.length} scrapbox pages...`); const count = Math.floor(pages.length / 10) + 1; for (let i = 0; i < count; i++) { console.log(`[getPages()] ${i * 10}/${pages.length}`); const responses = UrlFetchApp.fetchAll(pages.slice(i * 10, (i + 1) * 10).map(({ project, title }) => ({ url: `https://scrapbox.io/api/pages/${project}/${encodeURIComponent(title)}`, headers: { Cookie: `connect.sid=${sid}` }, muteHttpExceptions: true }))); const pages_ = pages.slice(i * 10, (i + 1) * 10); const jsons = responses.map((response, j1) => __spreadProps(__spreadValues({}, JSON.parse(response.getContentText())), { project: pages_[j1].project, title: pages_[j1].title })); yield jsons; } console.log("[getPages()] Finish fetching."); } function* getModifiedTitles(projects, { from, sid }) { console.log(`Start searching ${projects.length} scrapbox projects for pages which are updated from ${lightFormat(from, "yyyy-MM-dd HH:mm:ss")}: `, projects); for (const project of projects) { const data = getList(project, { sid, skip: 0 }); if ("name" in data) { console.error(`Error at "/${project}": ${data.name} ${data.message}`); continue; } const updates = []; for (const page of data.pages) { if (page.updated <= from.getTime() / 1e3) continue; updates.push(page.updated); yield { project, title: page.title, updated: page.updated }; } if (updates.length < 2) continue; console.log({ project, from: lightFormat(new Date(updates.length > 0 ? Math.min(...updates) * 1e3 : from.getTime()), "yyyy-MM-dd HH:mm:ss") }); } console.log(`Finish fetching.`); } const getList = (project, { sid, skip }) => { const response = UrlFetchApp.fetch(`https://scrapbox.io/api/pages/${project}?limit=1000&skip=${skip}`, { headers: { Cookie: `connect.sid=${sid}` }, muteHttpExceptions: true }); return JSON.parse(response.getContentText()); }; global.subscribe = (settings) => { const lastUpdated = getUpdated(); const sid = getSid(); if (sid === null) { throw Error("Please set cookie.sid"); } const updatedTitleList = [ ...getModifiedTitles([ ...new Set(settings.map(({ project }) => project)) ], { from: lastUpdated, sid }) ]; setUpdated(Math.max(...updatedTitleList.map(({ updated }) => updated), lastUpdated.getTime() / 1e3)); console.log(`Fetch updates until ${lightFormat(getUpdated(), "yyyy-MM-dd HH:mm:ss")}`); for (const jsons of getPages(updatedTitleList, { sid })) { const pageDataList = jsons.flatMap((json) => { if ("name" in json) { console.error(`Failed to fetch "/${json.project}/${json.title}" name: ${json.name} message: ${json.message}`); return []; } return [ { project: json.project, title: json.title, lineBlocks: getModifiedLines(json.lines, lastUpdated.getTime() / 1e3) } ]; }); const params = settings.flatMap(({ webhook, project, include, exclude }) => pageDataList.flatMap(({ project: project_, title, lineBlocks }) => { if (project_ !== project) return []; const lines = lineBlocks.flat(); if (include && !lines.some(({ text }) => include.test(text))) return []; if (exclude && lines.some(({ text }) => exclude.test(text))) return []; return [ { url: webhook, blocks: toBlocks(project, title, lineBlocks), description: lines.slice(0, 5).map((line) => line.text).join("\n") } ]; })); postToSlack(params); } }; const getModifiedLines = (lines, from) => { const result = []; let chunk = []; for (const line of lines) { if (line.updated <= from) { if (chunk.length === 0) continue; result.push([ ...chunk ]); chunk = []; continue; } chunk.push(line); } if (chunk.length > 0) result.push([ ...chunk ]); return result; }; const getUpdated = () => { var _a; const scriptProperties = PropertiesService.getScriptProperties(); const value = parseFloat((_a = scriptProperties.getProperty("LAST_UPDATED")) != null ? _a : "0"); return !isNaN(value) ? new Date(value * 1e3) : yesterday(); }; const setUpdated = (...times) => { const scriptProperties = PropertiesService.getScriptProperties(); scriptProperties.setProperty("LAST_UPDATED", `${Math.max(...times)}`); }; const getSid = () => { const scriptProperties = PropertiesService.getScriptProperties(); return scriptProperties.getProperty("CONNECT_SID"); }; const yesterday = () => { const now = new Date(); now.setDate(now.getDate() - 1); return now; }; })(); function main(){ subscribe(createDefaultSettings()) }