2024-11-14 23:52:37 -05:00
import { MemberModel } from "../../database/Member" ;
2024-11-15 16:57:36 -05:00
import Partner , {
2024-11-14 23:52:37 -05:00
PartnerModel ,
PartnerCommissionType ,
PartnerRoleType ,
PartnerDepartment ,
PartnerTitle ,
} from "../../database/Partner" ;
2024-11-15 16:57:36 -05:00
2024-04-05 20:12:22 -04:00
import DiscordInteractionCommand from "../../util/DiscordInteractionCommand" ;
2024-11-14 23:52:37 -05:00
import {
ChatInputCommandInteraction ,
InteractionContextType ,
PermissionFlagsBits ,
} from "discord.js" ;
//TODO: ad email validation
2024-04-05 20:12:22 -04:00
2024-11-14 23:52:37 -05:00
//TODO: cover all partner model properties in cooresponding sub commands
const partnerTitles : PartnerTitle [ ] = [
"Director of Engineering" ,
"Director of Operations" ,
"Deputy Director of Engineering" ,
"Deputy Director of Operations" ,
"Services Manager" ,
"Project Manager" ,
"Engineering Core Partner" ,
"Operations Core Partner" ,
"Community Moderator" ,
"Technician" ,
] ;
2024-11-15 16:57:36 -05:00
export default class PartnerCommand extends DiscordInteractionCommand {
2024-10-25 16:57:33 -04:00
constructor ( ) {
super ( "partner" , "Manipulates partner information." ) ;
2024-11-14 23:52:37 -05:00
this . builder
. setDefaultMemberPermissions ( PermissionFlagsBits . Administrator )
. setContexts ( InteractionContextType . Guild ) ;
this . builder . addSubcommand ( ( c ) = >
c
. setName ( "add" )
. setDescription ( "test" )
. addUserOption ( ( option ) = >
option . setName ( "partner" ) . setDescription ( "the partner you want to add." ) . setRequired ( true )
)
. addStringOption ( ( option ) = >
option . setName ( "email" ) . setDescription ( "their email address." ) . setRequired ( true )
)
. addStringOption ( ( option ) = >
option
. setName ( "role-type" )
. setDescription ( "their roleType." )
. setChoices ( this . formatOptionsForDiscordFromEnum ( PartnerRoleType ) )
. setRequired ( true )
)
. addStringOption ( ( option ) = >
option
. setName ( "commission-type" )
. setDescription ( "their commissionType." )
. setChoices ( this . formatOptionsForDiscordFromEnum ( PartnerCommissionType ) )
. setRequired ( true )
)
. addStringOption ( ( option ) = >
option
. setName ( "title" )
. setDescription ( "their title." )
. setChoices ( this . formatPartnerTitlesArrayForDiscord ( ) )
. setRequired ( true )
)
. addStringOption ( ( option ) = >
option
. setName ( "department" )
. setDescription ( "their department." )
. setChoices ( this . formatOptionsForDiscordFromEnum ( PartnerDepartment ) )
. setRequired ( true )
)
2024-11-15 16:57:36 -05:00
. addUserOption ( ( option ) = >
option . setName ( "direct-report" ) . setDescription ( "their direct report." )
)
2024-11-14 23:52:37 -05:00
) ;
this . builder . addSubcommand ( ( c ) = >
c
. setName ( "get" )
. setDescription ( "get" )
. addUserOption ( ( option ) = >
option
. setName ( "partner" )
. setDescription ( "the partner you want to get info abouts." )
. setRequired ( true )
)
) ;
this . builder . addSubcommand ( ( c ) = >
c
. setName ( "delete" )
. setDescription ( "delete" )
. addUserOption ( ( option ) = >
option
. setName ( "partner" )
. setDescription ( "the partner you want to delete." )
. setRequired ( true )
)
) ;
this . builder . addSubcommand ( ( c ) = >
c
. setName ( "update" )
. setDescription ( "update" )
. addUserOption ( ( option ) = >
option
. setName ( "partner" )
. setDescription ( "the partner you want to update." )
. setRequired ( true )
)
2024-11-15 16:57:36 -05:00
. addUserOption ( ( option ) = >
option . setName ( "direct-report" ) . setDescription ( "their direct report." )
)
2024-11-14 23:52:37 -05:00
. addStringOption ( ( option ) = > option . setName ( "email" ) . setDescription ( "their email address." ) )
. addStringOption ( ( option ) = >
option
. setName ( "role-type" )
. setDescription ( "their roleType." )
. setChoices ( this . formatOptionsForDiscordFromEnum ( PartnerRoleType ) )
)
. addStringOption ( ( option ) = >
option
. setName ( "commission-type" )
. setDescription ( "their commissionType." )
. setChoices ( this . formatOptionsForDiscordFromEnum ( PartnerCommissionType ) )
)
. addStringOption ( ( option ) = >
option
. setName ( "title" )
. setDescription ( "their title." )
. setChoices ( this . formatPartnerTitlesArrayForDiscord ( ) )
)
. addStringOption ( ( option ) = >
option
. setName ( "department" )
. setDescription ( "their department." )
. setChoices ( this . formatOptionsForDiscordFromEnum ( PartnerDepartment ) )
)
) ;
2024-10-25 16:57:33 -04:00
}
2024-04-05 20:12:22 -04:00
2024-10-25 16:57:33 -04:00
public async execute ( interaction : ChatInputCommandInteraction ) : Promise < void > {
2024-11-14 23:52:37 -05:00
const subcommandName = interaction . options . getSubcommand ( true ) ;
switch ( subcommandName ) {
case "get" :
await this . handleGetSubcommand ( interaction ) ;
break ;
case "add" :
await this . handleAddSubcommand ( interaction ) ;
break ;
case "delete" :
await this . handleDeleteSubcommand ( interaction ) ;
break ;
case "update" :
await this . handleUpdateSubcommand ( interaction ) ;
break ;
default :
//discord does not allow parent/main commands to be excutable, this range is limited to options entered via commandbuiler
break ;
2024-04-05 20:12:22 -04:00
}
2024-10-25 16:57:33 -04:00
}
2024-11-14 23:52:37 -05:00
async handleAddSubcommand ( interaction : ChatInputCommandInteraction ) {
const partnerOption = interaction . options . getUser ( "partner" , true ) ;
2024-11-15 16:57:36 -05:00
const directReport = interaction . options . getUser ( "direct-report" , false ) ;
2024-11-14 23:52:37 -05:00
const partnerOptionEmailAddress = interaction . options . getString ( "email" , true ) ;
const partnerOptionRoleType = interaction . options . getString ( "role-type" , true ) ;
const partnerOptionCommisioComissionType = interaction . options . getString (
"commission-type" ,
true
) ;
const partnerOptionDepartment = interaction . options . getString ( "department" , true ) ;
const partnerOptionTitle = interaction . options . getString ( "title" , true ) ;
const partner = await PartnerModel . findOne ( { discordID : partnerOption.id } ) . exec ( ) ;
if ( partner )
return interaction . reply ( {
content : "The specified user already has a partner entry." ,
ephemeral : false ,
} ) ;
/ *
const member = await MemberModel . findOne ( { discordID : partnerOption.id } ) . exec ( ) ;
if ( ! member )
return interaction . reply ( {
content : "The specified partner does not have a base member entry." ,
ephemeral : false ,
} ) ;
* /
2024-11-15 16:57:36 -05:00
let directReportPartnerDocumentFromDb ;
if ( directReport ) {
directReportPartnerDocumentFromDb = await PartnerModel . findOne ( {
discordID : directReport.id ,
} ) . exec ( ) ;
if ( ! directReportPartnerDocumentFromDb )
return interaction . reply ( {
content : ` the specified directReport ${ directReport . username } does not have an entry in partner database, please add them first them before assigning subordinates ` ,
ephemeral : false ,
} ) ;
}
2024-11-14 23:52:37 -05:00
let newPartner = new PartnerModel ( {
discordID : partnerOption.id ,
emailAddress : partnerOptionEmailAddress ,
roleType : partnerOptionRoleType ,
commissionType : partnerOptionCommisioComissionType ,
department : partnerOptionDepartment ,
title : partnerOptionTitle ,
2024-11-15 16:57:36 -05:00
directReport :
directReport && directReportPartnerDocumentFromDb
? directReportPartnerDocumentFromDb . _id
: null ,
2024-11-14 23:52:37 -05:00
} ) ;
await newPartner . save ( ) ;
return interaction . reply ( {
content : ` \` \` \` \ n ${ JSON . stringify ( newPartner , null , 2 ) } \ n \` \` \` ` ,
ephemeral : false ,
} ) ;
}
async handleGetSubcommand ( interaction : ChatInputCommandInteraction ) {
const partnerOption = interaction . options . getUser ( "partner" , true ) ;
2024-11-15 16:57:36 -05:00
let partner = await PartnerModel . findOne ( { discordID : partnerOption.id } ) . exec ( ) ;
2024-11-14 23:52:37 -05:00
if ( ! partner )
return interaction . reply ( {
content : "The specified partner does not an entry in the database." ,
ephemeral : false ,
} ) ;
2024-11-15 16:57:36 -05:00
if ( partner . directReport ) await partner . populate ( "directReport" ) ;
if ( partner . directReport && partner . directReport instanceof Partner ) {
console . log ( partner . directReport ) ;
return interaction . reply ( {
content : ` Raw entry \` \` \` \ n ${ JSON . stringify ( partner , null , 2 ) } \ n \` \` \` \ n \ nDirect report: \` \` \` \ n ${ JSON . stringify ( partner . directReport , null , 2 ) } \ n \` \` \` ` ,
ephemeral : false ,
} ) ;
}
2024-11-14 23:52:37 -05:00
return interaction . reply ( {
content : ` Raw entry \` \` \` \ n ${ JSON . stringify ( partner , null , 2 ) } \ n \` \` \` ` ,
ephemeral : false ,
} ) ;
}
async handleDeleteSubcommand ( interaction : ChatInputCommandInteraction ) {
const partnerOption = interaction . options . getUser ( "partner" , true ) ;
2024-11-15 16:57:36 -05:00
const partner = await PartnerModel . findOne ( { discordID : partnerOption.id } )
. populate ( "directReport" )
. exec ( ) ;
2024-11-14 23:52:37 -05:00
if ( ! partner )
return interaction . reply ( {
content : "The specified user does not have an entry." ,
ephemeral : false ,
} ) ;
2024-11-15 16:57:36 -05:00
if (
partner . directReport &&
partner . directReport instanceof Partner &&
interaction . user . id !== partner . directReport . discordID
)
return interaction . reply ( {
content :
"You're not authorized to delete this partner's information, only their direct report can." ,
ephemeral : false ,
} ) ;
2024-11-14 23:52:37 -05:00
await PartnerModel . findByIdAndDelete ( partner . id ) ;
return interaction . reply ( {
content : ` removed partner entry from the database. ` ,
ephemeral : false ,
} ) ;
}
async handleUpdateSubcommand ( interaction : ChatInputCommandInteraction ) {
const partnerOption = interaction . options . getUser ( "partner" , true ) ;
2024-11-15 16:57:36 -05:00
const directReport = interaction . options . getUser ( "direct-report" ) ;
2024-11-14 23:52:37 -05:00
const partnerOptionEmailAddress = interaction . options . getString ( "email" ) ;
const partnerOptionRoleType = interaction . options . getString ( "role-type" ) ;
const partnerOptionCommisioComissionType = interaction . options . getString ( "commission-type" ) ;
const partnerOptionDepartment = interaction . options . getString ( "department" ) ;
const partnerOptionTitle = interaction . options . getString ( "title" ) ;
if (
2024-11-15 16:57:36 -05:00
! directReport &&
2024-11-14 23:52:37 -05:00
! partnerOptionEmailAddress &&
! partnerOptionEmailAddress &&
! partnerOptionRoleType &&
! partnerOptionCommisioComissionType &&
! partnerOptionDepartment &&
! partnerOptionTitle
) {
return interaction . reply ( {
content : "You need to select atleast one option to update" ,
ephemeral : false ,
} ) ;
}
2024-11-15 16:57:36 -05:00
let partner = await PartnerModel . findOne ( { discordID : partnerOption.id } ) . exec ( ) ;
2024-11-14 23:52:37 -05:00
if ( ! partner )
return interaction . reply ( {
content : "The specified partner does not have an entry." ,
ephemeral : false ,
} ) ;
2024-11-15 16:57:36 -05:00
if ( partner . directReport ) partner = await partner . populate ( "directReport" ) ;
console . log ( partner . directReport ) ;
if (
partner . directReport instanceof PartnerModel &&
interaction . user . id !== partner . directReport . discordID
)
return interaction . reply ( {
content :
"You're not authorized to update this partner's information, only their direct report can." ,
ephemeral : false ,
} ) ;
let directReportPartnerDocumentFromDb ;
if ( directReport ) {
directReportPartnerDocumentFromDb = await PartnerModel . findOne ( {
discordID : directReport.id ,
} ) . exec ( ) ;
if ( ! directReportPartnerDocumentFromDb )
return interaction . reply ( {
content : ` the specified directReport ${ directReport . username } does not have an entry in partner database, please add them first them before assigning subordinates ` ,
ephemeral : false ,
} ) ;
}
2024-11-14 23:52:37 -05:00
let updateObj = {
discordID : partnerOption.id ,
emailAddress : partnerOptionEmailAddress ,
roleType : partnerOptionRoleType ,
commissionType : partnerOptionCommisioComissionType ,
department : partnerOptionDepartment ,
title : partnerOptionTitle ,
2024-11-15 16:57:36 -05:00
directReport :
directReport && directReportPartnerDocumentFromDb
? directReportPartnerDocumentFromDb . id
: null ,
2024-11-14 23:52:37 -05:00
} ;
try {
let updatedPartner = await this . updatePartnerInfo ( partner . id , updateObj ) ;
return interaction . reply ( {
content : ` updated partner! \ n \ n \` \` \` \ n ${ JSON . stringify ( updatedPartner , null , 2 ) } \ n \` \` \` ` ,
ephemeral : false ,
} ) ;
} catch ( error ) {
return interaction . reply ( {
content : ` an error occured ! \ n \ n \` \` \` \ n ${ JSON . stringify ( error , null , 2 ) } \ n \` \` \` ` ,
ephemeral : false ,
} ) ;
}
}
private formatPartnerTitlesArrayForDiscord ( ) : { name : PartnerTitle ; value : string } [ ] {
return partnerTitles . map ( ( title ) = > ( {
name : title ,
value : title ,
} ) ) ;
}
private formatOptionsForDiscordFromEnum ( args : any ) : { name : string ; value : string } [ ] {
return Object . entries ( args )
. filter ( ( [ key , value ] ) = > typeof value === "number" ) // Filter out reverse mappings
. map ( ( [ key , value ] ) = > ( {
name : key ,
value : ( value as number ) . toString ( ) ,
} ) ) ;
}
private async updatePartnerInfo < T > ( id : string , updateObj : Partial < T > ) {
// Remove keys with falsy values (undefined, null, etc.)
const filteredUpdate = Object . fromEntries (
Object . entries ( updateObj ) . filter ( ( [ key , value ] ) = > key !== "discordID" && value )
) ;
if ( Object . keys ( filteredUpdate ) . length === 0 ) {
throw new Error (
"Error in Partner update command, no options specified on update, you can safely ignore this error."
) ;
}
// Find and update the document by ID with only the valid fields
return await PartnerModel . findByIdAndUpdate ( id , filteredUpdate , { new : true } ) ;
}
2024-04-05 20:12:22 -04:00
}