From e216539f34742ad8bfe537e11434f52478177203 Mon Sep 17 00:00:00 2001 From: caadiq Date: Mon, 5 Jan 2026 22:26:44 +0900 Subject: [PATCH] =?UTF-8?q?feat(bot):=20studiofromis=5F9=20=EC=B1=84?= =?UTF-8?q?=EB=84=90=20=EB=B4=87=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20cron?= =?UTF-8?q?=20=ED=91=9C=ED=98=84=EC=8B=9D=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .env에 YouTube API 키 추가 - bots 테이블에 channel_id, rss_url, cron_expression 컬럼 추가 - youtube-scheduler.js: cron_expression 파라미터 지원 - youtube-bot.js: bots 테이블에서 직접 조회, Shorts 필터링 제거 - studiofromis_9 봇 등록 (채널: UCW0ORl7mZO8wIWkgtq-yzjQ, cron: 1,2 * * * *) --- .env | 3 ++ backend/services/youtube-bot.js | 40 +++++++++++---------------- backend/services/youtube-scheduler.js | 20 ++++++-------- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/.env b/.env index ec421bb..1f4d772 100644 --- a/.env +++ b/.env @@ -19,3 +19,6 @@ RUSTFS_BUCKET=fromis-9 # Kakao API KAKAO_REST_KEY=cf21156ae6cc8f6e95b3a3150926cdf8 + +# YouTube API +YOUTUBE_API_KEY=AIzaSyC6l3nFlcHgLc0d1Q9WPyYQjVKTv21ZqFs diff --git a/backend/services/youtube-bot.js b/backend/services/youtube-bot.js index 6b0b985..3797f7c 100644 --- a/backend/services/youtube-bot.js +++ b/backend/services/youtube-bot.js @@ -241,20 +241,19 @@ export async function createScheduleFromVideo(video, categoryId) { */ export async function syncNewVideos(botId) { try { - // 봇 정보 조회 - const [bots] = await pool.query( - `SELECT b.*, c.channel_id, c.rss_url, c.include_shorts - FROM bots b - JOIN bot_youtube_config c ON b.id = c.bot_id - WHERE b.id = ?`, - [botId] - ); + // 봇 정보 조회 (bots 테이블에서 직접) + const [bots] = await pool.query(`SELECT * FROM bots WHERE id = ?`, [botId]); if (bots.length === 0) { throw new Error("봇을 찾을 수 없습니다."); } const bot = bots[0]; + + if (!bot.rss_url) { + throw new Error("RSS URL이 설정되지 않았습니다."); + } + const categoryId = await getYoutubeCategory(); // RSS 피드 파싱 @@ -262,10 +261,7 @@ export async function syncNewVideos(botId) { let addedCount = 0; for (const video of videos) { - // Shorts 필터링 - if (!bot.include_shorts && video.videoType === "shorts") { - continue; - } + // Shorts도 포함하여 일정으로 추가 const scheduleId = await createScheduleFromVideo(video, categoryId); if (scheduleId) { @@ -303,20 +299,19 @@ export async function syncNewVideos(botId) { */ export async function syncAllVideos(botId) { try { - // 봇 정보 조회 - const [bots] = await pool.query( - `SELECT b.*, c.channel_id, c.include_shorts - FROM bots b - JOIN bot_youtube_config c ON b.id = c.bot_id - WHERE b.id = ?`, - [botId] - ); + // 봇 정보 조회 (bots 테이블에서 직접) + const [bots] = await pool.query(`SELECT * FROM bots WHERE id = ?`, [botId]); if (bots.length === 0) { throw new Error("봇을 찾을 수 없습니다."); } const bot = bots[0]; + + if (!bot.channel_id) { + throw new Error("Channel ID가 설정되지 않았습니다."); + } + const categoryId = await getYoutubeCategory(); // API로 전체 영상 수집 @@ -324,10 +319,7 @@ export async function syncAllVideos(botId) { let addedCount = 0; for (const video of videos) { - // Shorts 필터링 - if (!bot.include_shorts && video.videoType === "shorts") { - continue; - } + // Shorts도 포함하여 일정으로 추가 const scheduleId = await createScheduleFromVideo(video, categoryId); if (scheduleId) { diff --git a/backend/services/youtube-scheduler.js b/backend/services/youtube-scheduler.js index 57679e9..c6b5aef 100644 --- a/backend/services/youtube-scheduler.js +++ b/backend/services/youtube-scheduler.js @@ -8,14 +8,14 @@ const schedulers = new Map(); /** * 개별 봇 스케줄 등록 */ -export function registerBot(botId, intervalMinutes = 2) { +export function registerBot(botId, intervalMinutes = 2, cronExpression = null) { // 기존 스케줄이 있으면 제거 unregisterBot(botId); - // cron 표현식 생성 (1분 시작, X분 간격: 1,3,5,7...) - const cronExpression = `1-59/${intervalMinutes} * * * *`; + // cron 표현식: 지정된 표현식 사용, 없으면 기본값 생성 + const expression = cronExpression || `1-59/${intervalMinutes} * * * *`; - const task = cron.schedule(cronExpression, async () => { + const task = cron.schedule(expression, async () => { console.log(`[Bot ${botId}] 동기화 시작...`); try { const result = await syncNewVideos(botId); @@ -26,9 +26,7 @@ export function registerBot(botId, intervalMinutes = 2) { }); schedulers.set(botId, task); - console.log( - `[Bot ${botId}] 스케줄 등록됨 (${intervalMinutes}분 간격, 1분 오프셋)` - ); + console.log(`[Bot ${botId}] 스케줄 등록됨 (cron: ${expression})`); } /** @@ -48,11 +46,11 @@ export function unregisterBot(botId) { export async function initScheduler() { try { const [bots] = await pool.query( - "SELECT id, check_interval FROM bots WHERE status = 'running'" + "SELECT id, check_interval, cron_expression FROM bots WHERE status = 'running'" ); for (const bot of bots) { - registerBot(bot.id, bot.check_interval); + registerBot(bot.id, bot.check_interval, bot.cron_expression); } console.log(`[Scheduler] ${bots.length}개 봇 스케줄 등록됨`); @@ -72,8 +70,8 @@ export async function startBot(botId) { const bot = bots[0]; - // 스케줄 등록 - registerBot(botId, bot.check_interval); + // 스케줄 등록 (cron_expression 우선 사용) + registerBot(botId, bot.check_interval, bot.cron_expression); // 상태 업데이트 await pool.query(