added rzeczy
This commit is contained in:
36
src/commands/forceplay.ts
Normal file
36
src/commands/forceplay.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { CacheType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { connectToChannelByInteraction } from '../util/helpers';
|
||||||
|
import { forceRequestSong } from '../playback';
|
||||||
|
import { getPlayMsg } from '../messages';
|
||||||
|
|
||||||
|
const name = "forceplay"
|
||||||
|
|
||||||
|
function register() {
|
||||||
|
return new SlashCommandBuilder()
|
||||||
|
.setName(name)
|
||||||
|
.setDescription('YT')
|
||||||
|
.addStringOption((option) => option.setName("url")
|
||||||
|
.setDescription("YT url")
|
||||||
|
.setRequired(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function execute(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||||
|
try {
|
||||||
|
connectToChannelByInteraction(interaction)
|
||||||
|
|
||||||
|
const url = interaction.options.getString("url")!;
|
||||||
|
await forceRequestSong(interaction, url)
|
||||||
|
|
||||||
|
const msg = getPlayMsg(url)
|
||||||
|
await interaction.reply(msg);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
await interaction.reply('Coś poszło nie tak :/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name,
|
||||||
|
register,
|
||||||
|
execute
|
||||||
|
}
|
||||||
@@ -1,8 +1,20 @@
|
|||||||
|
import forceplay from "./forceplay"
|
||||||
|
import help from "./help"
|
||||||
|
import pause from "./pause"
|
||||||
import ping from "./ping"
|
import ping from "./ping"
|
||||||
import play from "./play"
|
import play from "./play"
|
||||||
|
import queue from "./queue"
|
||||||
|
import resume from "./resume"
|
||||||
|
import stop from "./stop"
|
||||||
|
|
||||||
export const commands: {[key: string]: Command} = {
|
export const commands: { [key: string]: Command } = {
|
||||||
ping,
|
ping,
|
||||||
play,
|
play,
|
||||||
|
forceplay,
|
||||||
|
queue,
|
||||||
|
help,
|
||||||
|
resume,
|
||||||
|
stop,
|
||||||
|
pause,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
import { CacheType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
|
||||||
import { connectToChannel } from '../util/helpers';
|
|
||||||
import { joinVoiceChannel } from '@discordjs/voice';
|
|
||||||
|
|
||||||
const name = "ping"
|
|
||||||
|
|
||||||
function register() {
|
|
||||||
return new SlashCommandBuilder()
|
|
||||||
.setName(name)
|
|
||||||
.setDescription('Replies with Pong!')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function execute(message: ChatInputCommandInteraction<CacheType>) {
|
|
||||||
const channelId = "515300847790325790"
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name,
|
|
||||||
register,
|
|
||||||
execute
|
|
||||||
}
|
|
||||||
27
src/commands/pause.ts
Normal file
27
src/commands/pause.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { CacheType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { pause_playback } from '../playback';
|
||||||
|
import { player } from '../main';
|
||||||
|
|
||||||
|
|
||||||
|
const name = "pause"
|
||||||
|
|
||||||
|
function register() {
|
||||||
|
return new SlashCommandBuilder()
|
||||||
|
.setName(name)
|
||||||
|
.setDescription('pause playback')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function execute(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||||
|
try {
|
||||||
|
pause_playback(player)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
await interaction.reply('Coś poszło nie tak :/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name,
|
||||||
|
register,
|
||||||
|
execute
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CacheType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
import { CacheType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
||||||
import { connectToChannel, playSong } from '../util/helpers';
|
import { connectToChannelByInteraction } from '../util/helpers';
|
||||||
import { player } from '../main';
|
import { requestSong } from '../playback';
|
||||||
import { get_audio } from '../util/downloader';
|
import { getPlayMsg } from '../messages';
|
||||||
|
|
||||||
const name = "play"
|
const name = "play"
|
||||||
|
|
||||||
@@ -16,27 +16,14 @@ function register() {
|
|||||||
|
|
||||||
async function execute(interaction: ChatInputCommandInteraction<CacheType>) {
|
async function execute(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||||
try {
|
try {
|
||||||
// await interaction.reply(`${interaction}`);
|
connectToChannelByInteraction(interaction)
|
||||||
const member = await interaction.guild?.members.fetch(interaction.user.id);
|
|
||||||
if (!member) throw new Error("ale chuj")
|
|
||||||
const voiceChannel = member.voice.channel;
|
|
||||||
if (!voiceChannel) throw new Error("ale chuj 2")
|
|
||||||
|
|
||||||
const connection = await connectToChannel(voiceChannel);
|
|
||||||
connection.subscribe(player);
|
|
||||||
|
|
||||||
await interaction.reply('https://gitea.papryk.com/Papryk/dj-spangebob pull requesty milewidziane XD');
|
|
||||||
const url = interaction.options.getString("url")!;
|
const url = interaction.options.getString("url")!;
|
||||||
const path = await get_audio(url)
|
await requestSong(interaction, url)
|
||||||
// const path = "/home/patryk/Papryk/dbot/data/Kino - Summer is ending ⧸ Кино - Кончится лето [6VqiMQoMXmw].opus"
|
|
||||||
console.log(path)
|
|
||||||
|
|
||||||
await playSong(player, path);
|
|
||||||
|
|
||||||
|
const msg = getPlayMsg(url)
|
||||||
|
await interaction.reply(msg);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
/**
|
|
||||||
* The song isn't ready to play for some reason :(
|
|
||||||
*/
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
await interaction.reply('Coś poszło nie tak :/');
|
await interaction.reply('Coś poszło nie tak :/');
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/commands/queue.ts
Normal file
24
src/commands/queue.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { CacheType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { getQueue } from '../playback';
|
||||||
|
|
||||||
|
const name = "queue"
|
||||||
|
|
||||||
|
function register() {
|
||||||
|
return new SlashCommandBuilder()
|
||||||
|
.setName(name)
|
||||||
|
.setDescription('Shows queue')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function execute(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||||
|
console.log(interaction.member)
|
||||||
|
const queue = getQueue()
|
||||||
|
await interaction.reply(`Current: ${queue.current}
|
||||||
|
Queue:
|
||||||
|
${queue.songList.join('\n ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name,
|
||||||
|
register,
|
||||||
|
execute
|
||||||
|
}
|
||||||
27
src/commands/resume.ts
Normal file
27
src/commands/resume.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { CacheType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { resume_playback } from '../playback';
|
||||||
|
import { player } from '../main';
|
||||||
|
|
||||||
|
|
||||||
|
const name = "resume"
|
||||||
|
|
||||||
|
function register() {
|
||||||
|
return new SlashCommandBuilder()
|
||||||
|
.setName(name)
|
||||||
|
.setDescription('resume playback')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function execute(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||||
|
try {
|
||||||
|
resume_playback(player)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
await interaction.reply('Coś poszło nie tak :/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name,
|
||||||
|
register,
|
||||||
|
execute
|
||||||
|
}
|
||||||
27
src/commands/stop.ts
Normal file
27
src/commands/stop.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { CacheType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { stop_playback } from '../playback';
|
||||||
|
import { player } from '../main';
|
||||||
|
|
||||||
|
|
||||||
|
const name = "stop"
|
||||||
|
|
||||||
|
function register() {
|
||||||
|
return new SlashCommandBuilder()
|
||||||
|
.setName(name)
|
||||||
|
.setDescription('stop playback')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function execute(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||||
|
try {
|
||||||
|
stop_playback(player)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
await interaction.reply('Coś poszło nie tak :/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name,
|
||||||
|
register,
|
||||||
|
execute
|
||||||
|
}
|
||||||
5
src/global.d.ts
vendored
5
src/global.d.ts
vendored
@@ -3,3 +3,8 @@ type Command = {
|
|||||||
register: () => any,
|
register: () => any,
|
||||||
execute: (interaction: ChatInputCommandInteraction<CacheType>) => Promise<void>,
|
execute: (interaction: ChatInputCommandInteraction<CacheType>) => Promise<void>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Queue = {
|
||||||
|
songList: string[],
|
||||||
|
current: string | null,
|
||||||
|
}
|
||||||
|
|||||||
17
src/main.ts
17
src/main.ts
@@ -2,12 +2,19 @@
|
|||||||
import { Client, Events, GatewayIntentBits, MessageFlags, REST, Routes } from 'discord.js';
|
import { Client, Events, GatewayIntentBits, MessageFlags, REST, Routes } from 'discord.js';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import { commands } from "./commands";
|
import { commands } from "./commands";
|
||||||
import { createAudioPlayer } from '@discordjs/voice';
|
import { AudioPlayerState, createAudioPlayer } from '@discordjs/voice';
|
||||||
import { connectToChannel, playSong } from './util/helpers';
|
import { connectToChannel, playSong } from './util/helpers';
|
||||||
|
import { updatePlayer } from './playback';
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
|
export const DATA_DIR = "./data";
|
||||||
|
|
||||||
// AUDIO
|
// AUDIO
|
||||||
export const player = createAudioPlayer();
|
export const player = createAudioPlayer();
|
||||||
|
player.on('stateChange', (oldState: AudioPlayerState, newState: AudioPlayerState) => {
|
||||||
|
updatePlayer()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Create a new client instance
|
// Create a new client instance
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
@@ -29,11 +36,17 @@ async function registerCommands() {
|
|||||||
const rest = new REST().setToken(process.env.DISCORD_TOKEN!);
|
const rest = new REST().setToken(process.env.DISCORD_TOKEN!);
|
||||||
const registry = Object.values(commands).map((cmd) => cmd.register().toJSON())
|
const registry = Object.values(commands).map((cmd) => cmd.register().toJSON())
|
||||||
console.log(registry)
|
console.log(registry)
|
||||||
const data = await rest.put(Routes.applicationCommands(
|
await rest.put(Routes.applicationGuildCommands(
|
||||||
process.env.DISCORD_APP_ID!,
|
process.env.DISCORD_APP_ID!,
|
||||||
|
process.env.GUILD_ID!
|
||||||
), {
|
), {
|
||||||
body: registry
|
body: registry
|
||||||
});
|
});
|
||||||
|
// await rest.put(Routes.applicationCommands(
|
||||||
|
// process.env.DISCORD_APP_ID!,
|
||||||
|
// ), {
|
||||||
|
// body: registry
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
17
src/messages.ts
Normal file
17
src/messages.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { getQueue } from "./playback";
|
||||||
|
|
||||||
|
export function getPlayMsg(url: string): string {
|
||||||
|
return `
|
||||||
|
Request: ${url}
|
||||||
|
https://gitea.papryk.com/Papryk/dj-spangebob pull requesty milewidziane XD\n` + getQueueMsg()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentSongMsg(): string {
|
||||||
|
return 'TODO-1'
|
||||||
|
}
|
||||||
|
export function getQueueMsg(): string {
|
||||||
|
const queue = getQueue()
|
||||||
|
return `Current: ${queue.current}
|
||||||
|
Queue:
|
||||||
|
${queue.songList.join('\n ')}`
|
||||||
|
}
|
||||||
57
src/playback.ts
Normal file
57
src/playback.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { AudioPlayer, AudioPlayerStatus } from "@discordjs/voice";
|
||||||
|
import { player } from "./main";
|
||||||
|
import { CacheType, ChatInputCommandInteraction } from "discord.js";
|
||||||
|
import { getAudioFile } from "./util/downloader";
|
||||||
|
import { playSong } from "./util/helpers";
|
||||||
|
|
||||||
|
const queue: Queue = {
|
||||||
|
songList: [],
|
||||||
|
current: null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function requestSong(
|
||||||
|
interaction: ChatInputCommandInteraction<CacheType>,
|
||||||
|
url: string) {
|
||||||
|
|
||||||
|
const path = await getAudioFile(url)
|
||||||
|
queue.songList.push(path)
|
||||||
|
updatePlayer()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function forceRequestSong(
|
||||||
|
interaction: ChatInputCommandInteraction<CacheType>,
|
||||||
|
url: string) {
|
||||||
|
|
||||||
|
const path = await getAudioFile(url)
|
||||||
|
queue.songList.push(path)
|
||||||
|
playSong(player, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function updatePlayer() {
|
||||||
|
if (player.state.status === AudioPlayerStatus.Idle) {
|
||||||
|
const nextSong = queue.songList.shift()
|
||||||
|
if (!nextSong) {
|
||||||
|
queue.current = null
|
||||||
|
return
|
||||||
|
};
|
||||||
|
playSong(player, nextSong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pause_playback(player: AudioPlayer) {
|
||||||
|
player.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stop_playback(player: AudioPlayer) {
|
||||||
|
player.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resume_playback(player: AudioPlayer) {
|
||||||
|
player.unpause()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQueue(): Queue {
|
||||||
|
return queue
|
||||||
|
}
|
||||||
@@ -1,51 +1,65 @@
|
|||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
|
import { DATA_DIR } from "../main";
|
||||||
|
|
||||||
export async function get_audio(url: string): Promise<string> {
|
export async function getAudioFile(url: string): Promise<string> {
|
||||||
const ytDlpBin = process.env.YT_DLP_BIN_PATH! ?? "yt-dlp";
|
const id = extractId(url)
|
||||||
const dataDir = "./data";
|
|
||||||
|
|
||||||
const idMatch = /[?&]v=([a-zA-Z0-9_-]{11})/.exec(url);
|
let path: string;
|
||||||
if (!idMatch) throw new Error("Cannot extract video ID");
|
try {
|
||||||
|
path = await findFileById(id)
|
||||||
const id = idMatch[1];
|
} catch (e) {
|
||||||
console.log(`ID: ${id}`);
|
await downloadYTVideo(url)
|
||||||
|
path = await findFileById(id)
|
||||||
// ---- run yt-dlp ----
|
}
|
||||||
await new Promise<void>((resolve, reject) => {
|
return path
|
||||||
const p = spawn(ytDlpBin, [
|
}
|
||||||
"-x",
|
|
||||||
"--audio-format", "opus",
|
export function extractId(url: string): string {
|
||||||
"--audio-quality", "0",
|
const idMatch = /[?&]v=([a-zA-Z0-9_-]{11})/.exec(url);
|
||||||
"--no-playlist",
|
if (!idMatch) throw new Error("Cannot extract video ID");
|
||||||
"--paths", dataDir,
|
const id = idMatch[1];
|
||||||
url,
|
console.log(`ID: ${id}`);
|
||||||
]);
|
return id
|
||||||
|
}
|
||||||
p.stderr.on("data", d => process.stderr.write(d));
|
|
||||||
p.on("close", code => code === 0 ? resolve() : reject(new Error("yt-dlp failed")));
|
export async function downloadYTVideo(url: string): Promise<void> {
|
||||||
});
|
const ytDlpBin = process.env.YT_DLP_BIN_PATH! ?? "yt-dlp";
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
// ---- find the file ----
|
const p = spawn(ytDlpBin, [
|
||||||
return await new Promise<string>((resolve, reject) => {
|
"-x",
|
||||||
const find = spawn("find", [
|
"--audio-format", "opus",
|
||||||
dataDir,
|
"--audio-quality", "0",
|
||||||
"-type", "f",
|
"--no-playlist",
|
||||||
"-iname", `*${id}*.opus`,
|
"--paths", DATA_DIR,
|
||||||
"-exec", "readlink", "-f", "{}", ";",
|
url,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let out = "";
|
p.stderr.on("data", d => process.stderr.write(d));
|
||||||
|
p.on("close", code => code === 0 ? resolve() : reject(new Error("yt-dlp failed")));
|
||||||
find.stdout.on("data", d => out += d.toString());
|
});
|
||||||
find.stderr.on("data", d => process.stderr.write(d));
|
}
|
||||||
|
|
||||||
find.on("close", code => {
|
export async function findFileById(id: string): Promise<string> {
|
||||||
if (code !== 0 || !out.trim()) {
|
return await new Promise<string>((resolve, reject) => {
|
||||||
reject(new Error("Audio file not found"));
|
const find = spawn("find", [
|
||||||
} else {
|
DATA_DIR,
|
||||||
resolve(out.trim());
|
"-type", "f",
|
||||||
}
|
"-iname", `*${id}*.opus`,
|
||||||
});
|
"-exec", "readlink", "-f", "{}", ";",
|
||||||
});
|
]);
|
||||||
|
|
||||||
|
let out = "";
|
||||||
|
|
||||||
|
find.stdout.on("data", d => out += d.toString());
|
||||||
|
find.stderr.on("data", d => process.stderr.write(d));
|
||||||
|
|
||||||
|
find.on("close", code => {
|
||||||
|
if (code !== 0 || !out.trim()) {
|
||||||
|
reject(new Error("Audio file not found"));
|
||||||
|
} else {
|
||||||
|
resolve(out.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,18 @@ import {
|
|||||||
joinVoiceChannel,
|
joinVoiceChannel,
|
||||||
type AudioPlayer,
|
type AudioPlayer,
|
||||||
} from '@discordjs/voice';
|
} from '@discordjs/voice';
|
||||||
import type { VoiceBasedChannel } from 'discord.js';
|
import type { CacheType, ChatInputCommandInteraction, VoiceBasedChannel } from 'discord.js';
|
||||||
import { createDiscordJSAdapter } from './adapter.js';
|
import { createDiscordJSAdapter } from './adapter.js';
|
||||||
|
import { player } from '../main.js';
|
||||||
|
|
||||||
|
export async function connectToChannelByInteraction(interaction: ChatInputCommandInteraction<CacheType>): Promise<void> {
|
||||||
|
const member = await interaction.guild?.members.fetch(interaction.user.id);
|
||||||
|
if (!member) throw new Error("ale chuj")
|
||||||
|
const voiceChannel = member.voice.channel;
|
||||||
|
if (!voiceChannel) throw new Error("ale chuj 2")
|
||||||
|
const connection = await connectToChannel(voiceChannel);
|
||||||
|
connection.subscribe(player);
|
||||||
|
}
|
||||||
|
|
||||||
export async function connectToChannel(channel: VoiceBasedChannel) {
|
export async function connectToChannel(channel: VoiceBasedChannel) {
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user