fix: 21 agenda , key invite

This commit is contained in:
2026-08-19 16:21:27 +07:00
parent cb54058550
commit 16bf5edb97
7 changed files with 898 additions and 164 deletions
+206
View File
@@ -0,0 +1,206 @@
import axios from "axios";
import * as fs from "fs";
import * as path from "path";
interface Speaker {
name: string;
title: string;
bio: string;
initials?: string;
}
interface RawSession {
id: string;
title: string;
start: string;
end: string;
track: string;
location: string;
description: string;
speaker: Speaker | null;
}
interface EnrichedSession extends RawSession {
trackLabel: string;
durationMinutes: number;
timeICT: string;
timeUTC: string;
}
interface SummitAgendaData {
event: {
title: string;
theme: string;
date: string;
location: string;
sourceUrl: string;
crawledAt: string;
};
summary: {
totalSessions: number;
sessionsWithSpeakers: number;
tracksBreakdown: Record<string, number>;
uniqueSpeakers: Array<{ name: string; title: string }>;
};
sessions: EnrichedSession[];
}
const TRACK_LABELS: Record<string, string> = {
ceremony: "Ceremonies",
keynote: "Keynotes & Address",
technical: "Technical & Infrastructure",
panel: "Forums & Panels",
networking: "Breaks & Networking"
};
function formatTime(dateStr: string, timeZone: string): string {
const date = new Date(dateStr);
return date.toLocaleTimeString("en-US", {
timeZone,
hour: "numeric",
minute: "2-digit",
hour12: true
}).replace(":", ".").toLowerCase();
}
function calculateDuration(startStr: string, endStr: string): number {
const start = new Date(startStr).getTime();
const end = new Date(endStr).getTime();
return Math.round((end - start) / (1000 * 60));
}
export async function crawlSummitAgenda(targetUrl = "https://apacv6.org/ai-summit-2026/"): Promise<SummitAgendaData> {
console.log(`[Crawler] Bắt đầu cào dữ liệu từ: ${targetUrl}...`);
// 1. Fetch main page HTML
const baseUrl = targetUrl.endsWith("/") ? targetUrl : targetUrl + "/";
const htmlResponse = await axios.get(baseUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}
});
const html = htmlResponse.data;
// 2. Fetch app.js containing the dataset
const appJsUrl = `${baseUrl}app.js`;
console.log(`[Crawler] Tải mã nguồn kịch bản dữ liệu từ: ${appJsUrl}...`);
const appJsResponse = await axios.get(appJsUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}
});
const jsContent = appJsResponse.data;
// 3. Extract the `sessions` array from app.js
const sessionsMatch = jsContent.match(/const\s+sessions\s*=\s*(\[\s*\{[\s\S]*?\}\s*\]);/);
if (!sessionsMatch || !sessionsMatch[1]) {
throw new Error("Không thể trích xuất mảng dữ liệu 'sessions' từ app.js!");
}
let rawSessions: RawSession[] = [];
try {
// Safely evaluate or parse JS object array
const rawSessionsJsonStr = sessionsMatch[1];
// Use Function constructor in isolated scope to parse JS array notation
const parseFn = new Function(`return ${rawSessionsJsonStr};`);
rawSessions = parseFn();
} catch (err) {
console.error("[Crawler] Lỗi khi parse dữ liệu sessions:", err);
throw err;
}
console.log(`[Crawler] Đã trích xuất thành công ${rawSessions.length} phiên (sessions).`);
// 4. Enrich sessions with friendly format and timings
const enrichedSessions: EnrichedSession[] = rawSessions.map((session) => {
const duration = calculateDuration(session.start, session.end);
const startIct = formatTime(session.start, "Asia/Bangkok");
const endIct = formatTime(session.end, "Asia/Bangkok");
const startUtc = formatTime(session.start, "UTC");
const endUtc = formatTime(session.end, "UTC");
// Fix known typo on live for Tran Quy
let speaker = session.speaker;
if (speaker && (speaker.name.includes("Ho So Chuyen") || session.id === "session-16")) {
speaker = {
name: "Mr. Tran Quy",
title: "",
bio: "Senior technical lead and infrastructure specialist, demonstrating next-generation network configurations.",
initials: "TQ"
};
}
return {
...session,
speaker,
trackLabel: TRACK_LABELS[session.track] || session.track,
durationMinutes: duration,
timeICT: `${startIct} - ${endIct} ICT`,
timeUTC: `${startUtc} - ${endUtc} UTC`
};
});
// 5. Summary statistics
const tracksBreakdown: Record<string, number> = {};
const uniqueSpeakersMap = new Map<string, { name: string; title: string }>();
enrichedSessions.forEach((s) => {
tracksBreakdown[s.track] = (tracksBreakdown[s.track] || 0) + 1;
if (s.speaker && s.speaker.name && !s.speaker.name.toLowerCase().startsWith("tbd")) {
uniqueSpeakersMap.set(s.speaker.name, {
name: s.speaker.name,
title: s.speaker.title
});
}
});
const agendaResult: SummitAgendaData = {
event: {
title: "IPv6 Enhanced for AI & Cloud Summit 2026",
theme: "AI-driven data centres, cloud technologies, Net 5.5G and next-generation connectivity solutions",
date: "2026-08-27",
location: "Hanoi, Vietnam",
sourceUrl: targetUrl,
crawledAt: new Date().toISOString()
},
summary: {
totalSessions: enrichedSessions.length,
sessionsWithSpeakers: enrichedSessions.filter((s) => s.speaker !== null).length,
tracksBreakdown,
uniqueSpeakers: Array.from(uniqueSpeakersMap.values())
},
sessions: enrichedSessions
};
return agendaResult;
}
// CLI Execution entry point
async function main() {
try {
const data = await crawlSummitAgenda();
// Ensure output directory exists
const outputDir = path.join(process.cwd(), "data");
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const outputPath = path.join(outputDir, "summit-2026-agenda.json");
fs.writeFileSync(outputPath, JSON.stringify(data, null, 2), "utf-8");
console.log(`\n======================================================`);
console.log(`🎉 CÀO DỮ LIỆU AGENDA THÀNH CÔNG!`);
console.log(`📁 File kết quả đã lưu tại: ${outputPath}`);
console.log(`📊 Tổng số sessions: ${data.summary.totalSessions}`);
console.log(`🎤 Số diễn giả đã xác định: ${data.summary.uniqueSpeakers.length}`);
console.log(`======================================================\n`);
} catch (error) {
console.error("❌ Lỗi cào dữ liệu:", error);
process.exit(1);
}
}
if (require.main === module) {
main();
}