Skip to content

Commit

Permalink
Created 24/7 feature, added node storage in db
Browse files Browse the repository at this point in the history
todo: better support for sharding in big bots
  • Loading branch information
HiiZun committed Oct 24, 2024
1 parent ec68fc2 commit aebdffe
Show file tree
Hide file tree
Showing 12 changed files with 454 additions and 13 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ package-lock.json
*.lock
config.json
config.js
nodes.json
nodes.json
database.sqlite
87 changes: 82 additions & 5 deletions Base/Dancefloor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@ const {Riffy} = require("riffy");
const pkg = require("../package.json");
const nodes = require("../nodes.json");
const {Classic} = require("musicard")
const db = require("./db");


class Client extends DiscordClient {

constructor(...args) {
super(...args);
this.riffy;
this.db = db;
this.summoned247 = false;
this.initMusic();
this.registerSlashCommands();
this.registerEvents();
this.auth(process.env.TOKEN||"TOKEN");
}

async initMusic() {
this.riffy = new Riffy(this, require("../nodes.json"), {
let nodes = await db.nodes.findAll()
if(nodes.length === 0) {
return Logger.log("No nodes found in the database", "error");
}
this.riffy = new Riffy(this, nodes, {
send: (payload) => {
const guild = this.guilds.cache.get(payload.d.guild_id);
if (guild) guild.shard.send(payload);
Expand All @@ -32,17 +39,20 @@ class Client extends DiscordClient {
Logger.log(`Riffy Error: ${error}`, "error");
}).on('debug', (message) => {
// Logger.log(`Riffy Debug: ${message}`, "debug");
}).on('nodeConnect', (node) => {
}).on('nodeConnect', async (node) => {
Logger.log(`Riffy Node Connected: ${node}`, "debug");
}).on('nodeError', (node, error) => {
Logger.log(`Riffy Node Error: ${node} - ${error}`, "error");
}).on('nodeReconnect', (node) => {
Logger.log(`Riffy Node Reconnecting: ${node}`, "debug");
}).on('nodeDisconnect', (node, reason) => {
}).on('nodeDisconnect', async (node, reason) => {
Logger.log(`Riffy Node Disconnected: ${node} - ${reason.toString()}`, "warn");
}).on('trackStart', async (player, track) => {
Logger.log(`Riffy Track Start: ${track.info.author} - ${track.info.title}`, "debug");

if(player.is247)
return;

let {embed, file: buffer} = await this._getNPEmbed(player, track);

let channel = await this.channels.cache.get(player.textChannel);
Expand All @@ -61,6 +71,10 @@ class Client extends DiscordClient {
Logger.log(`Riffy Track End`, "debug");

// remove the message id from the track object
if(player.is247) {
console.log("24/7 mode ON, changing song")
return;
}

if(track.messageId) {
try {
Expand All @@ -83,6 +97,9 @@ class Client extends DiscordClient {
player.destroy();
}

if(player.is247)
return;

// send a message to the text channel
let channel = this.channels.cache.get(player.textChannel);
channel.send("Queue has ended.");
Expand All @@ -92,8 +109,8 @@ class Client extends DiscordClient {
}).on('playerResume', (player) => {
Logger.log(`Riffy Player Resume`, "debug");
}).on('playerUpdate', (player, state) => {
Logger.log(`Riffy Player Update`, "debug");

if(player.is247)
return;
if(Math.random() > 0.3) return;
if(player.current && player.current.messageId) {
console.log("Updating message")
Expand Down Expand Up @@ -122,6 +139,66 @@ class Client extends DiscordClient {
Logger.log(`Riffy Player Error: ${player.guild} - ${error}`, "error");
});

// Start periodic check for 24/7 players every 5 minutes (300,000 milliseconds)
setInterval(async () => {
if (!this.db || !this.db.alwaysplay) return;

try {
// Fetch all players in the DB
let players = await this.db.alwaysplay.findAll();

// Iterate over each player and ensure they are still playing
for (const player of players) {
const existingPlayer = this.riffy.players.get(player.guild_id);

if (!existingPlayer || !existingPlayer.connected) {
Logger.log(`Reconnecting 24/7 player for guild ${player.guild_id}`, "debug");

const guild = this.guilds.cache.get(player.guild_id);
if (!guild) {
await this.db.alwaysplay.destroy({
where: { guild_id: player.guild_id }
});
continue;
}

const channel = guild.channels.cache.get(player.channel_id);
if (!channel) {
await this.db.alwaysplay.destroy({
where: { guild_id: player.guild_id }
});
continue;
}

const connection = await this.riffy.createConnection({
guildId: guild.id,
voiceChannel: channel.id,
textChannel: channel.id,
deaf: true,
});

const resolve = await this.riffy.resolve({
query: player.song,
requester: this.user,
});

const { loadType, tracks } = resolve;
if (loadType === "search" || loadType === "track") {
const track = tracks.shift();
track.info.requester = this.user;
connection.queue.add(track);

connection.autoplaymode = true;
connection.is247 = true;

if (!connection.playing && !connection.paused) connection.play();
}
}
}
} catch (error) {
Logger.log(`Error while checking 24/7 players: ${error}`, "error");
}
}, 0.10*60*1000);

this.on("raw", (d) => this.riffy.updateVoiceState(d));
}
Expand Down
85 changes: 85 additions & 0 deletions Base/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const { Sequelize } = require('sequelize');
const logger = require('./Logger');

require('dotenv').config();

let sequelize;

if(process.env.NODE_ENV === 'development') {

sequelize = new Sequelize({
dialect: 'sqlite',
storage: './database.sqlite',
logging: (msg) => logger.log(msg, 'debug')
});

} else {

sequelize = new Sequelize({
dialect: 'mariadb',
host: process.env.DB_HOST,
port: process.env.DB_PORT || "3306",
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
logging: (msg) => logger.log(msg, 'debug')
});

}

sequelize.alwaysplay = sequelize.define('alwaysplay', {
guild_id: {
type: Sequelize.STRING,
allowNull: false
},
channel_id: {
type: Sequelize.STRING,
allowNull: false
},
song: {
type: Sequelize.STRING,
allowNull: false
}
});

sequelize.nodes = sequelize.define('nodes', {
name: {
type: Sequelize.STRING,
allowNull: false
},
host: {
type: Sequelize.STRING,
allowNull: false
},
port: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
secure: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true
}
});

try {
sequelize.authenticate().then(() => {
logger.log('Connection has been established successfully.', 'debug');


sequelize.sync().then(() => {
logger.log('All models were synchronized successfully.', 'log');
})

})

} catch (error) {
logger.log('Unable to connect to the database:' + error, 'error');
throw new Error('Unable to connect to the DB');
}

module.exports = sequelize;
88 changes: 88 additions & 0 deletions commands/247.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const { PermissionsBitField } = require('discord.js');
const Logger = require('../Base/Logger');

module.exports = {
data: new SlashCommandBuilder()
.setName('247')
.setDescription('Play music 24/7 in the voice channel based on a query that will be played and then autoplayed')
.addStringOption(option =>
option.setName('query')
.setDescription('The song you want to play')
.setRequired(true)
),
async execute(interaction) {

// check if the user is in a voice channel
if (!interaction.member.voice.channel) {
return await interaction.reply('You need to be in a voice channel to play music!');
}

// Check if the bot has permission to join the voice channel
const permissions = interaction.member.voice.channel.permissionsFor(interaction.client.user);
if (!permissions.has(PermissionsBitField.Flags.Connect) || !permissions.has(PermissionsBitField.Flags.Speak)) {
return await interaction.reply('I need the permissions to join and speak in your voice channel!');
}

// there should be no queue and no player in the guild
const testplayer = interaction.client.riffy.players.get(interaction.guild.id);
if (testplayer) return await interaction.reply('There is already a player in this server!');

// check if there are no 24/7 songs playing
const test = await interaction.client.db.alwaysplay.findAll({
where: {
guild_id: interaction.guild.id,
}
});
if (test.length > 0) return await interaction.reply('There is already a 24/7 music playing in this server! To remove it use /stop');


// get the query
const query = interaction.options.getString('query');


// create a player
const player = await interaction.client.riffy.createConnection({
guildId: interaction.guild.id,
voiceChannel: interaction.member.voice.channel.id,
textChannel: interaction.channel.id,
deaf: true,
});

// resolve the query
const resolve = await interaction.client.riffy.resolve({
query: query,
requester: interaction.user,
});

// get the tracks
const { loadType, tracks, playlistInfo } = resolve;

// it should not be a playlist
if (loadType === "playlist") return await interaction.reply('This command does not support playlists!');


// add the track to the queue
player.queue.add(tracks[0]);

// enable autoplay
player.autoplaymode = true;

player.is247 = true;

// play the music
if (!player.playing && !player.paused)
player.play();

// add to the db for 24/7
await interaction.client.db.alwaysplay.create({
guild_id: interaction.guild.id,
channel_id: interaction.member.voice.channel.id,
song: query,
})

return interaction.reply(`Playing 24/7 music in the voice channel: **${interaction.member.voice.channel.name}**`);


},
};
Loading

0 comments on commit aebdffe

Please sign in to comment.