Compare commits
No commits in common. "9be36e71998bd1159a2e9ff729343e33469bb509" and "a530b4ae8e9d24d1449648653eefbfb6fc08773b" have entirely different histories.
9be36e7199
...
a530b4ae8e
27 changed files with 618 additions and 37 deletions
|
@ -1,35 +1,15 @@
|
||||||
// import { Constants } from "oceanic.js";
|
// import { Constants } from "oceanic.js";
|
||||||
// import database from "../../utils/database.js";
|
// import database from "../../utils/database.js";
|
||||||
import * as collections from "../../utils/collections.js";
|
// import * as collections from "../../utils/collections.js";
|
||||||
import { htmlescape } from "../../utils/misc.js";
|
// import { random } from "../../utils/misc.js";
|
||||||
// import paginator from "../../utils/pagination/pagination.js";
|
// import paginator from "../../utils/pagination/pagination.js";
|
||||||
import * as help from "../../utils/help.js";
|
// import * as help from "../../utils/help.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
// const tips = ["You can change the bot's prefix using the prefix command.", "Image commands also work with images previously posted in that channel.", "You can use the tags commands to save things for later use.", "You can visit https://esmbot.net/help.html for a web version of this command list.", "You can view a command's aliases by putting the command name after the help command (e.g. help image).", "Parameters wrapped in [] are required, while parameters wrapped in {} are optional.", "esmBot is hosted and paid for completely out-of-pocket by the main developer. If you want to support development, please consider donating! https://patreon.com/TheEssem"];
|
// const tips = ["You can change the bot's prefix using the prefix command.", "Image commands also work with images previously posted in that channel.", "You can use the tags commands to save things for later use.", "You can visit https://esmbot.net/help.html for a web version of this command list.", "You can view a command's aliases by putting the command name after the help command (e.g. help image).", "Parameters wrapped in [] are required, while parameters wrapped in {} are optional.", "esmBot is hosted and paid for completely out-of-pocket by the main developer. If you want to support development, please consider donating! https://patreon.com/TheEssem"];
|
||||||
|
|
||||||
class HelpCommand extends Command {
|
class HelpCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
let html;
|
return { html: "<h1>There are no mrmBot Docs Yet</h1>In the meantime, please refer to https://esmbot.net/help.html" };
|
||||||
if (this.args.length !== 0) {
|
|
||||||
if (collections.commands.has(this.args[0].toLowerCase())) {
|
|
||||||
const command = collections.aliases.get(this.args[0].toLowerCase()) ?? this.args[0].toLowerCase();
|
|
||||||
const info = collections.info.get(command);
|
|
||||||
// TODO: room-specific prefix
|
|
||||||
const prefix = htmlescape(process.env.PREFIX);
|
|
||||||
html = `<h3><ins>mrmBot Help</ins></h3><h6><code>${prefix}${command}</code></h6>${htmlescape(info.description)}`
|
|
||||||
return { html: html }
|
|
||||||
}
|
|
||||||
if (help.categories[this.args[0].toLowerCase()]) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
html = `<h2>mrmBot Help</h2><table><tr><th>Command</th><th>Description</th></tr>`
|
|
||||||
for (const [command] of collections.commands) {
|
|
||||||
const description = collections.info.get(command).description;
|
|
||||||
html = html + `<tr><td>${command}</td><td>${description}</td></tr>`
|
|
||||||
}
|
|
||||||
html = html + "</table>"
|
|
||||||
return { html: html }
|
|
||||||
// return { html: "<h1>There are no mrmBot Docs Yet</h1>In the meantime, please refer to https://esmbot.net/help.html" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
60
commands/music/host.js
Normal file
60
commands/music/host.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import { players } from "../../utils/soundplayer.js";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class HostCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.connection) return "I haven't completely connected yet!";
|
||||||
|
if (this.connection.host !== this.author && !process.env.OWNER.split(",").includes(this.connection.host)) return "Only the current voice session host can choose another host!";
|
||||||
|
const input = this.options.user ?? this.args.join(" ");
|
||||||
|
if (input?.trim()) {
|
||||||
|
let user;
|
||||||
|
if (this.type === "classic") {
|
||||||
|
const getUser = this.message.mentions.users.length >= 1 ? this.message.mentions.users[0] : this.client.users.get(input);
|
||||||
|
if (getUser) {
|
||||||
|
user = getUser;
|
||||||
|
} else if (input.match(/^<?[@#]?[&!]?\d+>?$/) && input >= 21154535154122752n) {
|
||||||
|
try {
|
||||||
|
user = await this.client.rest.users.get(input);
|
||||||
|
} catch {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const userRegex = new RegExp(input.split(" ").join("|"), "i");
|
||||||
|
const member = this.client.users.find(element => {
|
||||||
|
return userRegex.test(element.username);
|
||||||
|
});
|
||||||
|
user = member;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user = input;
|
||||||
|
}
|
||||||
|
if (!user) return "I can't find that user!";
|
||||||
|
if (user.bot) return "This is illegal, you know.";
|
||||||
|
const member = this.guild.members.get(user.id);
|
||||||
|
if (!member) return "That user isn't in this server!";
|
||||||
|
const object = this.connection;
|
||||||
|
object.host = member.id;
|
||||||
|
players.set(this.guildID, object);
|
||||||
|
this.success = true;
|
||||||
|
return `🔊 ${member.mention} is the new voice channel host.`;
|
||||||
|
} else {
|
||||||
|
const member = this.guild.members.get(players.get(this.guild.id).host);
|
||||||
|
this.success = true;
|
||||||
|
return `🔊 The current voice channel host is **${member?.username}#${member?.discriminator}**.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static flags = [{
|
||||||
|
name: "user",
|
||||||
|
type: 6,
|
||||||
|
description: "The user you want the new host to be"
|
||||||
|
}];
|
||||||
|
static description = "Gets or changes the host of the current voice session";
|
||||||
|
static aliases = ["sethost"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HostCommand;
|
23
commands/music/loop.js
Normal file
23
commands/music/loop.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { players } from "../../utils/soundplayer.js";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class LoopCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.connection) return "I haven't completely connected yet!";
|
||||||
|
if (this.connection.host !== this.author && !this.member.permissions.has("MANAGE_CHANNELS")) return "Only the current voice session host can loop the music!";
|
||||||
|
const object = this.connection;
|
||||||
|
object.loop = !object.loop;
|
||||||
|
players.set(this.guild.id, object);
|
||||||
|
this.success = true;
|
||||||
|
return object.loop ? "🔊 The player is now looping." : "🔊 The player is no longer looping.";
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Loops the music";
|
||||||
|
static aliases = ["toggleloop", "repeat"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoopCommand;
|
48
commands/music/music.js
Normal file
48
commands/music/music.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import Command from "../../classes/command.js";
|
||||||
|
import { commands, aliases, info, categories } from "../../utils/collections.js";
|
||||||
|
|
||||||
|
// all-in-one music command
|
||||||
|
class MusicAIOCommand extends Command {
|
||||||
|
async run() {
|
||||||
|
let cmd = this.type === "classic" ? this.args[0] : this.optionsArray[0].name;
|
||||||
|
if (cmd === "music" || this.constructor.aliases.includes(cmd)) return "https://esmbot.net/robotdance.gif";
|
||||||
|
// await this.acknowledge();
|
||||||
|
if (this.type === "classic") {
|
||||||
|
this.origOptions.args.shift();
|
||||||
|
} else {
|
||||||
|
this.origOptions.interaction.data.options.raw = this.origOptions.interaction.data.options.raw[0].options;
|
||||||
|
}
|
||||||
|
if (aliases.has(cmd)) cmd = aliases.get(cmd);
|
||||||
|
if (commands.has(cmd) && info.get(cmd).category === "music") {
|
||||||
|
const command = commands.get(cmd);
|
||||||
|
const inst = new command(this.client, this.origOptions);
|
||||||
|
const result = await inst.run();
|
||||||
|
this.success = inst.success;
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
this.success = false;
|
||||||
|
return "That isn't a valid music command!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static postInit() {
|
||||||
|
this.flags = [];
|
||||||
|
for (const cmd of categories.get("music")) {
|
||||||
|
if (cmd === "music") continue;
|
||||||
|
const cmdInfo = info.get(cmd);
|
||||||
|
this.flags.push({
|
||||||
|
name: cmd,
|
||||||
|
type: 1,
|
||||||
|
description: cmdInfo.description,
|
||||||
|
options: cmdInfo.flags
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Handles music playback";
|
||||||
|
static aliases = ["m"];
|
||||||
|
static directAllowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MusicAIOCommand;
|
51
commands/music/nowplaying.js
Normal file
51
commands/music/nowplaying.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import format from "format-duration";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class NowPlayingCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.connection) return "I haven't completely connected yet!";
|
||||||
|
const player = this.connection.player;
|
||||||
|
if (!player) return "I'm not playing anything!";
|
||||||
|
const track = await player.node.rest.decode(player.track);
|
||||||
|
const parts = Math.floor((player.position / track.length) * 10);
|
||||||
|
this.success = true;
|
||||||
|
return {
|
||||||
|
embeds: [{
|
||||||
|
color: 16711680,
|
||||||
|
author: {
|
||||||
|
name: "Now Playing",
|
||||||
|
iconURL: this.client.user.avatarURL()
|
||||||
|
},
|
||||||
|
fields: [{
|
||||||
|
name: "ℹ️ Title",
|
||||||
|
value: track.title ? track.title : "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "🎤 Artist",
|
||||||
|
value: track.author ? track.author : "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "💬 Channel",
|
||||||
|
value: (this.guild.channels.get(this.member.voiceState.channelID) ?? await this.client.rest.channels.get(this.member.voiceState.channelID)).name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "🌐 Node",
|
||||||
|
value: player.node ? player.node.name : "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `${"▬".repeat(parts)}🔘${"▬".repeat(10 - parts)}`,
|
||||||
|
value: `${format(player.position)}/${track.isStream ? "∞" : format(track.length)}`
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Shows the currently playing song";
|
||||||
|
static aliases = ["playing", "np", "current"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NowPlayingCommand;
|
41
commands/music/play.js
Normal file
41
commands/music/play.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { play } from "../../utils/soundplayer.js";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
const prefixes = ["scsearch:", "spsearch:", "sprec:", "amsearch:", "dzsearch:", "dzisrc:"];
|
||||||
|
if (process.env.YT_DISABLED !== "true") prefixes.push("ytsearch:", "ytmsearch:");
|
||||||
|
|
||||||
|
class PlayCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
const input = this.options.query ?? this.args.join(" ");
|
||||||
|
if (!input && ((!this.message || this.message?.attachments.size <= 0))) {
|
||||||
|
this.success = false;
|
||||||
|
return "You need to provide what you want to play!";
|
||||||
|
}
|
||||||
|
let query = input ? input.trim() : "";
|
||||||
|
const attachment = this.type === "classic" ? this.message.attachments.first() : null;
|
||||||
|
if (query.startsWith("||") && query.endsWith("||")) {
|
||||||
|
query = query.substring(2, query.length - 2);
|
||||||
|
}
|
||||||
|
if (query.startsWith("<") && query.endsWith(">")) {
|
||||||
|
query = query.substring(1, query.length - 1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const url = new URL(query);
|
||||||
|
return play(this.client, url, { channel: this.channel, member: this.member, type: this.type, interaction: this.interaction }, true);
|
||||||
|
} catch {
|
||||||
|
const search = prefixes.some(v => query.startsWith(v)) ? query : !query && attachment ? attachment.url : (process.env.YT_DISABLED !== "true" ? `ytsearch:${query}` : `dzsearch:${query}`);
|
||||||
|
return play(this.client, search, { channel: this.channel, member: this.member, type: this.type, interaction: this.interaction }, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static flags = [{
|
||||||
|
name: "query",
|
||||||
|
type: 3,
|
||||||
|
description: "An audio search query or URL",
|
||||||
|
required: true
|
||||||
|
}];
|
||||||
|
static description = "Plays a song or adds it to the queue";
|
||||||
|
static aliases = ["p"];
|
||||||
|
static arguments = ["[url]"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlayCommand;
|
65
commands/music/queue.js
Normal file
65
commands/music/queue.js
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { request } from "undici";
|
||||||
|
import format from "format-duration";
|
||||||
|
import { nodes } from "../../utils/soundplayer.js";
|
||||||
|
import paginator from "../../utils/pagination/pagination.js";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class QueueCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) return "I don't have the `Embed Links` permission!";
|
||||||
|
const player = this.connection;
|
||||||
|
if (!player) return "I haven't completely connected yet!";
|
||||||
|
const node = nodes.filter((val) => val.name === player.player.node.name)[0];
|
||||||
|
const tracks = await request(`http://${node.url}/decodetracks`, { method: "POST", body: JSON.stringify(this.queue), headers: { authorization: node.auth, "content-type": "application/json" } }).then(res => res.body.json());
|
||||||
|
const trackList = [];
|
||||||
|
const firstTrack = tracks.shift();
|
||||||
|
for (const [i, track] of tracks.entries()) {
|
||||||
|
trackList.push(`${i + 1}. ${track.info.author !== "" ? track.info.author : "(blank)"} - **${track.info.title !== "" ? track.info.title : "(blank)"}** (${track.info.isStream ? "∞" : format(track.info.length)})`);
|
||||||
|
}
|
||||||
|
const pageSize = 5;
|
||||||
|
const embeds = [];
|
||||||
|
const groups = trackList.map((item, index) => {
|
||||||
|
return index % pageSize === 0 ? trackList.slice(index, index + pageSize) : null;
|
||||||
|
}).filter(Boolean);
|
||||||
|
if (groups.length === 0) groups.push("del");
|
||||||
|
for (const [i, value] of groups.entries()) {
|
||||||
|
embeds.push({
|
||||||
|
embeds: [{
|
||||||
|
author: {
|
||||||
|
name: "Queue",
|
||||||
|
iconURL: this.client.user.avatarURL()
|
||||||
|
},
|
||||||
|
color: 16711680,
|
||||||
|
footer: {
|
||||||
|
text: `Page ${i + 1} of ${groups.length}`
|
||||||
|
},
|
||||||
|
fields: [{
|
||||||
|
name: "🎶 Now Playing",
|
||||||
|
value: `${firstTrack.info.author !== "" ? firstTrack.info.author : "(blank)"} - **${firstTrack.info.title !== "" ? firstTrack.info.title : "(blank)"}** (${firstTrack.info.isStream ? "∞" : format(firstTrack.info.length)})`
|
||||||
|
}, {
|
||||||
|
name: "🔁 Looping?",
|
||||||
|
value: player.loop ? "Yes" : "No"
|
||||||
|
}, {
|
||||||
|
name: "🌐 Node",
|
||||||
|
value: player.player.node ? player.player.node.name : "Unknown"
|
||||||
|
}, {
|
||||||
|
name: "🗒️ Queue",
|
||||||
|
value: value !== "del" ? value.join("\n") : "There's nothing in the queue!"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (embeds.length === 0) return "There's nothing in the queue!";
|
||||||
|
this.success = true;
|
||||||
|
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Shows the current queue";
|
||||||
|
static aliases = ["q"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QueueCommand;
|
33
commands/music/remove.js
Normal file
33
commands/music/remove.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { queues } from "../../utils/soundplayer.js";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class RemoveCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.connection) return "I haven't completely connected yet!";
|
||||||
|
if (this.connection.host !== this.author && !process.env.OWNER.split(",").includes(this.connection.host)) return "Only the current voice session host can remove songs from the queue!";
|
||||||
|
const pos = parseInt(this.options.position ?? this.args[0]);
|
||||||
|
if (isNaN(pos) || pos > this.queue.length || pos < 1) return "That's not a valid position!";
|
||||||
|
const removed = this.queue.splice(pos, 1);
|
||||||
|
if (removed.length === 0) return "That's not a valid position!";
|
||||||
|
const track = await this.connection.player.node.rest.decode(removed[0]);
|
||||||
|
queues.set(this.guildID, this.queue);
|
||||||
|
this.success = true;
|
||||||
|
return `🔊 The song \`${track.title ? track.title : "(blank)"}\` has been removed from the queue.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static flags = [{
|
||||||
|
name: "position",
|
||||||
|
type: 4,
|
||||||
|
description: "The queue position you want to remove",
|
||||||
|
min_value: 1,
|
||||||
|
required: true
|
||||||
|
}];
|
||||||
|
static description = "Removes a song from the queue";
|
||||||
|
static aliases = ["rm"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RemoveCommand;
|
38
commands/music/seek.js
Normal file
38
commands/music/seek.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class SeekCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.connection) return "I haven't completely connected yet!";
|
||||||
|
if (this.connection.host !== this.author) return "Only the current voice session host can seek the music!";
|
||||||
|
const player = this.connection.player;
|
||||||
|
const track = await player.node.rest.decode(player.track);
|
||||||
|
if (!track.isSeekable) return "This track isn't seekable!";
|
||||||
|
const pos = this.options.position ?? this.args[0];
|
||||||
|
let seconds;
|
||||||
|
if (typeof pos === "string" && pos.includes(":")) {
|
||||||
|
seconds = +(pos.split(":").reduce((acc, time) => (60 * acc) + +time));
|
||||||
|
} else {
|
||||||
|
seconds = parseFloat(pos);
|
||||||
|
}
|
||||||
|
if (isNaN(seconds) || (seconds * 1000) > track.length || (seconds * 1000) < 0) return "That's not a valid position!";
|
||||||
|
player.seekTo(seconds * 1000);
|
||||||
|
this.success = true;
|
||||||
|
return `🔊 Seeked track to ${seconds} second(s).`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static flags = [{
|
||||||
|
name: "position",
|
||||||
|
type: 3,
|
||||||
|
description: "Seek to this position",
|
||||||
|
required: true
|
||||||
|
}];
|
||||||
|
static description = "Seeks to a different position in the music";
|
||||||
|
static aliases = ["pos"];
|
||||||
|
static arguments = ["[seconds]"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SeekCommand;
|
23
commands/music/shuffle.js
Normal file
23
commands/music/shuffle.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { players } from "../../utils/soundplayer.js";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class ShuffleCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.connection) return "I haven't completely connected yet!";
|
||||||
|
if (this.connection.host !== this.author) return "Only the current voice session host can shuffle the music!";
|
||||||
|
const object = this.connection;
|
||||||
|
object.shuffle = !object.shuffle;
|
||||||
|
players.set(this.guildID, object);
|
||||||
|
this.success = true;
|
||||||
|
return object.shuffle ? "🔊 The player is now shuffling." : "🔊 The player is no longer shuffling.";
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Shuffles the music";
|
||||||
|
static aliases = ["toggleshuffle"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ShuffleCommand;
|
41
commands/music/skip.js
Normal file
41
commands/music/skip.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { skipVotes } from "../../utils/soundplayer.js";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class SkipCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
const player = this.connection;
|
||||||
|
if (!player) return "I haven't completely connected yet!";
|
||||||
|
if (player.host !== this.author && !this.member.permissions.has("MANAGE_CHANNELS")) {
|
||||||
|
const votes = skipVotes.get(this.guild.id) ?? { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) };
|
||||||
|
if (votes.ids.includes(this.author)) return "You've already voted to skip!";
|
||||||
|
const newObject = {
|
||||||
|
count: votes.count + 1,
|
||||||
|
ids: [...votes.ids, this.author].filter(item => !!item),
|
||||||
|
max: votes.max
|
||||||
|
};
|
||||||
|
if (votes.count + 1 === votes.max) {
|
||||||
|
await player.player.stopTrack(this.guild.id);
|
||||||
|
skipVotes.set(this.guild.id, { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) });
|
||||||
|
this.success = true;
|
||||||
|
if (this.type === "application") return "🔊 The current song has been skipped.";
|
||||||
|
} else {
|
||||||
|
skipVotes.set(this.guild.id, newObject);
|
||||||
|
this.success = true;
|
||||||
|
return `🔊 Voted to skip song (${votes.count + 1}/${votes.max} people have voted).`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await player.player.stopTrack();
|
||||||
|
this.success = true;
|
||||||
|
if (this.type === "application") return "🔊 The current song has been skipped.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Skips the current song";
|
||||||
|
static aliases = ["forceskip", "s"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SkipCommand;
|
28
commands/music/stop.js
Normal file
28
commands/music/stop.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { manager, players, queues } from "../../utils/soundplayer.js";
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class StopCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.connection) {
|
||||||
|
await manager.getNode().leaveChannel(this.guild.id);
|
||||||
|
this.success = true;
|
||||||
|
return "🔊 The current voice channel session has ended.";
|
||||||
|
}
|
||||||
|
if (this.connection.host !== this.author && !this.member.permissions.has("MANAGE_CHANNELS")) return "Only the current voice session host can stop the music!";
|
||||||
|
const connection = this.connection.player;
|
||||||
|
connection.node.leaveChannel(this.guild.id);
|
||||||
|
players.delete(this.guild.id);
|
||||||
|
queues.delete(this.guild.id);
|
||||||
|
this.success = true;
|
||||||
|
return `🔊 The voice channel session in \`${this.connection.voiceChannel.name}\` has ended.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Stops the music";
|
||||||
|
static aliases = ["disconnect"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StopCommand;
|
21
commands/music/toggle.js
Normal file
21
commands/music/toggle.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import MusicCommand from "../../classes/musicCommand.js";
|
||||||
|
|
||||||
|
class ToggleCommand extends MusicCommand {
|
||||||
|
async run() {
|
||||||
|
this.success = false;
|
||||||
|
if (!this.guild) return "This command only works in servers!";
|
||||||
|
if (!this.member.voiceState) return "You need to be in a voice channel first!";
|
||||||
|
if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!";
|
||||||
|
if (!this.connection) return "I haven't completely connected yet!";
|
||||||
|
if (this.connection.host !== this.author && !this.member.permissions.has("MANAGE_CHANNELS")) return "Only the current voice session host can pause/resume the music!";
|
||||||
|
const player = this.connection.player;
|
||||||
|
player.setPaused(!player.paused ? true : false);
|
||||||
|
this.success = true;
|
||||||
|
return `🔊 The player has been ${player.paused ? "paused" : "resumed"}.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Pauses/resumes the current song";
|
||||||
|
static aliases = ["pause", "resume"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToggleCommand;
|
9
commands/soundboard/boi.js
Normal file
9
commands/soundboard/boi.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class BoiCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/boi.ogg";
|
||||||
|
static description = "Plays the \"boi\" sound effect";
|
||||||
|
static aliases = ["boy", "neutron", "hugh"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BoiCommand;
|
9
commands/soundboard/boom.js
Normal file
9
commands/soundboard/boom.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class BoomCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/boom.ogg";
|
||||||
|
static description = "Plays the Vine boom sound effect";
|
||||||
|
static aliases = ["thud", "vine"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BoomCommand;
|
9
commands/soundboard/bruh.js
Normal file
9
commands/soundboard/bruh.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class BruhCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/bruh.ogg";
|
||||||
|
static description = "Plays the \"bruh\" sound effect";
|
||||||
|
static aliases = ["bro"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BruhCommand;
|
9
commands/soundboard/damndaniel.js
Normal file
9
commands/soundboard/damndaniel.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class DamnDanielCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/damndaniel.ogg";
|
||||||
|
static description = "Plays the \"damn daniel\" sound effect";
|
||||||
|
static aliases = ["daniel", "damn"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DamnDanielCommand;
|
8
commands/soundboard/explosion.js
Normal file
8
commands/soundboard/explosion.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class ExplosionCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/explosion.ogg";
|
||||||
|
static description = "Plays an explosion sound effect";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExplosionCommand;
|
9
commands/soundboard/fakeping.js
Normal file
9
commands/soundboard/fakeping.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class FakePingCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/ping.ogg";
|
||||||
|
static description = "Plays a Discord ping sound effect";
|
||||||
|
static aliases = ["notification", "notif"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FakePingCommand;
|
9
commands/soundboard/fart.js
Normal file
9
commands/soundboard/fart.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class FartCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/fart.ogg";
|
||||||
|
static description = "Plays a fart sound effect";
|
||||||
|
static aliases = ["toot"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FartCommand;
|
9
commands/soundboard/fartreverb.js
Normal file
9
commands/soundboard/fartreverb.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class FartReverbCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/fart2.ogg";
|
||||||
|
static description = "Plays a fart sound effect with extra reverb";
|
||||||
|
static aliases = ["fart2"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FartReverbCommand;
|
9
commands/soundboard/fbi.js
Normal file
9
commands/soundboard/fbi.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class FBICommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/fbi.ogg";
|
||||||
|
static description = "Plays the \"FBI OPEN UP\" sound effect";
|
||||||
|
static aliases = ["openup"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FBICommand;
|
9
commands/soundboard/mail.js
Normal file
9
commands/soundboard/mail.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class MailCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/mail.ogg";
|
||||||
|
static description = "Plays the \"You've got mail\" sound effect";
|
||||||
|
static aliases = ["yougotmail", "youvegotmail", "aol"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MailCommand;
|
9
commands/soundboard/oof.js
Normal file
9
commands/soundboard/oof.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class OofCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/oof.ogg";
|
||||||
|
static description = "Plays the Roblox \"oof\" sound";
|
||||||
|
static aliases = ["roblox", "commitdie"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OofCommand;
|
35
commands/soundboard/soundboard.js
Normal file
35
commands/soundboard/soundboard.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { play } from "../../utils/soundplayer.js";
|
||||||
|
import Command from "../../classes/command.js";
|
||||||
|
import { sounds, info } from "../../utils/collections.js";
|
||||||
|
|
||||||
|
// all-in-one soundboard command
|
||||||
|
class SoundboardAIOCommand extends Command {
|
||||||
|
async run() {
|
||||||
|
const soundName = this.type === "classic" ? this.args[0] : this.optionsArray[0].name;
|
||||||
|
if (!sounds.has(soundName)) {
|
||||||
|
this.success = false;
|
||||||
|
return "You need to provide a sound to play!";
|
||||||
|
}
|
||||||
|
const name = sounds.get(soundName);
|
||||||
|
// await this.acknowledge();
|
||||||
|
return await play(this.client, name, { channel: this.channel, member: this.member, type: this.type, interaction: this.interaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static postInit() {
|
||||||
|
this.flags = [];
|
||||||
|
for (const sound of sounds.keys()) {
|
||||||
|
this.flags.push({
|
||||||
|
name: sound,
|
||||||
|
type: 1,
|
||||||
|
description: info.get(sound).description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static description = "Plays a sound effect";
|
||||||
|
static aliases = ["sound", "sb"];
|
||||||
|
static directAllowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SoundboardAIOCommand;
|
9
commands/soundboard/winxp.js
Normal file
9
commands/soundboard/winxp.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import SoundboardCommand from "../../classes/soundboardCommand.js";
|
||||||
|
|
||||||
|
class WinXPCommand extends SoundboardCommand {
|
||||||
|
static file = "./assets/audio/winxp.ogg";
|
||||||
|
static description = "Plays the Windows XP startup sound";
|
||||||
|
static aliases = ["windows", "xp"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WinXPCommand;
|
|
@ -20,8 +20,6 @@ const optionalReplace = (token) => {
|
||||||
return token === undefined || token === "" ? "" : (token === "true" || token === "false" ? token : "<redacted>");
|
return token === undefined || token === "" ? "" : (token === "true" || token === "false" ? token : "<redacted>");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// clean(text) to clean message of any private info or mentions
|
// clean(text) to clean message of any private info or mentions
|
||||||
export function clean(text) {
|
export function clean(text) {
|
||||||
if (typeof text !== "string")
|
if (typeof text !== "string")
|
||||||
|
@ -65,17 +63,6 @@ export async function activityChanger(bot) {
|
||||||
setTimeout(() => activityChanger(bot), 900000);
|
setTimeout(() => activityChanger(bot), 900000);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function htmlescape (s) {
|
|
||||||
let lookup = {
|
|
||||||
'&': "&",
|
|
||||||
'"': """,
|
|
||||||
'\'': "'",
|
|
||||||
'<': "<",
|
|
||||||
'>': ">"
|
|
||||||
};
|
|
||||||
return s.replace( /[&"'<>]/g, c => lookup[c] );
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function checkBroadcast(bot) {
|
export async function checkBroadcast(bot) {
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in a new issue