forked from engineering/crv2
Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
|
277bb4e4ec | |
|
51c9d14dc1 | |
|
28d0a2d485 | |
|
ba6c9218c1 | |
|
fdaeeec513 | |
|
3b5b8db6c1 | |
|
3a2d5d44fc | |
|
0ee34462bf | |
|
58841b2830 | |
|
dac548d82c |
|
@ -1,4 +1,4 @@
|
|||
# Contributing to LOC's Community Relations Alpha Edition System
|
||||
# Contributing to LOC's Community Relations Gamma Edition System
|
||||
|
||||
Thank you for considering contributing to this project! Your contributions are highly valued, and we’re excited to collaborate with you.
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
# Community Relations Alpha Edition System - CRRA
|
||||
# Community Relations v2 Gamma Edition System - CRRA/G
|
||||
|
||||
[](LICENSE)
|
||||
|
||||
A brief description of what your project does, what problem it solves, and why it’s useful.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
@ -88,4 +86,4 @@ __Library of Code Department of Engineering__ @:
|
|||
- [Email](mailto:engineering@libraryofcode.org)
|
||||
---
|
||||
|
||||
Thank you for checking out CRRA!
|
||||
Thank you for checking out CRv2!
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { prop, getModelForClass } from "@typegoose/typegoose";
|
||||
import { prop, getModelForClass, Ref } from "@typegoose/typegoose";
|
||||
import Member from "./Member";
|
||||
|
||||
/* TODO
|
||||
|
@ -14,7 +14,7 @@ export type PartnerTitle =
|
|||
| "Deputy Director of Engineering"
|
||||
| "Deputy Director of Operations"
|
||||
| "Services Manager"
|
||||
| "Project Manager"
|
||||
| "Community Manager"
|
||||
| "Engineering Core Partner"
|
||||
| "Operations Core Partner"
|
||||
| "Community Moderator"
|
||||
|
@ -51,6 +51,9 @@ export default class Partner implements SharedMemberAttributes {
|
|||
@prop({ required: true })
|
||||
public roleType: PartnerRoleType | undefined;
|
||||
|
||||
@prop()
|
||||
public isKeyHolder: boolean | undefined;
|
||||
|
||||
@prop({ required: true })
|
||||
public commissionType: PartnerCommissionType | undefined;
|
||||
|
||||
|
@ -60,9 +63,9 @@ export default class Partner implements SharedMemberAttributes {
|
|||
@prop({ required: true })
|
||||
public title: PartnerTitle | "Partner" | undefined;
|
||||
|
||||
@prop()
|
||||
@prop({ ref: () => Partner })
|
||||
//
|
||||
public directReport: Partner | string | undefined;
|
||||
public directReport?: Ref<Partner> | string | undefined;
|
||||
|
||||
@prop()
|
||||
// this field dictates if the partner is able to perform developer commands, such as "eval"
|
||||
|
|
|
@ -2,10 +2,11 @@ import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
|
|||
import { ChatInputCommandInteraction } from "discord.js";
|
||||
import { inspect } from "util";
|
||||
import { discordBotToken } from "../../config.json";
|
||||
import { PartnerModel } from "../../database/Partner";
|
||||
|
||||
export default class Eval extends DiscordInteractionCommand {
|
||||
// This is a list of IDs that are allowed to use this command.
|
||||
private listOfAllowedIDs: string[];
|
||||
private listOfAllowedIDs: string[] = [];
|
||||
|
||||
constructor() {
|
||||
super("eval", "Executes arbitrary JS code and returns the output.");
|
||||
|
@ -25,9 +26,13 @@ export default class Eval extends DiscordInteractionCommand {
|
|||
option.setName("depth").setDescription("The depth of the inspection.").setRequired(false)
|
||||
);
|
||||
|
||||
this.listOfAllowedIDs = [
|
||||
"278620217221971968", // Matthew
|
||||
];
|
||||
// this checks against the database and adds all of the partners that are "allowed to perform dev commands"
|
||||
// doing the database check in the initialization prevents us from having to check the database every time this command is ran
|
||||
PartnerModel.find({ canPerformDevCommands: true }).then((partners) => {
|
||||
for (const partner of partners) {
|
||||
this.listOfAllowedIDs.push(partner.discordID as string);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async execute(interaction: ChatInputCommandInteraction) {
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
|
||||
import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
|
||||
import axios from "axios";
|
||||
|
||||
interface CertificateDetails {
|
||||
bitLength: number;
|
||||
connection: {
|
||||
cipherSuite: string;
|
||||
tlsVersion: string;
|
||||
};
|
||||
emailAddresses: [];
|
||||
extendedKeyUsage: number[];
|
||||
extendedKeyUsageAsText: string[];
|
||||
fingerprint: string;
|
||||
issuer: {
|
||||
commonName: string;
|
||||
country: string[];
|
||||
locality: never; // TODO: needs clarification
|
||||
province: string[];
|
||||
organization: string[];
|
||||
organizationalUnit: never; // TODO: needs clarification
|
||||
};
|
||||
keyUsageAsText: string[];
|
||||
notAfter: Date;
|
||||
notBefore: Date;
|
||||
publicKeyAlgorithm: string;
|
||||
san: string[];
|
||||
serialNumber: string;
|
||||
signatureAlgorithm: string;
|
||||
status: boolean;
|
||||
subject: {
|
||||
commonName: string;
|
||||
country: string[];
|
||||
locality: never; // TODO: needs clarification
|
||||
province: string[];
|
||||
organization: string[];
|
||||
organizationalUnit: never; // TODO: needs clarification
|
||||
};
|
||||
validationType: "DV" | "OV" | "EV";
|
||||
}
|
||||
|
||||
// Define an enum for security levels
|
||||
enum SecurityLevel {
|
||||
MostSecure,
|
||||
Secure,
|
||||
LessSecure,
|
||||
NotSecure,
|
||||
}
|
||||
|
||||
interface CipherSuite {
|
||||
cipher: string;
|
||||
securityLevel: SecurityLevel;
|
||||
}
|
||||
|
||||
const CipherSuites: CipherSuite[] = [
|
||||
// Most Secure (TLS 1.3 AEAD Ciphers)
|
||||
{ cipher: "TLS_AES_256_GCM_SHA384", securityLevel: SecurityLevel.MostSecure },
|
||||
{ cipher: "TLS_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.MostSecure },
|
||||
{ cipher: "TLS_AES_128_GCM_SHA256", securityLevel: SecurityLevel.MostSecure },
|
||||
|
||||
// Secure (TLS 1.2 AEAD Ciphers with Forward Secrecy)
|
||||
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
|
||||
{ cipher: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
|
||||
{ cipher: "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
|
||||
{ cipher: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
|
||||
{ cipher: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
|
||||
{ cipher: "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
|
||||
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
|
||||
{ cipher: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
|
||||
{ cipher: "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
|
||||
|
||||
// Less Secure (CBC with TLS 1.2 and SHA-256/SHA-384)
|
||||
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", securityLevel: SecurityLevel.LessSecure },
|
||||
{ cipher: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", securityLevel: SecurityLevel.LessSecure },
|
||||
{ cipher: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
|
||||
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
|
||||
{ cipher: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
|
||||
{ cipher: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
|
||||
{ cipher: "TLS_RSA_WITH_AES_256_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
|
||||
{ cipher: "TLS_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
|
||||
|
||||
// Not Secure (CBC with TLS 1.0/1.1 or SHA-1)
|
||||
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_RSA_WITH_3DES_EDE_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_RSA_WITH_RC4_128_SHA", securityLevel: SecurityLevel.NotSecure },
|
||||
{ cipher: "TLS_RSA_WITH_RC4_128_MD5", securityLevel: SecurityLevel.NotSecure },
|
||||
];
|
||||
|
||||
export default class TLS extends DiscordInteractionCommand {
|
||||
constructor() {
|
||||
super("tls", "Receives TLS information about an HTTP server with a FQDN.");
|
||||
this.builder.addStringOption((option) => {
|
||||
return option
|
||||
.setName("fqdn")
|
||||
.setDescription(
|
||||
"The Fully Qualified Domain Name (FQDN) for the server you want to perform the TLS/SSL lookup for."
|
||||
)
|
||||
.setRequired(true)
|
||||
.setMinLength(3);
|
||||
});
|
||||
}
|
||||
|
||||
public getCipherSecurityLevel(cipher: string): SecurityLevel | null {
|
||||
const result = CipherSuites.find((entry) => entry.cipher === cipher);
|
||||
return result ? result.securityLevel : null;
|
||||
}
|
||||
|
||||
public async execute(interaction: ChatInputCommandInteraction) {
|
||||
await interaction.deferReply({ ephemeral: false });
|
||||
try {
|
||||
const certAPIReq = await axios.get(`https://certapi.libraryofcode.org/`, {
|
||||
params: { q: interaction.options.getString("fqdn", true) },
|
||||
});
|
||||
if (certAPIReq.status !== 200) {
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"Could not fetch information for this FQDN's HTTP server. Please check the FQDN, its server, and try again.",
|
||||
});
|
||||
}
|
||||
const certData: CertificateDetails = certAPIReq.data;
|
||||
|
||||
if (!certData.status) {
|
||||
return interaction.editReply({
|
||||
content: "Issue when fetching this FQDN's TLS certificate. Please try again later.",
|
||||
});
|
||||
}
|
||||
const embed = new EmbedBuilder();
|
||||
embed.setAuthor({
|
||||
name: interaction.options.getString("fqdn", true),
|
||||
iconURL: `https://${interaction.options.getString("fqdn", true)}/favicon.ico`,
|
||||
});
|
||||
let desc = "";
|
||||
if (certData.validationType === "EV") {
|
||||
desc += `**Certificate issued to:** __${certData.subject.organization[0]} [${certData.subject.country[0]}]__\n**Verified by:** __${certData.issuer.organization[0]}__\n\n`;
|
||||
} else if (certData.issuer.organization) {
|
||||
desc += `**Verified by:** __${certData.issuer.organization[0]}__\n\n`;
|
||||
}
|
||||
if (certData.subject) {
|
||||
desc += "## Subject\n";
|
||||
if (certData.subject.organization && certData.issuer.commonName) {
|
||||
desc += `__**${certData.subject.organization[0]} (${certData.subject.commonName})**__\n`;
|
||||
}
|
||||
if (certData.subject.commonName) {
|
||||
desc += `**Common Name:** ${certData.subject.commonName}\n`;
|
||||
}
|
||||
if (certData.subject.organization) {
|
||||
desc += `**Organization:** ${certData.subject.organization[0]}\n`;
|
||||
}
|
||||
if (certData.subject.organizationalUnit) {
|
||||
desc += `**Organizational Unit:** ${certData.subject.organizationalUnit[0]}\n`;
|
||||
}
|
||||
if (certData.subject.locality) {
|
||||
desc += `**Locality:** ${certData.subject.locality[0]}\n`;
|
||||
}
|
||||
if (certData.subject.province) {
|
||||
desc += `**State/Province:** ${certData.subject.province[0]}\n`;
|
||||
}
|
||||
if (certData.subject.country) {
|
||||
desc += `**Country:** ${certData.subject.country[0]}\n`;
|
||||
}
|
||||
}
|
||||
if (certData.issuer) {
|
||||
desc += "## Issuer\n";
|
||||
if (certData.issuer.organization && certData.issuer.commonName) {
|
||||
desc += `__**${certData.issuer.organization[0]} (${certData.issuer.commonName})**__\n`;
|
||||
}
|
||||
if (certData.issuer.commonName) {
|
||||
desc += `**Common Name:** ${certData.issuer.commonName}\n`;
|
||||
}
|
||||
if (certData.issuer.organization) {
|
||||
desc += `**Organization:** ${certData.issuer.organization[0]}\n`;
|
||||
}
|
||||
if (certData.issuer.organizationalUnit) {
|
||||
desc += `**Organizational Unit:** ${certData.issuer.organizationalUnit[0]}\n`;
|
||||
}
|
||||
if (certData.subject.locality) {
|
||||
desc += `**Locality:** ${certData.subject.locality[0]}\n`;
|
||||
}
|
||||
if (certData.issuer.province) {
|
||||
desc += `**State/Province:** ${certData.issuer.province[0]}\n`;
|
||||
}
|
||||
if (certData.issuer.country) {
|
||||
desc += `**Country:** ${certData.issuer.country[0]}\n`;
|
||||
}
|
||||
}
|
||||
embed.setDescription(desc);
|
||||
let validationType:
|
||||
| "Domain Validation (DV)"
|
||||
| "Organization Validation (OV)"
|
||||
| ":lock: Extended Validation (EV)"
|
||||
| string;
|
||||
switch (certData.validationType) {
|
||||
case "DV":
|
||||
validationType = "Domain Validation (DV)";
|
||||
break;
|
||||
case "OV":
|
||||
validationType = "Organization Validation (OV)";
|
||||
embed.setColor("#4287f5");
|
||||
break;
|
||||
case "EV":
|
||||
embed.setColor("#42f554");
|
||||
validationType = ":lock: Extended Validation (EV)";
|
||||
break;
|
||||
default:
|
||||
validationType = "N/A';";
|
||||
break;
|
||||
}
|
||||
let cipherSuiteText: string = "";
|
||||
switch (this.getCipherSecurityLevel(certData.connection.cipherSuite)) {
|
||||
case SecurityLevel.MostSecure:
|
||||
cipherSuiteText = `:green_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
|
||||
break;
|
||||
|
||||
case SecurityLevel.Secure:
|
||||
cipherSuiteText = `:yellow_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
|
||||
break;
|
||||
|
||||
case SecurityLevel.LessSecure:
|
||||
cipherSuiteText = `:orange_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
|
||||
break;
|
||||
|
||||
case SecurityLevel.NotSecure:
|
||||
cipherSuiteText = `:red_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
|
||||
break;
|
||||
|
||||
default:
|
||||
cipherSuiteText = `:grey_question: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
|
||||
break;
|
||||
}
|
||||
embed.addFields(
|
||||
{
|
||||
name: "Validation Type",
|
||||
value: validationType,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Cipher Suite",
|
||||
value: cipherSuiteText,
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
name: "Public Key Algorithm",
|
||||
value: `${certData.publicKeyAlgorithm} ${certData.bitLength}`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Signature Algorithm",
|
||||
value: certData.signatureAlgorithm,
|
||||
inline: true,
|
||||
},
|
||||
{ name: "Not Before", value: new Date(certData.notBefore).toUTCString(), inline: true },
|
||||
{ name: "Not After", value: new Date(certData.notAfter).toUTCString(), inline: true },
|
||||
{
|
||||
name: "Serial Number",
|
||||
value: certData.serialNumber,
|
||||
inline: true,
|
||||
}
|
||||
);
|
||||
console.log(certData); // TODO: Remove after testing.
|
||||
if (certData.keyUsageAsText?.length)
|
||||
embed.addFields({
|
||||
name: "Key Usages",
|
||||
value: certData.keyUsageAsText.join(", "),
|
||||
inline: true,
|
||||
});
|
||||
if (certData.extendedKeyUsageAsText?.length)
|
||||
embed.addFields({
|
||||
name: "Extended Key Usages",
|
||||
value: certData.extendedKeyUsageAsText.join(", "),
|
||||
inline: true,
|
||||
});
|
||||
// embed.addField('Common Name', x509.data.subject.commonName, true);
|
||||
// embed.addField('Issuer', x509.data.issuer.commonName, true);
|
||||
// embed.addBlankField();
|
||||
// embed.addField('Public Key Algorithm', x509.data.publicKeyAlgorithm, true);
|
||||
// embed.addField('Not Before', new Date(x509.data.notBefore).toUTCString(), true);
|
||||
// embed.addField('Not After', new Date(x509.data.notAfter).toUTCString(), true);
|
||||
// if (x509.data.keyUsageAsText.length) embed.addField('Key Usages', x509.data.keyUsageAsText.join(', '), true);
|
||||
// if (x509.data.extendedKeyUsageAsText.length) embed.addField('Extended Key Usages', x509.data.extendedKeyUsageAsText.join(', '), true);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
} catch (err) {
|
||||
return interaction.editReply({ content: `Error processing retrieval from FQDN: ${err}` });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,15 @@
|
|||
import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
|
||||
import { MemberModel } from "../../database/Member";
|
||||
import { MemberAdditionalAcknowledgement, MemberModel } from "../../database/Member";
|
||||
import Partner, {
|
||||
PartnerCommissionType,
|
||||
PartnerDepartment,
|
||||
PartnerModel,
|
||||
PartnerRoleType,
|
||||
} from "../../database/Partner";
|
||||
import { ChatInputCommandInteraction, EmbedBuilder, GuildMember } from "discord.js";
|
||||
import { ChatInputCommandInteraction, EmbedBuilder, GuildMember, Snowflake } from "discord.js";
|
||||
import MemberUtil from "../../util/MemberUtil";
|
||||
import EmojiConfig from "../../util/EmojiConfig";
|
||||
import Formatters from "../../util/Formatters";
|
||||
|
||||
export default class Whois extends DiscordInteractionCommand {
|
||||
constructor() {
|
||||
|
@ -36,7 +37,7 @@ export default class Whois extends DiscordInteractionCommand {
|
|||
const embed = new EmbedBuilder();
|
||||
// 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
|
||||
const formattedName = MemberUtil.formatName(guildMember, partner);
|
||||
const formattedName = Formatters.formatName(guildMember, partner);
|
||||
embed.setAuthor({ name: formattedName.text, iconURL: formattedName.iconURL });
|
||||
// set the thumbnail to the user's avatar
|
||||
embed.setThumbnail(guildMember.user.displayAvatarURL());
|
||||
|
@ -79,16 +80,23 @@ export default class Whois extends DiscordInteractionCommand {
|
|||
break;
|
||||
}
|
||||
if (partner.directReport) {
|
||||
if (partner.directReport instanceof Partner) {
|
||||
embedDescription += `**Direct Report**: ${partner.directReport.title}\n`;
|
||||
// fetch direct report object ref
|
||||
await partner.populate("directReport");
|
||||
// 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);
|
||||
if (embedDescription?.length > 0) embed.setDescription(embedDescription);
|
||||
if (embedDescription?.length > 0)
|
||||
embed.setDescription(`${embedDescription}\n\n<@${guildMember.id}>`);
|
||||
// add status to embed
|
||||
if (guildMember.presence?.status) {
|
||||
// TODO: this currently doesn't work for some reason
|
||||
switch (guildMember.presence.status) {
|
||||
case "online":
|
||||
embed.addFields({ name: "Status", value: "Online", inline: true });
|
||||
|
@ -99,7 +107,7 @@ export default class Whois extends DiscordInteractionCommand {
|
|||
case "dnd":
|
||||
embed.addFields({ name: "Status", value: "Do Not Disturb", inline: true });
|
||||
break;
|
||||
case "offline" || "invisible":
|
||||
case "offline":
|
||||
embed.addFields({ name: "Status", value: "Online", inline: true });
|
||||
break;
|
||||
default:
|
||||
|
@ -107,9 +115,87 @@ export default class Whois extends DiscordInteractionCommand {
|
|||
embed.addFields({ name: "Status", value: "", inline: true });
|
||||
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({
|
||||
text: `Discord ID: ${guildMember.id}${databaseMember ? `Internal ID: ${databaseMember?._id}` : ""}`,
|
||||
text: `Discord ID: ${guildMember.id}${databaseMember ? `| Internal ID: ${databaseMember?._id}` : ""}${partner ? ` | Partner ID: ${partner?.id}` : ""}`,
|
||||
});
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
|
|
107
index.ts
107
index.ts
|
@ -1,5 +1,5 @@
|
|||
import { Client, GatewayIntentBits, Partials, REST, Routes } from "discord.js";
|
||||
import { discordBotToken, discordClientID } from "./config.json"
|
||||
import { discordBotToken, discordClientID, mongoDBConnectionURI } from "./config.json";
|
||||
import Collection from "./util/Collection";
|
||||
import DiscordInteractionCommand from "./util/DiscordInteractionCommand";
|
||||
import DiscordEvent from "./util/DiscordEvent";
|
||||
|
@ -12,66 +12,65 @@ export const DiscordEvents: Collection<DiscordEvent> = new Collection();
|
|||
|
||||
// Instantiates a new Discord client
|
||||
const discordClient = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.DirectMessages,
|
||||
GatewayIntentBits.GuildIntegrations,
|
||||
GatewayIntentBits.GuildPresences,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildInvites,
|
||||
GatewayIntentBits.GuildModeration,
|
||||
],
|
||||
partials: [ Partials.GuildMember, Partials.Message, Partials.User, Partials.Channel, ],
|
||||
intents: [
|
||||
GatewayIntentBits.DirectMessages,
|
||||
GatewayIntentBits.GuildIntegrations,
|
||||
GatewayIntentBits.GuildPresences,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildInvites,
|
||||
GatewayIntentBits.GuildModeration,
|
||||
],
|
||||
partials: [Partials.GuildMember, Partials.Message, Partials.User, Partials.Channel],
|
||||
});
|
||||
const discordREST = new REST().setToken(discordBotToken);
|
||||
// const stripeClient = new Stripe(stripeToken, { typescript: true });
|
||||
|
||||
export async function main() {
|
||||
// Connect to the databases
|
||||
try {
|
||||
mongoose.connection.once("open", () => {
|
||||
console.info("[Info - Database] Connected to MongoDB");
|
||||
})
|
||||
// TODO: Fetch the MongoDB URI from the config file
|
||||
await mongoose.connect("mongodb://localhost:27017/crra-main", {});
|
||||
} catch (error) {
|
||||
console.error(`[Error - Database] Failed to connect to MongoDB: ${error}`);
|
||||
process.exit(1);
|
||||
}
|
||||
// Load Discord interaction commands
|
||||
for (const Command of Object.values(DiscordInteractionCommandsIndex)) {
|
||||
const instance = new Command();
|
||||
DiscordInteractionCommands.add(instance.name, instance);
|
||||
console.info(`[Info - Discord] Loaded interaction command: ${instance.name}`);
|
||||
}
|
||||
// Load Discord events
|
||||
for (const Event of Object.values(DiscordEventsIndex)) {
|
||||
const instance = new Event(discordClient);
|
||||
DiscordEvents.add(instance.name, instance);
|
||||
discordClient.on(instance.name, instance.execute);
|
||||
console.info(`[Info - Discord] Loaded event: ${instance.name}`);
|
||||
}
|
||||
await discordClient.login(discordBotToken);
|
||||
// Connect to the databases
|
||||
try {
|
||||
mongoose.connection.once("open", () => {
|
||||
console.info("[Info - Database] Connected to MongoDB");
|
||||
});
|
||||
// TODO: Fetch the MongoDB URI from the config file
|
||||
await mongoose.connect(mongoDBConnectionURI, {});
|
||||
} catch (error) {
|
||||
console.error(`[Error - Database] Failed to connect to MongoDB: ${error}`);
|
||||
process.exit(1);
|
||||
}
|
||||
// Load Discord interaction commands
|
||||
for (const Command of Object.values(DiscordInteractionCommandsIndex)) {
|
||||
const instance = new Command();
|
||||
DiscordInteractionCommands.add(instance.name, instance);
|
||||
console.info(`[Info - Discord] Loaded interaction command: ${instance.name}`);
|
||||
}
|
||||
// Load Discord events
|
||||
for (const Event of Object.values(DiscordEventsIndex)) {
|
||||
const instance = new Event(discordClient);
|
||||
DiscordEvents.add(instance.name, instance);
|
||||
discordClient.on(instance.name, instance.execute);
|
||||
console.info(`[Info - Discord] Loaded event: ${instance.name}`);
|
||||
}
|
||||
await discordClient.login(discordBotToken);
|
||||
|
||||
try {
|
||||
console.log(`Started refreshing ${DiscordInteractionCommands.size} application (/) commands.`);
|
||||
const interactionCommandsData = [];
|
||||
for (const command of DiscordInteractionCommands.values()) {
|
||||
interactionCommandsData.push(command.builder.toJSON());
|
||||
}
|
||||
|
||||
// The put method is used to fully refresh all commands in the guild with the current set
|
||||
const data = await discordREST.put(
|
||||
Routes.applicationCommands(discordClientID),
|
||||
{ body: interactionCommandsData },
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
console.log(`Successfully reloaded ${data?.length} application (/) commands.`);
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
try {
|
||||
console.log(`Started refreshing ${DiscordInteractionCommands.size} application (/) commands.`);
|
||||
const interactionCommandsData = [];
|
||||
for (const command of DiscordInteractionCommands.values()) {
|
||||
interactionCommandsData.push(command.builder.toJSON());
|
||||
}
|
||||
|
||||
// The put method is used to fully refresh all commands in the guild with the current set
|
||||
const data = await discordREST.put(Routes.applicationCommands(discordClientID), {
|
||||
body: interactionCommandsData,
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
console.log(`Successfully reloaded ${data?.length} application (/) commands.`);
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
{
|
||||
"name": "CRRA",
|
||||
"name": "crv2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "crv2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@typegoose/typegoose": "^12.2.0",
|
||||
"auth0": "^4.12.0",
|
||||
"axios": "^1.7.8",
|
||||
"discord.js": "^14.14.1",
|
||||
"mongoose": "^8.2.2",
|
||||
"stripe": "^14.21.0",
|
||||
|
@ -325,9 +329,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz",
|
||||
"integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==",
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz",
|
||||
"integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"levn": "^0.4.1"
|
||||
|
@ -933,6 +937,39 @@
|
|||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/auth0": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/auth0/-/auth0-4.12.0.tgz",
|
||||
"integrity": "sha512-5WDAHb8EvWSmRyA9D+FTBrHdEL1RM48PTPHVPxSmzbiAXrhR4pSgwSJyoGjia2+rvMR2NMXhtMfuRRqosEp7PA==",
|
||||
"dependencies": {
|
||||
"jose": "^4.13.2",
|
||||
"undici-types": "^6.15.0",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/auth0/node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.8",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz",
|
||||
"integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
@ -1300,6 +1337,17 @@
|
|||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||
|
@ -1322,9 +1370,9 @@
|
|||
"devOptional": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
|
@ -1377,6 +1425,14 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
|
@ -1871,6 +1927,25 @@
|
|||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||
|
@ -1886,6 +1961,19 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
|
@ -2234,6 +2322,14 @@
|
|||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "4.15.9",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
|
||||
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
|
@ -2618,6 +2714,25 @@
|
|||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
|
||||
|
@ -3114,6 +3229,11 @@
|
|||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"name": "crv2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.13.0",
|
||||
|
@ -18,6 +19,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@typegoose/typegoose": "^12.2.0",
|
||||
"auth0": "^4.12.0",
|
||||
"discord.js": "^14.14.1",
|
||||
"mongoose": "^8.2.2",
|
||||
"stripe": "^14.21.0",
|
||||
|
|
|
@ -8,5 +8,5 @@ export default abstract class DiscordEvent {
|
|||
this.client = client;
|
||||
this.execute = this.execute.bind(this);
|
||||
}
|
||||
public abstract execute(...args: any[]): Error | Promise<void>;
|
||||
public abstract execute(...args: never[]): Error | Promise<void>;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
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 partner is designated as a KeyHolder, add a [k] to the end of the name
|
||||
// if the partner exists, set the iconURL to the organizational logo
|
||||
if (partner?.isKeyHolder) {
|
||||
return {
|
||||
text: `${target.displayName} [k]`,
|
||||
iconURL: target.displayAvatarURL(),
|
||||
};
|
||||
} else if (partner?.roleType === PartnerRoleType.MANAGERIAL) {
|
||||
// if a partner is of RoleType MANAGERIAL, add [m] to their name
|
||||
return {
|
||||
text: `${target.displayName} [m]`,
|
||||
iconURL:
|
||||
target instanceof GuildMember
|
||||
? target.user.displayAvatarURL()
|
||||
: 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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,13 +8,14 @@ import Partner, {
|
|||
import Member, { MemberAdditionalAcknowledgement, MemberModel } from "../database/Member";
|
||||
import { Client, GuildMember, User } from "discord.js";
|
||||
import { guildID } from "../config.json";
|
||||
import { Ref } from "@typegoose/typegoose";
|
||||
|
||||
export interface PartnerOptions {
|
||||
roleType: PartnerRoleType;
|
||||
commissionType: PartnerCommissionType;
|
||||
department: PartnerDepartment;
|
||||
title: PartnerTitle;
|
||||
directReport: Partner | string;
|
||||
directReport: Ref<Partner>;
|
||||
}
|
||||
|
||||
export interface FormatNameOptions {
|
||||
|
@ -25,7 +26,7 @@ export interface FormatNameOptions {
|
|||
// TODO: Add the rest of the remaining role configurations
|
||||
export const PartnerDiscordRoleMap = {
|
||||
// Director of Engineering, Management, Staff, Technician, Core Team, Play Caller
|
||||
"Director of Engineering": [
|
||||
Engineering: [
|
||||
"1077646568091570236",
|
||||
"1077646956890951690",
|
||||
"446104438969466890",
|
||||
|
@ -34,7 +35,7 @@ export const PartnerDiscordRoleMap = {
|
|||
"1014978134573064293",
|
||||
],
|
||||
// Director of Operations, Management, Staff, Moderator, Core Team, Play Caller
|
||||
"Director of Operations": [
|
||||
Operations: [
|
||||
"1077647072163020840",
|
||||
"1077646956890951690",
|
||||
"446104438969466890",
|
||||
|
@ -79,40 +80,4 @@ export default class MemberUtil {
|
|||
{ $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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue