initial commit
This commit is contained in:
commit
43b755e649
|
@ -0,0 +1,2 @@
|
||||||
|
/.git
|
||||||
|
/node_modules
|
|
@ -0,0 +1,130 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
|
@ -0,0 +1,16 @@
|
||||||
|
FROM node:18-alpine
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
RUN ["apk", "update"]
|
||||||
|
RUN ["apk", "add", "ffmpeg"]
|
||||||
|
RUN ["apk", "add", "yt-dlp"]
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY ["package.json", "./"]
|
||||||
|
|
||||||
|
RUN npm install --omit=dev
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["node", "."]
|
|
@ -0,0 +1,311 @@
|
||||||
|
const { Client, GatewayIntentBits } = require('discord.js');
|
||||||
|
const { DisTube, defaultFilters } = require('distube');
|
||||||
|
const { YtDlpPlugin } = require('@distube/yt-dlp');
|
||||||
|
/* const { SoundCloudPlugin } = require('@distube/soundcloud');
|
||||||
|
const { SpotifyPlugin } = require('@distube/spotify');
|
||||||
|
const { DeezerPlugin } = require('@distube/deezer'); */
|
||||||
|
const client = new Client({
|
||||||
|
intents: [
|
||||||
|
GatewayIntentBits.Guilds,
|
||||||
|
GatewayIntentBits.GuildVoiceStates,
|
||||||
|
GatewayIntentBits.MessageContent,
|
||||||
|
GatewayIntentBits.GuildMessages
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
prefix: '!!',
|
||||||
|
token: process.env.DISCORD_TOKEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a new DisTube
|
||||||
|
const distube = new DisTube(client, {
|
||||||
|
searchSongs: 0, // 5
|
||||||
|
searchCooldown: 0, // 30
|
||||||
|
leaveOnEmpty: false,
|
||||||
|
leaveOnFinish: false,
|
||||||
|
leaveOnStop: false,
|
||||||
|
|
||||||
|
plugins: [ new YtDlpPlugin({ update: true }) ],
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('ready', client => {
|
||||||
|
console.log(`Logged in as ${client.user.tag}!`);
|
||||||
|
});
|
||||||
|
// client.on("debug", console.log)
|
||||||
|
|
||||||
|
const checkQueue = message => {
|
||||||
|
if (typeof distube.getQueue(message) === 'undefined') {
|
||||||
|
message.channel.send('There is no queue');
|
||||||
|
return false;
|
||||||
|
} return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let formattedTimeToSeconds = str => {
|
||||||
|
var p = str.split(':'),
|
||||||
|
s = 0, m = 1;
|
||||||
|
|
||||||
|
while (p.length > 0) {
|
||||||
|
s += m * parseInt(p.pop(), 10);
|
||||||
|
m *= 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.on('messageCreate', message => {
|
||||||
|
if (message.author.bot || !message.inGuild()) return;
|
||||||
|
if (!message.content.startsWith(config.prefix)) return;
|
||||||
|
const args = message.content
|
||||||
|
.slice(config.prefix.length)
|
||||||
|
.trim()
|
||||||
|
.split(/ +/g);
|
||||||
|
const command = args.shift().toLowerCase();
|
||||||
|
if (command === 'play' || command === 'p') {
|
||||||
|
if (args.length < 1) {
|
||||||
|
message.channel.send('No song provided');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const voiceChannel = message.member?.voice?.channel;
|
||||||
|
if (voiceChannel) {
|
||||||
|
distube.play(voiceChannel, args.join(' '), {
|
||||||
|
message,
|
||||||
|
textChannel: message.channel,
|
||||||
|
member: message.member,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.channel.send(
|
||||||
|
'You must join a voice channel first.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['repeat', 'loop'].includes(command)) {
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
const mode = distube.setRepeatMode(message);
|
||||||
|
message.channel.send(
|
||||||
|
`Set repeat mode to \`${mode
|
||||||
|
? mode === 2
|
||||||
|
? 'All Queue'
|
||||||
|
: 'This Song'
|
||||||
|
: 'Off'
|
||||||
|
}\``,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'stop') {
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
distube.stop(message);
|
||||||
|
message.channel.send('Stopped the music!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'leave') {
|
||||||
|
distube.voices.get(message)?.leave();
|
||||||
|
message.channel.send('Left the voice channel!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['resume', 'unpause'].includes(command)) {
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
distube.resume(message);
|
||||||
|
message.channel.send(`Queue is now ${queue.paused ? 'paused' : 'unpaused'}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'pause') {
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
if (!queue.paused)
|
||||||
|
distube.pause(message);
|
||||||
|
else distube.resume(message);
|
||||||
|
message.channel.send(`Queue is now ${queue.paused ? 'paused' : 'unpaused'}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['skip', 's'].includes(command)) {
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
if (distube.getQueue(message).songs.length < 1) return message.channel.send('There is no song up next in queue');
|
||||||
|
distube.skip(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['queue', 'q'].includes(command)) {
|
||||||
|
const queue = distube.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
message.channel.send('Nothing playing right now!');
|
||||||
|
} else {
|
||||||
|
message.channel.send(
|
||||||
|
`Current queue:\n${queue.songs
|
||||||
|
.map(
|
||||||
|
(song, id) =>
|
||||||
|
`**${id ? id : 'Playing'}**. ${song.name
|
||||||
|
} - \`${song.formattedDuration}\``,
|
||||||
|
)
|
||||||
|
.slice(0, 10)
|
||||||
|
.join('\n')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['playing', 'nowplaying', 'np', 'current', 'cur'].includes(command)) {
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
let song = queue.songs[0];
|
||||||
|
message.channel.send(`Currently playing \`${song.name}\`\nDuration: \`${queue.formattedCurrentTime}\` / \`${queue.formattedDuration}\``);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['seek', 'jump', 'j'].includes(command)) {
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
if (args.length < 1) return message.channel.send('There is no time')
|
||||||
|
let seconds = formattedTimeToSeconds(args[0]);
|
||||||
|
if (isNaN(seconds)) return message.channel.send('Time is not valid')
|
||||||
|
queue.seek(seconds);
|
||||||
|
message.channel.send('Seeked');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'status') {
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
message.channel.send(status(queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['volume', 'vol'].includes(command)) {
|
||||||
|
let volume = parseInt(args[0]);
|
||||||
|
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
|
||||||
|
if (args.length < 1) {
|
||||||
|
message.channel.send(`Current volume: ${queue.volume}%`);
|
||||||
|
} else if (!isNaN(volume) && volume >= 0) {
|
||||||
|
queue.setVolume(volume);
|
||||||
|
message.channel.send(`New volume: ${queue.volume}%`);
|
||||||
|
} else {
|
||||||
|
message.channel.send('Invalid number');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'filter') {
|
||||||
|
const filters = [
|
||||||
|
'off',
|
||||||
|
'3d',
|
||||||
|
'bassboost',
|
||||||
|
'echo',
|
||||||
|
'karaoke',
|
||||||
|
'nightcore',
|
||||||
|
'vaporwave',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (args.length >= 1 && args.every(arg => filters.includes(arg))) {
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
|
||||||
|
if (args.includes('off'))
|
||||||
|
queue.filters.set([]);
|
||||||
|
else
|
||||||
|
queue.filters.set(args);
|
||||||
|
|
||||||
|
message.channel.send(
|
||||||
|
`Current queue filter: ${queue.filters.names.join(', ') || 'Off'}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
message.channel.send(`Available filters are ${filters.join(', ')}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['autoplay', 'ap', 'darrius', 'darriusmode'].includes(command)) {
|
||||||
|
let queue = distube.getQueue(message);
|
||||||
|
|
||||||
|
if (!checkQueue(message)) return;
|
||||||
|
|
||||||
|
queue.toggleAutoplay();
|
||||||
|
|
||||||
|
message.channel.send(`Autoplay is now ${queue.autoplay ? 'on' : 'off'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['restart'].includes(command)) {
|
||||||
|
if (false /*message.author.id !== '778441081883983893'*/) {
|
||||||
|
message.channel.send('no permission!')
|
||||||
|
} else {
|
||||||
|
message.channel.send('Restarting!!')
|
||||||
|
setTimeout(() => process.exit(1), 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Queue status template
|
||||||
|
const status = queue =>
|
||||||
|
`Volume: \`${queue.volume}%\` | Filter: \`${queue.filters.names.join(' ') || 'Off'
|
||||||
|
}\` | Loop: \`${queue.repeatMode
|
||||||
|
? queue.repeatMode === 2
|
||||||
|
? 'All Queue'
|
||||||
|
: 'This Song'
|
||||||
|
: 'Off'
|
||||||
|
}\` | Autoplay: \`${queue.autoplay ? 'On' : 'Off'}\``;
|
||||||
|
|
||||||
|
// DisTube event listeners, more in the documentation page
|
||||||
|
distube
|
||||||
|
.on('playSong', (queue, song) =>
|
||||||
|
queue.textChannel?.send(
|
||||||
|
`Playing \`${song.name}\` - \`${song.formattedDuration
|
||||||
|
}\`\nRequested by: ${song.user}\n${status(queue)}`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on('addSong', (queue, song) => {
|
||||||
|
if (queue.songs.length < 2) return;
|
||||||
|
queue.textChannel?.send(
|
||||||
|
`Added \`${song.name}\` - \`${song.formattedDuration}\` to the queue by ${song.user}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on('addList', (queue, playlist) =>
|
||||||
|
queue.textChannel?.send(
|
||||||
|
`Added \`${playlist.name}\` playlist (${playlist.songs.length
|
||||||
|
} songs) to queue\n${status(queue)}`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on('error', (textChannel, e) => {
|
||||||
|
console.error(e);
|
||||||
|
textChannel.send(
|
||||||
|
`An error encountered: ${e.message.slice(0, 2000)}`,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.on('finish', queue => queue.textChannel?.send('Finished queue!'))
|
||||||
|
.on('finishSong', queue =>
|
||||||
|
queue.textChannel?.send('Finished song!'),
|
||||||
|
)
|
||||||
|
.on('disconnect', queue =>
|
||||||
|
queue.textChannel?.send('Disconnected!'),
|
||||||
|
)
|
||||||
|
.on('empty', queue =>
|
||||||
|
queue.textChannel?.send(
|
||||||
|
'The voice channel is empty! Leaving the voice channel...',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// DisTubeOptions.searchSongs > 1
|
||||||
|
.on('searchResult', (message, result) => {
|
||||||
|
let i = 0;
|
||||||
|
message.channel.send(
|
||||||
|
`**Choose an option from below**\n${result
|
||||||
|
.map(
|
||||||
|
song =>
|
||||||
|
`**${++i}**. ${song.name} - \`${song.formattedDuration
|
||||||
|
}\``,
|
||||||
|
)
|
||||||
|
.join(
|
||||||
|
'\n',
|
||||||
|
)}\n*Enter anything else or wait 30 seconds to cancel*`,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.on('searchCancel', message =>
|
||||||
|
message.channel.send('Searching canceled'),
|
||||||
|
)
|
||||||
|
.on('searchInvalidAnswer', message =>
|
||||||
|
message.channel.send('Invalid number of result.'),
|
||||||
|
)
|
||||||
|
.on('searchNoResult', message =>
|
||||||
|
message.channel.send('No result found!'),
|
||||||
|
)
|
||||||
|
.on('searchDone', () => { });
|
||||||
|
|
||||||
|
client.login(config.token);
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "nodemusicbot",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@discordjs/opus": "^0.9.0",
|
||||||
|
"@discordjs/voice": "^0.16.0",
|
||||||
|
"@distube/deezer": "^1.0.0",
|
||||||
|
"@distube/soundcloud": "^1.3.0",
|
||||||
|
"@distube/spotify": "^1.5.1",
|
||||||
|
"@distube/yt-dlp": "^1.1.3",
|
||||||
|
"@distube/ytdl-core": "^4.11.9",
|
||||||
|
"discord.js": "^14.9.0",
|
||||||
|
"distube": "^4.0.5",
|
||||||
|
"libsodium-wrappers": "^0.7.11"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue