Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

11 changed files with 134 additions and 256 deletions

View File

@ -1,4 +1,4 @@
# Contributing to LOC's Community Relations Gamma Edition System # Contributing to LOC's Community Relations Alpha Edition System
Thank you for considering contributing to this project! Your contributions are highly valued, and were excited to collaborate with you. Thank you for considering contributing to this project! Your contributions are highly valued, and were excited to collaborate with you.

View File

@ -1,7 +1,9 @@
# Community Relations v2 Gamma Edition System - CRRA/G # Community Relations Alpha Edition System - CRRA
[![License](https://img.shields.io/badge/license-AGPLv3-blue.svg)](LICENSE) [![License](https://img.shields.io/badge/license-AGPLv3-blue.svg)](LICENSE)
A brief description of what your project does, what problem it solves, and why its useful.
--- ---
## Table of Contents ## Table of Contents
@ -86,4 +88,4 @@ __Library of Code Department of Engineering__ @:
- [Email](mailto:engineering@libraryofcode.org) - [Email](mailto:engineering@libraryofcode.org)
--- ---
Thank you for checking out CRv2! Thank you for checking out CRRA!

View File

@ -1,4 +1,4 @@
import { prop, getModelForClass, Ref } from "@typegoose/typegoose"; import { prop, getModelForClass } from "@typegoose/typegoose";
import Member from "./Member"; import Member from "./Member";
/* TODO /* TODO
@ -60,9 +60,9 @@ export default class Partner implements SharedMemberAttributes {
@prop({ required: true }) @prop({ required: true })
public title: PartnerTitle | "Partner" | undefined; public title: PartnerTitle | "Partner" | undefined;
@prop({ ref: () => Partner }) @prop()
// //
public directReport?: Ref<Partner> | string | undefined; public directReport: Partner | string | undefined;
@prop() @prop()
// this field dictates if the partner is able to perform developer commands, such as "eval" // this field dictates if the partner is able to perform developer commands, such as "eval"

View File

@ -2,11 +2,10 @@ import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
import { ChatInputCommandInteraction } from "discord.js"; import { ChatInputCommandInteraction } from "discord.js";
import { inspect } from "util"; import { inspect } from "util";
import { discordBotToken } from "../../config.json"; import { discordBotToken } from "../../config.json";
import { PartnerModel } from "../../database/Partner";
export default class Eval extends DiscordInteractionCommand { export default class Eval extends DiscordInteractionCommand {
// This is a list of IDs that are allowed to use this command. // This is a list of IDs that are allowed to use this command.
private listOfAllowedIDs: string[] = []; private listOfAllowedIDs: string[];
constructor() { constructor() {
super("eval", "Executes arbitrary JS code and returns the output."); super("eval", "Executes arbitrary JS code and returns the output.");
@ -26,13 +25,9 @@ export default class Eval extends DiscordInteractionCommand {
option.setName("depth").setDescription("The depth of the inspection.").setRequired(false) option.setName("depth").setDescription("The depth of the inspection.").setRequired(false)
); );
// this checks against the database and adds all of the partners that are "allowed to perform dev commands" this.listOfAllowedIDs = [
// doing the database check in the initialization prevents us from having to check the database every time this command is ran "278620217221971968", // Matthew
PartnerModel.find({ canPerformDevCommands: true }).then((partners) => { ];
for (const partner of partners) {
this.listOfAllowedIDs.push(partner.discordID as string);
}
});
} }
public async execute(interaction: ChatInputCommandInteraction) { public async execute(interaction: ChatInputCommandInteraction) {

View File

@ -1,15 +1,14 @@
import DiscordInteractionCommand from "../../util/DiscordInteractionCommand"; import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
import { MemberAdditionalAcknowledgement, MemberModel } from "../../database/Member"; import { MemberModel } from "../../database/Member";
import Partner, { import Partner, {
PartnerCommissionType, PartnerCommissionType,
PartnerDepartment, PartnerDepartment,
PartnerModel, PartnerModel,
PartnerRoleType, PartnerRoleType,
} from "../../database/Partner"; } from "../../database/Partner";
import { ChatInputCommandInteraction, EmbedBuilder, GuildMember, Snowflake } from "discord.js"; import { ChatInputCommandInteraction, EmbedBuilder, GuildMember } from "discord.js";
import MemberUtil from "../../util/MemberUtil"; import MemberUtil from "../../util/MemberUtil";
import EmojiConfig from "../../util/EmojiConfig"; import EmojiConfig from "../../util/EmojiConfig";
import Formatters from "../../util/Formatters";
export default class Whois extends DiscordInteractionCommand { export default class Whois extends DiscordInteractionCommand {
constructor() { constructor() {
@ -37,7 +36,7 @@ export default class Whois extends DiscordInteractionCommand {
const embed = new EmbedBuilder(); const embed = new EmbedBuilder();
// if the role type is managerial, add a [k] to the end of the name // if the role type is managerial, add a [k] to the end of the name
// if the partner exists, set the iconURL to the organizational logo // if the partner exists, set the iconURL to the organizational logo
const formattedName = Formatters.formatName(guildMember, partner); const formattedName = MemberUtil.formatName(guildMember, partner);
embed.setAuthor({ name: formattedName.text, iconURL: formattedName.iconURL }); embed.setAuthor({ name: formattedName.text, iconURL: formattedName.iconURL });
// set the thumbnail to the user's avatar // set the thumbnail to the user's avatar
embed.setThumbnail(guildMember.user.displayAvatarURL()); embed.setThumbnail(guildMember.user.displayAvatarURL());
@ -80,23 +79,16 @@ export default class Whois extends DiscordInteractionCommand {
break; break;
} }
if (partner.directReport) { if (partner.directReport) {
// fetch direct report object ref if (partner.directReport instanceof Partner) {
await partner.populate("directReport"); embedDescription += `**Direct Report**: ${partner.directReport.title}\n`;
// ensures that the population propagated correctly before adding to embed
if (partner.directReport instanceof PartnerModel) {
const directReportGuildMember = await guild?.members.fetch(
partner.directReport.discordID as Snowflake
);
// fetches GuildMember for the direct report
embedDescription += `**Direct Report**: ${directReportGuildMember ? Formatters.formatName(directReportGuildMember, partner.directReport).text + ", " : ""}${partner.directReport.title}\n`;
} }
} }
} }
embed.setColor(guildMember.displayColor); embed.setColor(guildMember.displayColor);
if (embedDescription?.length > 0) if (embedDescription?.length > 0) embed.setDescription(embedDescription);
embed.setDescription(`${embedDescription}\n\n<@${guildMember.id}>`);
// add status to embed // add status to embed
if (guildMember.presence?.status) { if (guildMember.presence?.status) {
// TODO: this currently doesn't work for some reason
switch (guildMember.presence.status) { switch (guildMember.presence.status) {
case "online": case "online":
embed.addFields({ name: "Status", value: "Online", inline: true }); embed.addFields({ name: "Status", value: "Online", inline: true });
@ -107,7 +99,7 @@ export default class Whois extends DiscordInteractionCommand {
case "dnd": case "dnd":
embed.addFields({ name: "Status", value: "Do Not Disturb", inline: true }); embed.addFields({ name: "Status", value: "Do Not Disturb", inline: true });
break; break;
case "offline": case "offline" || "invisible":
embed.addFields({ name: "Status", value: "Online", inline: true }); embed.addFields({ name: "Status", value: "Online", inline: true });
break; break;
default: default:
@ -115,87 +107,9 @@ export default class Whois extends DiscordInteractionCommand {
embed.addFields({ name: "Status", value: "", inline: true }); embed.addFields({ name: "Status", value: "", inline: true });
break; break;
} }
} else {
embed.addFields({ name: "Status", value: "Offline", inline: true });
}
// calculations for joined / created at
embed.addFields(
{
name: "Joined At",
value: guildMember.joinedTimestamp
? `<t:${Math.floor(guildMember.joinedTimestamp / 1000)}>`
: "Invalid Date",
inline: true,
},
{
name: "Created At",
value: guildMember.user.createdTimestamp
? `<t:${Math.floor(guildMember.user.createdTimestamp / 1000)}>`
: "Invalid Date",
inline: true,
}
);
// TODO: calculations for commscore
embed.addFields({ name: "CommScore™", value: "[PLACEHOLDER]", inline: false });
// role calculation (sorting roles by their position)
let roleString = "";
for (const role of guildMember.roles.valueOf().sort((a, b) => b.position - a.position)) {
roleString += `<@&${role[1].id}> `;
}
if (roleString) {
embed.addFields({ name: "Roles", value: roleString });
} else {
embed.addFields({ name: "Roles", value: "None" });
}
// listing permissions
const serializedPermissions = guildMember.permissions.serialize();
const permissionsArray: string[] = [];
// adding serialized string representation of permissions to array to use in embed field
if (serializedPermissions.Administrator) permissionsArray.push("Administrator");
if (serializedPermissions.ManageGuild) permissionsArray.push("Manage Guild");
if (serializedPermissions.ManageChannels) permissionsArray.push("Manage Channels");
if (serializedPermissions.ManageRoles) permissionsArray.push("Manage Roles");
if (serializedPermissions.ManageMessages) permissionsArray.push("Manage Messages");
if (serializedPermissions.ManageEvents) permissionsArray.push("Manage Events");
if (serializedPermissions.ManageNicknames) permissionsArray.push("Manage Nicknames");
if (serializedPermissions.ManageEmojisAndStickers) permissionsArray.push("Manage Emojis");
if (serializedPermissions.ManageWebhooks) permissionsArray.push("Manage Webhooks");
if (serializedPermissions.ModerateMembers) permissionsArray.push("Moderate Members");
if (serializedPermissions.BanMembers) permissionsArray.push("Ban Members");
if (serializedPermissions.KickMembers) permissionsArray.push("Kick Members");
if (serializedPermissions.DeafenMembers) permissionsArray.push("Deafen Members");
// setting key permissions embed field
if (permissionsArray?.length > 0) {
embed.addFields({ name: "Key Permissions", value: permissionsArray.join(", ") });
}
// determining acknowledgements: MemberAdditionalAcknowledgement || "Guild Owner", "Guild Admin", "Guild Manager", "Guild Moderator"
const acknowledgementsArray: MemberAdditionalAcknowledgement[] = [];
if (guildMember.id === guildMember.guild.ownerId) {
acknowledgementsArray.push("Guild Owner");
} else if (serializedPermissions.Administrator) {
acknowledgementsArray.push("Guild Admin");
} else if (serializedPermissions.ManageGuild) {
acknowledgementsArray.push("Guild Manager");
} else if (serializedPermissions.ModerateMembers || serializedPermissions.ManageMessages) {
acknowledgementsArray.push("Guild Moderator");
}
if (partner?.canPerformDevCommands) {
acknowledgementsArray.push("System Developer");
}
// adding acknowledgements to embed
if (acknowledgementsArray.length > 0) {
embed.addFields({ name: "Acknowledgements", value: acknowledgementsArray.join(", ") });
} }
embed.setFooter({ embed.setFooter({
text: `Discord ID: ${guildMember.id}${databaseMember ? `| Internal ID: ${databaseMember?._id}` : ""}${partner ? ` | Partner ID: ${partner?.id}` : ""}`, text: `Discord ID: ${guildMember.id}${databaseMember ? `Internal ID: ${databaseMember?._id}` : ""}`,
}); });
return await interaction.editReply({ embeds: [embed] }); return await interaction.editReply({ embeds: [embed] });

View File

@ -1,14 +1,13 @@
import { Client, GatewayIntentBits, Partials, REST, Routes } from 'discord.js'; import { Client, GatewayIntentBits, Partials, REST, Routes } from "discord.js";
import { discordBotToken, discordClientID, MongoDbUrl } from './config.json'; import { discordBotToken, discordClientID } from "./config.json"
import Collection from './util/Collection'; import Collection from "./util/Collection";
import DiscordInteractionCommand from './util/DiscordInteractionCommand'; import DiscordInteractionCommand from "./util/DiscordInteractionCommand";
import DiscordEvent from './util/DiscordEvent'; import DiscordEvent from "./util/DiscordEvent";
import * as DiscordInteractionCommandsIndex from './discord/commands'; import * as DiscordInteractionCommandsIndex from "./discord/commands";
import * as DiscordEventsIndex from './discord/events'; import * as DiscordEventsIndex from "./discord/events";
import mongoose from 'mongoose'; import mongoose from "mongoose";
export const DiscordInteractionCommands: Collection<DiscordInteractionCommand> = export const DiscordInteractionCommands: Collection<DiscordInteractionCommand> = new Collection();
new Collection();
export const DiscordEvents: Collection<DiscordEvent> = new Collection(); export const DiscordEvents: Collection<DiscordEvent> = new Collection();
// Instantiates a new Discord client // Instantiates a new Discord client
@ -22,12 +21,7 @@ const discordClient = new Client({
GatewayIntentBits.GuildInvites, GatewayIntentBits.GuildInvites,
GatewayIntentBits.GuildModeration, GatewayIntentBits.GuildModeration,
], ],
partials: [ partials: [ Partials.GuildMember, Partials.Message, Partials.User, Partials.Channel, ],
Partials.GuildMember,
Partials.Message,
Partials.User,
Partials.Channel,
],
}); });
const discordREST = new REST().setToken(discordBotToken); const discordREST = new REST().setToken(discordBotToken);
// const stripeClient = new Stripe(stripeToken, { typescript: true }); // const stripeClient = new Stripe(stripeToken, { typescript: true });
@ -35,12 +29,11 @@ const discordREST = new REST().setToken(discordBotToken);
export async function main() { export async function main() {
// Connect to the databases // Connect to the databases
try { try {
//@ts-ignore mongoose.connection.once("open", () => {
mongoose.connection.once('open', () => { console.info("[Info - Database] Connected to MongoDB");
console.info('[Info - Database] Connected to MongoDB'); })
});
// TODO: Fetch the MongoDB URI from the config file // TODO: Fetch the MongoDB URI from the config file
await mongoose.connect(MongoDbUrl, {}); await mongoose.connect("mongodb://localhost:27017/crra-main", {});
} catch (error) { } catch (error) {
console.error(`[Error - Database] Failed to connect to MongoDB: ${error}`); console.error(`[Error - Database] Failed to connect to MongoDB: ${error}`);
process.exit(1); process.exit(1);
@ -49,9 +42,7 @@ export async function main() {
for (const Command of Object.values(DiscordInteractionCommandsIndex)) { for (const Command of Object.values(DiscordInteractionCommandsIndex)) {
const instance = new Command(); const instance = new Command();
DiscordInteractionCommands.add(instance.name, instance); DiscordInteractionCommands.add(instance.name, instance);
console.info( console.info(`[Info - Discord] Loaded interaction command: ${instance.name}`);
`[Info - Discord] Loaded interaction command: ${instance.name}`
);
} }
// Load Discord events // Load Discord events
for (const Event of Object.values(DiscordEventsIndex)) { for (const Event of Object.values(DiscordEventsIndex)) {
@ -63,9 +54,7 @@ export async function main() {
await discordClient.login(discordBotToken); await discordClient.login(discordBotToken);
try { try {
console.log( console.log(`Started refreshing ${DiscordInteractionCommands.size} application (/) commands.`);
`Started refreshing ${DiscordInteractionCommands.size} application (/) commands.`
);
const interactionCommandsData = []; const interactionCommandsData = [];
for (const command of DiscordInteractionCommands.values()) { for (const command of DiscordInteractionCommands.values()) {
interactionCommandsData.push(command.builder.toJSON()); interactionCommandsData.push(command.builder.toJSON());
@ -74,11 +63,11 @@ export async function main() {
// The put method is used to fully refresh all commands in the guild with the current set // The put method is used to fully refresh all commands in the guild with the current set
const data = await discordREST.put( const data = await discordREST.put(
Routes.applicationCommands(discordClientID), Routes.applicationCommands(discordClientID),
{ body: interactionCommandsData } { body: interactionCommandsData },
);
console.log(
`Successfully reloaded ${interactionCommandsData?.length} application (/) commands.`
); );
// @ts-ignore
console.log(`Successfully reloaded ${data?.length} application (/) commands.`);
} catch (error) { } catch (error) {
// And of course, make sure you catch and log any errors! // And of course, make sure you catch and log any errors!
console.error(error); console.error(error);

View File

@ -1,5 +1,4 @@
{ {
"name": "crv2",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.13.0", "@eslint/js": "^9.13.0",
@ -19,7 +18,6 @@
}, },
"dependencies": { "dependencies": {
"@typegoose/typegoose": "^12.2.0", "@typegoose/typegoose": "^12.2.0",
"auth0": "^4.12.0",
"discord.js": "^14.14.1", "discord.js": "^14.14.1",
"mongoose": "^8.2.2", "mongoose": "^8.2.2",
"stripe": "^14.21.0", "stripe": "^14.21.0",

View File

@ -11,11 +11,11 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */ /* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, "target": "es2016", /* 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. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */ // "jsx": "preserve", /* Specify what JSX code is generated. */
"experimentalDecorators": true /* Enable experimental support for legacy experimental decorators. */, "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
"emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */, "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
@ -25,7 +25,7 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */ /* Modules */
"module": "commonjs" /* Specify what module code is generated. */, "module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */ // "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
@ -39,7 +39,7 @@
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
"resolveJsonModule": true /* Enable importing .json files. */, "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
@ -55,7 +55,7 @@
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist" /* Specify an output folder for all emitted files. */, // "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */ // "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */ // "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
@ -77,12 +77,12 @@
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */ /* Type Checking */
"strict": true /* Enable all strict type-checking options. */, "strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */

View File

@ -8,5 +8,5 @@ export default abstract class DiscordEvent {
this.client = client; this.client = client;
this.execute = this.execute.bind(this); this.execute = this.execute.bind(this);
} }
public abstract execute(...args: never[]): Error | Promise<void>; public abstract execute(...args: any[]): Error | Promise<void>;
} }

View File

@ -1,55 +0,0 @@
import { GuildMember, User } from "discord.js";
import Partner, { PartnerCommissionType, PartnerRoleType } from "../database/Partner";
import { FormatNameOptions } from "./MemberUtil";
export default class Formatters {
public static formatStandardDate(date: Date | string | number): string {
const resolvedDate = new Date(date);
if (!resolvedDate) return "";
const year = resolvedDate.getFullYear();
const month = String(resolvedDate.getMonth() + 1).padStart(2, "0");
const day = String(resolvedDate.getDate()).padStart(2, "0");
const hours = String(resolvedDate.getHours()).padStart(2, "0");
const minutes = String(resolvedDate.getMinutes()).padStart(2, "0");
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
return `${year}-${month}-${day} @ ${hours}:${minutes} (${timeZone})`;
}
// TODO: comments and extended formatting
public static formatName(
target: GuildMember | User,
partner?: Partner | null
): FormatNameOptions {
console.debug(
`[MemberUtil] Formatting name for ${target.displayName} at url ${target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL()}`
);
// if the role type is managerial, add a [k] to the end of the name
// if the partner exists, set the iconURL to the organizational logo
if (partner?.roleType == PartnerRoleType.MANAGERIAL) {
return {
text: `${target.displayName} [k]`,
iconURL: target.displayAvatarURL(),
};
} else if (partner?.commissionType == PartnerCommissionType.CONTRACTUAL) {
// if the commission type is contractual, add a [c] to the end of the name
return {
text: `${target.displayName} [c]`,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
} else {
// otherwise, just set the author to the member's display name
return {
text: target.displayName,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
}
}
}

View File

@ -8,14 +8,13 @@ import Partner, {
import Member, { MemberAdditionalAcknowledgement, MemberModel } from "../database/Member"; import Member, { MemberAdditionalAcknowledgement, MemberModel } from "../database/Member";
import { Client, GuildMember, User } from "discord.js"; import { Client, GuildMember, User } from "discord.js";
import { guildID } from "../config.json"; import { guildID } from "../config.json";
import { Ref } from "@typegoose/typegoose";
export interface PartnerOptions { export interface PartnerOptions {
roleType: PartnerRoleType; roleType: PartnerRoleType;
commissionType: PartnerCommissionType; commissionType: PartnerCommissionType;
department: PartnerDepartment; department: PartnerDepartment;
title: PartnerTitle; title: PartnerTitle;
directReport: Ref<Partner>; directReport: Partner | string;
} }
export interface FormatNameOptions { export interface FormatNameOptions {
@ -26,7 +25,7 @@ export interface FormatNameOptions {
// TODO: Add the rest of the remaining role configurations // TODO: Add the rest of the remaining role configurations
export const PartnerDiscordRoleMap = { export const PartnerDiscordRoleMap = {
// Director of Engineering, Management, Staff, Technician, Core Team, Play Caller // Director of Engineering, Management, Staff, Technician, Core Team, Play Caller
Engineering: [ "Director of Engineering": [
"1077646568091570236", "1077646568091570236",
"1077646956890951690", "1077646956890951690",
"446104438969466890", "446104438969466890",
@ -35,7 +34,7 @@ export const PartnerDiscordRoleMap = {
"1014978134573064293", "1014978134573064293",
], ],
// Director of Operations, Management, Staff, Moderator, Core Team, Play Caller // Director of Operations, Management, Staff, Moderator, Core Team, Play Caller
Operations: [ "Director of Operations": [
"1077647072163020840", "1077647072163020840",
"1077646956890951690", "1077646956890951690",
"446104438969466890", "446104438969466890",
@ -80,4 +79,40 @@ export default class MemberUtil {
{ $push: { additionalAcknowledgement: acknowledgement } } { $push: { additionalAcknowledgement: acknowledgement } }
); );
} }
// TODO: comments and extended formatting
public static formatName(
target: GuildMember | User,
partner?: Partner | null
): FormatNameOptions {
console.log(
`[MemberUtil] Formatting name for ${target.displayName} at url ${target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL()}`
);
// if the role type is managerial, add a [k] to the end of the name
// if the partner exists, set the iconURL to the organizational logo
if (partner?.roleType == PartnerRoleType.MANAGERIAL) {
return {
text: `${target.displayName} [k]`,
iconURL: target.displayAvatarURL(),
};
} else if (partner?.commissionType == PartnerCommissionType.CONTRACTUAL) {
// if the commission type is contractual, add a [c] to the end of the name
return {
text: `${target.displayName} [c]`,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
} else {
// otherwise, just set the author to the member's display name
return {
text: target.displayName,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
}
}
} }