diff --git a/.gitignore b/.gitignore index 2f75f8b..7f1e29d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Specific to this repository dist/ -data/ +data/* +!data/endpoints.json tmp/ test* !test/ diff --git a/data/endpoints.json b/data/endpoints.json new file mode 100644 index 0000000..69f15ac --- /dev/null +++ b/data/endpoints.json @@ -0,0 +1,31 @@ +{ + "sfw": { + "tickle": "/img/tickle", + "slap": "/img/slap", + "poke": "/img/poke", + "pat": "/img/pat", + "neko": "/img/neko", + "meow": "/img/meow", + "lizard": "/img/lizard", + "kiss": "/img/kiss", + "hug": "/img/hug", + "foxGirl": "/img/fox_girl", + "feed": "/img/feed", + "cuddle": "/img/cuddle", + "why": "/why", + "catText": "/cat", + "fact": "/fact", + "nekoGif": "/img/ngif", + "kemonomimi": "/img/kemonomimi", + "holo": "/img/holo", + "smug": "/img/smug", + "baka": "/img/baka", + "woof": "/img/woof", + "spoiler": "/spoiler", + "wallpaper": "/img/wallpaper", + "goose": "/img/goose", + "gecg": "/img/gecg", + "avatar": "/img/avatar", + "waifu": "/img/waifu" + } +} \ No newline at end of file diff --git a/src/commands/admin.ts b/src/commands/admin.ts index a8b9f56..ad34bc4 100644 --- a/src/commands/admin.ts +++ b/src/commands/admin.ts @@ -131,6 +131,23 @@ export default new Command({ await $.message.channel.bulkDelete(travMessages); }, }), + clear: new Command({ + description: "Clears a given amount of messages.", + usage: "", + run: "A number was not provided.", + number: new Command({ + description: "Amount of messages to delete.", + async run($: CommonLibrary): Promise { + $.message.delete(); + const fetched = await $.channel.messages.fetch({ + limit: $.args[0], + }); + /// @ts-ignore + $.channel.bulkDelete(fetched) + .catch((error: any) => $.channel.send(`Error: ${error}`)); + } + }) + }), eval: new Command({ description: 'Evaluate code.', usage: '', diff --git a/src/commands/fun/cookie.ts b/src/commands/fun/cookie.ts new file mode 100644 index 0000000..15f75e8 --- /dev/null +++ b/src/commands/fun/cookie.ts @@ -0,0 +1,50 @@ +import Command from '../../core/command'; +import { CommonLibrary } from '../../core/lib'; + +export default new Command({ + description: "Gives specified user a cookie.", + usage: "['all'/@user]", + run: ":cookie: Here's a cookie!", + any: new Command({ + async run($: CommonLibrary): Promise { + if ($.args[0] == "all") return $.channel.send(`${$.author} gave everybody a cookie!`) + } + }), + user: new Command({ + description: "User to give cookie to.", + async run($: CommonLibrary): Promise { + const sender = $.author; + const mention = $.message.mentions.users.first(); + if (!mention) return; + const cookies = [ + `has given <@${mention.id}> a chocolate chip cookie!`, + `has given <@${mention.id}> a soft homemade oatmeal cookie!`, + `has given <@${mention.id}> a plain, dry, old cookie. It was the last one in the bag. Gross.`, + `gives <@${mention.id}> a sugar cookie. What, no frosting and sprinkles? 0/10 would not touch.`, + `gives <@${mention.id}> a chocolate chip cookie. Oh wait, those are raisins. Bleck!`, + `gives <@${mention.id}> an enormous cookie. Poking it gives you more cookies. Weird.`, + `gives <@${mention.id}> a fortune cookie. It reads "Why aren't you working on any projects?"`, + `gives <@${mention.id}> a fortune cookie. It reads "Give that special someone a compliment"`, + `gives <@${mention.id}> a fortune cookie. It reads "Take a risk!"`, + `gives <@${mention.id}> a fortune cookie. It reads "Go outside."`, + `gives <@${mention.id}> a fortune cookie. It reads "Don't forget to eat your veggies!"`, + `gives <@${mention.id}> a fortune cookie. It reads "Do you even lift?"`, + `gives <@${mention.id}> a fortune cookie. It reads "m808 pls"`, + `gives <@${mention.id}> a fortune cookie. It reads "If you move your hips, you'll get all the ladies."`, + `gives <@${mention.id}> a fortune cookie. It reads "I love you."`, + `gives <@${mention.id}> a Golden Cookie. You can't eat it because it is made of gold. Dammit.`, + `gives <@${mention.id}> an Oreo cookie with a glass of milk!`, + `gives <@${mention.id}> a rainbow cookie made with love :heart:`, + `gives <@${mention.id}> an old cookie that was left out in the rain, it's moldy.`, + `bakes <@${mention.id}> fresh cookies, it smells amazing.`, + ]; + if (mention.id == sender.id) + return $.channel.send("You can't give yourself cookies!"); + $.channel.send( + `:cookie: <@${sender.id}> ` + + cookies[Math.floor(Math.random() * cookies.length)], + ); + + } + }) +}) \ No newline at end of file diff --git a/src/commands/fun/neko.ts b/src/commands/fun/neko.ts new file mode 100644 index 0000000..1fdcf05 --- /dev/null +++ b/src/commands/fun/neko.ts @@ -0,0 +1,26 @@ +/// @ts-nocheck +import { URL } from 'url' +import FileManager from '../../core/storage'; +import Command from '../../core/command'; +import { CommonLibrary, getContent } from '../../core/lib'; + +const endpoints = FileManager.read('endpoints'); + +export default new Command({ + description: 'Provides you with a random image with the selected argument.', + async run($: CommonLibrary): Promise { + console.log(endpoints.sfw) + $.channel.send(`Please provide an image type. Available arguments:\n\`[${Object.keys(endpoints.sfw).join(', ')}]\`.`) + }, + any: new Command({ + description: "Image type to send.", + async run($: CommonLibrary): Promise { + if (!$.args[0] in endpoints.sfw) + return $.channel.send("Couldn't find that endpoint!"); + let baseURL = 'https://nekos.life/api/v2'; + let url = new URL(`${baseURL}${endpoints.sfw[$.args[0]]}`); + const content = await getContent(url.toString()) + $.channel.send(content.url) + }, + }) +}); diff --git a/src/commands/fun/ok.ts b/src/commands/fun/ok.ts new file mode 100644 index 0000000..8fa0263 --- /dev/null +++ b/src/commands/fun/ok.ts @@ -0,0 +1,68 @@ +import Command from '../../core/command'; +import { CommonLibrary } from '../../core/lib'; + +export default new Command({ + description: "Sends random ok message.", + async run($: CommonLibrary): Promise { + const responses = [ + 'boomer', + 'zoomer', + 'the last generationer', + 'the last airbender', + 'fire nation', + 'fire lord', + 'guy fieri', + 'guy from final fight', + 'haggar', + 'Max Thunder from Streets of Rage 2', + 'police guy who fires bazookas', + 'Mr. X', + 'Leon Its Wrong If Its Not Ada Wong S. Kennedy.', + 'Jill', + 'JFK', + 'george bush', + 'obama', + 'the world', + 'copy of scott pilgrim vs the world', + 'ok', + 'ko', + 'Hot Daddy Venomous', + 'big daddy', + 'John Cena', + 'BubbleSpurJarJarBinks', + 'T-Series', + 'pewdiepie', + 'markiplier', + 'jacksepticeye', + 'vanossgaming', + 'miniladd', + 'Traves', + 'Wilbur Soot', + 'sootrhianna', + 'person with tiny ears', + 'anti-rabbit', + 'homo sapiens', + 'homo', + 'cute kitty', + 'ugly kitty', + 'sadness', + 'doomer', + 'gloomer', + 'bloomer', + 'edgelord', + 'weeb', + "m'lady", + 'Mr. Crabs', + 'hand', + 'lahoma', + 'big man', + 'fox', + 'pear', + 'cat', + 'large man', + ]; + $.channel.send( + 'ok ' + responses[Math.floor(Math.random() * responses.length)], + ); + } +}) \ No newline at end of file diff --git a/src/commands/fun/owoify.ts b/src/commands/fun/owoify.ts new file mode 100644 index 0000000..33be968 --- /dev/null +++ b/src/commands/fun/owoify.ts @@ -0,0 +1,12 @@ +/// @ts-nocheck +import Command from '../../core/command'; +import { CommonLibrary, getContent } from '../../core/lib'; + +export default new Command({ + description: 'OwO-ifies the input.', + async run($: CommonLibrary): Promise { + let url = new URL(`https://nekos.life/api/v2/owoify?text=${$.args.join(' ')}`); + const content = await getContent(url.toString()); + $.channel.send(content.owo); + }, +}); diff --git a/src/commands/utilities/desc.ts b/src/commands/utilities/desc.ts new file mode 100644 index 0000000..5b96306 --- /dev/null +++ b/src/commands/utilities/desc.ts @@ -0,0 +1,29 @@ +import Command from '../../core/command'; +import { CommonLibrary } from '../../core/lib'; + +export default new Command({ + description: "Renames current voice channel.", + usage: "", + async run($: CommonLibrary): Promise { + const voiceChannel = $.message.member?.voice.channel; + if (!voiceChannel) + return $.channel.send('You are not in a voice channel.'); + if (!$.guild?.me?.hasPermission('MANAGE_CHANNELS')) + return $.channel.send( + 'I am lacking the required permissions to perform this action.', + ); + if ($.args.length === 0) + return $.channel.send( + 'Please provide a new voice channel name.', + ); + const changeVC = $.guild.channels.resolve(voiceChannel.id); + $.channel + .send( + `Changed channel name from "${voiceChannel}" to "${$.args.join( + ' ', + )}".`, + ) + /// @ts-ignore + .then(changeVC?.setName($.args.join(' '))); + } +}) \ No newline at end of file diff --git a/src/commands/utilities/react.ts b/src/commands/utilities/react.ts new file mode 100644 index 0000000..d7135d4 --- /dev/null +++ b/src/commands/utilities/react.ts @@ -0,0 +1,68 @@ +import Command from '../../core/command'; +import { CommonLibrary } from '../../core/lib'; + +export default new Command({ + description: + 'Reacts to the a previous message in your place. You have to react with the same emote before the bot removes that reaction.', + usage: 'react ()', + async run($: CommonLibrary): Promise { + let target; + let distance = 1; + + if ($.args.length >= 2) { + const last = $.args[$.args.length - 1]; + + if (/\d{17,19}/g.test(last)) { + try { + target = await $.channel.messages.fetch(last); + } catch { + return $.channel.send( + `No valid message found by the ID \`${last}\`!`, + ); + } + $.args.pop(); + } + // The entire string has to be a number for this to match. Prevents leaCheeseAmerican1 from triggering this. + else if (/^\d+$/g.test(last)) { + distance = parseInt(last); + + if (distance >= 0 && distance <= 99) $.args.pop(); + else return $.channel.send('Your distance must be between 0 and 99!'); + } + } + + if (!target) { + // Messages are ordered from latest to earliest. + // You also have to add 1 as well because fetchMessages includes your own message. + target = ( + await $.message.channel.messages.fetch({ + limit: distance + 1, + }) + ).last(); + } + + let anyEmoteIsValid = false; + + for (const search of $.args) { + const emoji = $.client.emojis.cache.find( + (emoji) => emoji.name === search, + ); + + if (emoji) { + // Call the delete function only once to avoid unnecessary errors. + if (!anyEmoteIsValid && distance !== 0) $.message.delete(); + anyEmoteIsValid = true; + const reaction = await target?.react(emoji); + + // This part is called with a promise because you don't want to wait 5 seconds between each reaction. + + setTimeout(() => { + /// @ts-ignore + reaction.users.remove($.client.user); + }, 5000); + } + } + + if (!anyEmoteIsValid && !$.message.deleted) $.message.react('❓'); + }, +}); diff --git a/src/commands/utilities/say.ts b/src/commands/utilities/say.ts new file mode 100644 index 0000000..05695eb --- /dev/null +++ b/src/commands/utilities/say.ts @@ -0,0 +1,14 @@ +import Command from '../../core/command'; +import { CommonLibrary } from '../../core/lib'; + +export default new Command({ + description: "Repeats your message.", + usage: "", + run: "Please provide a message for me to say!", + any: new Command({ + description: "Message to repeat.", + async run($: CommonLibrary): Promise { + $.channel.send(`*${$.author} says:*\n${$.args.join(' ')}`); + } + }) +}) \ No newline at end of file diff --git a/src/core/lib.ts b/src/core/lib.ts index 5b61439..b30bcb5 100644 --- a/src/core/lib.ts +++ b/src/core/lib.ts @@ -16,6 +16,7 @@ import { Permissions, } from 'discord.js'; import chalk from 'chalk'; +import { get } from 'https'; import FileManager from './storage'; import { eventListeners } from '../events/messageReactionRemove'; import { client } from '../index'; @@ -438,6 +439,41 @@ export function formatBytes(bytes: any) { return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`; } +export function getContent(url: any) { + return new Promise((resolve, reject) => { + get( + url, + (res: { + resume?: any; + setEncoding?: any; + on?: any; + statusCode?: any; + }) => { + const { statusCode } = res; + if (statusCode !== 200) { + res.resume(); + reject(`Request failed. Status code: ${statusCode}`); + } + res.setEncoding('utf8'); + let rawData = ''; + res.on('data', (chunk: string) => { + rawData += chunk; + }); + res.on('end', () => { + try { + const parsedData = JSON.parse(rawData); + resolve(parsedData); + } catch (e) { + reject(`Error: ${e.message}`); + } + }); + }, + ).on('error', (err: { message: any }) => { + reject(`Error: ${err.message}`); + }); + }); +} + export interface GenericJSON { [key: string]: any; }