diff --git a/commands.ts b/commands.ts new file mode 100644 index 0000000..15234b5 --- /dev/null +++ b/commands.ts @@ -0,0 +1,77 @@ +import type { Client, Message } from 'discord.js'; + +type Commands = { + [cmd: string]: (c: Client, e: Message) => void | Promise; +}; + +export const commands: Commands = { + '!ping': async (c, e) => { + await e.reply(`${c.ws.ping}ms`); + }, + + '!why': async (c, e) => { + await e.reply({ + embeds: [ + { + title: 'Why PolyMC exists', + description: + 'https://polymc.org/wiki/overview/faq/#why-did-our-community-choose-to-fork\nhttps://polymc.org/news/moving-on/', + color: 'GREYPLE', + }, + ], + }); + }, + + '!paths': async (c, e) => { + await e.reply({ + embeds: [ + { + title: 'Data directories', + description: 'Where PolyMC stores your data (e.g. instances)', + color: 'AQUA', + fields: [ + { + name: 'Portable (Windows / Linux)', + value: 'In the PolyMC folder', + }, + { + name: 'Windows', + value: '`%APPDATA%/PolyMC`', + }, + { + name: 'macOS', + value: '`~/Library/Application Support/PolyMC`', + }, + { name: 'Linux', value: '`~/.local/share/PolyMC`' }, + { + name: 'Flatpak', + value: '`~/.var/app/org.polymc.PolyMC/data/PolyMC`', + }, + ], + }, + ], + }); + }, + + '!cursed': async (c, e) => { + e.reply({ + embeds: [ + { + title: "What's wrong with CurseForge?", + description: ` +There is a new option to block third party clients from accessing mod files. CurseForge started to enforce the option for modders to disallow third-party applications like PolyMC and other launchers. + +We probably can't fully fix this. If you find out which mod is causing this, tell the modder to toggle that option. +`.trim(), + color: 'ORANGE', + }, + ], + }); + }, +}; + +export const aliases = { + '!curse': '!cursed', + '!curseforge': '!cursed', + '!diff': '!why', +}; diff --git a/constants.ts b/constants.ts new file mode 100644 index 0000000..08f6586 --- /dev/null +++ b/constants.ts @@ -0,0 +1,2 @@ +export const GUILD_ID = '977176004797804585'; +export const MAINTAINERS_CHANNEL_ID = '977176106568396851'; diff --git a/index.ts b/index.ts index 9c8118e..c067521 100644 --- a/index.ts +++ b/index.ts @@ -1,10 +1,10 @@ import { Client, Intents } from 'discord.js'; +import { commands, aliases } from './commands'; + +import * as BuildConfig from './constants'; +import Filter from 'bad-words'; import Koa from 'koa'; -const MUTED_ROLE = '976055433431240734'; - -// Health check server - { const app = new Koa(); @@ -19,58 +19,64 @@ const MUTED_ROLE = '976055433431240734'; } const client = new Client({ - intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES], -}); - -client.once('ready', async () => { - console.log('Discord bot ready!'); + intents: [ + Intents.FLAGS.GUILDS, + Intents.FLAGS.GUILD_MESSAGES, + Intents.FLAGS.DIRECT_MESSAGES, + Intents.FLAGS.GUILD_MEMBERS, + Intents.FLAGS.GUILD_MESSAGE_REACTIONS, + ], }); client.login(process.env.DISCORD_TOKEN); -client.on('messageCreate', async (e) => { - if (e.author === client.user) return; +client.once('ready', async () => { + console.log('Discord bot ready!'); + console.log('Invite link:', client.generateInvite({ scopes: ['bot'] })); - if ([...e.mentions.users.values()].includes(client.user)) { - e.reply({ - content: `What\'s up <@${e.author.id}>`, - allowedMentions: { - parse: ['users'], - repliedUser: true, + const POLYMC_GUILD = await client.guilds.fetch(BuildConfig.GUILD_ID); + const MAINTAINERS_CHANNEL = POLYMC_GUILD.channels.cache.get( + BuildConfig.MAINTAINERS_CHANNEL_ID + ); + + if (!MAINTAINERS_CHANNEL.isText()) throw new Error(); + MAINTAINERS_CHANNEL.send({ + embeds: [ + { + title: 'Started!', + description: new Date().toISOString(), + color: 'AQUA', }, - }); + ], + }); - return; - } + client.on('messageCreate', async (e) => { + if (!e.content) return; + if (!e.channel.isText()) return; + if (e.author.bot) return; + if (e.author === client.user) return; - if (e.author.id === '360401361856364544') { - e.react('975940717622984724'); - return; - } + const profane = new Filter().isProfane(e.content); - if ( - e.member.roles.cache.has('975975986250256424') && - e.content.startsWith('!!mute') - ) { - const [, , time, ...more] = e.content.split(' '); - if (more.length) { - e.reply('Too many arguments!'); - return; + if (profane) { + e.reply({ + embeds: [ + { + title: 'Profanity detected!', + description: 'Please try not to use these words 😄', + color: 'FUCHSIA', + }, + ], + }); } - const parsedTime = parseInt(time); - if (isNaN(parsedTime)) { - e.reply('Not a number (seconds)!'); - return; + const cmd = e.content.split(' ')[0]; + if (!cmd.startsWith('!')) return; + let func = commands[cmd]; + func = func ?? commands[aliases[cmd]]; + + if (func !== undefined) { + await func(client, e); } - - const member = e.mentions.members.at(0); - await member.roles.add(MUTED_ROLE); - - setTimeout(() => { - member.roles.remove(MUTED_ROLE); - }, parsedTime * 1000); - - e.reply('Done.'); - } + }); }); diff --git a/package.json b/package.json index ce5f64e..3922fa9 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,13 @@ "build": "esbuild index.ts --format=cjs --platform=node --bundle --minify --outfile=index.js" }, "dependencies": { + "bad-words": "^3.0.4", "discord.js": "^13.7.0", + "kleur": "^4.1.4", "koa": "^2.13.4" }, "devDependencies": { + "@types/bad-words": "^3.0.1", "@types/koa": "^2.13.4", "esbuild": "^0.14.39", "esno": "^0.16.3", diff --git a/yarn.lock b/yarn.lock index 4d894af..8125b2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -77,6 +77,11 @@ dependencies: "@types/node" "*" +"@types/bad-words@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/bad-words/-/bad-words-3.0.1.tgz#6a20fb08814af5e1f2428fa9e0098ca7b36f730a" + integrity sha512-7la3ZDJG1tlRqySO+pnXycZpacaMEw/iLEm8kc4l+I+jN8KjBfoQVwO6jm98xzXVLrxV8vDrB5TaMoop8sKclQ== + "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -250,6 +255,18 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +bad-words@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/bad-words/-/bad-words-3.0.4.tgz#044c83935c4c363a905d47b5e0179f7241fecaec" + integrity sha512-v/Q9uRPH4+yzDVLL4vR1+S9KoFgOEUl5s4axd6NIAq8SV2mradgi4E8lma/Y0cw1ltVdvyegCQQKffCPRCp8fg== + dependencies: + badwords-list "^1.0.0" + +badwords-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/badwords-list/-/badwords-list-1.0.0.tgz#5e9856dbf13482a295c3b0b304afb9d4cfc5c579" + integrity sha512-oWhaSG67e+HQj3OGHQt2ucP+vAPm1wTbdp2aDHeuh4xlGXBdWwzZ//pfu6swf5gZ8iX0b7JgmSo8BhgybbqszA== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -1086,6 +1103,11 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +kleur@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" + integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== + koa-compose@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877"