big cleanup & use slash commands
This commit is contained in:
parent
386379b493
commit
01ce9ad000
15 changed files with 526 additions and 541 deletions
11
src/_delete.ts
Normal file
11
src/_delete.ts
Normal 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
45
src/_upload.ts
Normal 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);
|
||||
});
|
|
@ -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;
|
||||
};
|
|
@ -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] });
|
||||
},
|
||||
};
|
|
@ -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];
|
|
@ -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,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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`);
|
||||
},
|
||||
};
|
|
@ -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,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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] });
|
||||
},
|
||||
};
|
|
@ -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;
|
||||
}
|
174
src/index.ts
174
src/index.ts
|
@ -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
26
src/tagsTags.ts
Normal 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;
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue