|
|
|
|
@@ -6,8 +6,21 @@ import { Spotify } from "../utils/api/Spotify";
|
|
|
|
|
import { lavalinkManager } from "../index";
|
|
|
|
|
import { getGuildById, getVoiceChannelById } from "../utils/music/Channel";
|
|
|
|
|
import { channelJoin } from "../commands/join";
|
|
|
|
|
import { GuildPlayer } from "./GuildPlayer";
|
|
|
|
|
import { Guild, VoiceChannel } from "discord.js";
|
|
|
|
|
|
|
|
|
|
type SubAction = "search" | "player_play" | "player_playlist";
|
|
|
|
|
type SubAction =
|
|
|
|
|
"search" |
|
|
|
|
|
"player_now" |
|
|
|
|
|
"player_play" |
|
|
|
|
|
"player_playlist" |
|
|
|
|
|
"player_paused" |
|
|
|
|
|
"player_skip" |
|
|
|
|
|
"player_seek" |
|
|
|
|
|
"player_volume" |
|
|
|
|
|
"queue_list" |
|
|
|
|
|
"queue_set" |
|
|
|
|
|
"queue_remove";
|
|
|
|
|
|
|
|
|
|
export class RedisClient {
|
|
|
|
|
public pub: Redis = new Redis({ host: Config.redis.host, port: Config.redis.port });
|
|
|
|
|
@@ -38,7 +51,7 @@ export class RedisClient {
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify(results));
|
|
|
|
|
Logger.log(`[Redis Pub] [setex] 결과 저장: (${resultKey})`);
|
|
|
|
|
}
|
|
|
|
|
else if (data.action === "player_play") {
|
|
|
|
|
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를 찾을수 없습니다." }));
|
|
|
|
|
@@ -52,10 +65,10 @@ export class RedisClient {
|
|
|
|
|
}
|
|
|
|
|
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: "노래 추가 완료" }));
|
|
|
|
|
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.action === "player_playlist") {
|
|
|
|
|
const resultKey = `player:playlist:${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);
|
|
|
|
|
@@ -68,7 +81,112 @@ export class RedisClient {
|
|
|
|
|
}
|
|
|
|
|
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: "플레이리스트 추가 완료" }));
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: true, message: "플레이리스트 추가 완료" }));
|
|
|
|
|
}
|
|
|
|
|
if (data.action === "player_now") {
|
|
|
|
|
const resultKey = `player:now:${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 player = lavalinkManager.getPlayer(data.serverId);
|
|
|
|
|
// if (!player) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "player를 찾을수 없습니다." }));
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({
|
|
|
|
|
success: true,
|
|
|
|
|
botPlayer: !!player,
|
|
|
|
|
isPlaying: player?.isPlaying,
|
|
|
|
|
isPaused: player?.isPaused,
|
|
|
|
|
position: player?.position,
|
|
|
|
|
volume: player?.volume,
|
|
|
|
|
track: player?.nowTrack ?? null
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
if (data.action === "queue_list") {
|
|
|
|
|
const resultKey = `queue:list:${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 player = lavalinkManager.getPlayer(data.serverId);
|
|
|
|
|
// if (!player) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "player를 찾을수 없습니다." }));
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: true, queue: player?.queue?.slice(1) ?? [] }));
|
|
|
|
|
}
|
|
|
|
|
if (data.action === "queue_set") {
|
|
|
|
|
const resultKey = `queue:set:${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를 찾을수 없습니다." }));
|
|
|
|
|
if (!data.newQueue) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "newQueue를 찾을수 없습니다." }));
|
|
|
|
|
const context = await this.getContext(data.serverId, resultKey, data.userId);
|
|
|
|
|
if (!context.ok) return;
|
|
|
|
|
const nowTrack = context.player.nowTrack ?? context.player.queue?.[0];
|
|
|
|
|
context.player.queue.length = 0;
|
|
|
|
|
if (nowTrack) context.player.queue.push(nowTrack);
|
|
|
|
|
for (const rawTrack of data.newQueue) {
|
|
|
|
|
if (rawTrack.encoded) context.player.queue.push(rawTrack);
|
|
|
|
|
}
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: true }));
|
|
|
|
|
context.player.setMsg();
|
|
|
|
|
}
|
|
|
|
|
if (data.action === "queue_remove") {
|
|
|
|
|
const resultKey = `queue:remove:${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를 찾을수 없습니다." }));
|
|
|
|
|
if (!data.index) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "index를 찾을수 없습니다." }));
|
|
|
|
|
const context = await this.getContext(data.serverId, resultKey, data.userId);
|
|
|
|
|
if (!context.ok) return;
|
|
|
|
|
const numIndex = Number(data.index);
|
|
|
|
|
if (isNaN(numIndex)) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "index 타입이 올바르지 않습니다." }));
|
|
|
|
|
if (numIndex < 0) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "index는 0보다 크거나 같아야합니다." }));
|
|
|
|
|
if (numIndex >= context.player.queue.length-1) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "index가 queue.length보다 클수 없습니다." }));
|
|
|
|
|
const [removedTrack] = context.player.queue.splice(numIndex+1, 1);
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: true, removedTrack }));
|
|
|
|
|
context.player.setMsg();
|
|
|
|
|
}
|
|
|
|
|
if (data.action === "player_paused") {
|
|
|
|
|
const resultKey = `player:paused:${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를 찾을수 없습니다." }));
|
|
|
|
|
if (!data.isPaused) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "paused를 찾을수 없습니다." }));
|
|
|
|
|
const context = await this.getContext(data.serverId, resultKey, data.userId);
|
|
|
|
|
if (!context.ok) return;
|
|
|
|
|
await context.player.setPause();
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: true, paused: context.player.isPaused }));
|
|
|
|
|
}
|
|
|
|
|
if (data.action === "player_skip") {
|
|
|
|
|
const resultKey = `player:skip:${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 context = await this.getContext(data.serverId, resultKey, data.userId);
|
|
|
|
|
if (!context.ok) return;
|
|
|
|
|
if (context.player.isPlaying) context.player.skip();
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: true }));
|
|
|
|
|
}
|
|
|
|
|
if (data.action === "player_seek") {
|
|
|
|
|
const resultKey = `player:seek:${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를 찾을수 없습니다." }));
|
|
|
|
|
if (!data.seek) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "seek를 찾을수 없습니다." }));
|
|
|
|
|
const context = await this.getContext(data.serverId, resultKey, data.userId);
|
|
|
|
|
if (!context.ok) return;
|
|
|
|
|
if (!context.player.isPlaying || !context.player.nowTrack) await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "재생중인 노래가 없습니다." }));
|
|
|
|
|
const duration = context.player.nowTrack?.info.length || 0;
|
|
|
|
|
const numSeek = Number(data.seek);
|
|
|
|
|
if (isNaN(numSeek)) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "seek 타입이 올바르지 않습니다." }));
|
|
|
|
|
if (numSeek < 0) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "seek는 0보다 크거나 같아야합니다." }));
|
|
|
|
|
if (numSeek > duration) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "seek가 duration보다 클수 없습니다." }));
|
|
|
|
|
context.player.seek(numSeek);
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: true }));
|
|
|
|
|
}
|
|
|
|
|
if (data.action === "player_volume") {
|
|
|
|
|
const resultKey = `player:volume:${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를 찾을수 없습니다." }));
|
|
|
|
|
if (!data.volume) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "volume을 찾을수 없습니다." }));
|
|
|
|
|
const context = await this.getContext(data.serverId, resultKey, data.userId);
|
|
|
|
|
if (!context.ok) return;
|
|
|
|
|
if (!context.player.isPlaying || !context.player.nowTrack) await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "재생중인 노래가 없습니다." }));
|
|
|
|
|
const numVolume = Number(data.volume);
|
|
|
|
|
if (isNaN(numVolume)) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "volume 타입이 올바르지 않습니다." }));
|
|
|
|
|
if (numVolume < 0) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "volume은 0보다 크거나 같아야합니다." }));
|
|
|
|
|
if (numVolume > 100) return await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "volume이 100보다 클수 없습니다." }));
|
|
|
|
|
context.player.setVolume(numVolume);
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: true }));
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
Logger.error(`명령어 처리 중 에러: ${String(err)}`);
|
|
|
|
|
@@ -93,6 +211,38 @@ export class RedisClient {
|
|
|
|
|
Logger.log(`[Redis Pub] bot -> site 전송: ${event}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async getContext(guildId: string, resultKey: string, userId: string): Promise<{
|
|
|
|
|
ok: true;
|
|
|
|
|
guild: Guild;
|
|
|
|
|
voiceChannel: VoiceChannel | null;
|
|
|
|
|
player: GuildPlayer;
|
|
|
|
|
} | { ok: false; }> {
|
|
|
|
|
const guild = await getGuildById(guildId);
|
|
|
|
|
if (!guild) {
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "guild를 찾을수 없습니다." }));
|
|
|
|
|
return { ok: false };
|
|
|
|
|
}
|
|
|
|
|
let player = lavalinkManager.getPlayer(guild.id);
|
|
|
|
|
const voiceChannel = await getVoiceChannelById(guild, userId);
|
|
|
|
|
if (!player) {
|
|
|
|
|
if (!voiceChannel) {
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "음성채널에 들어가서 이용해주세요." }));
|
|
|
|
|
return { ok: false };
|
|
|
|
|
}
|
|
|
|
|
player = (await channelJoin(guild, voiceChannel.id)).player;
|
|
|
|
|
}
|
|
|
|
|
if (!player) {
|
|
|
|
|
await this.pub.setex(resultKey, 60, JSON.stringify({ success: false, message: "player를 찾을수 없습니다." }));
|
|
|
|
|
return { ok: false };
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
ok: true,
|
|
|
|
|
guild,
|
|
|
|
|
voiceChannel,
|
|
|
|
|
player,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public runTest() {
|
|
|
|
|
Logger.debug(`[Redis Test] 3초 뒤에 테스트 통신 시작...`);
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|