Fixes and Improvements

This commit is contained in:
Ahmad 2024-12-07 15:47:21 -05:00
parent 4c5a8d2e66
commit 82f354db1c
No known key found for this signature in database
GPG key ID: 8FD8A93530D182BF
12 changed files with 137 additions and 105 deletions

View file

@ -99,7 +99,7 @@ export default [
"no-var": "error",
"object-curly-spacing": ["error", "always"],
"prefer-const": "error",
quotes: ["warning", "single"],
quotes: ["warn", "single"],
semi: ["error", "always"],
"space-before-blocks": "error",

View file

@ -30,5 +30,5 @@
"tsx": "^4.19.2",
"typescript": "^5.7.2"
},
"packageManager": "yarn@4.5.2"
"packageManager": "yarn@4.5.3"
}

View file

@ -1,22 +1,29 @@
import { SlashCommandBuilder, CommandInteraction, EmbedBuilder } from "discord.js";
import { getAllMembers } from "../util/db.js";
import {
SlashCommandBuilder,
CommandInteraction,
EmbedBuilder,
} from 'discord.js';
import { getAllMembers } from '../util/db.js';
interface Command {
data: Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
data: Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
execute: (interaction: CommandInteraction) => Promise<void>;
}
const command: Command = {
data: new SlashCommandBuilder()
.setName("members")
.setDescription("Lists all non-bot members of the server"),
.setName('members')
.setDescription('Lists all non-bot members of the server'),
execute: async (interaction) => {
const members = await getAllMembers();
const memberList = members.map(m => `**${m.discordUsername}** (${m.discordId})`).join("\n");
const memberList = members
.map((m) => `**${m.discordUsername}** (${m.discordId})`)
.join('\n');
const membersEmbed = new EmbedBuilder()
.setTitle("Members")
.setTitle('Members')
.setDescription(memberList)
.setColor(0x0099ff);
.setColor(0x0099ff)
.addFields({ name: 'Total Members', value: members.length.toString() });
await interaction.reply({ embeds: [membersEmbed] });
},
};

View file

@ -1,14 +1,14 @@
import { SlashCommandBuilder, CommandInteraction } from "discord.js";
import { SlashCommandBuilder, CommandInteraction } from 'discord.js';
interface Command {
data: Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
data: Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
execute: (interaction: CommandInteraction) => Promise<void>;
}
const command: Command = {
data: new SlashCommandBuilder()
.setName("ping")
.setDescription("Check the latency from you to the bot"),
.setName('ping')
.setDescription('Check the latency from you to the bot'),
execute: async (interaction) => {
await interaction.reply(`Pong! Latency: ${Date.now() - interaction.createdTimestamp}ms`);
},

View file

@ -2,107 +2,107 @@ import {
SlashCommandBuilder,
CommandInteraction,
EmbedBuilder,
} from "discord.js";
} from 'discord.js';
interface Command {
data: Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
data: Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
execute: (interaction: CommandInteraction) => Promise<void>;
}
const rulesEmbed = new EmbedBuilder()
.setColor(0x0099ff)
.setTitle("Server Rules")
.setTitle('Server Rules')
.setAuthor({
name: "Poixixel",
name: 'Poixixel',
iconURL:
"https://cdn.discordapp.com/avatars/1052017329376071781/922947c726d7866d313744186c42ef49.webp",
'https://cdn.discordapp.com/avatars/1052017329376071781/922947c726d7866d313744186c42ef49.webp',
})
.setDescription(
"These are the rules for the server. Please read and follow them carefully."
'These are the rules for the server. Please read and follow them carefully.'
)
.addFields(
{
name: "**Rule #1: Be respectful**",
name: '**Rule #1: Be respectful**',
value:
"Treat everyone with kindness. No harassment, bullying, hate speech, or toxic behavior.",
'Treat everyone with kindness. No harassment, bullying, hate speech, or toxic behavior.',
},
{
name: "**Rule #2: Keep it Family-Friendly**",
name: '**Rule #2: Keep it Family-Friendly**',
value:
"No explicit content, including NSFW images, language, or discussions. This is a safe space for everyone.",
'No explicit content, including NSFW images, language, or discussions. This is a safe space for everyone.',
},
{
name: "**Rule #3: Use Common Sense**",
name: '**Rule #3: Use Common Sense**',
value:
"Think before you act or post. If something seems questionable, its probably best not to do it.",
'Think before you act or post. If something seems questionable, its probably best not to do it.',
},
{
name: "**Rule #4: No Spamming**",
name: '**Rule #4: No Spamming**',
value:
"Avoid excessive messages, emoji use, or CAPS LOCK. Keep the chat clean and readable.",
'Avoid excessive messages, emoji use, or CAPS LOCK. Keep the chat clean and readable.',
},
{
name: "**Rule #5: No Raiding**",
name: '**Rule #5: No Raiding**',
value:
"Do not disrupt the server or other servers with spam, unwanted content, or malicious behavior.",
'Do not disrupt the server or other servers with spam, unwanted content, or malicious behavior.',
},
{
name: "**Rule #6: No Self-Promotion**",
name: '**Rule #6: No Self-Promotion**',
value:
"Do not advertise your own content or other servers without permission from staff.",
'Do not advertise your own content or other servers without permission from staff.',
},
{
name: "**Rule #7: No Impersonation**",
name: '**Rule #7: No Impersonation**',
value:
"Do not pretend to be someone else, including staff or other members.",
'Do not pretend to be someone else, including staff or other members.',
},
{
name: "**Rule #8: No Violence**",
name: '**Rule #8: No Violence**',
value:
"Do not post or share content that is offensive, harmful, or contains violent or dangerous content.",
'Do not post or share content that is offensive, harmful, or contains violent or dangerous content.',
},
{
name: "**Rule #9: No Doxxing or Sharing Personal Information**",
name: '**Rule #9: No Doxxing or Sharing Personal Information**',
value:
"Protect your privacy and the privacy of others. Do not share personal details.",
'Protect your privacy and the privacy of others. Do not share personal details.',
},
{
name: "**Rule #10: No Ping Abuse**",
name: '**Rule #10: No Ping Abuse**',
value:
"Do not ping staff members unless it's absolutely necessary. Use pings responsibly for all members.",
'Do not ping staff members unless it\'s absolutely necessary. Use pings responsibly for all members.',
},
{
name: "**Rule #11: Use Appropriate Channels**",
name: '**Rule #11: Use Appropriate Channels**',
value:
"Post content in the right channels. Off-topic content may be moved or deleted.",
'Post content in the right channels. Off-topic content may be moved or deleted.',
},
{
name: "**Rule #12: Follow Discord's ToS and Community Guidelines**",
name: '**Rule #12: Follow Discord\'s ToS and Community Guidelines**',
value:
"All members must adhere to Discords Terms of Service and Community Guidelines.",
'All members must adhere to Discords Terms of Service and Community Guidelines.',
},
{
name: "**Rule #13: Moderator Discretion**",
name: '**Rule #13: Moderator Discretion**',
value:
"Moderators reserve the right to moderate at their discretion. If you feel mistreated, please create a support ticket.",
'Moderators reserve the right to moderate at their discretion. If you feel mistreated, please create a support ticket.',
},
{
name: "**Disclaimer:**",
name: '**Disclaimer:**',
value:
"**These rules may be updated at any time. It is your responsibility to review them regularly. Moderators and admins have the authority to enforce these rules and take appropriate action.**",
'**These rules may be updated at any time. It is your responsibility to review them regularly. Moderators and admins have the authority to enforce these rules and take appropriate action.**',
}
)
.setTimestamp()
.setFooter({
text: "Sent by the Poixpixel Bot",
text: 'Sent by the Poixpixel Bot',
iconURL:
"https://cdn.discordapp.com/avatars/1052017329376071781/922947c726d7866d313744186c42ef49.webp",
'https://cdn.discordapp.com/avatars/1052017329376071781/922947c726d7866d313744186c42ef49.webp',
});
const command: Command = {
data: new SlashCommandBuilder()
.setName("rules")
.setDescription("Sends the server rules"),
.setName('rules')
.setDescription('Sends the server rules'),
execute: async (interaction) => {
await interaction.reply({ embeds: [rulesEmbed] });
},

View file

@ -1,17 +1,17 @@
import { SlashCommandBuilder, CommandInteraction } from "discord.js";
import { SlashCommandBuilder, CommandInteraction } from 'discord.js';
interface Command {
data: Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
data: Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
execute: (interaction: CommandInteraction) => Promise<void>;
}
const command: Command = {
data: new SlashCommandBuilder()
.setName("server")
.setDescription("Provides information about the server."),
.setName('server')
.setDescription('Provides information about the server.'),
execute: async (interaction) => {
await interaction.reply(
`The server ${interaction?.guild?.name} has ${interaction?.guild?.memberCount} members and was created on ${interaction?.guild?.createdAt}. It is ${new Date().getFullYear() - interaction?.guild?.createdAt?.getFullYear()!} years old.`
`The server ${interaction!.guild!.name} has ${interaction!.guild!.memberCount} members and was created on ${interaction!.guild!.createdAt}. It is ${new Date().getFullYear() - interaction!.guild!.createdAt.getFullYear()!} years old.`
);
},
};

View file

@ -2,23 +2,24 @@ import {
SlashCommandBuilder,
CommandInteraction,
GuildMember,
} from "discord.js";
} from 'discord.js';
interface Command {
data: Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
data: Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
execute: (interaction: CommandInteraction) => Promise<void>;
}
const command: Command = {
data: new SlashCommandBuilder()
.setName("user")
.setDescription("Provides information about the user."),
.setName('user')
.setDescription('Provides information about the user.'),
execute: async (interaction) => {
if (interaction.member instanceof GuildMember) {
await interaction.reply(
`This command was run by ${interaction.user.username}, who joined this server on ${interaction.member.joinedAt}.`
);
} else {
}
else {
await interaction.reply(
`This command was run by ${interaction.user.username}.`
);

View file

@ -1,7 +1,7 @@
import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
import { integer, pgTable, varchar } from 'drizzle-orm/pg-core';
export const memberTable = pgTable("members", {
export const memberTable = pgTable('members', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
discordId: varchar("discord_id").notNull().unique(),
discordUsername: varchar("discord_username").notNull(),
discordId: varchar('discord_id').notNull().unique(),
discordUsername: varchar('discord_username').notNull(),
});

View file

@ -1,11 +1,11 @@
import fs from "node:fs";
import path from "node:path";
import { Client, Collection, Events, GatewayIntentBits } from "discord.js";
import fs from 'node:fs';
import path from 'node:path';
import { Client, Collection, Events, GatewayIntentBits } from 'discord.js';
import { deployCommands } from "./util/deployCommand.js";
import { getAllMembers, setMembers } from "./util/db.js";
import { deployCommands } from './util/deployCommand.js';
import { removeMember, setMembers } from './util/db.js';
const config = JSON.parse(fs.readFileSync("./config.json", "utf8"));
const config = JSON.parse(fs.readFileSync('./config.json', 'utf8'));
const { token, guildId } = config;
const client: any = new Client({
@ -16,35 +16,38 @@ client.commands = new Collection();
try {
const __dirname = path.resolve();
const commandsPath = path.join(__dirname, "/target/commands/");
const commandsPath = path.join(__dirname, '/target/commands/');
const commandFiles = fs
.readdirSync(commandsPath)
.filter((file) => file.endsWith(".js"));
.filter((file) => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join("file://", commandsPath, file);
const filePath = path.join('file://', commandsPath, file);
const commandModule = await import(filePath);
const command = commandModule.default;
if (
command instanceof Object &&
"data" in command &&
"execute" in command
'data' in command &&
'execute' in command
) {
client.commands.set(command.data.name, command);
} else {
}
else {
console.log(
`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`
);
}
}
} catch (error: any) {
}
catch (error: any) {
console.log(`Error while getting commands up: ${error}`);
}
try {
await deployCommands();
} catch (error: any) {
}
catch (error: any) {
console.log(`Error while registering commands: ${error}`);
}
@ -70,20 +73,34 @@ client.on(Events.InteractionCreate, async (interaction: any) => {
try {
await command.execute(interaction);
} catch (error) {
}
catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({
content: "There was an error while executing this command!",
content: 'There was an error while executing this command!',
ephemeral: true,
});
} else {
}
else {
await interaction.reply({
content: "There was an error while executing this command!",
content: 'There was an error while executing this command!',
ephemeral: true,
});
}
}
});
client.on(Events.GuildMemberAdd, async () => {
const guild = await client.guilds.fetch(guildId);
const members = await guild.members.fetch();
const nonBotMembers = members.filter((member: any) => !member.user.bot);
await setMembers(nonBotMembers);
});
client.on(Events.GuildMemberRemove, async (member: any) => {
await removeMember(member.user.id);
});
client.login(token);

View file

@ -1,12 +1,12 @@
import fs from "node:fs";
import pkg from "pg";
import { drizzle } from "drizzle-orm/node-postgres";
import { memberTable } from "../db/schema.js";
import { eq } from "drizzle-orm";
import fs from 'node:fs';
import pkg from 'pg';
import { drizzle } from 'drizzle-orm/node-postgres';
import { memberTable } from '../db/schema.js';
import { eq } from 'drizzle-orm';
const { Pool } = pkg;
const config = JSON.parse(fs.readFileSync("./config.json", "utf8"));
const { dbConnectionString, guildId } = config;
const config = JSON.parse(fs.readFileSync('./config.json', 'utf8'));
const { dbConnectionString } = config;
const dbPool = new Pool({
connectionString: dbConnectionString,
@ -29,7 +29,8 @@ export async function setMembers(nonBotMembers: any) {
.update(memberTable)
.set({ discordUsername: member.user.username })
.where(eq(memberTable.discordId, member.user.id));
} else {
}
else {
const members: typeof memberTable.$inferInsert = {
discordId: member.user.id,
discordUsername: member.user.username,
@ -38,3 +39,7 @@ export async function setMembers(nonBotMembers: any) {
}
});
}
export async function removeMember(discordId: string) {
await db.delete(memberTable).where(eq(memberTable.discordId, discordId));
}

View file

@ -1,17 +1,17 @@
import { REST, Routes } from "discord.js";
import fs from "fs";
import path from "path";
import { REST, Routes } from 'discord.js';
import fs from 'fs';
import path from 'path';
const config = JSON.parse(fs.readFileSync("./config.json", "utf8"));
const config = JSON.parse(fs.readFileSync('./config.json', 'utf8'));
const { token, clientId, guildId } = config;
const __dirname = path.resolve();
const commandsPath = path.join(__dirname, "target", "commands");
const commandsPath = path.join(__dirname, 'target', 'commands');
const commandFiles = fs
.readdirSync(commandsPath)
.filter((file) => file.endsWith(".js"));
.filter((file) => file.endsWith('.js'));
const rest = new REST({ version: "9" }).setToken(token);
const rest = new REST({ version: '9' }).setToken(token);
export const deployCommands = async () => {
try {
@ -20,13 +20,14 @@ export const deployCommands = async () => {
);
const commands = commandFiles.map(async (file) => {
const filePath = path.join("file://", commandsPath, file);
const filePath = path.join('file://', commandsPath, file);
const commandModule = await import(filePath);
const command = commandModule.default;
if (command instanceof Object && "data" in command) {
if (command instanceof Object && 'data' in command) {
return command.data.toJSON();
} else {
}
else {
console.log(
`[WARNING] The command at ${filePath} is missing a required "data" property.`
);
@ -46,7 +47,8 @@ export const deployCommands = async () => {
console.log(
`Successfully reloaded ${data.length} application (/) commands.`
);
} catch (error) {
}
catch (error) {
console.error(error);
}
};

View file

@ -11,8 +11,8 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2017", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"lib": ["ES2022"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */