2023-11-13 16:10:33 +01:00
import { AutocompleteInteraction , Interaction , SlashCommandBuilder } from 'discord.js' ;
2023-11-12 20:44:03 +01:00
import { Counter , CounterUserLink , db } from '../lib/db' ;
2023-11-13 17:29:49 +01:00
import { counterAutocomplete , counterConfigs , findCounter , getCounterConfigRaw , getOptions , parseConfig , setCounterConfig , toStringConfig , updateCounter } from '../lib/counter' ;
2023-11-12 20:44:03 +01:00
function extendOption ( t : string ) {
return { name : t , value : t } ;
}
module .exports = {
data : new SlashCommandBuilder ( )
. setName ( 'counter' )
. setDescription ( '[ADMIN] Counter management' )
. addSubcommandGroup ( grp = >
grp
. setName ( 'allowlist' )
. setDescription ( '[ADMIN] Counter allowlist management' )
. addSubcommand ( sub = >
sub
. setName ( 'add' )
. setDescription ( '[ADMIN] Add a user to the allowlist' )
. addStringOption ( opt = >
opt
. setName ( 'type' )
. setDescription ( 'The counter to operate on' )
. setRequired ( true )
. setAutocomplete ( true )
)
. addStringOption ( opt = >
opt
. setName ( 'usertype' )
. setDescription ( 'Type of user in this predicament' )
. setChoices ( . . . [ 'consumer' , 'producer' ] . map ( extendOption ) )
. setRequired ( true )
)
. addUserOption ( opt = >
opt
. setName ( 'user' )
. setDescription ( 'The user to add' )
. setRequired ( true )
)
)
. addSubcommand ( sub = >
sub
. setName ( 'remove' )
. setDescription ( '[ADMIN] Remove a user from the allowlist' )
. addStringOption ( opt = >
opt
. setName ( 'type' )
. setDescription ( 'The counter to operate on' )
. setRequired ( true )
. setAutocomplete ( true )
)
. addStringOption ( opt = >
opt
. setName ( 'usertype' )
. setDescription ( 'Type of user in this predicament' )
. setChoices ( . . . [ 'consumer' , 'producer' ] . map ( extendOption ) )
. setRequired ( true )
)
. addUserOption ( opt = >
opt
. setName ( 'user' )
. setDescription ( 'The user to remove' )
. setRequired ( true )
)
)
. addSubcommand ( sub = >
sub
. setName ( 'toggle' )
. setDescription ( '[ADMIN] Enable or disable the allowlist.' )
. addStringOption ( opt = >
opt
. setName ( 'type' )
. setDescription ( 'The counter to operate on' )
. setRequired ( true )
. setAutocomplete ( true )
)
. addStringOption ( opt = >
opt
. setName ( 'usertype' )
. setDescription ( 'Type of user in this predicament' )
. setChoices ( . . . [ 'consumer' , 'producer' ] . map ( extendOption ) )
. setRequired ( true )
)
. addBooleanOption ( opt = >
opt
. setName ( 'enabled' )
. setDescription ( 'Enable or disable the allowlist' )
. setRequired ( true )
)
)
. addSubcommand ( sub = >
sub
. setName ( 'list' )
. setDescription ( '[ADMIN] List people in the allowlist' )
. addStringOption ( opt = >
opt
. setName ( 'type' )
. setDescription ( 'The counter to operate on' )
. setRequired ( true )
. setAutocomplete ( true )
)
. addStringOption ( opt = >
opt
. setName ( 'usertype' )
. setDescription ( 'Type of user in this predicament' )
. setChoices ( . . . [ 'consumer' , 'producer' ] . map ( extendOption ) )
. setRequired ( true )
)
)
)
. addSubcommand ( sub = >
sub
. setName ( 'create' )
. setDescription ( '[ADMIN] Create a counter' )
. addChannelOption ( option = >
option
. setName ( 'channel' )
. setDescription ( 'Channel to put updates into' )
. setRequired ( true )
)
. addStringOption ( option = >
option
. setName ( 'key' )
. setDescription ( 'The codename. Best to leave descriptive for later; used in searching for counters' )
. setRequired ( true )
)
. addStringOption ( option = >
option
. setName ( 'emoji' )
. setDescription ( 'An emoji or symbol or something to represent the counter' )
. setRequired ( true )
2023-11-13 16:10:33 +01:00
. setMaxLength ( 100 )
2023-11-12 20:44:03 +01:00
)
. addNumberOption ( option = >
option
. setName ( 'value' )
. setDescription ( 'Initial value to start with' )
)
)
2023-11-13 16:10:33 +01:00
. addSubcommand ( sub = >
sub
. setName ( 'set' )
. setDescription ( '[ADMIN] Configure a counter' )
. addStringOption ( opt = >
opt
. setName ( 'type' )
. setDescription ( 'The counter to operate on' )
. setRequired ( true )
. setAutocomplete ( true )
)
. addStringOption ( opt = >
opt
. setName ( 'key' )
. setDescription ( 'The config name' )
. setRequired ( true )
. setChoices ( . . . [ . . . counterConfigs . keys ( ) ] . map ( extendOption ) )
)
. addStringOption ( opt = >
opt
. setName ( 'value' )
. setDescription ( 'The new value' )
. setRequired ( true )
. setAutocomplete ( true )
. setMaxLength ( 100 )
)
)
2023-11-12 20:44:03 +01:00
. addSubcommand ( sub = >
sub
. setName ( 'delete' )
. setDescription ( '[ADMIN] Delete a counter' )
. addStringOption ( opt = >
opt
. setName ( 'type' )
. setDescription ( 'The counter to operate on' )
. setRequired ( true )
. setAutocomplete ( true )
)
)
. setDefaultMemberPermissions ( '0' )
. setDMPermission ( false ) ,
execute : async ( interaction : Interaction ) = > {
if ( ! interaction . isChatInputCommand ( ) ) return ;
await interaction . deferReply ( { ephemeral : true } ) ;
const subcommand = interaction . options . getSubcommand ( true ) ;
const group = interaction . options . getSubcommandGroup ( ) ;
if ( group === 'allowlist' ) {
const type = interaction . options . getString ( 'type' ) ! ;
let counter ;
try {
2023-11-13 17:29:49 +01:00
counter = await findCounter ( type , interaction . guildId ! ) ;
2023-11-12 20:44:03 +01:00
} catch ( err ) {
await interaction . followUp ( {
content : 'No such counter!'
} ) ;
return ;
}
if ( subcommand === 'add' ) {
const user = interaction . options . getUser ( 'user' , true ) ;
const userType = interaction . options . getString ( 'usertype' , true ) ;
const link = await db < CounterUserLink > ( 'counterUserLink' )
2023-11-13 17:29:49 +01:00
. where ( 'id' , counter . id )
2023-11-12 20:44:03 +01:00
. where ( 'user' , user . id )
. where ( 'producer' , userType === 'producer' )
. first ( ) ;
if ( link ) {
await interaction . followUp ( {
content : ` <@ ${ user . id } > is already in the ${ counter . emoji } ** ${ userType } ** allowlist! `
} ) ;
return ;
}
await db < CounterUserLink > ( 'counterUserLink' )
. insert ( {
2023-11-13 17:29:49 +01:00
'id' : counter . id ,
2023-11-12 20:44:03 +01:00
'user' : user . id ,
'producer' : userType === 'producer'
} ) ;
await interaction . followUp ( {
content : ` <@ ${ user . id } > added to the ${ counter . emoji } ** ${ userType } ** allowlist. `
} ) ;
} else if ( subcommand === 'remove' ) {
const user = interaction . options . getUser ( 'user' , true ) ;
const userType = interaction . options . getString ( 'usertype' , true ) ;
const link = await db < CounterUserLink > ( 'counterUserLink' )
2023-11-13 17:29:49 +01:00
. where ( 'id' , counter . id )
2023-11-12 20:44:03 +01:00
. where ( 'user' , user . id )
. where ( 'producer' , userType === 'producer' )
. first ( ) ;
if ( ! link ) {
await interaction . followUp ( {
content : ` <@ ${ user . id } > is not in the ${ counter . emoji } ** ${ userType } ** allowlist! `
} ) ;
return ;
}
await interaction . followUp ( {
content : ` <@ ${ user . id } > has been removed from the ${ counter . emoji } ** ${ userType } ** allowlist. `
} ) ;
} else if ( subcommand === 'toggle' ) {
const enabled = interaction . options . getBoolean ( 'enabled' , true ) ;
const userType = interaction . options . getString ( 'usertype' , true ) ;
if ( userType === 'producer' ) {
await db < Counter > ( 'counters' )
2023-11-13 17:29:49 +01:00
. where ( 'id' , counter . id )
2023-11-12 20:44:03 +01:00
. update ( {
'allowlistProducer' : enabled
} ) ;
} else {
await db < Counter > ( 'counters' )
2023-11-13 17:29:49 +01:00
. where ( 'id' , counter . id )
2023-11-12 20:44:03 +01:00
. update ( {
2023-11-12 21:03:04 +01:00
'allowlistConsumer' : enabled
2023-11-12 20:44:03 +01:00
} ) ;
}
await interaction . followUp ( {
content : ` ${ counter . emoji } ${ userType . slice ( 0 , 1 ) . toUpperCase ( ) + userType . slice ( 1 ) } allowlist is now ** ${ enabled ? 'enabled' : 'disabled' } **. `
} ) ;
} else if ( subcommand === 'list' ) {
const userType = interaction . options . getString ( 'usertype' , true ) ;
const users = await db < CounterUserLink > ( 'counterUserLink' )
2023-11-13 17:29:49 +01:00
. where ( 'id' , counter . id )
2023-11-12 20:44:03 +01:00
. where ( 'producer' , userType === 'producer' ) ;
const enabled = ( userType === 'producer' ) ? counter.allowlistProducer : counter.allowlistConsumer ;
await interaction . followUp ( {
content : ` ${ counter . emoji } ${ userType . slice ( 0 , 1 ) . toUpperCase ( ) + userType . slice ( 1 ) } s: \ n ${ users . map ( u = > ` - <@ ${ u . user } > ` ) } \ nThe ${ userType } allowlist is currently ** ${ enabled ? 'enabled' : 'disabled' } **. `
} ) ;
}
} else {
if ( subcommand === 'create' ) {
const channel = interaction . options . getChannel ( 'channel' , true ) ;
const key = interaction . options . getString ( 'key' , true ) ;
const emoji = interaction . options . getString ( 'emoji' , true ) ;
const value = interaction . options . getNumber ( 'value' ) || 0 ;
const guild = interaction . guildId ! ;
2023-11-13 17:29:49 +01:00
const [ counter ] = await db < Counter > ( 'counters' )
2023-11-12 20:44:03 +01:00
. insert ( {
'key' : key ,
'emoji' : emoji ,
'value' : value ,
'channel' : channel . id ,
'guild' : guild
2023-11-13 17:29:49 +01:00
} )
. returning ( '*' ) ;
2023-11-12 20:44:03 +01:00
2023-11-13 17:29:49 +01:00
await updateCounter ( interaction . client , counter , value ) ;
2023-11-12 20:44:03 +01:00
await interaction . followUp ( {
content : ` <# ${ channel . id } > has been **enriched** with your new counter. Congratulations! `
} ) ;
2023-11-13 16:10:33 +01:00
} else if ( subcommand === 'set' ) {
const type = interaction . options . getString ( 'type' ) ! ;
let counter ;
try {
2023-11-13 17:29:49 +01:00
counter = await findCounter ( type , interaction . guildId ! ) ;
2023-11-13 16:10:33 +01:00
} catch ( err ) {
await interaction . followUp ( {
content : 'No such counter!'
} ) ;
return ;
}
2023-11-13 17:29:49 +01:00
const config = await getCounterConfigRaw ( counter ) ;
2023-11-13 16:10:33 +01:00
const key = interaction . options . getString ( 'key' , true ) ;
const value = interaction . options . getString ( 'value' , true ) ;
const defaultConfig = counterConfigs . get ( key ) ;
if ( ! defaultConfig ) return interaction . followUp ( ` No config named \` ${ key } \` exists! ` ) ;
const parsedValue = parseConfig ( value , defaultConfig . type ) ;
const restringedValue = toStringConfig ( parsedValue , defaultConfig . type ) ;
2023-11-13 17:29:49 +01:00
await setCounterConfig ( counter . id , key , restringedValue ) ;
2023-11-13 16:10:33 +01:00
await interaction . followUp ( ` ${ counter . emoji } \` ${ key } \` is now \` ${ restringedValue } \` . (was \` ${ config . get ( key ) || toStringConfig ( defaultConfig . default , defaultConfig . type ) } \` ) ` ) ;
2023-11-12 20:44:03 +01:00
} else if ( subcommand === 'delete' ) {
const type = interaction . options . getString ( 'type' ) ! ;
let counter ;
try {
2023-11-13 17:29:49 +01:00
counter = await findCounter ( type , interaction . guildId ! ) ;
2023-11-12 20:44:03 +01:00
} catch ( err ) {
await interaction . followUp ( {
content : 'No such counter!'
} ) ;
return ;
}
await db < Counter > ( 'counters' )
2023-11-13 17:29:49 +01:00
. where ( 'id' , counter . id )
2023-11-12 20:44:03 +01:00
. delete ( ) ;
await db < CounterUserLink > ( 'counterUserLink' )
2023-11-13 17:29:49 +01:00
. where ( 'id' , counter . id )
2023-11-12 20:44:03 +01:00
. delete ( ) ;
await interaction . followUp ( {
content : ` The ${ counter . emoji } counter has been removed. 😭 `
} ) ;
}
}
} ,
2023-11-13 16:10:33 +01:00
autocomplete : async ( interaction : AutocompleteInteraction ) = > { {
const focused = interaction . options . getFocused ( true ) ;
if ( focused . name === 'type' ) {
return counterAutocomplete ( interaction ) ;
} else if ( focused . name === 'value' ) {
const type = interaction . options . getString ( 'type' , true ) ;
2023-11-13 17:29:49 +01:00
const counter = await findCounter ( type , interaction . guildId ! ) ;
2023-11-13 16:10:33 +01:00
2023-11-13 17:29:49 +01:00
const config = await getCounterConfigRaw ( counter ) ;
2023-11-13 16:10:33 +01:00
const key = interaction . options . getString ( 'key' ) ;
if ( ! key ) return interaction . respond ( [ ] ) ;
const defaultConfig = counterConfigs . get ( key ) ;
if ( ! defaultConfig ) return interaction . respond ( [ ] ) ;
const defaultOptions = getOptions ( defaultConfig . type ) ;
let options = [
{
value : ` ${ config . get ( key ) || toStringConfig ( defaultConfig . default , defaultConfig . type ) } ` ,
name : ` [CURRENT] ${ config . get ( key ) || toStringConfig ( defaultConfig . default , defaultConfig . type ) } ` . slice ( 0 , 99 )
} ,
. . . defaultOptions . filter ( s = > s . startsWith ( focused . value ) ) . map ( extendOption )
] ;
if ( focused . value !== '' && ! options . find ( opt = > opt . value === focused . value ) ) {
options = [
{
value : focused.value ,
name : focused.value
} ,
. . . options
] ;
}
await interaction . respond ( options ) ;
}
} }
2023-11-12 20:44:03 +01:00
} ;