Slash commands for most fun commands (#45)

* Move few fun commands to interactions

* Move poll command to slash

* Move even more fun commands into interactions api

* Move the rest of fun commands to interactions

* Pull request review fixes
This commit is contained in:
smartfridge 2022-01-16 18:37:07 +01:00 committed by GitHub
parent 0bdb4514bc
commit 3c1642f05f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 336 additions and 38 deletions

View File

@ -1,5 +1,7 @@
import {NamedCommand, RestCommand} from "onion-lasers";
import {random} from "../../lib";
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
const responses = [
"Most likely,",
@ -23,7 +25,14 @@ const responses = [
"Outlook not so good,",
"Very doubtful,"
];
export const header = new SlashCommandBuilder()
.setDescription("Answers your question in an 8-ball manner.")
.addStringOption((option) =>
option.setName("question").setDescription("Question to ask the 8-ball.").setRequired(true)
);
export async function handler(interaction: CommandInteraction) {
interaction.reply(`${random(responses)} ${interaction.user.tag}`);
}
export default new NamedCommand({
description: "Answers your question in an 8-ball manner.",
usage: "<question>",

View File

@ -1,7 +1,8 @@
import {User} from "discord.js";
import {Command, NamedCommand} from "onion-lasers";
import {random, parseVars} from "../../lib";
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
const cookies = [
`has given %target% a chocolate chip cookie!`,
`has given %target% a soft homemade oatmeal cookie!`,
@ -25,6 +26,20 @@ const cookies = [
`bakes %target% fresh cookies, it smells amazing.`
];
export const header = new SlashCommandBuilder()
.setDescription("Gives specified user a cookie.")
.addUserOption((option) =>
option.setName("user").setDescription("User you want to give a cookie to.").setRequired(true)
);
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
return interaction.reply(
`:cookie: ${interaction.user.tag} ${parseVars(random(cookies), {
target: options.getUser("user", true).tag.toString()
})}`
);
}
export default new NamedCommand({
description: "Gives specified user a cookie.",
usage: "['all'/@user]",

View File

@ -1,7 +1,24 @@
import {NamedCommand, RestCommand} from "onion-lasers";
import figlet from "figlet";
import {Util} from "discord.js";
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
export const header = new SlashCommandBuilder()
.setDescription("Generates a figlet of your input.")
.addStringOption((option) =>
option.setName("text").setDescription("Text used to create the figlet.").setRequired(true)
);
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
return interaction.reply(
`\`\`\`\n${Util.cleanCodeBlockContent(
figlet.textSync(options.getString("text", true), {
horizontalLayout: "full"
})
)}\n\`\`\``
);
}
export default new NamedCommand({
description: "Generates a figlet of your input.",
run: "You have to provide input for me to create a figlet!",

View File

@ -1,5 +1,14 @@
import {NamedCommand} from "onion-lasers";
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
export const header = new SlashCommandBuilder().setDescription("Insult TravBot >:D");
export async function handler(interaction: CommandInteraction) {
interaction.reply(
`<@${interaction.user.id}> What the fuck did you just fucking say about me, you little bitch? I'll have you know I graduated top of my class in the Navy Seals, and I've been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I'm the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You're fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that's just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little "clever" comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn't, you didn't, and now you're paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You're fucking dead, kiddo.`
);
}
export default new NamedCommand({
description: "Insult TravBot! >:D",
async run({send, channel, author}) {

View File

@ -1,5 +1,13 @@
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
import {NamedCommand, CHANNEL_TYPE} from "onion-lasers";
export const header = new SlashCommandBuilder().setDescription("Chooses someone to love.");
export async function handler(interaction: CommandInteraction) {
const member = interaction.guild!.members.cache.random();
if (!member) return interaction.reply("For some reason, an error occurred fetching a member.");
return interaction.reply(`I love ${member.nickname ?? member.user.username}!`);
}
export default new NamedCommand({
description: "Chooses someone to love.",
channelType: CHANNEL_TYPE.GUILD,

View File

@ -1,5 +1,5 @@
import {Command, NamedCommand, confirm, poll} from "onion-lasers";
import {pluralise} from "../../../lib";
import {pluralise, parseDuration} from "../../../lib";
import {Storage} from "../../../structures";
import {isAuthorized, getMoneyEmbed} from "./eco-utils";
import {User} from "discord.js";
@ -167,30 +167,3 @@ export const BetCommand = new NamedCommand({
})
})
});
/**
* Parses a duration string into milliseconds
* Examples:
* - 3d -> 3 days -> 259200000ms
* - 2h -> 2 hours -> 7200000ms
* - 7m -> 7 minutes -> 420000ms
* - 3s -> 3 seconds -> 3000ms
*/
function parseDuration(duration: string): number {
// extract last char as unit
const unit = duration[duration.length - 1].toLowerCase();
// get the rest as value
let value: number = +duration.substring(0, duration.length - 1);
if (!["d", "h", "m", "s"].includes(unit) || isNaN(value)) return 0;
if (unit === "d") value *= 86400000;
// 1000ms * 60s * 60m * 24h
else if (unit === "h") value *= 3600000;
// 1000ms * 60s * 60m
else if (unit === "m") value *= 60000;
// 1000ms * 60s
else if (unit === "s") value *= 1000; // 1000ms
return value;
}

View File

@ -1,3 +1,5 @@
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
import {NamedCommand} from "onion-lasers";
import {random} from "../../lib";
@ -58,7 +60,10 @@ const responses = [
"cat",
"large man"
];
export const header = new SlashCommandBuilder().setDescription("Sends random ok message.");
export async function handler(interaction: CommandInteraction) {
interaction.reply(`ok ${random(responses)}`);
}
export default new NamedCommand({
description: "Sends random ok message.",
async run({send}) {

View File

@ -1,6 +1,25 @@
import {NamedCommand, RestCommand} from "onion-lasers";
import {random} from "../../lib";
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
export const header = new SlashCommandBuilder()
.setDescription("OwO-ifies the input.")
.addStringOption((option) => option.setName("text").setDescription("Text to owoify").setRequired(true));
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
const faces = ["owo", "UwU", ">w<", "^w^"];
const owoified = options
.getString("text", true)
.replace(/[rl]/g, "w")
.replace(/[RL]/g, "W")
.replace(/ove/g, "uv")
.replace(/n/g, "ny")
.replace(/N/g, "NY")
.replace(/\!/g, ` ${random(faces)} `);
interaction.reply(owoified);
}
export default new NamedCommand({
description: "OwO-ifies the input.",
run: "You need to specify some text to owoify.",

View File

@ -1,6 +1,23 @@
import {MessageAttachment, User} from "discord.js";
import {NamedCommand, Command, RestCommand, getUserByNickname} from "onion-lasers";
import petPetGif from "pet-pet-gif";
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
//Ravioli ravioli...
//number from 1 to 9
export const header = new SlashCommandBuilder()
.setDescription("Generates a pat GIF of the avatar of the mentioned user.")
.addUserOption((option) => option.setName("user").setDescription("User you want a pat gif of.").setRequired(true));
export async function handler(interaction: CommandInteraction) {
await interaction.reply("Generating pat gif...");
const {options} = interaction;
const pfp = options.getUser("user", true).displayAvatarURL({format: "png"});
const gif = await petPetGif(pfp);
const file = new MessageAttachment(gif, "pat.gif");
await interaction.editReply({content: "Here you go!", files: [file]});
}
export default new NamedCommand({
description: "Generates a pat GIF of the provided attachment image OR the avatar of the mentioned user.",

View File

@ -1,6 +1,23 @@
import {MessageEmbed, Message, User} from "discord.js";
import {MessageEmbed, Message, User, MessageActionRow, MessageButton} from "discord.js";
import {NamedCommand, RestCommand, poll, CHANNEL_TYPE, SendFunction, Command} from "onion-lasers";
import {pluralise} from "../../lib";
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
import {pluralise, parseDuration} from "../../lib";
export const header = new SlashCommandBuilder()
.setDescription("Create a poll.")
.addStringOption((option) => option.setName("question").setDescription("Question for the poll").setRequired(true))
.addIntegerOption((option) =>
option.setName("duration").setDescription("Duration of the poll in seconds.").setRequired(false)
);
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
const question = options.getString("question", true);
var duration = options.getInteger("duration", false);
duration = parseDuration(duration + "s"); //override the duration variable with miliseconds one
execSlashPoll(interaction, question, duration || 60000);
}
export default new NamedCommand({
description: "Create a poll.",
@ -27,6 +44,63 @@ export default new NamedCommand({
const AGREE = "✅";
const DISAGREE = "⛔";
async function execSlashPoll(interaction: CommandInteraction, question: string, duration = 60000) {
const responseButtons = new MessageActionRow().addComponents(
new MessageButton().setCustomId("agree").setLabel(AGREE).setStyle("SUCCESS"),
new MessageButton().setCustomId("disagree").setLabel(DISAGREE).setStyle("DANGER")
);
const icon =
interaction.user.avatarURL({
dynamic: true,
size: 2048
}) || interaction.user.defaultAvatarURL;
const embed = new MessageEmbed()
.setAuthor(`Poll created by ${interaction.user.username}`, icon)
.setColor(0xffffff)
.setFooter("Click the buttons to vote.")
.setDescription(question)
.addField(`${AGREE} `, `${pluralise(0, "", "people have voted")}\n`)
.addField(`${DISAGREE} `, `${pluralise(0, "", "people have voted")}\n`);
const msg = await interaction.reply({
embeds: [embed],
components: [responseButtons]
});
var idsArray: string[] = [];
const collector = interaction.channel?.createMessageComponentCollector({time: duration});
collector?.on("collect", async (i) => {
if (i.customId === "agree") {
if (idsArray.includes(i.user.id)) {
i.reply({content: "You have already voted!", ephemeral: true});
} else {
idsArray.push(i.user.id);
var agree = +1;
embed.fields[0].value = `${pluralise(agree, "", "people who agree", "person who agrees")}\n`;
interaction.editReply({embeds: [embed]});
i.reply({content: "You picked ✅!", ephemeral: true});
}
}
if (i.customId === "disagree") {
if (idsArray.includes(i.user.id)) {
i.reply({content: "You have already voted!", ephemeral: true});
} else {
idsArray.push(i.user.id);
var disagree = +1;
embed.fields[1].value = `${pluralise(disagree, "", "people who disagree", "person who disagrees")}\n`;
interaction.editReply({embeds: [embed]});
i.reply({content: "You picked ⛔!", ephemeral: true});
}
}
});
//This solution looks messy but works really well and stops from stuff like vote fraud happening.
//I'm not sure if it's the best solution but if you have a better idea then please let me know.
collector?.on("end", async (collected) => {
embed.setTitle(`The results of ${interaction.user.username}'s poll:`);
interaction.editReply({embeds: [embed]});
});
}
async function execPoll(send: SendFunction, message: Message, user: User, question: string, duration = 60000) {
const icon =
user.avatarURL({

View File

@ -1,6 +1,34 @@
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
import {Command, NamedCommand} from "onion-lasers";
import {Random} from "../../lib";
//Ravioli ravioli...
//number from 1 to 9
export const header = new SlashCommandBuilder()
.setDescription("Ravioli ravioli...")
.addIntegerOption((option) => option.setName("number").setDescription("Number from 1 to 9").setRequired(false));
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
try {
//get the number from the options if it fails fallback to the randomizer
var response = options.getInteger("number", true);
} catch (e) {
var response = Random.int(1, 10);
}
console.log(response);
interaction.reply({
embeds: [
{
title: "Ravioli ravioli...",
image: {
url: `https://raw.githubusercontent.com/keanuplayz/TravBot/master/assets/ravi${response}.png`
}
}
]
});
}
export default new NamedCommand({
description: "Ravioli ravioli...",
usage: "[number from 1 to 9]",

View File

@ -1,5 +1,6 @@
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
import {NamedCommand, RestCommand} from "onion-lasers";
const letters: {[letter: string]: string[]} = {
a: "aáàảãạâấầẩẫậăắằẳẵặ".split(""),
e: "eéèẻẽẹêếềểễệ".split(""),
@ -29,8 +30,22 @@ function transform(str: string) {
return out;
}
let phrase = "I have no currently set phrase!";
export const header = new SlashCommandBuilder()
.setDescription("Transforms your text into .")
.addStringOption((option) =>
option.setName("text").setDescription("The text you want to transform").setRequired(true)
);
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
const response = options.getString("text", true);
interaction.reply(transform(response));
// You might notice the remove message code is missing here. It's because reactions collectors are
//not a thing in interactions. The best alternative would be buttons
}
let phrase = "I have no currently set phrase!";
export default new NamedCommand({
description: "Transforms your text into .",
usage: "([text])",

View File

@ -1,7 +1,38 @@
import {SlashCommandBuilder} from "@discordjs/builders";
import {NamedCommand, RestCommand} from "onion-lasers";
import {MessageEmbed} from "discord.js";
import {MessageEmbed, CommandInteraction} from "discord.js";
import urban from "relevant-urban";
export const header = new SlashCommandBuilder()
.setDescription("Gives you a definition of the inputted word.")
.addStringOption((option) =>
option.setName("word").setDescription("The word you're looking for").setRequired(true)
);
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
await interaction.reply("Working on it....");
const response = options.getString("word", true);
// [Bug Fix]: Use encodeURIComponent() when emojis are used: "TypeError [ERR_UNESCAPED_CHARACTERS]: Request path contains unescaped characters"
urban(encodeURIComponent(response))
.then(async (res) => {
const embed = new MessageEmbed()
.setColor(0x1d2439)
.setTitle(res.word)
.setURL(res.urbanURL)
.setDescription(`**Definition:**\n*${res.definition}*\n\n**Example:**\n*${res.example}*`)
// [Bug Fix] When an embed field is empty (if the author field is missing, like the top entry for "british"): "RangeError [EMBED_FIELD_VALUE]: MessageEmbed field values may not be empty."
.addField("Author", res.author || "N/A", true)
.addField("Rating", `**\`Upvotes: ${res.thumbsUp} | Downvotes: ${res.thumbsDown}\`**`);
if (res.tags && res.tags.length > 0 && res.tags.join(" ").length < 1024)
embed.addField("Tags", res.tags.join(", "), true);
interaction.editReply("Here you go!");
await interaction.editReply({embeds: [embed]});
})
.catch(async () => {
await interaction.editReply("Sorry, that word was not found.");
});
}
export default new NamedCommand({
description: "Gives you a definition of the inputted word.",
run: "Please input a word.",

View File

@ -1,5 +1,6 @@
import {SlashCommandBuilder} from "@discordjs/builders";
import {CommandInteraction} from "discord.js";
import {NamedCommand, RestCommand} from "onion-lasers";
const vaporwave = (() => {
const map = new Map<string, string>();
const vaporwave =
@ -21,6 +22,17 @@ function getVaporwaveText(text: string): string {
return output;
}
export const header = new SlashCommandBuilder()
.setDescription("Transforms your text into .")
.addStringOption((option) =>
option.setName("text").setDescription("The text you want to .").setRequired(true)
);
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
const response = options.getString("text", true);
await interaction.reply(getVaporwaveText(response));
}
export default new NamedCommand({
description: "Transforms your text into .",
run: "You need to enter some text!",

View File

@ -1,7 +1,47 @@
import {NamedCommand, RestCommand} from "onion-lasers";
import {MessageEmbed} from "discord.js";
import {SlashCommandBuilder} from "@discordjs/builders";
import {MessageEmbed, CommandInteraction} from "discord.js";
import {find} from "weather-js";
export const header = new SlashCommandBuilder()
.setDescription("Shows weather info of specified location.")
.addStringOption((option) =>
option.setName("location").setDescription("The location you're looking for").setRequired(true)
);
export async function handler(interaction: CommandInteraction) {
const {options} = interaction;
await interaction.reply("Working on it....");
const response = options.getString("location", true);
find(
{
search: response,
degreeType: "C"
},
async function (error, result) {
if (error) return await interaction.editReply(error.toString());
if (result.length === 0) return await interaction.editReply("No city found by that name.");
var current = result[0].current;
var location = result[0].location;
const embed = new MessageEmbed()
.setDescription(`**${current.skytext}**`)
.setAuthor(`Weather for ${current.observationpoint}`)
.setThumbnail(current.imageUrl)
.setColor(0x00ae86)
.addField("Timezone", `UTC${location.timezone}`, true)
.addField("Degree Type", "C", true)
.addField("Temperature", `${current.temperature} Degrees`, true)
.addField("Feels like", `${current.feelslike} Degrees`, true)
.addField("Winds", current.winddisplay, true)
.addField("Humidity", `${current.humidity}%`, true);
interaction.editReply("Here you go!"); // remove the working on message
return await interaction.editReply({
embeds: [embed]
});
}
);
}
export default new NamedCommand({
description: "Shows weather info of specified location.",
run: "You need to provide a city.",

View File

@ -269,3 +269,29 @@ export function split<T>(array: T[], lengthOfEachSection: number): T[][] {
export function requireAllCasesHandledFor(variable: never): never {
throw new Error(`This function should never be called but got the value: ${variable}`);
}
/**
* Parses a duration string into milliseconds
* Examples:
* - 3d -> 3 days -> 259200000ms
* - 2h -> 2 hours -> 7200000ms
* - 7m -> 7 minutes -> 420000ms
* - 3s -> 3 seconds -> 3000ms
*/
export function parseDuration(duration: string): number {
// extract last char as unit
const unit = duration[duration.length - 1].toLowerCase();
// get the rest as value
let value: number = +duration.substring(0, duration.length - 1);
if (!["d", "h", "m", "s"].includes(unit) || isNaN(value)) return 0;
if (unit === "d") value *= 86400000;
// 1000ms * 60s * 60m * 24h
else if (unit === "h") value *= 3600000;
// 1000ms * 60s * 60m
else if (unit === "m") value *= 60000;
// 1000ms * 60s
else if (unit === "s") value *= 1000; // 1000ms
return value;
}