Skip to content

Commit

Permalink
Added playlists for play, meme command, help command and status command
Browse files Browse the repository at this point in the history
  • Loading branch information
Struck713 committed Sep 19, 2023
1 parent 4ffb535 commit 3145e30
Show file tree
Hide file tree
Showing 18 changed files with 288 additions and 101 deletions.
Binary file added meme.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 19 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 6 additions & 16 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { ActivityType, Client, Events, GatewayIntentBits } from "discord.js";
import { YOUTUBE, TOKEN, BRUH } from '../config.json';
import { Commands } from "./lib/command";
import { YOUTUBE, TOKEN } from '../config.json';
import { COMMANDS } from "./commands";
import { VoiceManager } from "./lib/voice";
import { StateManager } from "./lib/state";
import { BruhListener } from "./lib/listeners/bruh";

import YTDlpWrap from "yt-dlp-wrap";
import { Embeds } from "./lib/utils/embeds";
export const ytdl = new YTDlpWrap(YOUTUBE.BINARY);

export const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent ] });
export const voiceManager = new VoiceManager();
export const stateManager = new StateManager();
export const bruhManager = new BruhListener();

client.once(Events.ClientReady, async client => {
console.log(`Logged in as ${client.user.tag}`);
console.log(`| Registered ${Commands.length} commands!`);
console.log(`| Registered ${COMMANDS.length} commands!`);
console.log(`| Voice Manager Status: ${voiceManager ? "ONLINE" : "OFFLINE"}`);

client.user.setPresence({
Expand All @@ -31,22 +29,14 @@ client.once(Events.ClientReady, async client => {

client.on(Events.InteractionCreate, async interaction => {
if (interaction.isChatInputCommand()) {
const command = Commands.find(command => command.data.name === interaction.commandName);
const command = COMMANDS.find(command => command.data.name === interaction.commandName);
if (!command) {
interaction.followUp({ content: "An error has occurred." });
await Embeds.error(interaction, "Something went terribly wrong! Contact the developer.");
return;
}
await interaction.deferReply();
command.execute(client, interaction);
}
});

client.on(Events.MessageCreate, async message => {
if (message.author.bot) return;
let content = message.content.toLowerCase();
if (message.channelId === BRUH.MESSAGE && content === "bruh") stateManager.bruhCount++;
});

//deploy();

client.login(TOKEN);
20 changes: 20 additions & 0 deletions src/commands/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { SlashCommandBuilder } from "discord.js";
import { Command, deploy } from "../lib/command";
import { Embeds } from "../lib/utils/embeds";
import { COMMANDS } from ".";

const MY_SNOWFLAKE = "140520164629151744";

export const Deploy: Command = {
data: new SlashCommandBuilder()
.setName('deploy')
.setDescription('Deploys slash commands for the bot.'),
execute: async (client, interaction) => {
if (interaction.user.id === MY_SNOWFLAKE) {
deploy();
await Embeds.send(interaction, embed => embed
.setTitle("Deployed commands")
.setDescription(`${COMMANDS.length} commands have been deployed.`));
} else await Embeds.error(interaction, "You do not have permission to execute this command!");
},
}
17 changes: 17 additions & 0 deletions src/commands/help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SlashCommandBuilder } from "discord.js";
import { Command } from "../lib/command";
import { Embeds } from "../lib/utils/embeds";
import { Time } from "../lib/utils/misc";
import { COMMANDS } from ".";

export const Help: Command = {
data: new SlashCommandBuilder()
.setName('help')
.setDescription('Lists all the available commands.'),
execute: async (client, interaction) => {
await Embeds.send(interaction, embed => embed
.setTitle("Help")
.setDescription(`There's a lot I can do for you. Here is a list of the avaliable commands:`)
.addFields(COMMANDS.map(command => ({ name: `/${command.data.name}`, value: command.data.description, inline: true }))));
},
}
10 changes: 10 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Play } from "../commands/voice/play";
import { Skip } from "../commands/voice/skip"
import { Queue } from "../commands/voice/queue";
import { Stop } from "../commands/voice/stop";
import { Deploy } from "../commands/deploy";
import { Status } from "../commands/status";
import { Meme } from "./meme";
import { Help } from "./help";

export const COMMANDS = [ Help, Play, Skip, Queue, Stop, Meme, Status, Deploy ];
44 changes: 44 additions & 0 deletions src/commands/meme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { AttachmentBuilder, SlashCommandBuilder } from "discord.js";
import { Command } from "../lib/command";
import axios from "axios";
import { Embeds } from "../lib/utils/embeds";

export const Meme: Command = {
data: new SlashCommandBuilder()
.setName('meme')
.setDescription('Create a meme with top and bottom text.')
.addStringOption(option => option.setName("top").setRequired(true).setDescription("The text on the top of the meme."))
.addAttachmentOption(option => option.setName("image").setRequired(true).setDescription("The image to create a meme out of."))
.addStringOption(option => option.setName("bottom").setRequired(false).setDescription("The text on the bottom of the meme.")),
execute: async (client, interaction) => {
let top = interaction.options.get("top", true);
let bottom = interaction.options.get("bottom", false);
let { attachment } = interaction.options.get("image", true);

if (!attachment) {
await Embeds.error(interaction, "You must provide an image to generate a meme.");
return;
}

const res = await axios.post("https://api.memegen.link/images/custom",
{
"background": attachment.url,
"text": bottom ? [ top.value, bottom.value ] : [ top.value ],
"extension": "png",
"redirect": false
},
{
headers: {
"Content-Type": "application/json"
}
}
).catch(_ => null);

if (!res) {
await Embeds.error(interaction, "Failed to generate meme. (This probably was the APIs fault, not yours.)");
return;
}

interaction.editReply({ files: [ new AttachmentBuilder(res.data.url) ] });
},
}
14 changes: 0 additions & 14 deletions src/commands/ping.ts

This file was deleted.

22 changes: 22 additions & 0 deletions src/commands/status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { SlashCommandBuilder } from "discord.js";
import { Command } from "../lib/command";
import { Embeds } from "../lib/utils/embeds";
import { Time } from "../lib/utils/misc";
import { Style } from "../lib/utils/style";

const TIME_AT_START = Date.now();

export const Status: Command = {
data: new SlashCommandBuilder()
.setName('status')
.setDescription('Shows the current status for the bot.'),
execute: async (client, interaction) => {
await Embeds.send(interaction, embed => embed
.setTitle("Status")
.setDescription(`
Use \`/help\` to see all the commands. \n
Currently running version \`${Style.VERSION}\` using \`${Style.ENGINE_VERSION}\`.
${Style.NAME} has been online for \`${Time.latestTime(Date.now() - TIME_AT_START)}\`.
`));
},
}
67 changes: 63 additions & 4 deletions src/commands/voice/play.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { voiceManager } from "../../app";
import { Command } from "../../lib/command";
import { Embeds } from "../../lib/utils/embeds";
import YouTubeAPI, { YoutubeMetadata } from "../../lib/utils/youtube";
import { Text } from "../../lib/utils/misc";

export const Play: Command = {
data: new SlashCommandBuilder()
Expand Down Expand Up @@ -45,26 +46,84 @@ export const Play: Command = {
.setImage(metadata.getThumbnailUrl()));
}

let search = interaction.options.get("search", false);
const playPlaylist = async (metadata?: YoutubeMetadata[]) => {
if (!interaction.guild || !interaction.member) {
await Embeds.error(interaction, "You are not in a guild!");
return;
}

let user = await interaction.guild.members.cache.get(interaction.member.user.id);
if (!user || !user.voice || !user.voice.channel) {
await Embeds.error(interaction, "You are not in a voice channel!");
return;
}

if (!metadata || metadata.length === 0) {
await Embeds.error(interaction, `An invalid playlist was provided.`);
return;
}

let connection = voiceManager.get(interaction.guild.id);
if (connection) {
connection.play(...metadata);
await Embeds.send(interaction, embed => embed.setAuthor({ name: "Added to queue" })
.setTitle(`Playlist with ${Text.number(others.length, "song")}`)
.setThumbnail(metadata[0].getThumbnailUrl())
.addFields(metadata.map((metadata, index) =>
({
name: `${index}. ${metadata.getTitle()}`,
value: `by ${metadata.getAuthor()}`
}))));
return;
}

connection = voiceManager.join(user.voice.channel);
connection.play(...metadata);

let [ first, ...others ] = metadata;
await Embeds.send(interaction, embed => embed.setAuthor({ name: "Now Playing"})
.setTitle(first.getTitle())
.setURL(first.getUrl())
.setDescription(`
by ${first.getAuthor()}
Added ${Text.number(others.length, "other song")} to the queue:
`).addFields(others.map((other) =>
({
name: `${other.getTitle()}`,
value: `by ${other.getAuthor()}`
})))
.setImage(first.getThumbnailUrl())
);
}

let url = interaction.options.get("url", false);
if (url) {
if (YouTubeAPI.isPlaylist(url.value as string)) {
playPlaylist(await YouTubeAPI.getPlaylistMetadata(url.value as string));
return;
}

let metadata = await YouTubeAPI.getMetadata(url.value as string);
if (!metadata) {
await Embeds.error(interaction, `\`${url.value}\` is not a valid YouTube URL.`);
return;
}
await playMetadata(metadata);
} else if (search) {
return;
}

let search = interaction.options.get("search", false);
if (search) {
let metadata = await YouTubeAPI.search(search.value as string);
if (!metadata) {
await Embeds.error(interaction, `The search \`${search.value}\` did not return anything on YouTube.`);
return;
}
await playMetadata(metadata);
} else {
await Embeds.error(interaction, `Please specify either a search or valid YouTube URL.`);
return;
}

await Embeds.error(interaction, `Please specify either a search or valid YouTube URL.`);
return;
},
}
2 changes: 1 addition & 1 deletion src/commands/voice/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const Queue: Command = {
.setThumbnail(playing.getThumbnailUrl())
.addFields({ name: '\u200B', value: 'Next in the queue:' })

if (queue.length > 0) embed.addFields(queue.slice(0, Math.min(8, queue.length)).map((metadata, index) => ({ name: `${index + 2}. ${metadata.getTitle()}`, value: `by ${metadata.getAuthor()}` })));
if (queue.length > 0) embed.addFields(queue.slice(0, Math.min(9, queue.length)).map((metadata, index) => ({ name: `${index + 2}. ${metadata.getTitle()}`, value: `by ${metadata.getAuthor()}` })));
else embed.addFields({ name: 'There is nothing next in the queue.', value: '\u200B' })

await Embeds.send(interaction, () => embed);
Expand Down
14 changes: 4 additions & 10 deletions src/lib/command.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
import { CommandInteraction, Client, REST, Routes, SlashCommandBuilder } from "discord.js";
import { Ping } from "../commands/ping";
import { Play } from "../commands/voice/play";
import { Skip } from "../commands/voice/skip";
import { TOKEN, DEVELOPMENT } from '../../config.json';
import { Queue } from "../commands/voice/queue";
import { Stop } from "../commands/voice/stop";
import { COMMANDS } from "../commands";

export interface Command {
data: Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">,
execute: (client: Client, interaction: CommandInteraction) => void;
}

export const Commands: Command[] = [ Ping, Play, Skip, Queue, Stop ];

export const deploy = () => {
// Construct and prepare an instance of the REST module
const rest = new REST().setToken(TOKEN);

// and deploy your commands!
(async () => {
try {
console.log(`Started refreshing ${Commands.length} application (/) commands.`);
console.log(`Started refreshing ${COMMANDS.length} application (/) commands.`);

console.log(Commands.map(command => command.data.toJSON()))
console.log(COMMANDS.map(command => command.data.toJSON()))

// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands(DEVELOPMENT.APPLICATION_ID, DEVELOPMENT.GUILD_ID),
{ body: Commands.map(command => command.data) },
{ body: COMMANDS.map(command => command.data) },
) as any[];

console.log(`Successfully reloaded ${data.length} application (/) commands.`);
Expand Down
Loading

0 comments on commit 3145e30

Please sign in to comment.