first commit
This commit is contained in:
49
.eslintrc.json
Normal file
49
.eslintrc.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2021
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
||||||
|
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
|
||||||
|
"comma-dangle": ["error", "always-multiline"],
|
||||||
|
"comma-spacing": "error",
|
||||||
|
"comma-style": "error",
|
||||||
|
"curly": ["error", "multi-line", "consistent"],
|
||||||
|
"dot-location": ["error", "property"],
|
||||||
|
"handle-callback-err": "off",
|
||||||
|
"indent": ["error", "tab"],
|
||||||
|
"keyword-spacing": "error",
|
||||||
|
"max-nested-callbacks": ["error", { "max": 4 }],
|
||||||
|
"max-statements-per-line": ["error", { "max": 2 }],
|
||||||
|
"no-console": "off",
|
||||||
|
"no-empty-function": "error",
|
||||||
|
"no-floating-decimal": "error",
|
||||||
|
"no-inline-comments": "error",
|
||||||
|
"no-lonely-if": "error",
|
||||||
|
"no-multi-spaces": "error",
|
||||||
|
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
||||||
|
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
||||||
|
"no-trailing-spaces": ["error"],
|
||||||
|
"no-var": "error",
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"prefer-const": "error",
|
||||||
|
"quotes": ["error", "single"],
|
||||||
|
"semi": ["error", "always"],
|
||||||
|
"space-before-blocks": "error",
|
||||||
|
"space-before-function-paren": ["error", {
|
||||||
|
"anonymous": "never",
|
||||||
|
"named": "never",
|
||||||
|
"asyncArrow": "always"
|
||||||
|
}],
|
||||||
|
"space-in-parens": "error",
|
||||||
|
"space-infix-ops": "error",
|
||||||
|
"space-unary-ops": "error",
|
||||||
|
"spaced-comment": "error",
|
||||||
|
"yoda": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
config.json
|
||||||
|
dockerfile
|
||||||
92
commands/misc/pinguser.js
Normal file
92
commands/misc/pinguser.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
const { Client, Interaction, SlashCommandBuilder } = require("discord.js");
|
||||||
|
|
||||||
|
const cooldowns = new Map();
|
||||||
|
var isDisabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the ping user command.
|
||||||
|
* @param {Interaction} interaction - The interaction object.
|
||||||
|
*/
|
||||||
|
async function handlePingCommand(interaction) {
|
||||||
|
const user = interaction.options.getUser("user"); // Get the user from the interaction
|
||||||
|
const times = interaction.options.getInteger("times") || 1; // Default to 1 if not provided
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Ensure the user exists in the guild
|
||||||
|
const userPing = await interaction.guild.members.fetch(user.id);
|
||||||
|
if (!userPing) {
|
||||||
|
await interaction.reply("That user doesn't exist in this server.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent the bot from pinging itself
|
||||||
|
if (userPing.id === interaction.guild.members.me.id) {
|
||||||
|
await interaction.reply("I can't ping myself.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cooldown logic
|
||||||
|
const coolDown = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
|
||||||
|
const now = Date.now();
|
||||||
|
const lastExec = cooldowns.get(interaction.user.id);
|
||||||
|
|
||||||
|
if (lastExec && now - lastExec < coolDown) {
|
||||||
|
const timeLeft = coolDown - (now - lastExec);
|
||||||
|
|
||||||
|
const days = Math.floor(timeLeft / (1000 * 60 * 60 * 24));
|
||||||
|
const hours = Math.floor((timeLeft % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||||
|
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
|
||||||
|
|
||||||
|
await interaction.reply(
|
||||||
|
`You need to wait ${days} days, ${hours} hours, ${minutes} minutes, and ${seconds} seconds before using this command again.`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the pings
|
||||||
|
await interaction.reply({ content: "Ok, sending pings.", ephemeral: false });
|
||||||
|
|
||||||
|
for (let i = 0; i < times; i++) {
|
||||||
|
await interaction.channel.send(`Pinging ${userPing}`);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 300)); // Wait 300ms between pings
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the cooldown
|
||||||
|
cooldowns.set(interaction.user.id, now);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("There was an error in pinguser: ", error);
|
||||||
|
await interaction.reply({
|
||||||
|
content: "An error occurred while executing the command.",
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("pinguser")
|
||||||
|
.setDescription("Ping the mentioned user")
|
||||||
|
.addUserOption((option) =>
|
||||||
|
option.setName("user").setDescription("The user to ping").setRequired(true),
|
||||||
|
)
|
||||||
|
.addIntegerOption((option) =>
|
||||||
|
option
|
||||||
|
.setName("times")
|
||||||
|
.setDescription("Number of times to ping the user")
|
||||||
|
.setRequired(false),
|
||||||
|
),
|
||||||
|
async execute(interaction) {
|
||||||
|
try {
|
||||||
|
if (isDisabled) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: "The command is disabled... Try again later.",
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await handlePingCommand(interaction); // Ensure we await this function
|
||||||
|
} catch (error) {
|
||||||
|
console.error("There was an error executing the command: ", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
92
commands/moderation/ban.js
Normal file
92
commands/moderation/ban.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
const {
|
||||||
|
Client,
|
||||||
|
Interaction,
|
||||||
|
ApplicationCommandOptionType,
|
||||||
|
PermissionFlagsBits,
|
||||||
|
SlashCommandBuilder,
|
||||||
|
} = require("discord.js");
|
||||||
|
|
||||||
|
async function handleBanCommand(interaction) {
|
||||||
|
if (!interaction.member.permissions.has(PermissionFlagsBits.BanMembers)) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: "You do not have permission to ban members.",
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetID = interaction.options.get("user").value;
|
||||||
|
const reason =
|
||||||
|
interaction.options.get("reason")?.value || "No reason provided";
|
||||||
|
|
||||||
|
await interaction.deferReply();
|
||||||
|
const targetUser = await interaction.guild.members.fetch(targetID);
|
||||||
|
|
||||||
|
if (!targetUser) {
|
||||||
|
await interaction.editReply("That user is not in the server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUser.id === interaction.guild.ownerId) {
|
||||||
|
await interaction.editReply("I can't ban the server owner");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUser.id === interaction.guild.members.me) {
|
||||||
|
await interaction.editReply("I cant't ban myself");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetUserRolePostion = targetUser.roles.highest.postion; // check the user highest role
|
||||||
|
const requestUserRolePostion = interaction.member.roles.highest.postion; // check the user issuing the command is higher than the target
|
||||||
|
const botRolePostion = interaction.guild.members.me.roles.highest.postion; // check the bot has permissions
|
||||||
|
|
||||||
|
if (targetUserRolePostion >= requestUserRolePostion) {
|
||||||
|
await interaction.editReply(
|
||||||
|
"You can't ban someone that has the same/higher role than you."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUserRolePostion >= botRolePostion) {
|
||||||
|
await interaction.editReply(
|
||||||
|
"I can't ban that user because they have the same/higher role than me."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await targetUser.ban({ reason });
|
||||||
|
await interaction.editReply(
|
||||||
|
`User ${targetUser} was banned. Reason: ${reason}`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("There was an error banning the target: ", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("ban")
|
||||||
|
.setDescription("Bans the specified user")
|
||||||
|
.addUserOption((option) =>
|
||||||
|
option
|
||||||
|
.setName("user")
|
||||||
|
.setDescription("Mention the user to ban")
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
.addStringOption((option) =>
|
||||||
|
option
|
||||||
|
.setName("reason")
|
||||||
|
.setDescription("Specify the reason for banning")
|
||||||
|
.setRequired(false)
|
||||||
|
)
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
|
||||||
|
async execute(interaction) {
|
||||||
|
try {
|
||||||
|
handleBanCommand(interaction);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("There was an error in banning: ", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
75
commands/moderation/kick.js
Normal file
75
commands/moderation/kick.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
const {
|
||||||
|
Client,
|
||||||
|
Interaction,
|
||||||
|
ApplicationCommandOptionType,
|
||||||
|
PermissionFlagsBits,
|
||||||
|
SlashCommandBuilder,
|
||||||
|
} = require("discord.js");
|
||||||
|
|
||||||
|
async function handleKickCommand(interaction) {
|
||||||
|
if (!interaction.member.permissions.has(PermissionFlagsBits.KickMembers)) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: "You do not have permission to kick members.",
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetID = interaction.options.get('user').value;
|
||||||
|
const reason = interaction.options.get("reason")?.value || "No reason provided";
|
||||||
|
|
||||||
|
await interaction.deferReply();
|
||||||
|
const targetUser = await interaction.guild.members.fetch(targetID)
|
||||||
|
|
||||||
|
if (!targetUser) {
|
||||||
|
await interaction.editReply('That user does not exist in this server');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUser.id === interaction.guild.ownerId) {
|
||||||
|
await interaction.editReply("I can't kick the server owner")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUser.id === interaction.guild.members.me) {
|
||||||
|
await interaction.editReply("I can't kick myself")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetUserRolePostion = targetUser.roles.highest.postion;
|
||||||
|
const requestUserRolePostion = interaction.member.roles.highest.postion
|
||||||
|
const botRolePostion = interaction.guild.members.me.roles.highest.postion
|
||||||
|
|
||||||
|
if (targetUserRolePostion >= requestUserRolePostion) {
|
||||||
|
await interaction.editReply("You can't kick someone that has the same/higher role than you")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUserRolePostion >= botRolePostion) {
|
||||||
|
await interaction.editReply("I can't kick that user because they have the same/higher role than me.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await targetUser.kick({ reason })
|
||||||
|
await interaction.editReply(`User ${targetUser} was kicked. Reason: ${reason}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('There was a problem kicking: ', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("kick")
|
||||||
|
.setDescription("Kicks the specified user")
|
||||||
|
.addUserOption((option) => option.setName("user").setDescription("Mention the user to kick").setRequired(true))
|
||||||
|
.addStringOption((option) => option.setName('reason').setDescription('Specify the reason for kicking').setRequired(false))
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers),
|
||||||
|
async execute(interaction) {
|
||||||
|
try {
|
||||||
|
handleKickCommand(interaction)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('There was an error in kicking:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
132
commands/music/bplay.js
Normal file
132
commands/music/bplay.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
const { Client, Interaction, SlashCommandBuilder, VoiceChannel } = require('discord.js');
|
||||||
|
const { playFromQueue, queue} = require('../../playback');
|
||||||
|
const ytdl = require('ytdl-core');
|
||||||
|
const { default: axios } = require('axios');
|
||||||
|
const { setStopped, cancelTimer } = require('../../playback')
|
||||||
|
var isDisabled = false
|
||||||
|
|
||||||
|
const API_KEY = process.env.YoutubeToken
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {Interaction} interaction
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
async function addToQueue(interaction, link, songName) {
|
||||||
|
const voiceChannel = interaction.member.voice.channel;
|
||||||
|
const textChannel = interaction.channel;
|
||||||
|
const getMember = interaction.member.displayName;
|
||||||
|
|
||||||
|
const wasQueueEmpty = queue.length === 0;
|
||||||
|
queue.push({interaction, getMember, link, songName})
|
||||||
|
cancelTimer()
|
||||||
|
if (wasQueueEmpty) {
|
||||||
|
await playFromQueue(voiceChannel, textChannel, getMember, link, songName)
|
||||||
|
} else {
|
||||||
|
//console.log(queue)
|
||||||
|
interaction.reply(`Added to queue: ${songName}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchYoutubeSong(input) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('https://www.googleapis.com/youtube/v3/search', {
|
||||||
|
params: {
|
||||||
|
part: 'snippet',
|
||||||
|
q: input,
|
||||||
|
key: API_KEY,
|
||||||
|
maxResults: 1,
|
||||||
|
type: 'video',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.items && response.data.items.length > 0) {
|
||||||
|
const video = response.data.items[0];
|
||||||
|
const videoId = video.id.videoId;
|
||||||
|
const videoTitle = video.snippet.title;
|
||||||
|
const videoUrl = `https://www.youtube.com/watch?v=${videoId}`;
|
||||||
|
return { videoUrl, videoTitle}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error searching Youtube: ', error)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function handlePlayCommand(interaction, input) {
|
||||||
|
const voiceChannel = interaction.member.voice.channel;
|
||||||
|
|
||||||
|
if (!voiceChannel) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: 'You need to be in a voice channel to use this command.',
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let link = '';
|
||||||
|
let songName = input;
|
||||||
|
|
||||||
|
if (ytdl.validateURL(input)) {
|
||||||
|
// If it's a valid YouTube URL, extract the song information
|
||||||
|
const videoInfo = await ytdl.getBasicInfo(input);
|
||||||
|
songName = videoInfo.videoDetails.title;
|
||||||
|
link = input;
|
||||||
|
} else {
|
||||||
|
// If it's a search query, find the video
|
||||||
|
const searchResult = await searchYoutubeSong(input);
|
||||||
|
if (searchResult) {
|
||||||
|
link = searchResult.videoUrl;
|
||||||
|
songName = searchResult.videoTitle;
|
||||||
|
} else {
|
||||||
|
return interaction.reply({
|
||||||
|
content: 'No results found for your query.',
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the song to the queue and play if needed
|
||||||
|
await addToQueue(interaction, link, songName);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error handling play command:', error);
|
||||||
|
await interaction.reply({
|
||||||
|
content: 'An error occurred while processing your request.',
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('bplay')
|
||||||
|
.setDescription('Plays a song')
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('query')
|
||||||
|
.setDescription('The song name or URL to play')
|
||||||
|
.setRequired(true)
|
||||||
|
),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
try {
|
||||||
|
if (isDisabled) return interaction.reply({ content: 'The command is Disabled... Try again later', ephemeral: true})
|
||||||
|
const query = interaction.options.getString('query');
|
||||||
|
setStopped(false)
|
||||||
|
await handlePlayCommand(interaction, query);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error executing play command:', error);
|
||||||
|
await interaction.reply({
|
||||||
|
content: 'An error occurred while processing your request.',
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
36
commands/music/queue.js
Normal file
36
commands/music/queue.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const { Client, Interaction, SlashCommandBuilder, EmbedBuilder } = require('discord.js')
|
||||||
|
const { queue } = require('./../../playback')
|
||||||
|
const { musicQueueEmbed } = require('../../embeds/queue');
|
||||||
|
|
||||||
|
async function checkQueue(interaction) {
|
||||||
|
if (!queue || queue.length === 0 ) {
|
||||||
|
interaction.reply('The queue is empty.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const embed = musicQueueEmbed(queue);
|
||||||
|
await interaction.reply({ embeds: [embed] })
|
||||||
|
} catch (error) {
|
||||||
|
console.log('There is an error in the queue command: ', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('queue')
|
||||||
|
.setDescription('Check the playback queue'),
|
||||||
|
async execute(interaction) {
|
||||||
|
const voiceChannel = interaction.member.voice.channel
|
||||||
|
const textChannel = interaction.textChannel
|
||||||
|
|
||||||
|
if (!voiceChannel) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: 'You need to be in a voice channel to use that queue',
|
||||||
|
ephemeral: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await checkQueue(interaction, voiceChannel, textChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
60
commands/music/skip.js
Normal file
60
commands/music/skip.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
const { Client, Interaction, SlashCommandBuilder } = require('discord.js')
|
||||||
|
|
||||||
|
const { playFromQueue, queue } = require('../../playback');
|
||||||
|
const { getVoiceConnection } = require('@discordjs/voice')
|
||||||
|
|
||||||
|
async function skipSong(voiceChannel, textChannel) {
|
||||||
|
const connection = getVoiceConnection(voiceChannel.guild.id)
|
||||||
|
|
||||||
|
if (!connection) {
|
||||||
|
console.log('No active voice connections')
|
||||||
|
textChannel.send('No active voice connections')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queue || queue.length === 0) {
|
||||||
|
console.log('Queue is empty. No songs to skip')
|
||||||
|
connection.state.subscription.player.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
queue.shift()
|
||||||
|
|
||||||
|
if (queue.length > 0 ) {
|
||||||
|
const nextSong = queue[0];
|
||||||
|
await playFromQueue(voiceChannel, textChannel, nextSong.getMember, nextSong.link, nextSong.songName)
|
||||||
|
} else {
|
||||||
|
textChannel.send('No more songs in the queue')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error skipping song: ', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('skip')
|
||||||
|
.setDescription('Skips a song in the queue'),
|
||||||
|
async execute(interaction) {
|
||||||
|
const voiceChannel = interaction.member.voice.channel
|
||||||
|
const textChannel = interaction.channel
|
||||||
|
if (!voiceChannel) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: 'You need to be in a voice channel to use the command',
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await skipSong(voiceChannel, textChannel)
|
||||||
|
interaction.reply('Skipped the current song')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in skip command: ', error)
|
||||||
|
interaction.reply({
|
||||||
|
content: 'An error has occurred while processing the command',
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
commands/music/stop.js
Normal file
20
commands/music/stop.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const { SlashCommandBuilder } = require("discord.js");
|
||||||
|
const { resetAll, setStopped } = require("../../playback");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("stop")
|
||||||
|
.setDescription("Stop playback and leave the voice channel"),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
const voiceChannel = interaction.member.voice.channel;
|
||||||
|
if (!voiceChannel) {
|
||||||
|
return interaction.reply({ content: "You need to be in a voice channel to use this command.", ephemeral: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
setStopped(true); // Mark playback as stopped
|
||||||
|
resetAll(voiceChannel.guild.id); // Reset everything for a clean state
|
||||||
|
|
||||||
|
return interaction.reply("Stopped playback and left the voice channel.");
|
||||||
|
},
|
||||||
|
};
|
||||||
46
commands/utility/auto-welcome.js
Normal file
46
commands/utility/auto-welcome.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
const {
|
||||||
|
Client,
|
||||||
|
Interaction,
|
||||||
|
ApplicationCommandOptionType,
|
||||||
|
PermissionFlagsBits,
|
||||||
|
SlashCommandBuilder,
|
||||||
|
} = require("discord.js");
|
||||||
|
const db = require("../../db");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("autowelcome")
|
||||||
|
.setDescription("Enable or disable auto-welcome messages.")
|
||||||
|
.addChannelOption((option) =>
|
||||||
|
option
|
||||||
|
.setName("channel")
|
||||||
|
.setDescription(
|
||||||
|
"Channel Id to send welcome messages (required when enabling)"
|
||||||
|
)
|
||||||
|
.setRequired(false)
|
||||||
|
)
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
const channelId = interaction.options.getChannel("channel");
|
||||||
|
const guildId = interaction.guild.id;
|
||||||
|
|
||||||
|
if (channelId) {
|
||||||
|
await db.query(
|
||||||
|
`INSERT INTO auto_welcome (guild_id, channel_id, enabled)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
ON CONFLICT (guild_id) DO UPDATE SET channel_id = $2, enabled = $3`,
|
||||||
|
[guildId, channelId.id, true]
|
||||||
|
);
|
||||||
|
return interaction.reply(
|
||||||
|
`Auto-welcome enabled! Messages will be sent to <#${channelId.id}>.`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await db.query(
|
||||||
|
`UPDATE auto_welcome SET enabled = $1 WHERE guild_id = $2`,
|
||||||
|
[false, guildId]
|
||||||
|
);
|
||||||
|
return interaction.reply("Auto-welcome has been disabled.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
17
commands/utility/ping.js
Normal file
17
commands/utility/ping.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const { SlashCommandBuilder } = require("discord.js");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("ping")
|
||||||
|
.setDescription("Replies with Pong! and websocket ping"),
|
||||||
|
async execute(interaction) {
|
||||||
|
// Send the initial reply and wait for it to be sent
|
||||||
|
const reply = await interaction.reply({
|
||||||
|
content: "Pong!",
|
||||||
|
fetchReply: true // This ensures we can get the reply object
|
||||||
|
});
|
||||||
|
|
||||||
|
const ping = reply.createdTimestamp - interaction.createdTimestamp; // Calculate the ping
|
||||||
|
await interaction.editReply(`Pong! client ${ping}ms | websocket: ${interaction.client.ws.ping}ms`);
|
||||||
|
},
|
||||||
|
};
|
||||||
33
commands/utility/setBluChannel.js
Normal file
33
commands/utility/setBluChannel.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { Client, Interaction, ApplicationCommandOptionType, PermissionFlagsBits, SlashCommandBuilder } = require("discord.js");
|
||||||
|
const db = require("../../db");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("setbluchannel")
|
||||||
|
.setDescription("Set the channel for Blu to send messages.")
|
||||||
|
.addChannelOption((option) =>
|
||||||
|
option
|
||||||
|
.setName("channel")
|
||||||
|
.setDescription("Channel to set for Blu messages")
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||||
|
async execute(interaction) {
|
||||||
|
const channelId = interaction.options.getChannel("channel");
|
||||||
|
const guildId = interaction.guild.id;
|
||||||
|
const guildName = interaction.guild.name;
|
||||||
|
const channelName = channelId.name;
|
||||||
|
|
||||||
|
if (channelId) {
|
||||||
|
await db.query(
|
||||||
|
`INSERT INTO bot_channel (guild_id, channel_id, guild_name, channel_name)
|
||||||
|
VALUES ($1, $2, $3, $4)
|
||||||
|
ON CONFLICT (guild_id) DO UPDATE SET Channel_id = $2, guild_name = $3, channel_name = $4`,
|
||||||
|
[guildId, channelId.id, guildName, channelName]
|
||||||
|
);
|
||||||
|
return interaction.reply(`Blu messages will be sent to <#${channelId.id}>.`);
|
||||||
|
} else {
|
||||||
|
return interaction.reply("Please provide a valid channel.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
37
db.js
Normal file
37
db.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
const { Pool } = require('pg');
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
const pool = new Pool({
|
||||||
|
host: process.env.PGHOST,
|
||||||
|
user: process.env.PGUSER,
|
||||||
|
database: process.env.PGDATABASE,
|
||||||
|
password: process.env.PGPASSWORD,
|
||||||
|
port: process.env.PGPORT,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function connectDB() {
|
||||||
|
try {
|
||||||
|
await pool.connect();
|
||||||
|
console.log('Database connected.')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to connect to the database: ',error)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDB() {
|
||||||
|
try {
|
||||||
|
pool.end();
|
||||||
|
console.log('Database connection closed.')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error closing the database: ', error )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
query: (text, params) => pool.query(text, params),
|
||||||
|
closeDB,
|
||||||
|
connectDB
|
||||||
|
};
|
||||||
46
deploy-commands-private.js
Normal file
46
deploy-commands-private.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
const { REST, Routes } = require('discord.js');
|
||||||
|
require('dotenv').config();
|
||||||
|
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(process.env.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(process.env.ClientID, process.env.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);
|
||||||
|
}
|
||||||
|
})();
|
||||||
46
deploy-commands-public.js
Normal file
46
deploy-commands-public.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
const { REST, Routes } = require('discord.js');
|
||||||
|
require('dotenv').config();
|
||||||
|
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(process.env.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.applicationCommands(process.env.ClientID),
|
||||||
|
{ 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);
|
||||||
|
}
|
||||||
|
})();
|
||||||
44
embeds/modLogs.js
Normal file
44
embeds/modLogs.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const { EmbedBuilder } = require('discord.js')
|
||||||
|
|
||||||
|
function modLogEmbed(action, member, reason ) {
|
||||||
|
let title, description, color;
|
||||||
|
|
||||||
|
//console.log(reason);
|
||||||
|
switch (action) {
|
||||||
|
case "ban":
|
||||||
|
title = ":hammer: User Banned";
|
||||||
|
description = `**User:** **${member.user.username}** has been banned.`;
|
||||||
|
reason2 = `**Reason:** ${reason}`;
|
||||||
|
color = "#FF0000";
|
||||||
|
break;
|
||||||
|
case "unban":
|
||||||
|
title = ":tada::party_popper: User Unbanned";
|
||||||
|
description = `**User:** **${member.user.username}** has been unbanned.`;
|
||||||
|
reason2 = `**Reason:** ${reason}`;
|
||||||
|
color = "#00FF00";
|
||||||
|
break;
|
||||||
|
case "mute":
|
||||||
|
return muteEmbed(member);
|
||||||
|
case "unmute":
|
||||||
|
return unmuteEmbed(member);
|
||||||
|
case "warn":
|
||||||
|
return warnEmbed(member);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.setTitle(title)
|
||||||
|
.setDescription(`${description}\n${reason2}`)
|
||||||
|
.setColor(color)
|
||||||
|
.setTimestamp()
|
||||||
|
.setFooter({ text: `User ID: ${member.user.id}` })
|
||||||
|
.setThumbnail(member.user.displayAvatarURL({ dynamic: true }));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { modLogEmbed };
|
||||||
19
embeds/nowPlaying.js
Normal file
19
embeds/nowPlaying.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const { EmbedBuilder } = require('discord.js')
|
||||||
|
|
||||||
|
function nowPlayingEmbed(queue) {
|
||||||
|
if (!queue || queue.length === 0) {
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.setTitle('**🎵 Now Playing:**')
|
||||||
|
.setDescription('No song is currently playing.')
|
||||||
|
.setColor('Grey');
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentSong = queue[0]
|
||||||
|
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.setTitle('**🎵 Now Playing:**')
|
||||||
|
.setDescription(`${currentSong.songName}\n**Requested By:** ${currentSong.getMember || 'Unknown User'}`)
|
||||||
|
.setColor('Green')
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { nowPlayingEmbed }
|
||||||
15
embeds/queue.js
Normal file
15
embeds/queue.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const { EmbedBuilder } = require('discord.js')
|
||||||
|
|
||||||
|
|
||||||
|
function musicQueueEmbed(queue) {
|
||||||
|
const songLines = queue.map((item, index) =>
|
||||||
|
index === 0 ? `**🎵 Now Playing:** ${item.songName} **Requested By:** ${item.getMember}\n` : `${index + 1}. ${item.songName} - **Requested By:** ${item.getMember}\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.setTitle('Current Queue')
|
||||||
|
.setDescription(songLines.join('\n') || 'No Songs in the queue')
|
||||||
|
.setColor('Blue')
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { musicQueueEmbed }
|
||||||
33
embeds/userStreaming.js
Normal file
33
embeds/userStreaming.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { EmbedBuilder, ButtonBuilder, ActionRowBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
function userStreamingEmbed(user, status) {
|
||||||
|
|
||||||
|
const username = user?.username || 'Unknown User';
|
||||||
|
const avatarURL = user?.displayAvatarURL({ dynamic: true }) || null;
|
||||||
|
const streamTitle = status?.details || 'No title available';
|
||||||
|
const gameName = status?.name || 'No game available';
|
||||||
|
const streamURL = status?.url || 'No URL available';
|
||||||
|
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle('📡 Live Stream Alert!')
|
||||||
|
.setDescription(`**${username}** Just went Live on Twitch!`)
|
||||||
|
.addFields( { name: 'Stream Title', value: streamTitle, inline: false }, { name: 'Game / Category', value: gameName, inline: true } )
|
||||||
|
.setColor('Green')
|
||||||
|
.setTimestamp()
|
||||||
|
.setFooter({ text: 'User Streaming Status' })
|
||||||
|
.setThumbnail(avatarURL);
|
||||||
|
|
||||||
|
const streamButton = new ButtonBuilder()
|
||||||
|
.setLabel('🎥 Watch Stream')
|
||||||
|
.setURL(streamURL)
|
||||||
|
.setStyle(5);
|
||||||
|
|
||||||
|
const row = new ActionRowBuilder()
|
||||||
|
.addComponents(streamButton);
|
||||||
|
return { embeds: [embed], components: [row] };
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
userStreamingEmbed
|
||||||
|
};
|
||||||
32
embeds/welcomeMember.js
Normal file
32
embeds/welcomeMember.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const { GuildMember, EmbedBuilder } = require("discord.js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {GuildMember} member
|
||||||
|
*/
|
||||||
|
|
||||||
|
function welcomeEmbed(member) {
|
||||||
|
let welcomeMessages = [
|
||||||
|
`Welcome to the server ${member.user.username}. We hope you enjoy your stay.`,
|
||||||
|
`We hope you brought pizza ${member.user.username}.`,
|
||||||
|
`Yippe.... Welcome to the server ${member.user.username}.`,
|
||||||
|
`A wild ${member.user.username} has appeared.`,
|
||||||
|
];
|
||||||
|
const randomMessage =
|
||||||
|
welcomeMessages[Math.floor(Math.random() * welcomeMessages.length)];
|
||||||
|
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.setTitle("**New Member**")
|
||||||
|
.setDescription(randomMessage)
|
||||||
|
.addFields(
|
||||||
|
{ name: "Joined at", value: `${member.joinedAt}`, inline: true },
|
||||||
|
{
|
||||||
|
name: "User Created at",
|
||||||
|
value: `${member.user.createdAt}`,
|
||||||
|
inline: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.setThumbnail(member.user.avatarURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { welcomeEmbed };
|
||||||
26
events/InteractionCreate/interactionCreate.js
Normal file
26
events/InteractionCreate/interactionCreate.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const { Events } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.InteractionCreate,
|
||||||
|
async execute(interaction) {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
|
const command = interaction.client.commands.get(interaction.commandName);
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await command.execute(interaction);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
if (interaction.replied || interaction.deferred) {
|
||||||
|
await interaction.followUp({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||||
|
} else {
|
||||||
|
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
33
events/guildBanAdd/chatlog.js
Normal file
33
events/guildBanAdd/chatlog.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { Events, GuildMember } = require("discord.js");
|
||||||
|
const { modLogEmbed } = require("../../embeds/modLogs");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.GuildBanAdd,
|
||||||
|
async execute(member) {
|
||||||
|
try {
|
||||||
|
const auditLogs = await member.guild.fetchAuditLogs({ type: 22 });
|
||||||
|
const banEntry = auditLogs.entries.first();
|
||||||
|
const modLogsChannel = member.guild.channels.cache.find(channel => channel.name === 'mod-logs');
|
||||||
|
|
||||||
|
if (!banEntry) return;
|
||||||
|
|
||||||
|
const { target, executor, reason } = banEntry;
|
||||||
|
|
||||||
|
if (!modLogsChannel) return;
|
||||||
|
|
||||||
|
// Call modLogEmbed and send the embed
|
||||||
|
const embed = modLogEmbed("ban", member, reason || "No reason provided");
|
||||||
|
|
||||||
|
if (embed) {
|
||||||
|
await modLogsChannel.send({ embeds: [embed] });
|
||||||
|
//console.log(`Ban logged: ${target.tag} banned by ${executor.tag}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
48
events/guildBanAdd/logtodb.js
Normal file
48
events/guildBanAdd/logtodb.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
const { Events, GuildMember } = require("discord.js");
|
||||||
|
const db = require("../../db");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {GuildMember} member
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.GuildBanAdd,
|
||||||
|
async execute(member) {
|
||||||
|
const auditLogs = await member.guild.fetchAuditLogs({ type: 22 });
|
||||||
|
const banEntry = auditLogs.entries.first();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (banEntry) {
|
||||||
|
const { reason, executor, target, createdAt } = banEntry;
|
||||||
|
|
||||||
|
const guildID = member.guild.id;
|
||||||
|
const guildName = member.guild.name;
|
||||||
|
const userID = target.id;
|
||||||
|
const userName = target.username;
|
||||||
|
const userTag = target.tag;
|
||||||
|
const avatarURL = target.displayAvatarURL({ dynamic: true });
|
||||||
|
const banDate = createdAt.toISOString();
|
||||||
|
const banReason = reason || "No reason provided";
|
||||||
|
const banExecutor = executor.tag;
|
||||||
|
|
||||||
|
await db.query(
|
||||||
|
"INSERT INTO bans (guild_ID, guild_Name, user_ID, username, user_Tag, avatar_URL, ban_Date, ban_Reason, ban_Executor) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||||
|
[
|
||||||
|
guildID,
|
||||||
|
guildName,
|
||||||
|
userID,
|
||||||
|
userName,
|
||||||
|
userTag,
|
||||||
|
avatarURL,
|
||||||
|
banDate,
|
||||||
|
banReason,
|
||||||
|
banExecutor,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("There was an error", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
33
events/guildBanRemove/chatlog.js
Normal file
33
events/guildBanRemove/chatlog.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { Events, GuildMember } = require("discord.js");
|
||||||
|
const { modLogEmbed } = require("../../embeds/modLogs");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.GuildBanRemove,
|
||||||
|
async execute(member) {
|
||||||
|
try {
|
||||||
|
const auditLogs = await member.guild.fetchAuditLogs({ type: 23 });
|
||||||
|
const banEntry = auditLogs.entries.first();
|
||||||
|
const modLogsChannel = member.guild.channels.cache.find(channel => channel.name === 'mod-logs');
|
||||||
|
|
||||||
|
if (!banEntry) return;
|
||||||
|
|
||||||
|
const { target, executor, reason } = banEntry;
|
||||||
|
|
||||||
|
if (!modLogsChannel) return;
|
||||||
|
|
||||||
|
// Call modLogEmbed and send the embed
|
||||||
|
const embed = modLogEmbed("unban", member, reason || "No reason provided");
|
||||||
|
|
||||||
|
if (embed) {
|
||||||
|
await modLogsChannel.send({ embeds: [embed] });
|
||||||
|
//console.log(`Ban logged: ${target.tag} banned by ${executor.tag}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
21
events/guildCreate/addGuildToDB.js
Normal file
21
events/guildCreate/addGuildToDB.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const { Guild, Events } = require('discord.js');
|
||||||
|
|
||||||
|
const db = require('../../db');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.GuildCreate,
|
||||||
|
async execute(guild) {
|
||||||
|
try {
|
||||||
|
await db.query('CREATE TABLE IF NOT EXISTS guilds (guild_id VARCHAR PRIMARY KEY, guild_name VARCHAR, created_at TIMESTAMP, guild_owner_id VARCHAR)');
|
||||||
|
const result = await db.query('SELECT * FROM guilds WHERE guild_id = $1', [guild.id]);
|
||||||
|
if (result.rows.length === 0) {
|
||||||
|
await db.query('INSERT INTO guilds (guild_id, guild_name, created_at, guild_owner_id) VALUES ($1, $2, $3, $4)', [guild.id, guild.name, guild.createdAt, guild.ownerId]);
|
||||||
|
console.log(`Added new guild to the database: ${guild.name}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Guild already exists in the database: ${guild.name}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding guild to the database:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
34
events/guildCreate/createChannel.js
Normal file
34
events/guildCreate/createChannel.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
const { Guild, Events, PermissionFlagsBits } = require('discord.js');
|
||||||
|
|
||||||
|
const db = require('../../db');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.GuildCreate,
|
||||||
|
async execute(guild) {
|
||||||
|
try {
|
||||||
|
await db.query('CREATE TABLE IF NOT EXISTS bot_channel (guild_id VARCHAR PRIMARY KEY, guild_name VARCHAR, channel_id VARCHAR, channel_name VARCHAR)');
|
||||||
|
const result = await db.query('SELECT * FROM bot_channel WHERE guild_id = $1', [guild.id]);
|
||||||
|
if (result.rows.length === 0) {
|
||||||
|
// Create Channel then add to database
|
||||||
|
const channel = await guild.channels.create({
|
||||||
|
name: 'BluBot',
|
||||||
|
type: 0,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.id,
|
||||||
|
allow: [PermissionFlagsBits.ViewChannel],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await db.query('INSERT INTO bot_channel (guild_id, guild_name, channel_id, channel_name) VALUES ($1, $2, $3, $4)', [guild.id, guild.name, channel.id, channel.name]);
|
||||||
|
//await db.query('INSERT INTO bot_channel (guild_id, guild_name, channel_id, channel_name) VALUES ($1, $2, $3, $4)', [guild.id, guild.name, guild.channels.id, guild.channels.name]);
|
||||||
|
console.log(`Added new guild to the database: ${guild.name}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Guild already exists in the database: ${guild.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('Error creating channel or adding to database:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
26
events/guildMemberAdd/logToDB.js
Normal file
26
events/guildMemberAdd/logToDB.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const { Events, GuildMember } = require("discord.js");
|
||||||
|
const db = require("../../db");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {GuildMember} member
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.GuildMemberAdd,
|
||||||
|
async execute(member) {
|
||||||
|
try {
|
||||||
|
const userID = member.id;
|
||||||
|
const userName = member.user.username;
|
||||||
|
const userTag = member.user.tag;
|
||||||
|
const avatarURL = member.displayAvatarURL({ dynamic: true });
|
||||||
|
|
||||||
|
await db.query(
|
||||||
|
"INSERT INTO members (user_ID, username, user_Tag, avatar_URL) VALUES ($1, $2, $3, $4) ON CONFLICT (user_ID) DO NOTHING",
|
||||||
|
[userID, userName, userTag, avatarURL]
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("There was an error", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
34
events/guildMemberAdd/sendWelcome.js
Normal file
34
events/guildMemberAdd/sendWelcome.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
const { GuildMember, Events } = require('discord.js')
|
||||||
|
const db = require('../../db')
|
||||||
|
const { welcomeEmbed } = require('../../embeds/welcomeMember')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {GuildMember} member
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.GuildMemberAdd,
|
||||||
|
async execute(member) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const embed = welcomeEmbed(member)
|
||||||
|
const result = await db.query(`SELECT channel_id, enabled FROM auto_welcome WHERE guild_id = $1`,
|
||||||
|
[member.guild.id])
|
||||||
|
|
||||||
|
const welcomeData = result.rows[0]
|
||||||
|
|
||||||
|
if (welcomeData && welcomeData.enabled) {
|
||||||
|
const welcomeChannel = member.guild.channels.cache.get(welcomeData.channel_id)
|
||||||
|
|
||||||
|
if (welcomeChannel) {
|
||||||
|
welcomeChannel.send({ embeds: [embed]})
|
||||||
|
} else {
|
||||||
|
console.error('Invalid channel id')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('There was an error in sendWelcome:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
events/messageCreate/messageCreate.js
Normal file
26
events/messageCreate/messageCreate.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const { Events, Message } = require('discord.js');
|
||||||
|
const db = require('../../db')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param { Message } message
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.MessageCreate,
|
||||||
|
async execute(message) {
|
||||||
|
const messageContent = message.content
|
||||||
|
const author = message.author.tag
|
||||||
|
const guildID = message.guild.id
|
||||||
|
if (!message.inGuild() || message.author.bot) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.query("INSERT INTO messages (message, author, guild_ID) VALUES ($1, $2, $3)", [
|
||||||
|
messageContent,
|
||||||
|
author,
|
||||||
|
guildID,
|
||||||
|
]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error saving message: ", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
events/presenceUpdate/checktwitchPres.js
Normal file
47
events/presenceUpdate/checktwitchPres.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
const { Guild, GuildMember, Events } = require('discord.js')
|
||||||
|
const db = require('../../db')
|
||||||
|
|
||||||
|
const { userStreamingEmbed } = require('../../embeds/userStreaming')
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.PresenceUpdate,
|
||||||
|
async execute(oldPresence, newPresence) {
|
||||||
|
|
||||||
|
//console.log('Presence Update:', newPresence);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Check if the user is streaming
|
||||||
|
if (!newPresence.activities) return;
|
||||||
|
const streamingActivity = newPresence.activities.find(activity => activity.type === 1);
|
||||||
|
if (!streamingActivity) return;
|
||||||
|
|
||||||
|
const user = newPresence.user;
|
||||||
|
|
||||||
|
if (streamingActivity)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
for (const [guildId, guild] of newPresence.client.guilds.cache) {
|
||||||
|
const member = await guild.members.fetch(user.id).catch(() => null);
|
||||||
|
if (!member) continue;
|
||||||
|
const result = await db.query("SELECT channel_id FROM bot_channel WHERE guild_id = $1", [guildId]);
|
||||||
|
if (result.rows.length === 0) continue;
|
||||||
|
const channelId = result.rows[0].channel_id;
|
||||||
|
const channel = guild.channels.cache.get(channelId);
|
||||||
|
|
||||||
|
if (channel && channel.isTextBased()) {
|
||||||
|
const { embeds, components } = userStreamingEmbed(user, streamingActivity);
|
||||||
|
await channel.send({
|
||||||
|
embeds,
|
||||||
|
components
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching user:', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
10
events/ready/ready.js
Normal file
10
events/ready/ready.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const { Events, PresenceUpdateStatus } = require('discord.js')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.ClientReady,
|
||||||
|
once: true,
|
||||||
|
execute(client) {
|
||||||
|
console.log(`Ready! Logged in as ${client.user.tag}`);
|
||||||
|
client.user.setStatus(PresenceUpdateStatus.Online)
|
||||||
|
},
|
||||||
|
};
|
||||||
41
events/ready/setActivity.js
Normal file
41
events/ready/setActivity.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const { Client, Guild, Events, ActivityType, PresenceUpdateStatus } = require('discord.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {Guild} guild
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.ClientReady,
|
||||||
|
async execute(client) {
|
||||||
|
const guildsCount = client.guilds.cache.size;
|
||||||
|
|
||||||
|
let status = [
|
||||||
|
{
|
||||||
|
name: "I am Live",
|
||||||
|
type: ActivityType.Watching,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Choo Choo 🚂",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Hippity Hoppity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `I am in ${guildsCount} Server(s)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Yippe 🐻'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
let random = Math.floor(Math.random() * status.length);
|
||||||
|
client.user.setActivity(status[random]);
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
|
};
|
||||||
0
events/voiceStateUpdate/checkUpdate.js
Normal file
0
events/voiceStateUpdate/checkUpdate.js
Normal file
73
index.js
Normal file
73
index.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path')
|
||||||
|
const {Client, Events, GatewayIntentBits, Collection, InteractionResponse } = require('discord.js');
|
||||||
|
require('dotenv').config();
|
||||||
|
const { connectDB, closeDB } = require('./db')
|
||||||
|
|
||||||
|
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildModeration, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildPresences ] });
|
||||||
|
|
||||||
|
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);
|
||||||
|
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||||
|
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.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadEvents(dir, client) {
|
||||||
|
const files = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const fullPath = path.join(dir, file.name);
|
||||||
|
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
// Recursively load events from subdirectories
|
||||||
|
loadEvents(fullPath, client);
|
||||||
|
} else if (file.isFile() && file.name.endsWith('.js')) {
|
||||||
|
// Load the event file
|
||||||
|
const event = require(fullPath);
|
||||||
|
|
||||||
|
if (event.once) {
|
||||||
|
client.once(event.name, (...args) => event.execute(...args));
|
||||||
|
} else {
|
||||||
|
client.on(event.name, (...args) => event.execute(...args));
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log(`Loaded event: ${event.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the function to load all events
|
||||||
|
const eventsPath = path.join(__dirname, 'events');
|
||||||
|
loadEvents(eventsPath, client);
|
||||||
|
|
||||||
|
|
||||||
|
connectDB().then(() => {
|
||||||
|
client.login(process.env.TOKEN).catch(console.error)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
console.log('Bot is shutting down...')
|
||||||
|
closeDB();
|
||||||
|
process.exit(0)
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGTERM', async () => {
|
||||||
|
console.log('Bot received termination signal...');
|
||||||
|
closeDB();
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
1545
package-lock.json
generated
Normal file
1545
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
package.json
Normal file
29
package.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "blubot",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"run": "node .",
|
||||||
|
"dev": "nodemon ."
|
||||||
|
},
|
||||||
|
"author": "Megabyte",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@discordjs/voice": "^0.17.0",
|
||||||
|
"@distube/ytdl-core": "^4.14.4",
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"discord-api-types": "^0.37.119",
|
||||||
|
"discord.js": "^14.18.0",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"ffmpeg": "^0.0.4",
|
||||||
|
"ffmpeg-static": "^5.2.0",
|
||||||
|
"libsodium-wrappers": "^0.7.15",
|
||||||
|
"nodemon": "^3.1.9",
|
||||||
|
"opusscript": "^0.0.8",
|
||||||
|
"pg": "^8.13.0",
|
||||||
|
"play-dl": "^1.9.7",
|
||||||
|
"undici": "^7.7.0",
|
||||||
|
"ytdl-core": "^4.11.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
171
playback.js
Normal file
171
playback.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
const { Client, Interaction, Member } = require("discord.js");
|
||||||
|
const {
|
||||||
|
joinVoiceChannel,
|
||||||
|
createAudioPlayer,
|
||||||
|
createAudioResource,
|
||||||
|
NoSubscriberBehavior,
|
||||||
|
AudioPlayerStatus,
|
||||||
|
} = require("@discordjs/voice");
|
||||||
|
const ytdl = require("@distube/ytdl-core");
|
||||||
|
const { nowPlayingEmbed } = require("./embeds/nowPlaying");
|
||||||
|
|
||||||
|
const queue = [];
|
||||||
|
const activeChannels = {};
|
||||||
|
let stopped = false;
|
||||||
|
let timeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {Interaction} interaction
|
||||||
|
*/
|
||||||
|
|
||||||
|
function resetAll(guildId) {
|
||||||
|
//console.log(`Resetting all: ${guildId}`)
|
||||||
|
queue.length = 0;
|
||||||
|
cancelTimer()
|
||||||
|
const connection = activeChannels[guildId]
|
||||||
|
if (connection) {
|
||||||
|
connection.destroy()
|
||||||
|
// console.log('Conection Destroyed')
|
||||||
|
}
|
||||||
|
delete activeChannels[guildId]
|
||||||
|
stopped = false
|
||||||
|
//console.log('All States reset')
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTimer(connection) {
|
||||||
|
console.log(`Disconnect timer started `);
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
connection.destroy();
|
||||||
|
}, 30_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelTimer() {
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
console.log("Disconnect timer stopped");
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function playFromQueue(
|
||||||
|
voiceChannel,
|
||||||
|
textChannel,
|
||||||
|
getMemeber,
|
||||||
|
videoUrl,
|
||||||
|
videoTitle
|
||||||
|
) {
|
||||||
|
if (queue.length === 0) {
|
||||||
|
if (textChannel && activeChannels[voiceChannel.id] === textChannel.id) {
|
||||||
|
textChannel.send("Queue has finished playing.");
|
||||||
|
delete activeChannels[voiceChannel.id];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("Current queue: ", queue)
|
||||||
|
|
||||||
|
const { interaction, link, songName } = queue[0];
|
||||||
|
|
||||||
|
try {
|
||||||
|
let connection = activeChannels[interaction.guild.id];
|
||||||
|
// Join the voice channel
|
||||||
|
|
||||||
|
if (!connection) {
|
||||||
|
connection = joinVoiceChannel({
|
||||||
|
channelId: voiceChannel.id,
|
||||||
|
guildId: interaction.guild.id,
|
||||||
|
adapterCreator: interaction.guild.voiceAdapterCreator,
|
||||||
|
});
|
||||||
|
activeChannels[interaction.guild.id] = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream audio using ytdl-core
|
||||||
|
const stream = ytdl(videoUrl, {
|
||||||
|
filter: "audioonly",
|
||||||
|
highWaterMark: 1 << 25, // Larger buffer size
|
||||||
|
quality: "highestaudio",
|
||||||
|
requestOptions: {
|
||||||
|
headers: { Connection: "keep-alive" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const resource = createAudioResource(stream, { inlineVolume: true });
|
||||||
|
resource.volume.setVolume(1); // Set volume to 1 (max)
|
||||||
|
|
||||||
|
const player = createAudioPlayer({
|
||||||
|
behaviors: {
|
||||||
|
noSubscriber: NoSubscriberBehavior.Play,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
player.play(resource);
|
||||||
|
connection.subscribe(player);
|
||||||
|
|
||||||
|
player.on(AudioPlayerStatus.Playing, () => {
|
||||||
|
if (startTimer) {
|
||||||
|
cancelTimer()
|
||||||
|
} else {
|
||||||
|
console.log('No timer running')
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
player.on(AudioPlayerStatus.Idle, () => {
|
||||||
|
//console.log('The audio player is idle.');
|
||||||
|
if (stopped) {
|
||||||
|
console.log("Playback was stopped by user");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.shift();
|
||||||
|
if (queue.length > 0) {
|
||||||
|
const nextsong = queue[0];
|
||||||
|
playFromQueue(
|
||||||
|
voiceChannel,
|
||||||
|
textChannel,
|
||||||
|
nextsong.getMember,
|
||||||
|
nextsong.link,
|
||||||
|
nextsong.songName
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
textChannel.send("The queue is empty.");
|
||||||
|
if (connection) {
|
||||||
|
startTimer(connection);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (interaction.replied || interaction.deferred) {
|
||||||
|
const embed = nowPlayingEmbed(queue);
|
||||||
|
await interaction.followUp({ embeds: [embed] });
|
||||||
|
} else {
|
||||||
|
const embed = nowPlayingEmbed(queue);
|
||||||
|
await interaction.reply({ embeds: [embed] });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle errors
|
||||||
|
player.on("error", (error) => {
|
||||||
|
console.error("Error with the audio player:", error);
|
||||||
|
interaction.followUp({
|
||||||
|
content: "There was an error playing the music.",
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in playback.js: ", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
playFromQueue,
|
||||||
|
queue,
|
||||||
|
cancelTimer,
|
||||||
|
setStopped: (value) => {
|
||||||
|
stopped = value;
|
||||||
|
},
|
||||||
|
getConnection: (guildId) => activeChannels[guildId], // Helper to get connection
|
||||||
|
resetAll,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user