105 lines
3.3 KiB
TypeScript
105 lines
3.3 KiB
TypeScript
import { writeFileSync, existsSync, readFileSync } from "fs";
|
|
import * as cheerio from "cheerio";
|
|
|
|
const CACHE_FILE = "./temp/scheduleCache.json";
|
|
|
|
const URLS = {
|
|
section1:
|
|
"https://isangjeong.icems.kr/schdList.do?section=1&m=021101&s=isangjeong",
|
|
section2:
|
|
"https://isangjeong.icems.kr/schdList.do?section=2&m=021101&s=isangjeong",
|
|
};
|
|
|
|
const MONTH_XPATH_MAP: Record<
|
|
string,
|
|
{ section: keyof typeof URLS; divIndex: number }
|
|
> = {
|
|
"03": { section: "section1", divIndex: 2 },
|
|
"04": { section: "section1", divIndex: 3 },
|
|
"05": { section: "section1", divIndex: 4 },
|
|
"06": { section: "section1", divIndex: 5 },
|
|
"07": { section: "section1", divIndex: 6 },
|
|
"08": { section: "section1", divIndex: 7 },
|
|
"09": { section: "section2", divIndex: 2 },
|
|
"10": { section: "section2", divIndex: 3 },
|
|
"11": { section: "section2", divIndex: 4 },
|
|
"12": { section: "section2", divIndex: 5 },
|
|
"01": { section: "section2", divIndex: 6 },
|
|
"02": { section: "section2", divIndex: 7 },
|
|
};
|
|
|
|
async function fetchSectionSchedule(
|
|
url: string,
|
|
divIndexes: { month: string; index: number }[]
|
|
): Promise<Record<string, { date: string; desc: string }[]>> {
|
|
const res = await fetch(url, {
|
|
headers: {
|
|
"User-Agent": "Mozilla/5.0 (compatible; today.isangjeong/1.0)",
|
|
},
|
|
});
|
|
|
|
const html = await res.text();
|
|
const $ = cheerio.load(html);
|
|
|
|
const sectionSchedule: Record<string, { date: string; desc: string }[]> = {};
|
|
|
|
for (const { month, index } of divIndexes) {
|
|
const selector = `#all-scroll > div > form > div > div:nth-of-type(2) > div:nth-of-type(${index}) > dl > dd > ul`;
|
|
const items: { date: string; desc: string }[] = [];
|
|
|
|
$(selector).find("li").each((_, li) => {
|
|
const text = $(li).text().trim().replace(/\u00A0/g, " ");
|
|
if (text) {
|
|
const [date, desc] = text.split(" : ");
|
|
if (date && desc) {
|
|
items.push({ date: date.trim(), desc: desc.trim() });
|
|
}
|
|
}
|
|
});
|
|
|
|
sectionSchedule[month] = items;
|
|
}
|
|
|
|
return sectionSchedule;
|
|
}
|
|
|
|
export async function getAllSchedules(
|
|
refresh = false
|
|
): Promise<Record<string, { date: string; desc: string }[]>> {
|
|
if (!refresh && existsSync(CACHE_FILE)) {
|
|
const cached = readFileSync(CACHE_FILE, "utf-8");
|
|
return JSON.parse(cached);
|
|
}
|
|
|
|
const sectionMap: Record<keyof typeof URLS, { month: string; index: number }[]> = {
|
|
section1: [],
|
|
section2: [],
|
|
};
|
|
|
|
for (const [month, { section, divIndex }] of Object.entries(MONTH_XPATH_MAP)) {
|
|
sectionMap[section].push({ month, index: divIndex });
|
|
}
|
|
|
|
// 병렬로 section1, section2 요청
|
|
const promises = (Object.keys(sectionMap) as (keyof typeof URLS)[]).map(
|
|
async (section) => {
|
|
const url = URLS[section];
|
|
try {
|
|
const sectionSchedule = await fetchSectionSchedule(url, sectionMap[section]);
|
|
console.log(`✅ ${section} 완료`);
|
|
return sectionSchedule;
|
|
} catch (err) {
|
|
console.error(`❌ ${section} 실패:`, err);
|
|
return Object.fromEntries(sectionMap[section].map(({ month }) => [month, []]));
|
|
}
|
|
}
|
|
);
|
|
|
|
const results = await Promise.all(promises);
|
|
|
|
const schedule = Object.assign({}, ...results);
|
|
|
|
writeFileSync(CACHE_FILE, JSON.stringify(schedule, null, 2), "utf-8");
|
|
console.log(`📦 캐시 저장됨 → ${CACHE_FILE}`);
|
|
return schedule;
|
|
}
|