-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.ts
128 lines (112 loc) · 3.54 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import * as v from "valibot";
import { NotionTypes, TypeChecker, Url, check } from "./validator";
/*
API REFERENCE:
https://developers.notion.com/docs/working-with-databases
*/
// the url is safe to publish.
const NOTION_TASK_PAGE_URL = "https://www.notion.so/utcode/e8d7215fb5224be4a9a3e7d3be4d41ff";
const DAY = 24 * 60 * 60 * 1000;
const query = JSON.stringify({
filter: {
and: [
{
property: "期日",
date: {
before: new Date(Date.now() + 3 * DAY).toISOString().match(/^\d{4}-\d{2}-\d{2}/)?.[0],
},
},
{
property: "対応済",
checkbox: {
equals: false,
},
},
],
},
sorts: [
{
property: "期日",
direction: "ascending",
},
],
});
const NotionFetchResponse = v.object({
results: v.array(
v.object({
properties: v.object({
期日: v.union([NotionTypes.date, v.undefined()]),
タイトル: v.union([NotionTypes.title, v.undefined()]),
担当者: v.union([NotionTypes.people, v.undefined()]),
}),
}),
),
});
async function main() {
const tc = new TypeChecker();
const response = await fetch("https://api.notion.com/v1/databases/e8d7215f-b522-4be4-a9a3-e7d3be4d41ff/query", {
method: "POST",
headers: {
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.NOTION_API_KEY}`,
},
body: query,
});
const json = tc.check("notion fetch response", await response.json(), NotionFetchResponse);
const promises = json.results.map(async (result) => {
const due = result.properties.期日?.date.start;
const title = result.properties.タイトル?.title.map((title) => title.plain_text).join("");
const assignee = result.properties.担当者?.people.map((person) => person.name).join(" / ");
if (!assignee) {
return `・【${due}】${title}`;
}
return `・【${due}】${title} # @${assignee}`;
});
const tasks = await Promise.all(promises);
let message =
json.results.length === 0
? "本日は期限が迫っているタスクはありませんでした。"
: `
3日以内に期限が迫っているタスクがあります!
${tasks.join("\n")}
完了したら、タスクを対応済みにしてください。
<<${NOTION_TASK_PAGE_URL}|運営タスク>>
`.trim();
if (tc.hasFailed()) message += `\n---\n 一つ以上の型チェックが失敗しました: ${tc.errors}`;
return message;
}
const result = await retry(3, async () => await main());
if (typeof result === "string") {
await webhook(result);
} else {
await webhook(`Auto Moderator の実行に失敗しました: ${result.message}`);
process.exit(1);
}
// lib section
async function retry(count: number, func: () => Promise<string>): Promise<string | Error> {
let err: Error = new Error("失敗していません");
for (const _ of new Array(count).fill(0)) {
try {
return await func();
} catch (e) {
console.error(e);
err = e as Error;
}
}
return err;
}
async function webhook(message: string) {
// not appending this to messages, as it includes secrets
const { err, val: webhookURL } = check("env SLACK_WEBHOOK_URL", process.env.SLACK_WEBHOOK_URL, Url);
if (err) {
console.error(`Failed to parse webhook. first and last characters are as follows.
first: ${webhookURL.at(0)}
last: ${webhookURL.at(-1)}`);
}
await fetch(webhookURL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: message }),
});
}