114 lines
5.8 KiB
TypeScript
114 lines
5.8 KiB
TypeScript
import { Redis } from "ioredis";
|
|
import { Config } from "../utils/Config";
|
|
import { Logger } from "../utils/Logger";
|
|
import { YoutubeMusic } from "../utils/api/YoutubeMusic";
|
|
import { Spotify } from "../utils/api/Spotify";
|
|
import { lavalinkManager } from "../index";
|
|
import { getGuildById, getVoiceChannelById } from "../utils/music/Channel";
|
|
import { channelJoin } from "../commands/join";
|
|
|
|
type SubAction = "search" | "player_play" | "player_playlist";
|
|
|
|
export class RedisClient {
|
|
public pub: Redis = new Redis({ host: Config.redis.host, port: Config.redis.port });
|
|
public sub: Redis = new Redis({ host: Config.redis.host, port: Config.redis.port });
|
|
|
|
constructor() {
|
|
this.pub.on("connect", () => {
|
|
Logger.ready(`[Redis Pub] 연결 완료 (말하는 입)`);
|
|
});
|
|
this.sub.on("connect", () => {
|
|
Logger.ready(`[Redis Sub] 연결 완료 (듣는 귀)`);
|
|
});
|
|
|
|
this.sub.subscribe("site-bot", (err, count) => {
|
|
if (err) return Logger.error(`[Redis Sub] 구독 실패: ${err.message}`);
|
|
Logger.log(`[Redis Sub] 'bot-commands' 채널 구독 중... (현재 구독 채널 수: ${count})`);
|
|
});
|
|
|
|
this.sub.on("message", async (ch, msg): Promise<any> => {
|
|
if (ch !== "site-bot") return;
|
|
Logger.log(`[Redis Sub] [Message] 수신: {\n 채널: ${ch}\n 내용: ${msg}\n}`);
|
|
try {
|
|
const data = JSON.parse(msg) as { action: SubAction; requestId: string; userId?: string; [key: string]: any; };
|
|
|
|
if (data.action === "search") {
|
|
const resultKey = `search:${data.requestId}`;
|
|
const results = await Spotify.getSearchFull(data.query) ?? await YoutubeMusic.getSearchFull(data.query) ?? [];
|
|
await this.pub.setex(resultKey, 60, JSON.stringify(results));
|
|
Logger.log(`[Redis Pub] [setex] 결과 저장: (${resultKey})`);
|
|
}
|
|
else if (data.action === "player_play") {
|
|
const resultKey = `player:play:${data.requestId}`;
|
|
if (!data.serverId) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "serverId를 찾을수 없습니다." }));
|
|
if (!data.userId) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "userId를 찾을수 없습니다." }));
|
|
const guild = await getGuildById(data.serverId);
|
|
if (!guild) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "guild를 찾을수 없습니다." }));
|
|
let player = lavalinkManager.getPlayer(guild.id);
|
|
const voiceChannel = await getVoiceChannelById(guild, data.userId);
|
|
if (!player) {
|
|
if (!voiceChannel) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "음성채널에 들어가서 이용해주세요." }));
|
|
player = (await channelJoin(guild, voiceChannel.id)).player;
|
|
}
|
|
if (!player) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "세션을 찾을수 없습니다." }));
|
|
await lavalinkManager.search(guild.id, data.track.url, data.userId, player);
|
|
// await this.pub.setex(resultKey, 60, JSON.stringify({ success: true, message: "노래 추가 완료" }));
|
|
}
|
|
else if (data.action === "player_playlist") {
|
|
const resultKey = `player:play:${data.requestId}`;
|
|
if (!data.serverId) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "serverId를 찾을수 없습니다." }));
|
|
if (!data.userId) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "userId를 찾을수 없습니다." }));
|
|
const guild = await getGuildById(data.serverId);
|
|
if (!guild) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "guild를 찾을수 없습니다." }));
|
|
let player = lavalinkManager.getPlayer(guild.id);
|
|
const voiceChannel = await getVoiceChannelById(guild, data.userId);
|
|
if (!player) {
|
|
if (!voiceChannel) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "음성채널에 들어가서 이용해주세요." }));
|
|
player = (await channelJoin(guild, voiceChannel.id)).player;
|
|
}
|
|
if (!player) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "세션을 찾을수 없습니다." }));
|
|
await lavalinkManager.search(guild.id, data.playlistUrl, data.userId, player);
|
|
// await this.pub.setex(resultKey, 60, JSON.stringify({ success: true, message: "플레이리스트 추가 완료" }));
|
|
}
|
|
} catch (err) {
|
|
Logger.error(`명령어 처리 중 에러: ${String(err)}`);
|
|
}
|
|
});
|
|
|
|
this.pub.on("error", (err) => {
|
|
Logger.error(`[Redis Pub] [Error] ${err.message}`);
|
|
});
|
|
this.sub.on("error", (err) => {
|
|
Logger.error(`[Redis Sub] [Error] ${err.message}`);
|
|
});
|
|
}
|
|
|
|
public publishState(event: string, data: any) {
|
|
const payload = JSON.stringify({
|
|
event,
|
|
timestamp: Date.now(),
|
|
...data,
|
|
});
|
|
this.pub.publish("bot-site", payload);
|
|
Logger.log(`[Redis Pub] bot -> site 전송: ${event}`);
|
|
}
|
|
|
|
public runTest() {
|
|
Logger.debug(`[Redis Test] 3초 뒤에 테스트 통신 시작...`);
|
|
setTimeout(() => {
|
|
// 1. 봇 -> 사이트(웹) 방향 전송 테스트
|
|
this.publishState("TRACK_START", {
|
|
author: "테스트",
|
|
title: "제목",
|
|
duration: 196000,
|
|
});
|
|
|
|
// 2. 사이트(웹) -> 봇 방향 수신 테스트 (가짜 명령을 쏴서 스스로 수신하는지 확인)
|
|
setTimeout(() => {
|
|
const mockCommand = JSON.stringify({ action: "skip", userId: "12345" });
|
|
// 테스트를 위해 본인이 site-bot 채널로 발행해 봅니다.
|
|
this.pub.publish("site-bot", mockCommand);
|
|
}, 1000);
|
|
}, 3000);
|
|
}
|
|
} |