Compare commits

..

10 Commits

Author SHA1 Message Date
ReaperOfVeriod f4a480f813 test 2026-02-08 19:09:24 +01:00
ReaperOfVeriod f00cbb2091 initial music stuff 2026-01-31 02:50:20 +01:00
ReaperOfVeriod e262300def notification button handling - dumb typo 😄 2025-08-04 11:23:09 +02:00
ReaperOfVeriod 53094f615f notification button handling 2025-08-04 11:20:56 +02:00
ReaperOfVeriod 9b9f7bf0c4 notifi role embed and button 2025-08-04 11:03:18 +02:00
Peter Kuijpers 65e476db73 Update README.md 2025-08-03 18:35:30 +02:00
ReaperOfVeriod 1a62daadc7 really? 2025-08-03 16:30:53 +02:00
ReaperOfVeriod b448112fa2 update readme 2025-08-03 16:28:45 +02:00
Peter Kuijpers d830573c7b Create README.md 2025-08-03 16:23:39 +02:00
ReaperOfVeriod ef7e9536b6 initial upload 2025-08-03 16:19:58 +02:00
19 changed files with 1170 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
node_modules
Dockerfile
+2
View File
@@ -137,3 +137,5 @@ dist
# Vite logs files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
config.json
+11
View File
@@ -0,0 +1,11 @@
# Config
For this code to work you need a **config.json** file with the following content:
```json
{
"token":"Your bot token here",
"clientId":"Your Bot id here",
"guildId":"Your server id here"
}
```
+78
View File
@@ -0,0 +1,78 @@
const { SlashCommandBuilder } = require('discord.js');
const {
joinVoiceChannel,
createAudioPlayer,
createAudioResource,
StreamType,
} = require('@discordjs/voice');
const { spawn } = require('node:child_process');
module.exports = {
data: new SlashCommandBuilder()
.setName('play')
.setDescription('Proof-of-life music command'),
async execute(interaction) {
const voiceChannel = interaction.member.voice.channel;
if (!voiceChannel) {
return interaction.reply({
content: '❌ You need to join a voice channel first!',
ephemeral: true,
});
}
// 1️⃣ Defer reply immediately to prevent 3s timeout
await interaction.deferReply();
try {
// 2️⃣ Spawn yt-dlp → ffmpeg → Discord
const ytdlp = spawn('yt-dlp', [
'-f', 'bestaudio',
'-o', '-',
'https://www.youtube.com/watch?v=rPwUh0gqv7Y', // test URL
]);
const ffmpeg = spawn('ffmpeg', [
'-i', 'pipe:0',
'-f', 's16le', // raw PCM for Discord
'-ar', '48000', // sample rate
'-ac', '2', // stereo
'pipe:1',
]);
ytdlp.stdout.pipe(ffmpeg.stdin);
const resource = createAudioResource(ffmpeg.stdout, {
inputType: StreamType.Raw,
});
const player = createAudioPlayer();
player.play(resource);
const connection = joinVoiceChannel({
channelId: voiceChannel.id,
guildId: interaction.guild.id,
adapterCreator: interaction.guild.voiceAdapterCreator,
});
connection.subscribe(player);
// 3️⃣ Edit deferred reply
await interaction.editReply('▶️ Now playing: Rick Astley!');
// Optional logging
ytdlp.stderr.on('data', (data) => console.error('yt-dlp:', data.toString()));
ffmpeg.stderr.on('data', (data) => console.error('ffmpeg:', data.toString()));
player.on('error', (err) => console.error('AudioPlayer error:', err));
} catch (err) {
console.error(err);
// 4️⃣ Safe error reply
if (interaction.deferred || interaction.replied) {
await interaction.editReply('❌ Failed to play audio!');
} else {
await interaction.reply('❌ Failed to play audio!');
}
}
},
};
+29
View File
@@ -0,0 +1,29 @@
const { ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder, MessageFlags, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
const rulesEmbed = new EmbedBuilder()
.setColor(0x0099FF)
.setTitle('Notifications.')
.setDescription('Would you like to be notified when i go live?')
.setThumbnail('https://i.imgur.com/3AlTfNL.png');
module.exports = {
cooldown: 30,
data: new SlashCommandBuilder()
.setName('createnotifiembed')
.setDescription('sends a embed with the notification embed')
.setDefaultMemberPermissions(0),
async execute(interaction) {
const acceptNotifications = new ButtonBuilder()
.setCustomId('acceptnotifications')
.setLabel('I want notifications!')
.setStyle(ButtonStyle.Primary);
const row = new ActionRowBuilder().addComponents(acceptNotifications);
await interaction.reply({
embeds: [rulesEmbed],
components: [row],
});
},
};
+44
View File
@@ -0,0 +1,44 @@
const { ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder, MessageFlags, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
const rulesEmbed = new EmbedBuilder()
.setColor(0x0099FF)
.setTitle('Rules')
.setDescription('Follow these simple rules and we will be the best of friends!')
.setThumbnail('https://i.imgur.com/3AlTfNL.png')
.addFields(
{ name: 'Common sense', value: 'Seriously this gets you a long way.' },
{ name: 'Be respectful to everyone', value: 'No harassment, bullying or personal attacks.'},
{ name: 'No hate speech', value: 'Racism, sexism, homophobia, transphobia or any discriminatory remarks will result in an immediate ban.'},
{ name: 'English only', value: 'I cant be fucked to moderate other languages i do or dont speak.'},
{ name: 'No roleplay, flirting or weird parasocial behavior', value: 'I dont care if you think you are an animal or feel attracted to me keep it clean.'},
{ name: 'Respect personal boundaries', value: `Don't ask invasive or inappropriate questions.`},
{ name: 'Mods have the final say', value: 'Dont argue its an uphill battle.'},
{ name: 'Warnings may be given once (or not at all)', value: 'Depending on the severety of the offence.'},
{ name: 'No Trauma dumping or serious personal issues', value: `This isnt therapy, and chat isn't the place for it.`},
{ name: 'Keep political/religious debates out', value: 'I dont care for it and it only leads to a mess.'},
{ name: 'This is an 18+ stream', value: 'I will ban minors on the spot, joke that you are under 18 and see yourself get banned without warning.'},
{ name: 'No NSFW', value: 'I might be weird but lets keep it clean alright?' },
{ name: 'Enjoy your time here', value: `Thats what we're here for right`});
module.exports = {
cooldown: 30,
data: new SlashCommandBuilder()
.setName('createrulesembed')
.setDescription('sends a embed with the rules')
.setDefaultMemberPermissions(0),
async execute(interaction) {
const acceptRules = new ButtonBuilder()
.setCustomId('acceptrules')
.setLabel('I accept the rules.')
.setStyle(ButtonStyle.Success);
const row = new ActionRowBuilder().addComponents(acceptRules);
await interaction.reply({
embeds: [rulesEmbed],
components: [row],
});
},
};
+9
View File
@@ -0,0 +1,9 @@
services:
app:
container_name: reapersbeepboop
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/opt/app
- /opt/app/node_modules
+46
View File
@@ -0,0 +1,46 @@
const { REST, Routes } = require('discord.js');
const { clientId, guildId, token } = require('./config.json');
const fs = require('node:fs');
const path = require('node:path');
const commands = [];
// Grab all the command folders from the commands directory you created earlier
const foldersPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(foldersPath);
for (const folder of commandFolders) {
// Grab all the command files from the commands directory you created earlier
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
// Construct and prepare an instance of the REST module
const rest = new REST().setToken(token);
// and deploy your commands!
(async () => {
try {
// for guild-based commands
rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: [] })
.then(() => console.log('Successfully deleted all guild commands.'))
.catch(console.error);
// for global commands
rest.put(Routes.applicationCommands(clientId), { body: [] })
.then(() => console.log('Successfully deleted all application commands.'))
.catch(console.error);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
+46
View File
@@ -0,0 +1,46 @@
const { REST, Routes } = require('discord.js');
const { clientId, guildId, token } = require('./config.json');
const fs = require('node:fs');
const path = require('node:path');
const commands = [];
// Grab all the command folders from the commands directory you created earlier
const foldersPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(foldersPath);
for (const folder of commandFolders) {
// Grab all the command files from the commands directory you created earlier
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
// Construct and prepare an instance of the REST module
const rest = new REST().setToken(token);
// and deploy your commands!
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands(clientId, guildId),
{ body: commands },
);
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
+16
View File
@@ -0,0 +1,16 @@
FROM node:24-alpine
# Install system dependencies
RUN apk add --no-cache \
ffmpeg \
python3 \
yt-dlp
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
CMD ["node", "main.js"]
+81
View File
@@ -0,0 +1,81 @@
const { Events, MessageFlags, Collection } = require('discord.js');
async function safeReply(interaction, content) {
if (interaction.replied || interaction.deferred) {
await interaction.editReply(content).catch(() => {
interaction.followUp({ content, flags: MessageFlags.Ephemeral }).catch(console.error);
});
} else {
await interaction.reply({ content, flags: MessageFlags.Ephemeral }).catch(console.error);
}
}
module.exports = {
name: Events.InteractionCreate,
async execute(interaction) {
if (interaction.isChatInputCommand()) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return console.error(`No command matching ${interaction.commandName} found.`);
const { cooldowns } = interaction.client;
if (!cooldowns.has(command.data.name)) cooldowns.set(command.data.name, new Collection());
const now = Date.now();
const timestamps = cooldowns.get(command.data.name);
const defaultCooldown = 3;
const cooldownAmount = (command.cooldown ?? defaultCooldown) * 1000;
if (timestamps.has(interaction.user.id)) {
const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount;
if (now < expirationTime) {
const expired = Math.round(expirationTime / 1000);
return interaction.reply({
content: `Please wait, you are on cooldown for \`${command.data.name}\`. Try again <t:${expired}:R>.`,
flags: MessageFlags.Ephemeral,
});
}
}
timestamps.set(interaction.user.id, now);
setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
await safeReply(interaction, '❌ There was an error while executing this command!');
}
} else if (interaction.isButton()) {
if (interaction.customId === 'acceptnotifications') {
const notifiRoleId = '1401849479149391882';
try {
if (interaction.member.roles.cache.has(notifiRoleId)) {
await interaction.reply({ content: 'You already have the notification role!', flags: MessageFlags.Ephemeral });
} else {
await interaction.member.roles.add(notifiRoleId);
await interaction.reply({ content: 'Thank you! Hope to see you soon in my streams!', flags: MessageFlags.Ephemeral });
}
} catch (error) {
console.error(error);
await interaction.reply({ content: 'Something went wrong', flags: MessageFlags.Ephemeral });
}
}
if (interaction.customId === 'acceptrules') {
const viewerRoleId = '1401153107601395774';
try {
if (interaction.member.roles.cache.has(viewerRoleId)) {
await interaction.reply({ content: 'You already accepted the rules!', flags: MessageFlags.Ephemeral });
} else {
await interaction.member.roles.add(viewerRoleId);
await interaction.reply({ content: 'Thank you for accepting the rules!', flags: MessageFlags.Ephemeral });
}
} catch (error) {
console.error(error);
await interaction.reply({ content: 'Something went wrong', flags: MessageFlags.Ephemeral });
}
}
} else if (interaction.isStringSelectMenu()) {
// handle select menu
}
},
};
+9
View File
@@ -0,0 +1,9 @@
const { Events } = require('discord.js');
module.exports = {
name: Events.ClientReady,
once: true,
execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
},
};
+45
View File
@@ -0,0 +1,45 @@
const fs = require('node:fs');
const path = require('node:path');
const { Client, Collection, Events, GatewayIntentBits, MessageFlags } = require('discord.js');
const { token } = require('./config.json');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
],
});
client.cooldowns = new Collection();
client.commands = new Collection();
const foldersPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(foldersPath);
for (const folder of commandFolders) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
client.commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file);
const event = require(filePath);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
} else {
client.on(event.name, (...args) => event.execute(...args));
}
}
client.login(token);
+675
View File
@@ -0,0 +1,675 @@
{
"name": "discord-bot",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "discord-bot",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@discordjs/voice": "^0.19.0",
"@snazzah/davey": "^0.1.9",
"discord.js": "^14.21.0",
"opusscript": "^0.0.8"
}
},
"node_modules/@discordjs/builders": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.11.2.tgz",
"integrity": "sha512-F1WTABdd8/R9D1icJzajC4IuLyyS8f3rTOz66JsSI3pKvpCAtsMBweu8cyNYsIyvcrKAVn9EPK+Psoymq+XC0A==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/formatters": "^0.6.1",
"@discordjs/util": "^1.1.1",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "^0.38.1",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/collection": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.1.tgz",
"integrity": "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==",
"license": "Apache-2.0",
"dependencies": {
"discord-api-types": "^0.38.1"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.5.1.tgz",
"integrity": "sha512-Tg9840IneBcbrAjcGaQzHUJWFNq1MMWZjTdjJ0WS/89IffaNKc++iOvffucPxQTF/gviO9+9r8kEPea1X5J2Dw==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.1",
"@discordjs/util": "^1.1.1",
"@sapphire/async-queue": "^1.5.3",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.1",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.21.3"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/util": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz",
"integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/voice": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.19.0.tgz",
"integrity": "sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==",
"license": "Apache-2.0",
"dependencies": {
"@types/ws": "^8.18.1",
"discord-api-types": "^0.38.16",
"prism-media": "^1.3.5",
"tslib": "^2.8.1",
"ws": "^8.18.3"
},
"engines": {
"node": ">=22.12.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz",
"integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.0",
"@discordjs/rest": "^2.5.1",
"@discordjs/util": "^1.1.0",
"@sapphire/async-queue": "^1.5.2",
"@types/ws": "^8.5.10",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "^0.38.1",
"tslib": "^2.6.2",
"ws": "^8.17.0"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@emnapi/core": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
"integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.1.0",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
"integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
"integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
"integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1",
"@tybys/wasm-util": "^0.10.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz",
"integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/shapeshift": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz",
"integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v16"
}
},
"node_modules/@sapphire/snowflake": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
"integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@snazzah/davey": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey/-/davey-0.1.9.tgz",
"integrity": "sha512-vNZk5y+IsxjwzTAXikvzz5pqMLb35YytC64nVF2MAFVhjpXu9ITOKUriZ0JG/llwzCAi56jb5x0cXDRIyE2A2A==",
"license": "MIT",
"engines": {
"node": ">= 10"
},
"funding": {
"url": "https://github.com/sponsors/Snazzah"
},
"optionalDependencies": {
"@snazzah/davey-android-arm-eabi": "0.1.9",
"@snazzah/davey-android-arm64": "0.1.9",
"@snazzah/davey-darwin-arm64": "0.1.9",
"@snazzah/davey-darwin-x64": "0.1.9",
"@snazzah/davey-freebsd-x64": "0.1.9",
"@snazzah/davey-linux-arm-gnueabihf": "0.1.9",
"@snazzah/davey-linux-arm64-gnu": "0.1.9",
"@snazzah/davey-linux-arm64-musl": "0.1.9",
"@snazzah/davey-linux-x64-gnu": "0.1.9",
"@snazzah/davey-linux-x64-musl": "0.1.9",
"@snazzah/davey-wasm32-wasi": "0.1.9",
"@snazzah/davey-win32-arm64-msvc": "0.1.9",
"@snazzah/davey-win32-ia32-msvc": "0.1.9",
"@snazzah/davey-win32-x64-msvc": "0.1.9"
}
},
"node_modules/@snazzah/davey-android-arm-eabi": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-android-arm-eabi/-/davey-android-arm-eabi-0.1.9.tgz",
"integrity": "sha512-Dq0WyeVGBw+uQbisV/6PeCQV2ndJozfhZqiNIfQxu6ehIdXB7iHILv+oY+AQN2n+qxiFmLh/MOX9RF+pIWdPbA==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-android-arm64": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-android-arm64/-/davey-android-arm64-0.1.9.tgz",
"integrity": "sha512-OE16OZjv7F/JrD7Mzw5eL2gY2vXRPC8S7ZrmkcMyz/sHHJsGHlT+L7X5s56Bec1YDTVmzAsH4UBuvVBoXuIWEQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-darwin-arm64": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-darwin-arm64/-/davey-darwin-arm64-0.1.9.tgz",
"integrity": "sha512-z7oORvAPExikFkH6tvHhbUdZd77MYZp9VqbCpKEiI+sisWFVXgHde7F7iH3G4Bz6gUYJfgvKhWXiDRc+0SC4dg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-darwin-x64": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-darwin-x64/-/davey-darwin-x64-0.1.9.tgz",
"integrity": "sha512-f1LzGyRGlM414KpXml3OgWVSd7CgylcdYaFj/zDBb8bvWjxyvsI9iMeuPfe/cduloxRj8dELde/yCDZtFR6PdQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-freebsd-x64": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-freebsd-x64/-/davey-freebsd-x64-0.1.9.tgz",
"integrity": "sha512-k6p3JY2b8rD6j0V9Ql7kBUMR4eJdcpriNwiHltLzmtGuz/nK5RGQdkEP68gTLc+Uj3xs5Cy0jRKmv2xJQBR4sA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-linux-arm-gnueabihf": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm-gnueabihf/-/davey-linux-arm-gnueabihf-0.1.9.tgz",
"integrity": "sha512-xDaAFUC/1+n/YayNwKsqKOBMuW0KI6F0SjgWU+krYTQTVmAKNjOM80IjemrVoqTpBOxBsT80zEtct2wj11CE3Q==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-linux-arm64-gnu": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm64-gnu/-/davey-linux-arm64-gnu-0.1.9.tgz",
"integrity": "sha512-t1VxFBzWExPNpsNY/9oStdAAuHqFvwZvIO2YPYyVNstxfi2KmAbHMweHUW7xb2ppXuhVQZ4VGmmeXiXcXqhPBw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-linux-arm64-musl": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm64-musl/-/davey-linux-arm64-musl-0.1.9.tgz",
"integrity": "sha512-Xvlr+nBPzuFV4PXHufddlt08JsEyu0p8mX2DpqdPxdpysYIH4I8V86yJiS4tk04a6pLBDd8IxTbBwvXJKqd/LQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-linux-x64-gnu": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-linux-x64-gnu/-/davey-linux-x64-gnu-0.1.9.tgz",
"integrity": "sha512-6Uunc/NxiEkg1reroAKZAGfOtjl1CGa7hfTTVClb2f+DiA8ZRQWBh+3lgkq/0IeL262B4F14X8QRv5Bsv128qw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-linux-x64-musl": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-linux-x64-musl/-/davey-linux-x64-musl-0.1.9.tgz",
"integrity": "sha512-fFQ/n3aWt1lXhxSdy+Ge3gi5bR3VETMVsWhH0gwBALUKrbo3ZzgSktm4lNrXE9i0ncMz/CDpZ5i0wt/N3XphEQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-wasm32-wasi": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-wasm32-wasi/-/davey-wasm32-wasi-0.1.9.tgz",
"integrity": "sha512-xWvzej8YCVlUvzlpmqJMIf0XmLlHqulKZ2e7WNe2TxQmsK+o0zTZqiQYs2MwaEbrNXBhYlHDkdpuwoXkJdscNQ==",
"cpu": [
"wasm32"
],
"license": "MIT",
"optional": true,
"dependencies": {
"@napi-rs/wasm-runtime": "^1.1.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@snazzah/davey-win32-arm64-msvc": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-win32-arm64-msvc/-/davey-win32-arm64-msvc-0.1.9.tgz",
"integrity": "sha512-sTqry/DfltX2OdW1CTLKa3dFYN5FloAEb2yhGsY1i5+Bms6OhwByXfALvyMHYVo61Th2+sD+9BJpQffHFKDA3w==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-win32-ia32-msvc": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-win32-ia32-msvc/-/davey-win32-ia32-msvc-0.1.9.tgz",
"integrity": "sha512-twD3LwlkGnSwphsCtpGb5ztpBIWEvGdc0iujoVkdzZ6nJiq5p8iaLjJMO4hBm9h3s28fc+1Qd7AMVnagiOasnA==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@snazzah/davey-win32-x64-msvc": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@snazzah/davey-win32-x64-msvc/-/davey-win32-x64-msvc-0.1.9.tgz",
"integrity": "sha512-eMnXbv4GoTngWYY538i/qHz2BS+RgSXFsvKltPzKqnqzPzhQZIY7TemEJn3D5yWGfW4qHve9u23rz93FQqnQMA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@types/node": {
"version": "24.1.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
"license": "MIT",
"dependencies": {
"undici-types": "~7.8.0"
}
},
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",
"integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/discord-api-types": {
"version": "0.38.17",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.17.tgz",
"integrity": "sha512-/fCx5jdUoR2hBFcj77Qx7Tmx1Ub8V/QpyS6uorjFvxRLcJJ348QxMFml9QW/eXh3i46eO4Ve8qGVepStpInEPg==",
"license": "MIT",
"workspaces": [
"scripts/actions/documentation"
]
},
"node_modules/discord.js": {
"version": "14.21.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.21.0.tgz",
"integrity": "sha512-U5w41cEmcnSfwKYlLv5RJjB8Joa+QJyRwIJz5i/eg+v2Qvv6EYpCRhN9I2Rlf0900LuqSDg8edakUATrDZQncQ==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/builders": "^1.11.2",
"@discordjs/collection": "1.5.3",
"@discordjs/formatters": "^0.6.1",
"@discordjs/rest": "^2.5.1",
"@discordjs/util": "^1.1.1",
"@discordjs/ws": "^1.2.3",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "^0.38.1",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.21.3"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.snakecase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
"license": "MIT"
},
"node_modules/magic-bytes.js": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.12.1.tgz",
"integrity": "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==",
"license": "MIT"
},
"node_modules/opusscript": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.8.tgz",
"integrity": "sha512-VSTi1aWFuCkRCVq+tx/BQ5q9fMnQ9pVZ3JU4UHKqTkf0ED3fKEPdr+gKAAl3IA2hj9rrP6iyq3hlcJq3HELtNQ==",
"license": "MIT"
},
"node_modules/prism-media": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz",
"integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==",
"license": "Apache-2.0",
"peerDependencies": {
"@discordjs/opus": ">=0.8.0 <1.0.0",
"ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0",
"node-opus": "^0.3.3",
"opusscript": "^0.0.8"
},
"peerDependenciesMeta": {
"@discordjs/opus": {
"optional": true
},
"ffmpeg-static": {
"optional": true
},
"node-opus": {
"optional": true
},
"opusscript": {
"optional": true
}
}
},
"node_modules/ts-mixer": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
"integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==",
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/undici": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/undici-types": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"license": "MIT"
},
"node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"name": "discord-bot",
"version": "1.0.0",
"description": "",
"license": "MIT",
"author": "ReaperOfVeriod",
"type": "commonjs",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"@discordjs/voice": "^0.19.0",
"@snazzah/davey": "^0.1.9",
"discord.js": "^14.21.0",
"opusscript": "^0.0.8"
}
}
+24
View File
@@ -0,0 +1,24 @@
const { spawn } = require('node:child_process');
console.log('Starting smoke test...');
const ytdlp = spawn('yt-dlp', [
'-f', 'bestaudio',
'-o', '-',
'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
]);
const ffmpeg = spawn('ffmpeg', [
'-i', 'pipe:0',
'-f', 'null',
'-'
]);
ytdlp.stdout.pipe(ffmpeg.stdin);
ffmpeg.on('close', (code) => {
console.log('FFmpeg exited with code:', code);
});
ytdlp.on('error', err => console.error('yt-dlp error:', err));
ffmpeg.on('error', err => console.error('ffmpeg error:', err));
+12
View File
@@ -0,0 +1,12 @@
const { SlashCommandBuilder, MessageFlags, PermissionFlagsBits } = require('discord.js');
module.exports = {
cooldown: 30,
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!')
.setDefaultMemberPermissions(0),
async execute(interaction) {
await interaction.reply({ content: 'Pong!', flags: MessageFlags.Ephemeral });
},
};
+11
View File
@@ -0,0 +1,11 @@
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('server')
.setDescription('Provides information about the server.'),
async execute(interaction) {
// interaction.guild is the object representing the Guild in which the command was run
await interaction.reply(`This server is ${interaction.guild.name} and has ${interaction.guild.memberCount} members.`);
},
};
+12
View File
@@ -0,0 +1,12 @@
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('user')
.setDescription('Provides information about the user.'),
async execute(interaction) {
// interaction.user is the object representing the User who ran the command
// interaction.member is the GuildMember object, which represents the user in the specific guild
await interaction.reply(`This command was run by ${interaction.user.username}, who joined on ${interaction.member.joinedAt}.`);
},
};