mirror of
https://github.com/keanuplayz/TravBot-v3.git
synced 2024-08-15 02:33:12 +00:00
Implemented various ideas from backlog
This commit is contained in:
parent
dd6f04fb25
commit
20fb2135c7
16 changed files with 322 additions and 64 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -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.
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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
13
src/commands/fun/party.ts
Normal 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!"
|
||||
});
|
||||
}
|
||||
});
|
|
@ -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}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
34
src/commands/fun/vaporwave.ts
Normal file
34
src/commands/fun/vaporwave.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import {Command, NamedCommand} from "../../core";
|
||||
|
||||
const vaporwave = (() => {
|
||||
const map = new Map<string, string>();
|
||||
const vaporwave =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$%&'()*+,-./0123456789:;<=>?@[\]^_`{|}~ ";
|
||||
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 vaporwave.",
|
||||
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.");
|
||||
}
|
||||
})
|
||||
});
|
|
@ -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
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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...>",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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`.");
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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
9
src/defs/translate.d.ts
vendored
Normal 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
17
src/defs/urban.d.ts
vendored
Normal 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
92
src/defs/weather.d.ts
vendored
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue