summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/send-recap.ts74
1 files changed, 27 insertions, 47 deletions
diff --git a/scripts/send-recap.ts b/scripts/send-recap.ts
index 5a11d0a..232ea76 100644
--- a/scripts/send-recap.ts
+++ b/scripts/send-recap.ts
@@ -1,8 +1,8 @@
#!/usr/bin/env node
/**
* Daily live event recap mailer.
- * Queries upcoming events (tomorrow + 6 days) from SQLite and sends an HTML
- * digest via the internal Postfix container (mail:25 on the web Docker network).
+ * Queries today's events (JST) from SQLite and sends an HTML digest via the
+ * internal Postfix container (mail:25 on the web Docker network).
*
* Env vars:
* DB_PATH path to SQLite database (default: /app/data/events.db)
@@ -19,7 +19,7 @@ const MAIL_HOST = process.env.MAIL_HOST ?? "mail";
const MAIL_PORT = parseInt(process.env.MAIL_PORT ?? "25", 10);
const FROM = "noreply@golive.yyamashita.com";
const TO = process.env.RECAP_TO ?? "efemeraladdress+golive@gmail.com";
-const DAYS = 7;
+const APP_URL = "https://golive.yyamashita.com";
// ---- date helpers (JST = UTC+9) ----------------------------------------
@@ -34,12 +34,6 @@ function isoDate(d: Date): string {
return new Date(d.getTime() + JST_MS).toISOString().slice(0, 10);
}
-function addDays(d: Date, n: number): Date {
- const r = new Date(d);
- r.setUTCDate(r.getUTCDate() + n);
- return r;
-}
-
function fmtJST(dateStr: string): string {
const DOW = ["日", "月", "火", "水", "木", "金", "土"];
// Parse as JST midnight
@@ -89,7 +83,8 @@ function esc(s: string): string {
.replace(/"/g, """);
}
-function buildHtml(events: EventRow[], from: string, to: string): string {
+function buildHtml(events: EventRow[], date: string): string {
+ const pageUrl = `${APP_URL}/events/by-date?date=${date}`;
const head = `<!DOCTYPE html>
<html lang="ja">
<head>
@@ -97,7 +92,6 @@ function buildHtml(events: EventRow[], from: string, to: string): string {
<style>
body{font-family:sans-serif;font-size:14px;color:#333;max-width:800px;margin:0 auto;padding:16px}
h1{font-size:20px;border-bottom:3px solid #c00;padding-bottom:6px;color:#c00}
-h2{font-size:15px;color:#555;margin:20px 0 6px;background:#f5f5f5;padding:4px 8px;border-left:4px solid #c00}
table{border-collapse:collapse;width:100%;margin-bottom:4px}
th,td{border:1px solid #ddd;padding:6px 10px;text-align:left;vertical-align:top}
th{background:#fafafa;font-weight:bold;font-size:12px;color:#666}
@@ -106,45 +100,33 @@ th{background:#fafafa;font-weight:bold;font-size:12px;color:#666}
a{color:#1a7a3c;text-decoration:none}
a:hover{text-decoration:underline}
.none{color:#aaa;font-style:italic;font-size:12px}
+.footer{margin-top:16px;font-size:12px;color:#999;border-top:1px solid #eee;padding-top:8px}
</style>
</head>
<body>
-<h1>東京ライブハウス 週間イベント</h1>
-<p style="color:#777;font-size:13px">${esc(fmtJST(from))} 〜 ${esc(fmtJST(to))} のイベント一覧</p>
+<h1>東京ライブハウス ${esc(fmtJST(date))} のライブ</h1>
+<p><a href="${esc(pageUrl)}">${esc(pageUrl)}</a></p>
`;
if (events.length === 0) {
- return (
- head +
- `<p>この期間のイベントはまだ登録されていません。</p></body></html>`
- );
+ return head + `<p>本日のイベントはまだ登録されていません。</p></body></html>`;
}
- const byDate = new Map<string, EventRow[]>();
+ let body = `<table>\n<tr><th>会場</th><th>アーティスト / タイトル</th><th>開演</th><th>料金</th><th>リンク</th></tr>\n`;
for (const e of events) {
- const list = byDate.get(e.date) ?? [];
- list.push(e);
- byDate.set(e.date, list);
- }
-
- let body = "";
- for (const [date, rows] of byDate) {
- body += `<h2>${esc(fmtJST(date))} <span style="font-weight:normal;font-size:13px">${rows.length} 件</span></h2>\n`;
- body += `<table>\n<tr><th>会場</th><th>アーティスト / タイトル</th><th>開演</th><th>料金</th><th>リンク</th></tr>\n`;
- for (const e of rows) {
- const nameCell = e.artist
- ? `<span class="artist">${esc(e.artist)}</span><br><span class="sub">${esc(e.title)}</span>`
- : `<span class="sub">${esc(e.title)}</span>`;
- const time = e.start_time ? esc(e.start_time) : `<span class="none">-</span>`;
- const price = e.price ? esc(e.price) : `<span class="none">-</span>`;
- const url = e.ticket_url ?? e.source_url;
- const link = url
- ? `<a href="${esc(url)}">詳細</a>`
- : `<span class="none">-</span>`;
- body += `<tr><td>${esc(e.venue_name)}</td><td>${nameCell}</td><td>${time}</td><td>${price}</td><td>${link}</td></tr>\n`;
- }
- body += `</table>\n`;
+ const nameCell = e.artist
+ ? `<span class="artist">${esc(e.artist)}</span><br><span class="sub">${esc(e.title)}</span>`
+ : `<span class="sub">${esc(e.title)}</span>`;
+ const time = e.start_time ? esc(e.start_time) : `<span class="none">-</span>`;
+ const price = e.price ? esc(e.price) : `<span class="none">-</span>`;
+ const url = e.ticket_url ?? e.source_url;
+ const link = url
+ ? `<a href="${esc(url)}">詳細</a>`
+ : `<span class="none">-</span>`;
+ body += `<tr><td>${esc(e.venue_name)}</td><td>${nameCell}</td><td>${time}</td><td>${price}</td><td>${link}</td></tr>\n`;
}
+ body += `</table>\n`;
+ body += `<p class="footer">${events.length} 件 ・ <a href="${esc(pageUrl)}">一覧ページで開く</a></p>\n`;
return head + body + `</body></html>`;
}
@@ -255,16 +237,14 @@ function smtpSend(
// ---- main ---------------------------------------------------------------
async function main() {
- const today = nowJST();
- const from = isoDate(addDays(today, 1)); // tomorrow
- const to = isoDate(addDays(today, DAYS)); // 7 days out
+ const date = isoDate(nowJST());
- console.log(`[recap] querying events ${from} – ${to}`);
- const events = fetchEvents(from, to);
+ console.log(`[recap] querying events for ${date}`);
+ const events = fetchEvents(date, date);
console.log(`[recap] ${events.length} events found`);
- const subject = `東京ライブハウス ${fmtJST(from).slice(5, 10)} 〜 ${fmtJST(to).slice(5, 10)} のイベント(${events.length} 件)`;
- const html = buildHtml(events, from, to);
+ const subject = `東京ライブハウス ${fmtJST(date)} のライブ(${events.length} 件)`;
+ const html = buildHtml(events, date);
const message = buildMessage(subject, html);
console.log(`[recap] sending to ${TO} via ${MAIL_HOST}:${MAIL_PORT}`);