feat(bot): studiofromis_9 채널 봇 추가 및 cron 표현식 지원

- .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 * * * *)
This commit is contained in:
caadiq 2026-01-05 22:26:44 +09:00
parent 1b01182028
commit e216539f34
3 changed files with 28 additions and 35 deletions

3
.env
View file

@ -19,3 +19,6 @@ RUSTFS_BUCKET=fromis-9
# Kakao API
KAKAO_REST_KEY=cf21156ae6cc8f6e95b3a3150926cdf8
# YouTube API
YOUTUBE_API_KEY=AIzaSyC6l3nFlcHgLc0d1Q9WPyYQjVKTv21ZqFs

View file

@ -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) {

View file

@ -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(