Implemented various ideas from backlog

This commit is contained in:
WatDuhHekBro 2021-04-08 06:37:49 -05:00
parent dd6f04fb25
commit 20fb2135c7
16 changed files with 322 additions and 64 deletions

View File

@ -1,3 +1,13 @@
# ???
- `vaporwave`: Transforms input into full-width text
- `eco post`: A play on `eco get`
- `admin set prefix <prefix> (<@bot>)`: Allows you to target a bot when setting a prefix if two bots have conflicting prefixes
- `party`: Sets the bot's status to streaming with a certain URL
- `eco award`: Awards users with Mons, only accessible by that person
- `thonk`: A result can now be discarded if the person who called the command reacts with ❌
- `scanemotes forcereset`: Removes the cooldown on `scanemotes`, only accessible by bot support and up
- `urban`: Bug fixes
# 3.2.0 - Internal refactor, more subcommand types, and more command type guards (2021-??-??)
- The custom logger changed: `$.log` no longer exists, it's just `console.log`. Now you don't have to do `import $ from "../core/lib"` at the top of every file that uses the custom logger.
- Utility functions are no longer attached to the command menu. Stuff like `$.paginate()` and `$(5).pluralise()` instead need to be imported and used as regular functions.

View File

@ -2,7 +2,7 @@ import {Command, NamedCommand, callMemberByUsername} from "../../core";
import {isAuthorized, getMoneyEmbed} from "./modules/eco-utils";
import {DailyCommand, PayCommand, GuildCommand, LeaderboardCommand} from "./modules/eco-core";
import {BuyCommand, ShopCommand} from "./modules/eco-shop";
import {MondayCommand} from "./modules/eco-extras";
import {MondayCommand, AwardCommand} from "./modules/eco-extras";
import {BetCommand} from "./modules/eco-bet";
export default new NamedCommand({
@ -18,7 +18,12 @@ export default new NamedCommand({
buy: BuyCommand,
shop: ShopCommand,
monday: MondayCommand,
bet: BetCommand
bet: BetCommand,
award: AwardCommand,
post: new NamedCommand({
description: "A play on `eco get`",
run: "`405 Method Not Allowed`"
})
},
id: "user",
user: new Command({

View File

@ -1,6 +1,8 @@
import {Command, NamedCommand} from "../../../core";
import {Storage} from "../../../structures";
import {isAuthorized, getMoneyEmbed} from "./eco-utils";
import {User} from "discord.js";
import {pluralise} from "../../../lib";
const WEEKDAY = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
@ -32,3 +34,45 @@ export const MondayCommand = new NamedCommand({
}
}
});
export const AwardCommand = new NamedCommand({
description: "Only usable by Mon, awards one or a specified amount of Mons to the user.",
usage: "<user> (<amount>)",
aliases: ["give"],
run: "You need to specify a user!",
user: new Command({
async run({message, channel, guild, author, member, client, args}) {
if (author.id === "394808963356688394" || IS_DEV_MODE) {
const target = args[0] as User;
const user = Storage.getUser(target.id);
user.money++;
Storage.save();
channel.send(`1 Mon given to ${target.username}.`, getMoneyEmbed(target));
} else {
channel.send("This command is restricted to the bean.");
}
},
number: new Command({
async run({message, channel, guild, author, member, client, args}) {
if (author.id === "394808963356688394" || IS_DEV_MODE) {
const target = args[0] as User;
const amount = Math.floor(args[1]);
if (amount > 0) {
const user = Storage.getUser(target.id);
user.money += amount;
Storage.save();
channel.send(
`${pluralise(amount, "Mon", "s")} given to ${target.username}.`,
getMoneyEmbed(target)
);
} else {
channel.send("You need to enter a number greater than 0.");
}
} else {
channel.send("This command is restricted to the bean.");
}
}
})
})
});

13
src/commands/fun/party.ts Normal file
View File

@ -0,0 +1,13 @@
import {Command, NamedCommand} from "../../core";
export default new NamedCommand({
description: "Initiates a celebratory stream from the bot.",
async run({message, channel, guild, author, member, client, args}) {
channel.send("This calls for a celebration!");
client.user!.setActivity({
type: "STREAMING",
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
name: "Celebration!"
});
}
});

View File

@ -36,6 +36,13 @@ export default new NamedCommand({
usage: "thonk ([text])",
async run({message, channel, guild, author, member, client, args}) {
if (args.length > 0) phrase = args.join(" ");
channel.send(transform(phrase));
const msg = await channel.send(transform(phrase));
msg.createReactionCollector(
(reaction, user) => {
if (user.id === author.id && reaction.emoji.name === "❌") msg.delete();
return false;
},
{time: 60000}
);
}
});

View File

@ -1,27 +1,31 @@
import {Command, NamedCommand} from "../../core";
import {MessageEmbed} from "discord.js";
// Anycasting Alert
const urban = require("relevant-urban");
import urban from "relevant-urban";
export default new NamedCommand({
description: "Gives you a definition of the inputted word.",
async run({message, channel, guild, author, member, client, args}) {
if (!args[0]) {
channel.send("Please input a word.");
run: "Please input a word.",
any: new Command({
async run({message, channel, guild, author, member, client, args}) {
// [Bug Fix]: Use encodeURIComponent() when emojis are used: "TypeError [ERR_UNESCAPED_CHARACTERS]: Request path contains unescaped characters"
urban(encodeURIComponent(args.join(" ")))
.then((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);
channel.send(embed);
})
.catch(() => {
channel.send("Sorry, that word was not found.");
});
}
const res = await urban(args.join(" ")).catch((e: Error) => {
return channel.send("Sorry, that word was not found.");
});
const embed = new MessageEmbed()
.setColor(0x1d2439)
.setTitle(res.word)
.setURL(res.urbanURL)
.setDescription(`**Definition:**\n*${res.definition}*\n\n**Example:**\n*${res.example}*`)
.addField("Author", res.author, true)
.addField("Rating", `**\`Upvotes: ${res.thumbsUp} | Downvotes: ${res.thumbsDown}\`**`);
if (res.tags.length > 0 && res.tags.join(" ").length < 1024) {
embed.addField("Tags", res.tags.join(", "), true);
}
channel.send(embed);
}
})
});

View File

@ -0,0 +1,34 @@
import {Command, NamedCommand} from "../../core";
const vaporwave = (() => {
const map = new Map<string, string>();
const vaporwave =
"_ ";
const normal = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\"#$%&'()*+,-./0123456789:;<=>?@[\\]^_`{|}~ ";
if (vaporwave.length !== normal.length) console.error("Vaporwave text failed to load properly!");
for (let i = 0; i < vaporwave.length; i++) map.set(normal[i], vaporwave[i]);
return map;
})();
function getVaporwaveText(text: string): string {
let output = "";
for (const c of text) {
const transformed = vaporwave.get(c);
if (transformed) output += transformed;
}
return output;
}
export default new NamedCommand({
description: "Transforms your text into .",
run: "You need to enter some text!",
any: new Command({
async run({message, channel, guild, author, member, client, args}) {
const text = getVaporwaveText(args.join(" "));
if (text !== "") channel.send(text);
else channel.send("Make sure to enter at least one valid character.");
}
})
});

View File

@ -1,36 +1,38 @@
import {Command, NamedCommand} from "../../core";
import {MessageEmbed} from "discord.js";
// Anycasting Alert
const weather = require("weather-js");
import {find} from "weather-js";
export default new NamedCommand({
description: "Shows weather info of specified location.",
async run({message, channel, guild, author, member, client, args}) {
if (args.length == 0) return channel.send("You need to provide a city.");
return weather.find(
{
search: args.join(" "),
degreeType: "C"
},
function (err: any, result: any) {
if (err) channel.send(err);
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);
channel.send({
embed
});
}
);
}
run: "You need to provide a city.",
any: new Command({
async run({message, channel, guild, author, member, client, args}) {
find(
{
search: args.join(" "),
degreeType: "C"
},
function (error, result) {
if (error) return channel.send(error.toString());
if (result.length === 0) return channel.send("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);
return channel.send({
embed
});
}
);
}
})
});

View File

@ -1,7 +1,7 @@
import {Command, NamedCommand, botHasPermission, getPermissionLevel, getPermissionName, CHANNEL_TYPE} from "../../core";
import {clean} from "../../lib";
import {Config, Storage} from "../../structures";
import {Permissions, TextChannel} from "discord.js";
import {Permissions, TextChannel, User} from "discord.js";
import {logs} from "../../modules/globals";
function getLogBuffer(type: string) {
@ -34,7 +34,7 @@ export default new NamedCommand({
subcommands: {
prefix: new NamedCommand({
description: "Set a custom prefix for your guild. Removes your custom prefix if none is provided.",
usage: "(<prefix>)",
usage: "(<prefix>) (<@bot>)",
async run({message, channel, guild, author, member, client, args}) {
Storage.getGuild(guild!.id).prefix = null;
Storage.save();
@ -47,7 +47,17 @@ export default new NamedCommand({
Storage.getGuild(guild!.id).prefix = args[0];
Storage.save();
channel.send(`The custom prefix for this guild is now \`${args[0]}\`.`);
}
},
user: new Command({
description: "Specifies the bot in case of conflicting prefixes.",
async run({message, channel, guild, author, member, client, args}) {
if ((args[1] as User).id === client.user!.id) {
Storage.getGuild(guild!.id).prefix = args[0];
Storage.save();
channel.send(`The custom prefix for this guild is now \`${args[0]}\`.`);
}
}
})
})
}),
welcome: new NamedCommand({

View File

@ -2,8 +2,9 @@ import {Command, NamedCommand} from "../../core";
import {processEmoteQueryFormatted} from "./modules/emote-utils";
export default new NamedCommand({
description: "Send the specified emote.",
run: "Please provide a command name.",
description:
"Send the specified emote list. Enter + to move an emote list to the next line, - to add a space, and _ to add a zero-width space.",
run: "Please provide a list of emotes.",
any: new Command({
description: "The emote(s) to send.",
usage: "<emotes...>",

View File

@ -76,6 +76,7 @@ function processEmoteQuery(query: string[], isFormatted: boolean): string[] {
if (isFormatted) {
if (emote == "-") return " ";
if (emote == "+") return "\n";
if (emote == "_") return "\u200b";
}
// Selector number used for disambiguating multiple emotes with same name.

View File

@ -182,5 +182,15 @@ export default new NamedCommand({
}
return await channel.send(lines, {split: true});
},
subcommands: {
forcereset: new NamedCommand({
description: "Forces the cooldown timer to reset.",
permission: PERMISSIONS.BOT_SUPPORT,
async run({message, channel, guild, author, member, client, args}) {
lastUsedTimestamps[guild!.id] = 0;
channel.send("Reset the cooldown on `scanemotes`.");
}
})
}
});

View File

@ -1,6 +1,5 @@
import {Command, NamedCommand} from "../../core";
// Anycasting Alert
const translate = require("translate-google");
import translate from "translate-google";
export default new NamedCommand({
description: "Translates your input.",
@ -11,7 +10,7 @@ export default new NamedCommand({
translate(input, {
to: lang
})
.then((res: any) => {
.then((res) => {
channel.send({
embed: {
title: "Translation",
@ -28,10 +27,10 @@ export default new NamedCommand({
}
});
})
.catch((err: any) => {
console.error(err);
.catch((error) => {
console.error(error);
channel.send(
`${err}\nPlease use the following list: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes`
`${error}\nPlease use the following list: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes`
);
});
}

9
src/defs/translate.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
interface TranslateOptions {
from?: string;
to?: string;
}
declare module "translate-google" {
function translate(input: string, options: TranslateOptions): Promise<string>;
export = translate;
}

17
src/defs/urban.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
interface Definition {
id: number;
word: string;
thumbsUp: number;
thumbsDown: number;
author: string;
urbanURL: string;
example: string;
definition: string;
tags: string[] | null;
sounds: string[] | null;
}
declare module "relevant-urban" {
function urban(query: string): Promise<Definition>;
export = urban;
}

92
src/defs/weather.d.ts vendored Normal file
View File

@ -0,0 +1,92 @@
interface WeatherJSOptions {
search: string;
lang?: string;
degreeType?: string;
timeout?: number;
}
interface WeatherJSResult {
location: {
name: string;
lat: string;
long: string;
timezone: string;
alert: string;
degreetype: string;
imagerelativeurl: string;
};
current: {
temperature: string;
skycode: string;
skytext: string;
date: string;
observationtime: string;
observationpoint: string;
feelslike: string;
humidity: string;
winddisplay: string;
day: string;
shortday: string;
windspeed: string;
imageUrl: string;
};
forecast: [
{
low: string;
high: string;
skycodeday: string;
skytextday: string;
date: string;
day: string;
shortday: string;
precip: string;
},
{
low: string;
high: string;
skycodeday: string;
skytextday: string;
date: string;
day: string;
shortday: string;
precip: string;
},
{
low: string;
high: string;
skycodeday: string;
skytextday: string;
date: string;
day: string;
shortday: string;
precip: string;
},
{
low: string;
high: string;
skycodeday: string;
skytextday: string;
date: string;
day: string;
shortday: string;
precip: string;
},
{
low: string;
high: string;
skycodeday: string;
skytextday: string;
date: string;
day: string;
shortday: string;
precip: string;
}
];
}
declare module "weather-js" {
const find: (
options: WeatherJSOptions,
callback: (error: Error | string | null, result: WeatherJSResult[]) => any
) => void;
}