2026-02-13 18:20:29 -05:00
import { db } from '../sequelize.js' ;
2026-02-13 21:23:33 -05:00
import { enums } from "../enums.js" ;
2026-02-14 01:00:23 -05:00
import { loadImage } from "canvas" ;
2026-02-14 13:10:43 -05:00
import { EmptyResultError } from "sequelize" ;
2026-02-14 14:27:28 -05:00
import { EmbedBuilder } from "@fluxerjs/core" ;
import { messageHelper } from "./messageHelper.js" ;
2026-02-13 18:20:29 -05:00
const mh = { } ;
2026-02-13 22:18:43 -05:00
// Has an empty "command" to parse the help message properly
2026-02-14 14:27:28 -05:00
const commandList = [ '--help' , 'add' , 'remove' , 'name' , 'listall' , 'displayName' , 'proxy' , 'propic' , '' ] ;
2026-02-13 21:45:01 -05:00
2026-02-13 21:23:33 -05:00
/ * *
* Parses through the subcommands that come after "pf;member" and calls functions accordingly .
*
2026-02-14 14:27:28 -05:00
* @ param { Object } author - The id of the message author
2026-02-13 21:23:33 -05:00
* @ param { string [ ] } args - The message arguments
2026-02-14 14:27:28 -05:00
* @ param { string | null } attachmentUrl - The message attachments
* @ returns { Promise < string > | Promise < EmbedBuilder > } A message , or an information embed .
2026-02-13 21:23:33 -05:00
* /
2026-02-14 14:27:28 -05:00
mh . parseMemberCommand = async function ( author , args , attachmentUrl ) {
const authorId = author . id ;
const authorFull = ` ${ author . username } ${ author . discriminator } ` ;
2026-02-13 22:18:43 -05:00
let member ;
// checks whether command is in list, otherwise assumes it's a name
2026-02-13 21:45:01 -05:00
if ( ! commandList . includes ( args [ 0 ] ) ) {
2026-02-13 22:18:43 -05:00
member = await getMemberInfo ( authorId , args [ 0 ] ) ;
2026-02-13 21:45:01 -05:00
}
2026-02-13 18:20:29 -05:00
switch ( args [ 0 ] ) {
2026-02-13 20:08:24 -05:00
case '--help' :
2026-02-13 21:18:44 -05:00
return enums . help . MEMBER ;
2026-02-13 20:08:24 -05:00
case 'add' :
2026-02-13 22:18:43 -05:00
return await addNewMember ( authorId , args ) ;
2026-02-13 18:56:36 -05:00
case 'remove' :
2026-02-13 22:18:43 -05:00
return await removeMember ( authorId , args ) ;
2026-02-14 13:37:51 -05:00
case 'name' :
return enums . help . NAME ;
2026-02-13 21:30:22 -05:00
case 'displayname' :
2026-02-13 22:18:43 -05:00
return enums . help . DISPLAY _NAME ;
2026-02-13 21:30:22 -05:00
case 'proxy' :
return enums . help . PROXY ;
2026-02-14 01:00:23 -05:00
case 'propic' :
return enums . help . PROPIC ;
2026-02-14 14:27:28 -05:00
case 'listall' :
return await getAllMembersInfo ( authorId , authorFull ) ;
2026-02-13 21:30:22 -05:00
case '' :
return enums . help . MEMBER ;
2026-02-13 18:20:29 -05:00
}
switch ( args [ 1 ] ) {
2026-02-13 18:56:36 -05:00
case '--help' :
2026-02-13 21:18:44 -05:00
return enums . help . MEMBER ;
2026-02-14 13:37:51 -05:00
case 'name' :
return await updateName ( authorId , args ) ;
2026-02-13 18:20:29 -05:00
case 'displayname' :
2026-02-13 22:18:43 -05:00
return await updateDisplayName ( authorId , args ) ;
2026-02-13 18:56:36 -05:00
case 'proxy' :
2026-02-13 22:18:43 -05:00
return await updateProxy ( authorId , args ) ;
2026-02-14 00:20:36 -05:00
case 'propic' :
2026-02-14 12:55:36 -05:00
return await updatePropic ( authorId , args , attachmentUrl )
2026-02-13 18:20:29 -05:00
default :
2026-02-13 22:18:43 -05:00
return member ;
2026-02-13 18:20:29 -05:00
}
}
2026-02-13 21:23:33 -05:00
/ * *
* Adds a member , first checking that there is no member of that name associated with the author .
*
* @ param { string } authorId - The author of the message
* @ param { string [ ] } args - The message arguments
2026-02-14 12:55:36 -05:00
* @ returns { Promise < string > } A successful addition .
* @ throws { Error } When creating a member doesn ' t work .
2026-02-13 21:23:33 -05:00
* /
2026-02-13 20:38:51 -05:00
async function addNewMember ( authorId , args ) {
if ( args [ 1 ] && args [ 1 ] === "--help" || ! args [ 1 ] ) {
2026-02-13 21:18:44 -05:00
return enums . help . ADD ;
2026-02-13 18:20:29 -05:00
}
2026-02-13 20:38:51 -05:00
const memberName = args [ 1 ] ;
const displayName = args [ 2 ] ;
const trimmedName = displayName ? displayName . replaceAll ( ' ' , '' ) : null ;
2026-02-13 18:20:29 -05:00
return await db . members . create ( {
2026-02-13 20:38:51 -05:00
name : memberName ,
userid : authorId ,
displayname : trimmedName !== null ? displayName : null ,
2026-02-13 18:20:29 -05:00
} ) . then ( ( m ) => {
let success = ` Member was successfully added. \n Name: ${ m . dataValues . name } `
2026-02-13 20:38:51 -05:00
success += displayName ? ` \n Display name: ${ m . dataValues . displayname } ` : "" ;
2026-02-13 18:20:29 -05:00
return success ;
} ) . catch ( e => {
2026-02-14 12:55:36 -05:00
throw new Error ( ` ${ enums . err . ADD _ERROR } : ${ e . message } ` )
2026-02-13 18:20:29 -05:00
} )
}
2026-02-14 13:37:51 -05:00
/ * *
* Updates the name for a member .
*
* @ param { string } authorId - The author of the message
* @ param { string [ ] } args - The message arguments
* @ returns { Promise < string > } A successful update .
* @ throws { RangeError } When the name doesn ' t exist .
* /
async function updateName ( authorId , args ) {
if ( args [ 1 ] && args [ 1 ] === "--help" || ! args [ 1 ] ) {
return enums . help . DISPLAY _NAME ;
}
const name = args [ 2 ] ;
const trimmed _name = name ? name . replaceAll ( ' ' , '' ) : null ;
if ( ! name || trimmed _name === null ) {
throw new RangeError ( ` Display name ${ enums . err . NO _VALUE } ` ) ;
}
return updateMember ( authorId , args ) ;
}
2026-02-13 21:23:33 -05:00
/ * *
* Updates the display name for a member .
*
* @ param { string } authorId - The author of the message
* @ param { string [ ] } args - The message arguments
2026-02-14 12:55:36 -05:00
* @ returns { Promise < string > } A successful update .
* @ throws { RangeError } When the display name is too long or doesn ' t exist .
2026-02-13 21:23:33 -05:00
* /
2026-02-13 20:38:51 -05:00
async function updateDisplayName ( authorId , args ) {
if ( args [ 1 ] && args [ 1 ] === "--help" || ! args [ 1 ] ) {
2026-02-13 22:18:43 -05:00
return enums . help . DISPLAY _NAME ;
2026-02-13 18:20:29 -05:00
}
2026-02-13 20:38:51 -05:00
const memberName = args [ 0 ] ;
const displayName = args [ 2 ] ;
const trimmed _name = displayName ? displayName . replaceAll ( ' ' , '' ) : null ;
if ( ! displayName || trimmed _name === null ) {
2026-02-13 22:18:43 -05:00
let member = await mh . getMemberByName ( authorId , memberName ) ;
2026-02-13 18:20:29 -05:00
if ( member . displayname ) {
2026-02-13 22:18:43 -05:00
return ` Display name for ${ memberName } is: \" ${ member . displayname } \" . ` ;
2026-02-13 18:20:29 -05:00
}
2026-02-14 12:55:36 -05:00
throw new RangeError ( ` Display name ${ enums . err . NO _VALUE } ` ) ;
2026-02-13 18:20:29 -05:00
}
2026-02-13 21:23:33 -05:00
else if ( displayName . length > 32 ) {
2026-02-14 12:55:36 -05:00
throw new RangeError ( enums . err . DISPLAY _NAME _TOO _LONG ) ;
2026-02-13 18:56:36 -05:00
}
2026-02-13 21:23:33 -05:00
return updateMember ( authorId , args ) ;
2026-02-13 18:20:29 -05:00
}
2026-02-13 21:23:33 -05:00
/ * *
* Updates the proxy for a member , first checking that no other members attached to the author have the tag .
*
* @ param { string } authorId - The author of the message
* @ param { string [ ] } args - The message arguments
2026-02-14 00:20:36 -05:00
* @ returns { Promise < string > } A successful update , or an error message .
2026-02-14 12:55:36 -05:00
* @ throws { RangeError | Error } When an empty proxy was provided , or no proxy exists .
2026-02-13 21:23:33 -05:00
* /
2026-02-13 20:38:51 -05:00
async function updateProxy ( authorId , args ) {
if ( args [ 1 ] && args [ 1 ] === "--help" || ! args [ 1 ] ) {
2026-02-13 21:18:44 -05:00
return enums . help . PROXY ;
2026-02-13 20:38:51 -05:00
}
2026-02-13 18:56:36 -05:00
const proxy = args [ 2 ] ;
2026-02-13 20:38:51 -05:00
const trimmedProxy = proxy ? proxy . replaceAll ( ' ' , '' ) : null ;
2026-02-13 18:56:36 -05:00
2026-02-13 20:38:51 -05:00
if ( trimmedProxy == null ) {
2026-02-14 12:55:36 -05:00
throw new RangeError ( ` Proxy ${ enums . err . NO _VALUE } ` ) ;
2026-02-13 18:56:36 -05:00
}
2026-02-13 20:38:51 -05:00
const members = await mh . getMembersByAuthor ( authorId ) ;
2026-02-13 20:08:24 -05:00
const proxyExists = members . some ( member => member . proxy === proxy ) ;
if ( proxyExists ) {
2026-02-14 12:55:36 -05:00
throw new Error ( enums . err . PROXY _EXISTS ) ;
2026-02-13 20:08:24 -05:00
}
2026-02-13 21:23:33 -05:00
return updateMember ( authorId , args ) ;
2026-02-13 18:56:36 -05:00
}
2026-02-14 01:00:23 -05:00
/ * *
* Updates the profile pic for a member , based on either the attachment or the args provided .
*
* @ param { string } authorId - The author of the message
* @ param { string [ ] } args - The message arguments
* @ param { string } attachment - The url of the first attachment in the message
2026-02-14 12:55:36 -05:00
* @ returns { Promise < string > } A successful update .
* @ throws { Error } When loading the profile picture from a URL doesn ' t work .
2026-02-14 01:00:23 -05:00
* /
2026-02-14 00:20:36 -05:00
async function updatePropic ( authorId , args , attachment ) {
if ( args [ 1 ] && args [ 1 ] === "--help" ) {
return enums . help . PROPIC ;
}
let img ;
const updatedArgs = args ;
if ( ! updatedArgs [ 1 ] && ! attachment ) {
return enums . help . PROPIC ;
} else if ( attachment ) {
updatedArgs [ 2 ] = attachment . url ;
2026-02-14 01:00:23 -05:00
updatedArgs [ 3 ] = attachment . expires _at ;
2026-02-14 00:20:36 -05:00
}
if ( updatedArgs [ 2 ] ) {
img = updatedArgs [ 2 ] ;
}
return await loadImage ( img ) . then ( ( ) => {
return updateMember ( authorId , updatedArgs ) ;
} ) . catch ( ( err ) => {
2026-02-14 12:55:36 -05:00
throw new Error ( ` ${ enums . err . PROPIC _CANNOT _LOAD } : ${ err . message } ` ) ;
2026-02-14 00:20:36 -05:00
} ) ;
}
2026-02-13 21:23:33 -05:00
/ * *
* Removes a member .
*
* @ param { string } authorId - The author of the message
* @ param { string [ ] } args - The message arguments
2026-02-14 12:55:36 -05:00
* @ returns { Promise < string > } A successful removal .
* @ throws { EmptyResultError } When there is no member to remove .
2026-02-13 21:23:33 -05:00
* /
2026-02-13 20:38:51 -05:00
async function removeMember ( authorId , args ) {
2026-02-14 12:55:36 -05:00
if ( args [ 1 ] && args [ 1 ] === "--help" || ! args [ 1 ] ) {
2026-02-13 21:18:44 -05:00
return enums . help . REMOVE ;
2026-02-13 20:38:51 -05:00
}
const memberName = args [ 1 ] ;
return await db . members . destroy ( { where : { name : memberName , userid : authorId } } ) . then ( ( ) => {
2026-02-14 10:07:27 -05:00
return ` Member " ${ memberName } " has been deleted. ` ;
2026-02-13 18:20:29 -05:00
} ) . catch ( e => {
2026-02-14 12:55:36 -05:00
throw new EmptyResultError ( ` ${ enums . err . NO _MEMBER } : ${ e . message } ` ) ;
2026-02-13 18:20:29 -05:00
} ) ;
}
2026-02-14 00:20:36 -05:00
/*======Non-Subcommands======*/
2026-02-13 21:23:33 -05:00
/ * *
* Updates a member ' s fields in the database .
*
* @ param { string } authorId - The author of the message
* @ param { string [ ] } args - The message arguments
2026-02-14 12:55:36 -05:00
* @ returns { Promise < string > } A successful update .
* @ throws { EmptyResultError | Error } When the member is not found , or catchall error .
2026-02-13 21:23:33 -05:00
* /
2026-02-13 20:38:51 -05:00
async function updateMember ( authorId , args ) {
const memberName = args [ 0 ] ;
const columnName = args [ 1 ] ;
const value = args [ 2 ] ;
2026-02-14 01:00:23 -05:00
let fluxerPropicWarning ;
// indicates that an attachment was uploaded on Fluxer directly
if ( columnName === "propic" && args [ 3 ] ) {
console . log ( args ) ;
fluxerPropicWarning = setExpirationWarning ( args [ 3 ] ) ;
}
2026-02-13 20:38:51 -05:00
return await db . members . update ( { [ columnName ] : value } , { where : { name : memberName , userid : authorId } } ) . then ( ( ) => {
2026-02-14 10:07:27 -05:00
return ` Updated ${ columnName } for ${ memberName } to " ${ value } " ${ fluxerPropicWarning ? ? '' } . ` ;
2026-02-13 18:20:29 -05:00
} ) . catch ( e => {
2026-02-14 12:55:36 -05:00
if ( e === EmptyResultError ) {
throw new EmptyResultError ( ` ${ enums . err . NO _MEMBER } : ${ e . message } ` ) ;
}
else {
throw new Error ( e . message ) ;
}
2026-02-13 18:20:29 -05:00
} ) ;
}
2026-02-14 01:00:23 -05:00
/ * *
* Sets the warning for an expiration date .
*
* @ param { string } expirationString - An expiration date string .
2026-02-14 12:55:36 -05:00
* @ returns { string } A description of the expiration , interpolating the expiration string .
2026-02-14 01:00:23 -05:00
* /
function setExpirationWarning ( expirationString ) {
let expirationDate = new Date ( expirationString ) ;
if ( ! isNaN ( expirationDate . valueOf ( ) ) ) {
expirationDate = expirationDate . toDateString ( ) ;
return ` \n **NOTE:** Because this profile picture was uploaded via Fluxer, it will currently expire on * ${ expirationDate } *. To avoid this, upload the picture to another website like <https://imgbb.com/> and link to it directly. `
}
}
2026-02-13 21:23:33 -05:00
/ * *
* Gets the details for a member .
*
* @ param { string } authorId - The author of the message
* @ param { string } memberName - The message arguments
2026-02-14 14:27:28 -05:00
* @ returns { Promise < EmbedBuilder > } The member ' s info .
2026-02-14 12:55:36 -05:00
* @ throws { EmptyResultError } When the member is not found .
2026-02-13 21:23:33 -05:00
* /
2026-02-13 20:38:51 -05:00
async function getMemberInfo ( authorId , memberName ) {
2026-02-14 14:27:28 -05:00
let member = mh . getMemberByName ( authorId , memberName ) ;
let member _info = new EmbedBuilder ( )
. setTitle ( memberName )
. setDescription ( ` Details for ${ memberName } ` )
. addFields (
{ name : 'Display name' , value : member . displayname , inline : true } ,
{ name : 'Proxy tag' , value : member . proxy , inline : true } ,
)
. setImage ( member . propic ) ;
2026-02-14 12:55:36 -05:00
return member _info ;
2026-02-13 20:38:51 -05:00
}
2026-02-14 14:27:28 -05:00
/ * *
* Gets all members for an author .
*
* @ param { string } authorId - The id of the message author
* @ param { string } authorName - The id name the message author
* @ returns { Promise < EmbedBuilder > } The info for all members .
* /
async function getAllMembersInfo ( authorId , authorName ) {
const members = await mh . getMembersByAuthor ( authorId ) ;
const fields = [ ... members . entries ( ) ] . map ( ( [ name , member ] ) => ( {
name : member . name ,
value : ` (Proxy: \` ${ member . proxy } \` ) ` ,
inline : true ,
} ) ) ;
return new EmbedBuilder ( )
. setTitle ( ` Members for ${ authorName } ` )
. addFields ( ... fields ) ;
}
2026-02-13 21:23:33 -05:00
/ * *
* Gets a member based on the author and proxy tag .
*
* @ param { string } authorId - The author of the message .
* @ param { string } name - The member ' s name .
2026-02-14 12:55:36 -05:00
* @ returns { Promise < model > } The member object .
* @ throws { EmptyResultError } When the member is not found .
2026-02-13 21:23:33 -05:00
* /
mh . getMemberByName = async function ( authorId , name ) {
return await db . members . findOne ( { where : { userid : authorId , name : name } } ) . catch ( e => {
2026-02-14 12:55:36 -05:00
throw new EmptyResultError ( ` ${ enums . err . NO _MEMBER } : ${ e . message } ` ) ;
2026-02-13 21:23:33 -05:00
} ) ;
}
/ * *
* Gets a member based on the author and proxy tag .
*
* @ param { string } authorId - The author of the message
* @ param { string } proxy - The proxy tag
2026-02-14 12:55:36 -05:00
* @ returns { Promise < model > } The member object .
* @ throws { EmptyResultError } When the member is not found .
2026-02-13 21:23:33 -05:00
* /
2026-02-13 20:38:51 -05:00
mh . getMemberByProxy = async function ( authorId , proxy ) {
return await db . members . findOne ( { where : { userid : authorId , proxy : proxy } } ) . catch ( e => {
2026-02-14 12:55:36 -05:00
throw new EmptyResultError ( ` ${ enums . err . NO _MEMBER } : ${ e . message } ` ) ;
2026-02-13 18:20:29 -05:00
} ) ;
}
2026-02-13 21:23:33 -05:00
/ * *
* Gets all members belonging to the author .
*
* @ param { string } authorId - The author of the message
2026-02-14 12:55:36 -05:00
* @ returns { Promise < model [ ] > } The member object array .
* @ throws { EmptyResultError } When no members are found .
2026-02-13 21:23:33 -05:00
* /
2026-02-13 20:38:51 -05:00
mh . getMembersByAuthor = async function ( authorId ) {
return await db . members . findAll ( { where : { userid : authorId } } ) . catch ( e => {
2026-02-14 12:55:36 -05:00
throw new EmptyResultError ( ` ${ enums . err . USER _NO _MEMBERS } : ${ e . message } ` ) ;
2026-02-13 18:20:29 -05:00
} ) ;
}
export const memberHelper = mh ;