feat: 봇 일정 추가 시 Meilisearch 실시간 동기화
- syncScheduleById 함수 추가: 개별 일정 동기화 - YouTube 봇: 영상 추가 시 Meilisearch 동기화 - X 봇: 트윗/유튜브 링크 추가 시 Meilisearch 동기화 - description 컬럼 제거 (schedules 테이블에 없음) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ea9922de00
commit
2fec6c552d
3 changed files with 71 additions and 7 deletions
|
|
@ -178,7 +178,7 @@ function formatScheduleResponse(hit) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 일정 추가/업데이트
|
* 일정 추가/업데이트 (데이터 직접 전달)
|
||||||
*/
|
*/
|
||||||
export async function addOrUpdateSchedule(meilisearch, schedule) {
|
export async function addOrUpdateSchedule(meilisearch, schedule) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -187,7 +187,6 @@ export async function addOrUpdateSchedule(meilisearch, schedule) {
|
||||||
const document = {
|
const document = {
|
||||||
id: schedule.id,
|
id: schedule.id,
|
||||||
title: schedule.title,
|
title: schedule.title,
|
||||||
description: schedule.description || '',
|
|
||||||
date: schedule.date,
|
date: schedule.date,
|
||||||
time: schedule.time || '',
|
time: schedule.time || '',
|
||||||
category_id: schedule.category_id,
|
category_id: schedule.category_id,
|
||||||
|
|
@ -204,6 +203,59 @@ export async function addOrUpdateSchedule(meilisearch, schedule) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 일정 ID로 DB에서 조회 후 Meilisearch에 동기화
|
||||||
|
*/
|
||||||
|
export async function syncScheduleById(meilisearch, db, scheduleId) {
|
||||||
|
try {
|
||||||
|
const [rows] = await db.query(`
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
s.title,
|
||||||
|
s.date,
|
||||||
|
s.time,
|
||||||
|
s.category_id,
|
||||||
|
c.name as category_name,
|
||||||
|
c.color as category_color,
|
||||||
|
sy.channel_name as source_name,
|
||||||
|
GROUP_CONCAT(DISTINCT m.name ORDER BY m.id SEPARATOR ',') as member_names
|
||||||
|
FROM schedules s
|
||||||
|
LEFT JOIN schedule_categories c ON s.category_id = c.id
|
||||||
|
LEFT JOIN schedule_youtube sy ON s.id = sy.schedule_id
|
||||||
|
LEFT JOIN schedule_members sm ON s.id = sm.schedule_id
|
||||||
|
LEFT JOIN members m ON sm.member_id = m.id AND m.is_former = 0
|
||||||
|
WHERE s.id = ?
|
||||||
|
GROUP BY s.id
|
||||||
|
`, [scheduleId]);
|
||||||
|
|
||||||
|
if (rows.length === 0) {
|
||||||
|
logger.warn(`일정을 찾을 수 없음: ${scheduleId}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const s = rows[0];
|
||||||
|
const document = {
|
||||||
|
id: s.id,
|
||||||
|
title: s.title,
|
||||||
|
date: s.date instanceof Date ? s.date.toISOString().split('T')[0] : s.date,
|
||||||
|
time: s.time || '',
|
||||||
|
category_id: s.category_id,
|
||||||
|
category_name: s.category_name || '',
|
||||||
|
category_color: s.category_color || '',
|
||||||
|
source_name: s.source_name || '',
|
||||||
|
member_names: s.member_names || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const index = meilisearch.index(INDEX_NAME);
|
||||||
|
await index.addDocuments([document]);
|
||||||
|
logger.info(`일정 동기화: ${scheduleId}`);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`일정 동기화 오류 (${scheduleId}): ${err.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 일정 삭제
|
* 일정 삭제
|
||||||
*/
|
*/
|
||||||
|
|
@ -233,7 +285,6 @@ export async function syncAllSchedules(meilisearch, db) {
|
||||||
SELECT
|
SELECT
|
||||||
s.id,
|
s.id,
|
||||||
s.title,
|
s.title,
|
||||||
s.description,
|
|
||||||
s.date,
|
s.date,
|
||||||
s.time,
|
s.time,
|
||||||
s.category_id,
|
s.category_id,
|
||||||
|
|
@ -255,7 +306,6 @@ export async function syncAllSchedules(meilisearch, db) {
|
||||||
const documents = schedules.map(s => ({
|
const documents = schedules.map(s => ({
|
||||||
id: s.id,
|
id: s.id,
|
||||||
title: s.title,
|
title: s.title,
|
||||||
description: s.description || '',
|
|
||||||
date: s.date instanceof Date ? s.date.toISOString().split('T')[0] : s.date,
|
date: s.date instanceof Date ? s.date.toISOString().split('T')[0] : s.date,
|
||||||
time: s.time || '',
|
time: s.time || '',
|
||||||
category_id: s.category_id,
|
category_id: s.category_id,
|
||||||
|
|
@ -310,7 +360,7 @@ async function recreateIndex(meilisearch) {
|
||||||
|
|
||||||
// 설정 복원
|
// 설정 복원
|
||||||
await index.updateSearchableAttributes([
|
await index.updateSearchableAttributes([
|
||||||
'title', 'member_names', 'description', 'source_name', 'category_name',
|
'title', 'member_names', 'source_name', 'category_name',
|
||||||
]);
|
]);
|
||||||
await index.updateFilterableAttributes(['category_id', 'date']);
|
await index.updateFilterableAttributes(['category_id', 'date']);
|
||||||
await index.updateSortableAttributes(['date', 'time']);
|
await index.updateSortableAttributes(['date', 'time']);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { fetchVideoInfo } from '../youtube/api.js';
|
||||||
import { formatDate, formatTime, nowKST } from '../../utils/date.js';
|
import { formatDate, formatTime, nowKST } from '../../utils/date.js';
|
||||||
import bots from '../../config/bots.js';
|
import bots from '../../config/bots.js';
|
||||||
import { withTransaction } from '../../utils/transaction.js';
|
import { withTransaction } from '../../utils/transaction.js';
|
||||||
|
import { syncScheduleById } from '../meilisearch/index.js';
|
||||||
|
|
||||||
const X_CATEGORY_ID = 3;
|
const X_CATEGORY_ID = 3;
|
||||||
const YOUTUBE_CATEGORY_ID = 2;
|
const YOUTUBE_CATEGORY_ID = 2;
|
||||||
|
|
@ -141,8 +142,12 @@ async function xBotPlugin(fastify, opts) {
|
||||||
// 관리 중인 채널이면 스킵
|
// 관리 중인 채널이면 스킵
|
||||||
if (managedChannels.includes(video.channelId)) continue;
|
if (managedChannels.includes(video.channelId)) continue;
|
||||||
|
|
||||||
const saved = await saveYoutubeFromTweet(video);
|
const scheduleId = await saveYoutubeFromTweet(video);
|
||||||
if (saved) addedCount++;
|
if (scheduleId) {
|
||||||
|
// Meilisearch 동기화
|
||||||
|
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||||
|
addedCount++;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fastify.log.error(`YouTube 영상 처리 오류 (${videoId}): ${err.message}`);
|
fastify.log.error(`YouTube 영상 처리 오류 (${videoId}): ${err.message}`);
|
||||||
}
|
}
|
||||||
|
|
@ -166,6 +171,8 @@ async function xBotPlugin(fastify, opts) {
|
||||||
for (const tweet of tweets) {
|
for (const tweet of tweets) {
|
||||||
const scheduleId = await saveTweet(tweet);
|
const scheduleId = await saveTweet(tweet);
|
||||||
if (scheduleId) {
|
if (scheduleId) {
|
||||||
|
// Meilisearch 동기화
|
||||||
|
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||||
addedCount++;
|
addedCount++;
|
||||||
// YouTube 링크 처리
|
// YouTube 링크 처리
|
||||||
ytAddedCount += await processYoutubeLinks(tweet);
|
ytAddedCount += await processYoutubeLinks(tweet);
|
||||||
|
|
@ -187,6 +194,8 @@ async function xBotPlugin(fastify, opts) {
|
||||||
for (const tweet of tweets) {
|
for (const tweet of tweets) {
|
||||||
const scheduleId = await saveTweet(tweet);
|
const scheduleId = await saveTweet(tweet);
|
||||||
if (scheduleId) {
|
if (scheduleId) {
|
||||||
|
// Meilisearch 동기화
|
||||||
|
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||||
addedCount++;
|
addedCount++;
|
||||||
ytAddedCount += await processYoutubeLinks(tweet);
|
ytAddedCount += await processYoutubeLinks(tweet);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { fetchRecentVideos, fetchAllVideos, getUploadsPlaylistId } from './api.j
|
||||||
import bots from '../../config/bots.js';
|
import bots from '../../config/bots.js';
|
||||||
import { CATEGORY_IDS } from '../../config/index.js';
|
import { CATEGORY_IDS } from '../../config/index.js';
|
||||||
import { withTransaction } from '../../utils/transaction.js';
|
import { withTransaction } from '../../utils/transaction.js';
|
||||||
|
import { syncScheduleById } from '../meilisearch/index.js';
|
||||||
|
|
||||||
const YOUTUBE_CATEGORY_ID = CATEGORY_IDS.YOUTUBE;
|
const YOUTUBE_CATEGORY_ID = CATEGORY_IDS.YOUTUBE;
|
||||||
const PLAYLIST_CACHE_PREFIX = 'yt_uploads:';
|
const PLAYLIST_CACHE_PREFIX = 'yt_uploads:';
|
||||||
|
|
@ -126,6 +127,8 @@ async function youtubeBotPlugin(fastify, opts) {
|
||||||
for (const video of videos) {
|
for (const video of videos) {
|
||||||
const scheduleId = await saveVideo(video, bot);
|
const scheduleId = await saveVideo(video, bot);
|
||||||
if (scheduleId) {
|
if (scheduleId) {
|
||||||
|
// Meilisearch 동기화
|
||||||
|
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||||
addedCount++;
|
addedCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +147,8 @@ async function youtubeBotPlugin(fastify, opts) {
|
||||||
for (const video of videos) {
|
for (const video of videos) {
|
||||||
const scheduleId = await saveVideo(video, bot);
|
const scheduleId = await saveVideo(video, bot);
|
||||||
if (scheduleId) {
|
if (scheduleId) {
|
||||||
|
// Meilisearch 동기화
|
||||||
|
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||||
addedCount++;
|
addedCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue