// vts-fetch.ts const BASE = "https://isangjeong.icems.kr"; async function main() { const listUrl = `${BASE}/boardCnts/list.do?boardID=33523&m=0601&s=isangjeong`; const listRes = await fetch(listUrl, { headers: { 'User-Agent': 'today.isangjeong' } }); if (!listRes.ok) throw new Error(`list fetch failed: ${listRes.status}`); const listHtml = await listRes.text(); // 더 안정적으로: 먼저 subject_ID 검사, 없으면 goView의 3번째 인자 사용 let boardSeq: string | null = null; const idMatch = listHtml.match(/id=["']subject_(\d+)["']/); if (idMatch) boardSeq = idMatch[1]; else { const gv = listHtml.match(/goView\(\s*['"]\d+['"]\s*,\s*['"]\d+['"]\s*,\s*['"](\d+)['"]/); if (gv) boardSeq = gv[1]; } if (!boardSeq) { console.error("게시글 번호( boardSeq )를 찾을 수 없음."); return; } console.log("게시글 번호:", boardSeq); const viewUrl = `${BASE}/boardCnts/updateCnt.do?boardID=33523&viewBoardID=33523&boardSeq=${boardSeq}&lev=0&action=view`; console.log("상세 페이지 URL:", viewUrl); const viewRes = await fetch(viewUrl, { headers: { 'User-Agent': 'today.isangjeong' } }); if (!viewRes.ok) throw new Error(`view fetch failed: ${viewRes.status}`); const viewHtml = await viewRes.text(); // 첨부파일 링크 추출 (첫 번째 fileDown 링크 사용) const fileHrefMatch = viewHtml.match(/href=["'](\/boardCnts\/fileDown\.do\?fileSeq=[^"']+)["']/i); if (!fileHrefMatch) { console.error("파일 다운로드 링크 없음."); return; } const fileUrl = BASE + fileHrefMatch[1]; console.log("파일 URL:", fileUrl); const fileRes = await fetch(fileUrl, { headers: { 'User-Agent': 'today.isangjeong' } }); if (!fileRes.ok) throw new Error(`file fetch failed: ${fileRes.status}`); const ab = await fileRes.arrayBuffer(); const outputPath = "/var/static/f.imnya.ng/.today.isangjeong/vts.xlsx"; await Bun.write(outputPath, new Uint8Array(ab)); console.log("저장됨:", outputPath); } main().catch((e) => { console.error(e); process.exit(1); });