2021-09-17 11:14:54 -07:00
import { Channel , channel } from "diagnostics_channel" ;
import { GuildChannel , GuildMember , Message , Permissions , TextChannel , MessageActionRow , MessageButton , Interaction , CommandInteraction , Role , ThreadChannel } from "discord.js" ;
2021-09-15 04:11:28 -07:00
import App from ".." ;
2021-09-16 01:18:30 -07:00
import { getEmotes , makeSuccessEmbed , makeProcessingEmbed , makeInfoEmbed , makeErrorEmbed , sendMessage , sendReply } from "../utils/DiscordMessage" ;
2021-09-15 04:11:28 -07:00
import Logger from "../libs/Logger" ;
import DiscordProvider from "../providers/Discord" ;
import Prisma from "../providers/Prisma" ;
2021-09-17 11:14:54 -07:00
import { MessageOptions } from "child_process" ;
const EMBEDS = {
NO_PERMISSION : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'You need ``ADMINISTRATOR`` permission on this guild!' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
NO_PARAMETER : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'Missing parameter' ,
description : `You must define membership screening channel and role to enable this feature` ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
NO_ROLE_MENTIONED : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'No role mentioned' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
NO_ROLE_FOUND : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'Cannot find that role' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
NO_CHANNEL_MENTIONED : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'No channel mentioned' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
NO_CHANNEL_FOUND : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'Cannot find that channel' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
MSINFO : ( data : Message | Interaction ) => {
return makeInfoEmbed ({
title : 'Membership Screening' ,
description : `Membership Screening is a feature to prevent unwanted people to join your guild, similar to whitelist feature. Moderators can approve or deny join request` ,
fields : [
{
name : 'Available arguments' ,
value : '``setRole`` ``setChannel`` ``createMessage``'
}
],
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
ALREADY_ENABLED : ( data : Message | Interaction ) => {
return makeInfoEmbed ({
title : 'Membership Screening is already enabled' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
ALREADY_DISABLED : ( data : Message | Interaction ) => {
return makeInfoEmbed ({
title : 'Membership Screening is already disabled' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
ENABLED : ( data : Message | Interaction ) => {
return makeSuccessEmbed ({
title : 'Enabled Membership Screening' ,
description : `All new member join request will be sent in your defined channel` ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
DISABLED : ( data : Message | Interaction ) => {
return makeSuccessEmbed ({
title : 'Disabled Membership Screening' ,
description : `No longer accepting request, all new member can join directly` ,
user : ( data instanceof Interaction ) ? data.user : data.author
})
},
MANAGED_ROLE : ( data : Message | Interaction , role : Role ) => {
return makeErrorEmbed ({
title : 'You cannot use this role' ,
description : '``' + role . name + '``' + ' is managed by external service and cannot be used' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
CONFIGURED_ROLE : ( data : Message | Interaction , role : Role ) => {
return makeSuccessEmbed ({
title : 'Configured Membership Screening Role' ,
description : 'New member will be given a ' + '``' + role . name + '``' + ' role after approval' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
CONFIGURED_CHANNEL : ( data : Message | Interaction , channel : GuildChannel ) => {
return makeSuccessEmbed ({
title : 'Configured Membership Screening Role' ,
description : 'Anyone with ``VIEW_CHANNEL`` permission in ' + '``' + channel . name + '``' + ' can approve or deny request' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
INVALID_CHANNEL : ( data : Message | Interaction , channel : GuildChannel ) => {
return makeErrorEmbed ({
title : 'Invalid channel type, only TextChannel is supported' ,
description : '``' + channel . name + '``' + ' is not a text channel' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
INVALID_CHANNEL_THREAD : ( data : Message | Interaction , channel : GuildChannel | ThreadChannel ) => {
return makeErrorEmbed ({
title : 'Thread channel is not supported' ,
description : '``' + channel . name + '``' + ' a thread channel. Please use regular channel' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
BOT_NO_PERMISSION : ( data : Message | Interaction , channel : GuildChannel ) => {
return makeErrorEmbed ({
title : `I don't have permission` ,
description : 'I cannot access/send message in ' + '``' + channel . name + '``' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
NO_LONGER_VALID_ROLE : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'The configured role is no longer valid. Please update the role in configuration' ,
user : ( data instanceof Interaction ) ? data.user : data.author
})
},
CANNOT_PERFORM_ASSIGN_ROLE : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'Cannot grant the role to user, make sure I have permission to do that' ,
user : ( data instanceof Interaction ) ? data.user : data.author
});
},
CANNOT_PERFORM_ASSIGN_KICK : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'Cannot kick the user, make sure I have permission to do that' ,
user : ( data instanceof Interaction ) ? data.user : data.author
})
},
CANNOT_PERFORM_ASSIGN_BAN : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : 'Cannot ban the user, make sure I have permission to do that' ,
user : ( data instanceof Interaction ) ? data.user : data.author
})
},
CREATE_MESSAGE : () => {
return makeInfoEmbed ({
icon : '👋' ,
title : 'Welcome to this server!' ,
description : `This server have membership screening enabled, **you'll have access to the server when the moderators let you in**. \ n \ nAlso please make sure that you acknowledged the common rules that everyone should be doing no matter where, Discord ToS (https://discordapp.com/terms)` ,
user : DiscordProvider.client.user
})
}
}
2021-09-15 04:11:28 -07:00
export default class MembershipScreening {
async onCommand ( command : string , args : any , message : Message ) {
2021-09-17 11:14:54 -07:00
2021-09-15 04:11:28 -07:00
if ( command . toLowerCase () !== 'membershipscreening' && command . toLowerCase () !== 'ms' ) return ;
2021-09-17 11:14:54 -07:00
await this . process ( message , args );
}
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
async process ( data : Interaction | Message , args : any ) {
const isSlashCommand = data instanceof CommandInteraction && data . isCommand ();
const isMessage = data instanceof Message ;
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
if ( ! isSlashCommand && isMessage ) return ;
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
if ( data . guild === null || data . guildId === null ) return ;
const Guild = await Prisma . client . guild . findFirst ({ where : { id : data.guildId }})
if ( ! Guild ) throw new Error ( 'TODO: Handle if guild is not found' );
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
const funct = {
enable : async ( data : Message | Interaction ) => {
2021-09-15 04:11:28 -07:00
if ( ! Guild . MembershipScreening_Enabled ) {
2021-09-17 11:14:54 -07:00
if ( Guild . MembershipScreening_GivenRole === null || Guild . MembershipScreening_ApprovalChannel == null )
return await this . sendResponse ( data , { embeds : [ EMBEDS . NO_PARAMETER ( data )] });
await Prisma . client . guild . update ({ where : { id : data.guildId ! }, data : { MembershipScreening_Enabled : true }});
return await this . sendResponse ( data , { embeds : [ EMBEDS . ENABLED ( data )] });
2021-09-15 04:11:28 -07:00
}
2021-09-17 11:14:54 -07:00
2021-09-15 04:11:28 -07:00
else
2021-09-17 11:14:54 -07:00
return await this . sendResponse ( data , { embeds : [ EMBEDS . ALREADY_ENABLED ( data )] });
},
disable : async ( data : Message | Interaction ) => {
2021-09-15 04:11:28 -07:00
if ( Guild . MembershipScreening_Enabled ) {
await Prisma . client . guild . update ({
2021-09-17 11:14:54 -07:00
where : { id : data.guildId ! },
2021-09-15 04:11:28 -07:00
data : { MembershipScreening_Enabled : false }
});
2021-09-17 11:14:54 -07:00
return await this . sendResponse ( data , { embeds : [ EMBEDS . DISABLED ( data )] });
2021-09-15 04:11:28 -07:00
}
else
2021-09-17 11:14:54 -07:00
return await this . sendResponse ( data , { embeds : [ EMBEDS . ALREADY_DISABLED ( data )] });
},
setRole : async ( data : Message | Interaction ) => {
let role : Role | undefined ;
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
if ( data instanceof Message ) {
let _name : string ;
if ( typeof args [ 1 ] === 'undefined' )
return await this . sendResponse ( data , { embeds : [ EMBEDS . NO_ROLE_MENTIONED ( data )] });
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
let __name = args ;
__name . shift ();
_name = __name . join ( " " );
role = data . mentions . roles . first () || data . guild ! . roles . cache . find ( role => role . name === _name );
}
else if ( data instanceof CommandInteraction )
role = data . guild ! . roles . cache . find ( role => role . id === data . options . getRole ( 'role' ) ? . id );
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
if ( typeof role === 'undefined' || ! role )
return await this . sendResponse ( data , { embeds : [ EMBEDS . NO_ROLE_FOUND ( data )] });
await Prisma . client . guild . update ({ where : { id : data.guildId ! }, data : { MembershipScreening_GivenRole : role.id }});
2021-09-15 04:11:28 -07:00
if ( role . managed )
2021-09-17 11:14:54 -07:00
return await this . sendResponse ( data , { embeds : [ EMBEDS . MANAGED_ROLE ( data , role )] });
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
return await this . sendResponse ( data , { embeds : [ EMBEDS . CONFIGURED_ROLE ( data , role )] });
},
setChannel : async ( data : Message | CommandInteraction ) => {
let channel ;
if ( data instanceof Message ) {
if ( typeof args [ 1 ] === 'undefined' )
return await this . sendResponse ( data , { embeds : [ EMBEDS . NO_CHANNEL_MENTIONED ( data )] });
channel = data . mentions . channels . first ();
}
else if ( data instanceof CommandInteraction )
channel = data . options . getChannel ( 'channel' );
2021-09-15 04:11:28 -07:00
if ( ! channel )
2021-09-17 11:14:54 -07:00
return await this . sendResponse ( data , { embeds : [ EMBEDS . NO_CHANNEL_FOUND ( data )] });
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
let TargetChannel = data . guild ! . channels . cache . get ( channel . id );
if ( typeof TargetChannel === 'undefined' ) return ;
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
if ( TargetChannel instanceof ThreadChannel || TargetChannel . isThread ())
return await this . sendResponse ( data , { embeds : [ EMBEDS . INVALID_CHANNEL_THREAD ( data , TargetChannel )] });
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
if ( ! TargetChannel . isText ())
return await this . sendResponse ( data , { embeds : [ EMBEDS . INVALID_CHANNEL ( data , TargetChannel )] });
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
if ( ! ( data ! . guild ! . me ? . permissionsIn ( TargetChannel ). has ([ Permissions . FLAGS . SEND_MESSAGES , Permissions . FLAGS . VIEW_CHANNEL ])))
return await this . sendResponse ( data , { embeds : [ EMBEDS . BOT_NO_PERMISSION ( data , TargetChannel )] });
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
await Prisma . client . guild . update ({ where : { id : data.guildId ! }, data : { MembershipScreening_ApprovalChannel : channel.id }});
return await this . sendResponse ( data , { embeds : [ EMBEDS . CONFIGURED_CHANNEL ( data , TargetChannel )] });
},
createMessage : async ( data : Message | CommandInteraction ) => {
return await this . sendResponse ( data , { embeds : [ EMBEDS . CREATE_MESSAGE ()] });
2021-09-15 04:11:28 -07:00
}
2021-09-17 11:14:54 -07:00
}
let query ;
if ( isMessage ) {
if ( data === null || ! data . guildId || data . member === null || data . guild === null ) return ;
if ( ! data . member . permissions . has ([ Permissions . FLAGS . ADMINISTRATOR ]))
return await this . sendResponse ( data , { embeds : [ EMBEDS . NO_PERMISSION ( data )] });
if ( args . length === 0 ) {
return await this . sendResponse ( data , { embeds : [ EMBEDS . MSINFO ( data )] });
2021-09-15 04:11:28 -07:00
}
2021-09-17 11:14:54 -07:00
query = args [ 0 ]. toLowerCase ();
}
else if ( isSlashCommand ) {
if ( ! data . guild . members . cache . get ( data . user . id ) ? . permissions . has ([ Permissions . FLAGS . ADMINISTRATOR ]))
return await this . sendResponse ( data , { embeds : [ EMBEDS . NO_PERMISSION ( data )] });
query = args . getSubcommand ();
}
switch ( query ) {
case "enable" :
case "on" :
return await funct . enable ( data );
case "disable" :
case "off" :
return await funct . disable ( data );
case "setrole" :
return await funct . setRole ( data );
case "setchannel" :
return await funct . setChannel ( data as CommandInteraction );
case "createmessage" :
return await funct . createMessage ( data as CommandInteraction );
2021-09-15 04:11:28 -07:00
}
}
async interactionCreate ( interaction : Interaction ) {
2021-09-17 11:14:54 -07:00
2021-09-15 04:11:28 -07:00
if ( interaction . isButton ()) {
2021-09-17 11:14:54 -07:00
if ( ! interaction . guild || ! interaction . guildId ) return ;
2021-09-15 04:11:28 -07:00
if ( ! ( interaction . channel instanceof TextChannel )) return ;
if ( ! this . tryParseJSONObject ( interaction . customId )) return ;
Logger . log ( 'debug' , ` ${ interaction . user . username } # ${ interaction . user . discriminator } (UserID: ${ interaction . user . id } ) (InteractionID: ${ interaction . id } ) executed button interaction ${ interaction . customId } ` );
let payload = JSON . parse ( interaction . customId );
2021-09-17 11:14:54 -07:00
if ( typeof payload . module === 'undefined' ||
typeof payload . action === 'undefined' ||
payload . module !== 'MembershipScreening' ) return ;
2021-09-15 04:11:28 -07:00
const message = await interaction . channel . messages . fetch ( interaction . message . id );
if ( typeof message === 'undefined' ) return ;
const embed = message . embeds ;
if ( typeof embed === 'undefined' || embed . length === 0 ) return ;
const Guild = await Prisma . client . guild . findFirst ({ where : { id : interaction.guildId } });
2021-09-17 11:14:54 -07:00
if ( ! Guild ) throw new Error ( 'TODO: Handle if guild is not found' );
2021-09-15 04:11:28 -07:00
2021-09-17 11:14:54 -07:00
if ( ! Guild . MembershipScreening_Enabled ||
Guild . MembershipScreening_ApprovalChannel === null ||
Guild . MembershipScreening_GivenRole === null ) return ;
2021-09-15 04:11:28 -07:00
embed [ 0 ]. setFooter ( ` ${ interaction . user . username } | v ${ App . version } ` , ( interaction . user . avatarURL () || '' ));
if ([ 'approve' , 'deny' , 'ban' ]. includes ( payload . action )) {
2021-09-17 11:14:54 -07:00
2021-09-15 04:11:28 -07:00
if ( ! payload . data . requester ) return ;
let role = ( await interaction . guild . roles . fetch ()). find ( role => role . id === Guild . MembershipScreening_GivenRole );
let member = ( await interaction . guild . members . fetch ()). find ( member => member . id === payload . data . requester );
2021-09-17 11:14:54 -07:00
if ( ! role )
return await interaction . reply ({ ephemeral : true , embeds : [ EMBEDS . NO_LONGER_VALID_ROLE ( interaction )]});
2021-09-15 04:11:28 -07:00
if ( ! member ) {
embed [ 0 ]. addField ( "❌ Invalid" , `Unable to find the user. User left already?` );
2021-09-17 11:14:54 -07:00
return await message . edit ({ components : [], embeds : embed });
2021-09-15 04:11:28 -07:00
}
if ( member . roles . cache . has ( Guild . MembershipScreening_GivenRole )) {
embed [ 0 ]. addField ( "✅ Approved" , `By: Unknown (User already obtained the role by other means)` );
2021-09-17 11:14:54 -07:00
return await message . edit ({ components : [], embeds : embed });
2021-09-15 04:11:28 -07:00
}
if ( payload . action === 'approve' ) {
try {
await member . roles . add ( role );
embed [ 0 ]. addField ( "✅ Approved" , `By: ${ interaction . user . username } # ${ interaction . user . discriminator } ( ${ interaction . user . id } )` );
2021-09-17 11:14:54 -07:00
await message . edit ({ components : [], embeds : embed });
2021-09-15 04:11:28 -07:00
}
catch ( err ) {
2021-09-17 11:14:54 -07:00
return interaction . reply ({ ephemeral : true , embeds : [ EMBEDS . CANNOT_PERFORM_ASSIGN_ROLE ( message )]});
}
2021-09-15 04:11:28 -07:00
}
2021-09-17 11:14:54 -07:00
2021-09-15 04:11:28 -07:00
else if ( payload . action === 'deny' ) {
try {
await member . kick ();
embed [ 0 ]. addField ( "❌ Denied" , `By: ${ interaction . user . username } # ${ interaction . user . discriminator } ( ${ interaction . user . id } )` );
2021-09-17 11:14:54 -07:00
await message . edit ({ components : [], embeds : embed });
2021-09-15 04:11:28 -07:00
}
catch ( err ) {
2021-09-17 11:14:54 -07:00
return interaction . reply ({ ephemeral : true , embeds : [ EMBEDS . CANNOT_PERFORM_ASSIGN_KICK ( message )]});
2021-09-15 04:11:28 -07:00
}
}
2021-09-17 11:14:54 -07:00
2021-09-15 04:11:28 -07:00
else if ( payload . action === 'ban' ) {
try {
await member . ban ({
reason : `Membership Screening, action issued by ${ interaction . user . username } # ${ interaction . user . discriminator } ( ${ interaction . user . id } )`
});
embed [ 0 ]. addField ( "🔪 Banned" , `By: ${ interaction . user . username } # ${ interaction . user . discriminator } ( ${ interaction . user . id } )` );
2021-09-17 11:14:54 -07:00
await message . edit ({ components : [], embeds : embed });
2021-09-15 04:11:28 -07:00
}
catch ( err ) {
2021-09-17 11:14:54 -07:00
return interaction . reply ({ ephemeral : true , embeds : [ EMBEDS . CANNOT_PERFORM_ASSIGN_BAN ( message )]});
2021-09-15 04:11:28 -07:00
}
}
}
}
2021-09-17 11:14:54 -07:00
else if ( interaction . isCommand ()) {
if ( typeof interaction . commandName === 'undefined' ) return ;
if (( interaction . commandName ). toLowerCase () !== 'membershipscreening' ) return ;
await this . process ( interaction , interaction . options );
}
2021-09-15 04:11:28 -07:00
}
async guildMemberAdd ( member : GuildMember ) {
if ( member . user . bot ) return ;
const Guild = await Prisma . client . guild . findFirst ({ where : { id : member.guild.id } });
if ( ! Guild ) return ;
if ( ! Guild . MembershipScreening_Enabled ) return ;
if ( Guild . MembershipScreening_ApprovalChannel === null ) return ;
if ( Guild . MembershipScreening_GivenRole === null ) return ;
const channel = await DiscordProvider . client . channels . fetch ( Guild . MembershipScreening_ApprovalChannel ) as TextChannel ;
if ( ! channel ) return ;
const embed = makeInfoEmbed ({
title : 'New member joined!' ,
description : ` ${ member . user . username } # ${ member . user . discriminator } ( ${ member . user . id } )` ,
fields : [
{
name : "Account Information" ,
value : `Account age: <t: ${ Math . round ( member . user . createdAt . getTime () / 1000 ) } :R>`
}
]
});
embed . setThumbnail ( member . user . avatarURL () || '' );
const row = new MessageActionRow ()
. addComponents (
new MessageButton ()
. setCustomId ( JSON . stringify ({
module : 'MembershipScreening' ,
action : 'approve' ,
data : {
requester : member.id
}
}))
. setEmoji ( '✅' )
. setLabel ( ' Approve' )
. setStyle ( 'SUCCESS' ),
)
. addComponents (
new MessageButton ()
. setCustomId ( JSON . stringify ({
module : 'MembershipScreening' ,
action : 'deny' ,
data : {
requester : member.id
}
}))
2021-09-17 11:14:54 -07:00
. setEmoji ( '⛔' )
. setLabel ( ' Deny and kick' )
2021-09-15 04:11:28 -07:00
. setStyle ( 'DANGER' ),
)
. addComponents (
new MessageButton ()
. setCustomId ( JSON . stringify ({
module : 'MembershipScreening' ,
action : 'ban' ,
data : {
requester : member.id
}
}))
. setEmoji ( '🔪' )
2021-09-17 11:14:54 -07:00
. setLabel ( ' Vision Hunt Decree (Ban)' )
2021-09-15 04:11:28 -07:00
. setStyle ( 'DANGER' ),
)
await channel . send ({ content : '\u200b' , embeds : [ embed ], components : [ row ] });
}
2021-09-17 11:14:54 -07:00
2021-09-15 04:11:28 -07:00
tryParseJSONObject ( jsonString : string ) {
try {
let o = JSON . parse ( jsonString );
if ( o && typeof o === "object" ) {
return true ;
//return o;
}
}
catch ( e ) { }
return false ;
2021-09-17 11:14:54 -07:00
}
async sendResponse ( data : Message | Interaction , payload : any ) {
const isSlashCommand = data instanceof Interaction && data . isCommand ();
const isMessage = data instanceof Message ;
if ( isSlashCommand ) return await data . reply ( payload );
else if ( isMessage ) return await sendReply ( data , payload );
}
2021-09-15 04:11:28 -07:00
}