2023-05-07 04:41:20 -04:00
import Discord , { Client , WebhookClient , GatewayIntentBits , Partials } from 'discord.js' ;
2023-05-23 01:14:17 -04:00
import { readFileSync , readdirSync } from 'node:fs' ;
2023-04-14 06:47:58 -04:00
import { exec } from 'node:child_process' ;
2023-02-24 19:55:11 -05:00
import mongoose from 'mongoose' ;
2023-05-07 04:41:20 -04:00
import { formatTimeOpt , Tokens , Config , repeatedMessages , MPServerCache } from './typings/interfaces' ;
2023-04-14 06:47:58 -04:00
import bannedWords from './models/bannedWords.js' ;
import userLevels from './models/userLevels.js' ;
import suggestion from './models/suggestion.js' ;
import punishments from './models/punishments.js' ;
import bonkCount from './models/bonkCount.js' ;
import MPServer from './models/MPServer.js' ;
import xjs from 'xml-js' ;
2022-12-18 23:22:28 -05:00
import axios from 'axios' ;
import moment from 'moment' ;
2023-07-20 08:13:09 -04:00
const tokens = JSON . parse ( readFileSync ( 'src/tokens.json' , { encoding : 'utf8' } ) ) ;
// Import assertion warning workaround yes
2023-01-06 19:41:20 -05:00
let importconfig :Config
try {
2023-05-23 01:14:17 -04:00
importconfig = JSON . parse ( readFileSync ( 'src/DB-Beta.config.json' , { encoding : 'utf8' } ) ) ;
2023-03-05 05:04:10 -05:00
console . log ( 'Using development config :: Daggerbot Beta' )
2023-01-06 19:41:20 -05:00
} catch ( e ) {
2023-05-23 01:14:17 -04:00
importconfig = JSON . parse ( readFileSync ( 'src/config.json' , { encoding : 'utf8' } ) )
2023-03-05 05:04:10 -05:00
console . log ( 'Using production config' )
2023-01-06 19:41:20 -05:00
}
2023-01-27 21:33:55 -05:00
export default class TClient extends Client {
2023-03-05 05:04:10 -05:00
invites : Map < any , any > ;
commands : Discord.Collection < string , any > ;
registry : Array < Discord.ApplicationCommandDataResolvable > ;
config : Config ;
tokens : Tokens ;
YTCache : any ;
embed : typeof Discord . EmbedBuilder ;
collection : any ;
messageCollector : any ;
attachmentBuilder : any ;
moment : typeof moment ;
2023-04-14 06:47:58 -04:00
xjs : typeof xjs ;
2023-03-05 05:04:10 -05:00
axios : typeof axios ;
ms : any ;
userLevels : userLevels ;
punishments : punishments ;
bonkCount : bonkCount ;
bannedWords : bannedWords ;
MPServer : MPServer ;
2023-05-07 04:41:20 -04:00
MPServerCache : MPServerCache ;
2023-03-05 05:04:10 -05:00
suggestion : suggestion ;
repeatedMessages : repeatedMessages ;
statsGraph : number ;
2022-11-11 19:58:11 -05:00
2023-03-05 05:04:10 -05:00
constructor ( ) {
super ( {
intents : [
GatewayIntentBits . Guilds , GatewayIntentBits . GuildMembers ,
2023-05-01 20:12:45 -04:00
GatewayIntentBits . GuildModeration , GatewayIntentBits . GuildInvites ,
GatewayIntentBits . GuildMessageReactions , GatewayIntentBits . GuildPresences ,
2023-06-24 13:47:42 -04:00
GatewayIntentBits . MessageContent , GatewayIntentBits . GuildMessages ,
GatewayIntentBits . GuildVoiceStates
2023-04-24 07:04:32 -04:00
] , partials : [
2023-05-01 20:12:45 -04:00
Partials . Channel , Partials . Reaction , Partials . Message
2023-04-24 07:04:32 -04:00
] , allowedMentions : { users : [ ] , roles : [ ] }
2023-03-05 05:04:10 -05:00
} )
this . invites = new Map ( ) ;
this . commands = new Discord . Collection ( ) ;
this . registry = [ ] ;
this . config = importconfig as Config ;
this . tokens = tokens as Tokens ;
this . YTCache = {
'UCQ8k8yTDLITldfWYKDs3xFg' : undefined , // Daggerwin
'UCguI73--UraJpso4NizXNzA' : undefined // Machinery Restorer
2022-11-11 19:58:11 -05:00
}
2023-03-05 05:04:10 -05:00
this . embed = Discord . EmbedBuilder ;
this . collection = Discord . Collection ;
this . messageCollector = Discord . MessageCollector ;
this . attachmentBuilder = Discord . AttachmentBuilder ;
this . moment = moment ;
2023-04-14 06:47:58 -04:00
this . xjs = xjs ;
2023-03-05 05:04:10 -05:00
this . axios = axios ;
2023-04-14 06:47:58 -04:00
this . ms = import ( 'ms' ) . then ( i = > i ) ;
2023-03-05 05:04:10 -05:00
this . userLevels = new userLevels ( this ) ;
this . bonkCount = new bonkCount ( this ) ;
this . punishments = new punishments ( this ) ;
this . bannedWords = new bannedWords ( this ) ;
this . MPServer = new MPServer ( this ) ;
2023-05-20 03:17:23 -04:00
this . MPServerCache = { players : [ ] , status : null , name : null } as MPServerCache ;
2023-03-05 05:04:10 -05:00
this . suggestion = new suggestion ( this ) ;
this . repeatedMessages = { } ;
2023-06-11 08:54:07 -04:00
this . setMaxListeners ( 45 ) ;
2023-03-05 05:04:10 -05:00
this . statsGraph = - 60 ;
}
async init ( ) {
console . time ( 'Startup' ) ;
mongoose . set ( 'strictQuery' , true ) ;
await mongoose . connect ( this . tokens . mongodb_uri , {
replicaSet : 'toastyy' ,
autoIndex : true ,
2023-05-20 03:17:23 -04:00
authMechanism : 'DEFAULT' ,
2023-05-15 21:21:25 -04:00
authSource : 'admin' ,
2023-03-05 05:04:10 -05:00
serverSelectionTimeoutMS : 15000 ,
waitQueueTimeoutMS : 50000 ,
socketTimeoutMS : 30000 ,
family : 4
2023-06-17 12:37:58 -04:00
} ) . then ( ( ) = > console . log ( this . logTime ( ) , 'Successfully connected to MongoDB' ) ) . catch ( err = > { console . error ( this . logTime ( ) , ` Failed to connect to MongoDB \ n ${ err } ` ) ; exec ( 'pm2 stop Daggerbot' ) } )
2023-06-09 21:12:15 -04:00
this . login ( this . tokens . main ) ;
2023-05-23 01:14:17 -04:00
for await ( const file of readdirSync ( 'dist/events' ) ) {
2023-04-14 06:47:58 -04:00
const eventFile = await import ( ` ./events/ ${ file } ` ) ;
2023-04-21 02:20:01 -04:00
this . on ( file . replace ( '.js' , '' ) , async ( . . . args ) = > eventFile . default . run ( this , . . . args ) )
} ;
2023-05-23 01:14:17 -04:00
for await ( const file of readdirSync ( 'dist/commands' ) ) {
2023-04-14 06:47:58 -04:00
const command = await import ( ` ./commands/ ${ file } ` ) ;
2023-04-21 02:20:01 -04:00
this . commands . set ( command . default . data . name , { command , uses : 0 } ) ;
2023-03-05 05:04:10 -05:00
this . registry . push ( command . default . data . toJSON ( ) )
2023-04-21 02:20:01 -04:00
} ;
2023-03-05 05:04:10 -05:00
}
formatTime ( integer : number , accuracy = 1 , options? : formatTimeOpt ) {
let achievedAccuracy = 0 ;
let text :any = '' ;
2023-04-24 07:04:32 -04:00
for ( const timeName of [
{ name : 'year' , length : 31536000000 } ,
{ name : 'month' , length : 2592000000 } ,
{ name : 'week' , length : 604800000 } ,
{ name : 'day' , length : 86400000 } ,
{ name : 'hour' , length : 3600000 } ,
{ name : 'minute' , length : 60000 } ,
{ name : 'second' , length : 1000 }
] ) {
2023-03-05 05:04:10 -05:00
if ( achievedAccuracy < accuracy ) {
const fullTimelengths = Math . floor ( integer / timeName . length ) ;
2023-04-24 07:04:32 -04:00
if ( fullTimelengths === 0 ) continue ;
2023-03-05 05:04:10 -05:00
achievedAccuracy ++ ;
text += fullTimelengths + ( options ? . longNames ? ( ' ' + timeName . name + ( fullTimelengths === 1 ? '' : 's' ) ) : timeName . name . slice ( 0 , timeName . name === 'month' ? 2 : 1 ) ) + ( options ? . commas ? ', ' : ' ' ) ;
integer -= fullTimelengths * timeName . length ;
} else break ;
2022-11-11 19:58:11 -05:00
}
2023-03-05 05:04:10 -05:00
if ( text . length == 0 ) text = integer + ( options ? . longNames ? ' milliseconds' : 'ms' ) + ( options ? . commas ? ', ' : '' ) ;
if ( options ? . commas ) {
text = text . slice ( 0 , - 2 ) ;
if ( options ? . longNames ) {
text = text . split ( '' ) ;
text [ text . lastIndexOf ( ',' ) ] = ' and' ;
text = text . join ( '' ) ;
}
} return text . trim ( ) ;
}
2023-05-07 04:41:20 -04:00
formatPlayerUptime ( oldTime :number ) {
var Hours = 0 ;
oldTime = Math . floor ( Number ( oldTime ) ) ;
if ( oldTime >= 60 ) {
var Hours = Math . floor ( Number ( oldTime ) / 60 ) ;
var Minutes = ( Number ( oldTime ) - ( Hours * 60 ) ) ;
} else Minutes = Number ( oldTime )
if ( Hours >= 24 ) {
var Days = Math . floor ( Number ( Hours ) / 24 ) ;
var Hours = ( Hours - ( Days * 24 ) ) ;
} return ( Days > 0 ? Days + ' d ' : '' ) + ( Hours > 0 ? Hours + ' h ' : '' ) + ( Minutes > 0 ? Minutes + ' m' : '' )
}
2023-03-05 05:04:10 -05:00
isStaff = ( guildMember :Discord.GuildMember ) = > this . config . mainServer . staffRoles . map ( ( x : string ) = > this . config . mainServer . roles [ x ] ) . some ( ( x : string ) = > guildMember . roles . cache . has ( x ) ) ;
youNeedRole = ( interaction :Discord.CommandInteraction , role :string ) = > interaction . reply ( ` This command is restricted to <@& ${ this . config . mainServer . roles [ role ] } > ` ) ;
2022-12-01 17:53:57 -05:00
2023-03-05 05:04:10 -05:00
logTime = ( ) = > ` [ ${ this . moment ( ) . format ( 'DD/MM/YY HH:mm:ss' ) } ] ` ;
2022-12-01 17:53:57 -05:00
2023-05-07 04:41:20 -04:00
async punish ( interaction : Discord.ChatInputCommandInteraction < 'cached' > , type : string ) {
if ( ! this . isStaff ( interaction . member as Discord . GuildMember ) ) return this . youNeedRole ( interaction , "dcmod" ) ;
2023-05-01 20:12:45 -04:00
2023-03-05 05:04:10 -05:00
const time = interaction . options . getString ( 'time' ) ? ? undefined ;
const reason = interaction . options . getString ( 'reason' ) ? ? 'Reason unspecified' ;
const GuildMember = interaction . options . getMember ( 'member' ) ? ? undefined ;
const User = interaction . options . getUser ( 'member' , true ) ;
2023-05-07 04:41:20 -04:00
2023-06-09 21:11:19 -04:00
console . log ( this . logTime ( ) , ` [PunishmentLog] ${ GuildMember ? . user ? . username ? ? User ? . username ? ? 'No user data' } ${ time ? [ 'warn' , 'kick' ] . includes ( this . punishments . type ) ? 'and no duration set' : ` and ${ time } (duration) ` : '' } was used in / ${ interaction . commandName } for ${ reason } ` ) ;
( this . channels . cache . get ( this . config . mainServer . channels . punishment_log ) as Discord . TextChannel ) . send ( { embeds : [ new this . embed ( ) . setColor ( this . config . embedColor ) . setAuthor ( { name : interaction?.user?.username , iconURL : interaction?.user?.displayAvatarURL ( { size :2048 } ) } ) . setTitle ( 'Punishment Log' ) . setDescription ( ` ${ GuildMember ? . user ? . username ? ? User ? . username ? ? 'No user data' } ${ time ? [ 'warn' , 'kick' ] . includes ( this . punishments . type ) ? 'and no duration set' : ` and ${ time } (duration) ` : '' } was used in \` / ${ interaction . commandName } \` for \` ${ reason } \` ` ) . setTimestamp ( ) ] } ) ;
2023-05-28 06:21:40 -04:00
if ( interaction . user . id === User . id ) return interaction . reply ( ` You cannot ${ type } yourself. ` ) ;
if ( ! GuildMember && type != 'unban' ) return interaction . reply ( ` You cannot ${ type } someone who is not in the server. ` ) ;
2023-03-05 05:04:10 -05:00
if ( User . bot ) return interaction . reply ( ` You cannot ${ type } a bot! ` ) ;
await interaction . deferReply ( ) ;
2023-05-07 04:41:20 -04:00
await this . punishments . addPunishment ( type , { time , interaction } , interaction . user . id , reason , User , GuildMember ) ;
2023-03-05 05:04:10 -05:00
}
async YTLoop ( YTChannelID : string , YTChannelName : string , DCChannelID : string ) {
let Data :any ;
try {
2023-07-07 09:49:24 -04:00
await this . axios . get ( ` https://www.youtube.com/feeds/videos.xml?channel_id= ${ YTChannelID } ` , { timeout : 5000 } ) . then ( xml = > Data = this . xjs . xml2js ( xml . data , { compact : true } ) )
2023-03-05 05:04:10 -05:00
} catch ( err ) {
console . log ( this . logTime ( ) , ` ${ YTChannelName } YT fail ` )
2022-11-13 08:46:50 -05:00
}
2022-11-17 12:58:19 -05:00
2023-03-05 05:04:10 -05:00
if ( ! Data ) return ;
2023-07-07 09:49:24 -04:00
if ( this . YTCache [ YTChannelID ] === undefined ) {
2023-03-05 05:04:10 -05:00
this . YTCache [ YTChannelID ] = Data . feed . entry [ 0 ] [ 'yt:videoId' ] . _text ;
return ;
2022-11-13 08:46:50 -05:00
}
2023-07-07 09:49:24 -04:00
if ( Data . feed . entry [ 1 ] [ 'yt:videoId' ] . _text === this . YTCache [ YTChannelID ] ) {
2023-03-05 05:04:10 -05:00
this . YTCache [ YTChannelID ] = Data . feed . entry [ 0 ] [ 'yt:videoId' ] . _text ;
( this . channels . resolve ( DCChannelID ) as Discord . TextChannel ) . send ( ` ** ${ YTChannelName } ** just uploaded a video! \ n ${ Data . feed . entry [ 0 ] . link . _attributes . href } ` )
}
}
2023-06-24 13:47:42 -04:00
// Bytes conversion
formatBytes ( bytes :number , decimals :number = 2 ) {
if ( bytes === 0 ) return '0 Bytes' ;
const k = 1024 ;
const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ;
2023-07-07 09:49:24 -04:00
return parseFloat ( ( bytes / Math . pow ( k , i ) ) . toFixed ( decimals < 0 ? 0 : decimals ) ) + ' ' + [ 'Bytes' , 'KB' , 'MB' , 'GB' ] [ i ] ;
2023-06-24 13:47:42 -04:00
} ;
2022-11-14 03:45:40 -05:00
}
2023-02-08 20:51:59 -05:00
2023-03-05 05:04:10 -05:00
export class WClient extends WebhookClient {
tokens : Tokens ;
constructor ( ) {
2023-07-07 09:49:24 -04:00
super ( { url : tokens.webhook_url } )
2023-04-14 06:47:58 -04:00
this . tokens = tokens as Tokens ;
2023-03-05 05:04:10 -05:00
}
}