big cleanup & use slash commands

This commit is contained in:
Ryan Cao 2022-08-24 18:32:10 +08:00
parent 386379b493
commit 01ce9ad000
No known key found for this signature in database
GPG key ID: 528A2C1B6656B97F
15 changed files with 526 additions and 541 deletions

11
src/_delete.ts Normal file
View file

@ -0,0 +1,11 @@
import { REST } from '@discordjs/rest';
import { Routes } from 'discord.js';
import 'dotenv/config';
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN!);
rest
.put(Routes.applicationCommands('977174139297230888'), { body: [] })
.then(() => console.log('Successfully deleted all application commands.'))
.catch(console.error);

45
src/_upload.ts Normal file
View file

@ -0,0 +1,45 @@
import { SlashCommandBuilder, Routes } from 'discord.js';
import { REST } from '@discordjs/rest';
import { getTags } from './tagsTags';
import 'dotenv/config';
(async () => {
const tags = await getTags();
const commands = [
new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with pong!'),
new SlashCommandBuilder()
.setName('stars')
.setDescription('Returns GitHub stargazer count'),
new SlashCommandBuilder()
.setName('members')
.setDescription('Returns the number of members in the server'),
new SlashCommandBuilder()
.setName('rolypoly')
.setDescription('Rooooooly Pooooooly'),
new SlashCommandBuilder()
.setName('tag')
.setDescription('Send a tag')
.addStringOption((option) =>
option
.setName('name')
.setDescription('The tag name')
.setRequired(true)
.addChoices(...tags.map((b) => ({ name: b.name, value: b.name })))
),
].map((command) => command.toJSON());
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN!);
await rest.put(Routes.applicationCommands('977174139297230888'), {
body: commands,
});
console.log('Successfully registered application commands.');
})().catch((e) => {
console.error(e);
process.exit(1);
});

View file

@ -1,37 +0,0 @@
import { FiltersEngine, Request } from '@cliqz/adblocker';
let engine: FiltersEngine;
const init = async () => {
if (engine) return;
console.log('initializing FiltersEngine');
engine = await FiltersEngine.fromLists(
fetch,
[
'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/badware.txt',
'https://malware-filter.gitlab.io/malware-filter/phishing-filter.txt',
],
{
enableInMemoryCache: true,
enableOptimizations: true,
enableCompression: true,
}
);
};
export const isBad = async (url: string) => {
await init();
const { match } = engine.match(
Request.fromRawDetails({
type: 'mainFrame',
url,
})
);
console.log('Testing URL', url, match);
return match;
};

View file

@ -1,36 +0,0 @@
import { EmbedBuilder } from 'discord.js';
import { commands } from '.';
import type { Command } from '..';
import { COLORS } from '../constants';
export const cmd: Command = {
name: 'help',
desc: 'Shows this menu.',
exec: async (e) => {
const embed = new EmbedBuilder()
.setTitle('Help Menu')
.setColor(COLORS.green);
const comman = commands;
comman.sort((x, y) => {
return x.name == 'help' ? -1 : y.name == 'help' ? 1 : 0;
});
for (const i in comman) {
const cmd = comman[i];
const resp = [];
if (cmd.desc) {
resp.push(cmd.desc);
}
if (cmd.aliases && cmd.aliases[0]) {
resp.push(`**Aliases**: ${cmd.aliases.join(', ')}`);
}
if (cmd.examples && cmd.examples[0]) {
resp.push(`**Examples**: \n${cmd.examples.join('\n> ')}`);
}
embed.addFields({ name: '!' + cmd.name, value: resp.join('\n') });
}
await e.reply({ embeds: [embed] });
},
};

View file

@ -1,7 +0,0 @@
import { cmd as help } from './help';
import { cmd as members } from './members';
import { cmd as ping } from './ping';
import { cmd as stars } from './stars';
import { cmd as tags } from './tags';
export const commands = [help, members, ping, stars, tags];

View file

@ -1,29 +1,25 @@
import type { Command } from '..';
import type { CacheType, CommandInteraction } from 'discord.js';
import { COLORS } from '../constants';
export const cmd: Command = {
name: 'members',
desc: 'Shows the amount of online users in PolyMC Discord',
aliases: ['mems', 'memcount'],
exec: async (e) => {
const memes = await e.guild?.members.fetch().then((r) => r.toJSON());
if (!memes) return;
export const membersCommand = async (i: CommandInteraction<CacheType>) => {
const memes = await i.guild?.members.fetch().then((r) => r.toJSON());
if (!memes) return;
await e.reply({
embeds: [
{
title: `${memes.length} total members!`,
description: `${
memes.filter(
(m) =>
m.presence?.status === 'online' ||
m.presence?.status === 'idle' ||
m.presence?.status === 'dnd'
).length
} online members`,
color: COLORS.blue,
},
],
});
},
await i.reply({
embeds: [
{
title: `${memes.length} total members!`,
description: `${
memes.filter(
(m) =>
m.presence?.status === 'online' ||
m.presence?.status === 'idle' ||
m.presence?.status === 'dnd'
).length
} online members`,
color: COLORS.blue,
},
],
});
};

View file

@ -1,10 +0,0 @@
import type { Command } from '..';
export const cmd: Command = {
name: 'ping',
desc: 'Shows the ping of the bot',
aliases: ['test'],
exec: async (e) => {
await e.reply(`${e.client.ws.ping}ms`);
},
};

View file

@ -1,21 +1,17 @@
import type { CacheType, CommandInteraction } from 'discord.js';
import { COLORS } from '../constants';
import type { Command } from '../index';
export const cmd: Command = {
name: 'stars',
desc: 'Shows the number of stars in PolyMC',
aliases: ['star', 'stargazers'],
exec: async (e) => {
const count = await fetch('https://api.github.com/repos/PolyMC/PolyMC')
.then((r) => r.json() as Promise<{ stargazers_count: number }>)
.then((j) => j.stargazers_count);
await e.reply({
embeds: [
{
title: `${count} total stars!`,
color: COLORS.yellow,
},
],
});
},
export const starsCommand = async (i: CommandInteraction<CacheType>) => {
const count = await fetch('https://api.github.com/repos/PolyMC/PolyMC')
.then((r) => r.json() as Promise<{ stargazers_count: number }>)
.then((j) => j.stargazers_count);
await i.reply({
embeds: [
{
title: `${count} total stars!`,
color: COLORS.yellow,
},
],
});
};

View file

@ -1,32 +0,0 @@
import { EmbedBuilder } from 'discord.js';
import { getTags, type Command } from '..';
import { COLORS } from '../constants';
export const cmd: Command = {
name: 'tags',
desc: 'Lists the tags available',
exec: async (e) => {
const em = new EmbedBuilder().setTitle('tags').setColor(COLORS.green);
const tags = await getTags();
for (const i in tags) {
const tag = tags[i];
let text = '';
if (tag.aliases && tag.aliases[0]) {
text += '**Aliases**: ' + tag.aliases.join(', ') + '\n';
}
if (tag.text) {
text += tag.text;
} else if (tag.embed) {
text += '\n[embedded message]';
}
em.addFields({ name: '?' + tag.name, value: text });
}
await e.reply({ embeds: [em] });
},
};

View file

@ -1,34 +0,0 @@
import type { Message } from 'discord.js';
import { isBad } from './badLinks';
import urlRegex from 'url-regex';
import { COLORS } from './constants';
// true if message is ok, false if filtered
export async function filterMessage(e: Message): Promise<boolean> {
// url matcher
const urlMatches = [...e.content.matchAll(urlRegex())];
if (urlMatches.length) {
console.log('Found links in message from', e.author.tag);
for (const match of urlMatches) {
console.log('[link]', match[0]);
if (await isBad(match[0])) {
await e.reply({
embeds: [
{
title: 'Hold on!',
description:
'There seems to be a phishing / malware link in your message.',
color: COLORS.red,
},
],
});
return false;
}
}
}
return true;
}

View file

@ -1,61 +1,23 @@
import {
Client,
Message,
EmbedBuilder,
type EmbedData,
GatewayIntentBits,
Partials,
ChannelType,
OAuth2Scopes,
} from 'discord.js';
import * as BuildConfig from './constants';
import { commands } from './commands';
import { filterMessage } from './filters';
import { parseLog } from './logs';
import { getLatestMinecraftVersion } from './utils/remoteVersions';
import {
parse as discordParse,
type SuccessfulParsedMessage,
} from 'discord-command-parser';
import { membersCommand } from './commands/members';
import { starsCommand } from './commands/stars';
import random from 'just-random';
import { readFile } from 'fs/promises';
import { join } from 'path';
import { green, bold, yellow } from 'kleur/colors';
import 'dotenv/config';
export interface Command {
name: string;
aliases?: string[];
desc?: string;
examples?: string[];
exec(
m: Message,
p: SuccessfulParsedMessage<Message<boolean>>
): Promise<void> | void;
}
interface Tag {
name: string;
aliases?: Array<string>;
text?: string;
embed?: EmbedData;
}
export const getTags = async (): Promise<Tag[]> => {
const raw = JSON.parse(
await readFile(join(__dirname, 'tags.json'), { encoding: 'utf8' })
) as Tag[];
return raw.map((tag) => {
if (tag.embed?.color) {
tag.embed.color = BuildConfig.COLORS[tag.embed.color];
}
return tag;
});
};
import { getTags } from './tagsTags';
const client = new Client({
intents: [
@ -74,6 +36,29 @@ const client = new Client({
client.once('ready', async () => {
console.log(green('Discord bot ready!'));
console.log(
client.generateInvite({
scopes: [OAuth2Scopes.Bot],
permissions: [
'AddReactions',
'ViewChannel',
'BanMembers',
'KickMembers',
'CreatePublicThreads',
'CreatePrivateThreads',
'EmbedLinks',
'ManageChannels',
'ManageRoles',
'ModerateMembers',
'MentionEveryone',
'MuteMembers',
'SendMessages',
'SendMessagesInThreads',
'ReadMessageHistory',
],
})
);
if (process.env.NODE_ENV !== 'development')
console.warn(yellow(bold('Running in production mode!')));
@ -82,7 +67,9 @@ client.once('ready', async () => {
activities: [
{
name: `Minecraft ${mcVersion}${
mcVersion === '1.19.1' ? ' w/ No Chat Reports' : ''
mcVersion === '1.19.1' || mcVersion === '1.19.2'
? ' w/ No Chat Reports'
: ''
}`,
},
],
@ -92,6 +79,7 @@ client.once('ready', async () => {
client.on('messageCreate', async (e) => {
if (!e.content) return;
if (!e.channel.isTextBased()) return;
if (e.author === client.user) return;
if (
@ -108,22 +96,12 @@ client.once('ready', async () => {
return;
}
const messageIsOK = await filterMessage(e);
if (!messageIsOK) {
return;
}
if (e.cleanContent.match(BuildConfig.ETA_REGEX)) {
await e.reply(
`${random(BuildConfig.ETA_MESSAGES)} <:pofat:964546613194420294>`
);
}
const commanded = await parseMsgForCommands(e);
if (commanded) return;
const tagged = await parseMsgForTags(e);
if (tagged) return;
const log = await parseLog(e.content);
if (log != null) {
e.reply({ embeds: [log] });
@ -132,59 +110,57 @@ client.once('ready', async () => {
});
});
async function parseMsgForCommands(e: Message) {
const parsed = discordParse(e, '!', { allowBots: true });
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (!parsed.success) return false;
const cmd = commands.find(
(c) => c.name == parsed.command || c.aliases?.includes(parsed.command)
);
if (!cmd) {
return false;
if (
process.env.NODE_ENV === 'development' &&
interaction.channelId !== BuildConfig.DEBUG_CHANNEL_ID
) {
return;
}
if (
process.env.NODE_ENV !== 'development' &&
interaction.channelId === BuildConfig.DEBUG_CHANNEL_ID
) {
return;
}
try {
await cmd.exec(e, parsed);
} catch (err: unknown) {
const em = new EmbedBuilder()
.setTitle('Error')
.setColor(BuildConfig.COLORS.red)
// @ts-expect-error no why
.setDescription(err['message'] as string);
const { commandName } = interaction;
await e.reply({ embeds: [em] });
}
if (commandName === 'ping') {
await interaction.reply({
content: `Pong! \`${client.ws.ping}ms\``,
ephemeral: true,
});
} else if (commandName === 'members') {
await membersCommand(interaction);
} else if (commandName === 'stars') {
await starsCommand(interaction);
} else if (commandName === 'rolypoly') {
await interaction.reply(
'https://media.discordapp.net/attachments/985048903126769764/985051373886382100/rollin-time.gif?width=324&height=216'
);
} else if (commandName === 'tag') {
const tags = await getTags();
const tagName = interaction.options.getString('name', true);
const tag = tags.find(
(tag) => tag.name === tagName || tag.aliases?.includes(tagName)
);
return true;
}
async function parseMsgForTags(e: Message) {
const parsed = discordParse(e, '?', { allowBots: true });
if (!parsed.success) return false;
const tag = await getTags().then((r) =>
r.find(
(t) => t.name == parsed.command || t.aliases?.includes(parsed.command)
)
);
if (tag) {
const requesterAvatarURL = e.author.displayAvatarURL({ size: 64 });
const tagRequester = {
text: `Requested by ${e.author.tag}`,
...(requesterAvatarURL ? { icon_url: requesterAvatarURL } : null),
};
if (tag.text) {
await e.reply(tag.text);
} else if (tag.embed) {
const em = new EmbedBuilder(tag.embed).setFooter(tagRequester);
await e.reply({ embeds: [em] });
if (!tag) {
await interaction.reply({
content: `Tag \`${tagName}\` does not exist.`,
ephemeral: true,
});
return;
}
return true;
await interaction.reply({
content: tag.text ? `**${tag.name}**\n\n` + tag.text : tag.text,
embeds: tag.embed ? [new EmbedBuilder(tag.embed)] : [],
});
}
}
});
client.login(process.env.DISCORD_TOKEN);

26
src/tagsTags.ts Normal file
View file

@ -0,0 +1,26 @@
import type { EmbedData } from 'discord.js';
import { readFile } from 'fs/promises';
import { join } from 'path';
import { COLORS } from './constants';
interface Tag {
name: string;
aliases?: Array<string>;
text?: string;
embed?: EmbedData;
}
export const getTags = async (): Promise<Tag[]> => {
const raw = JSON.parse(
await readFile(join(__dirname, 'tags.json'), { encoding: 'utf8' })
) as Tag[];
return raw.map((tag) => {
if (tag.embed?.color) {
tag.embed.color = COLORS[tag.embed.color];
}
return tag;
});
};