import prisma from "./src/lib/prisma"; import dayjs from "dayjs"; import { RankingService } from "./src/services/ranking.service"; import { NaverRealEstate } from "./src/services/naver.service"; import { realestateTypes, realtorIds } from "./config"; import { TelegramService, telegramUsers, type TelegramUser, } from "./src/services/telegram.service"; import type { RealEstateArticle } from "./src/generated/prisma"; import * as XLSX from "xlsx"; const telegramService = new TelegramService( "233460568:AAHWgRQo5IgcWR0uXdsiMEzNnsmIqjOgk24", false ); async function main() { await updateRanking(); // await sendTelegram(); } async function sendTelegram() { for (let telegramUser of telegramUsers) { const articles = await prisma.realEstateArticle.findMany({ where: { isActive: true, realtorId: telegramUser.realtorId, ...(telegramUser.site !== "ALL" && { cpNm: telegramUser.site }), ...(telegramUser.site === "부동산포스" && { brokerPhone: telegramUser.phone, }), }, }); const excelFilePath = await createExcelFile(telegramUser, articles); await telegramService.sendDocument( telegramUser.chatId, excelFilePath, `네이버 부동산 매물 목록 (${dayjs().format("YYYY-MM-DD")})` ); } } async function createExcelFile( telegramUser: TelegramUser, articles: RealEstateArticle[] ): Promise { // 데이터를 배열로 변환 const data = articles.map((item) => { const getOwnerType = () => { if (["MOBL", "NDOC1", "OWNER"].includes(item.verificationType || "")) { if ( ["VL", "APT", "OPST", "DDDGG"].includes(item.realEstateType || "") ) { return "집주인"; } else { return "소유자"; } } if (["SITE", "S_VR"].includes(item.verificationType || "")) { return "현장"; } return ""; }; const getPrice = () => { return item.prcInfo; }; return { 매물번호: item.articleNumber, 바로가기: `https://fin.land.naver.com/articles/${item.articleNumber}`, 소유자구분: getOwnerType(), 매물형태: item.articleName || "", 매매구분: item.tradTpNm || "", 주소: `${item.city || ""} ${item.division || ""} ${item.sector || ""}`, 상세주소: item.detailAddress || "", 층수: item.floorInfo || "", 가격: getPrice(), 매물특징: item.articleDescription || "", 광고사: item.cpNm || "", 확인일자: item.articleConfirmDate ? dayjs(item.articleConfirmDate).format("YYYY-MM-DD") : "", 순위: item.ranking || 9999, }; }); // 워크시트 생성 const worksheet = XLSX.utils.json_to_sheet(data); // 바로가기 컬럼에 하이퍼링크 추가 (B열, 2번째 컬럼) articles.forEach((item, index) => { const cellAddress = `B${index + 2}`; // 헤더가 1행이므로 데이터는 2행부터 const url = `https://fin.land.naver.com/articles/${item.articleNumber}`; worksheet[cellAddress] = { t: "s", // string type v: "열기", // 표시되는 텍스트 l: { Target: url }, // hyperlink }; }); // 컬럼 너비 설정 worksheet["!cols"] = [ { wch: 12 }, // 매물번호 { wch: 10 }, // 바로가기 { wch: 10 }, // 소유자구분 { wch: 12 }, // 매물형태 { wch: 10 }, // 매매구분 { wch: 30 }, // 주소 { wch: 40 }, // 상세주소 { wch: 8 }, // 층수 { wch: 14 }, // 가격 { wch: 30 }, // 매물특징 { wch: 14 }, // 광고사 { wch: 10 }, // 확인일자 { wch: 6 }, // 순위 ]; // 워크북 생성 const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "매물목록"); // 파일 저장 const filePath = `./xlsx/${dayjs().format("YYYY-MM-DD_HH-mm")}_순위_${ telegramUser.site }_${telegramUser.realtorId}.xlsx`; XLSX.writeFile(workbook, filePath); return filePath; } async function updateRanking() { const checkDate = dayjs().toDate(); for (let realtorId of realtorIds) { const articles = await prisma.realEstateArticle.findMany({ where: { isActive: true, realtorId: realtorId, }, }); const rankingService = new RankingService( new NaverRealEstate({ realtorId: realtorId, }) ); await rankingService.updateRanking(articles, checkDate); } } main();