import mongoose = require ( 'mongoose' ) ; import { guildDoc , logDoc , commandsDoc , filtersDoc , globalSettingsDoc , staffDoc , prefixDoc , commandCacheDoc , guildSchema , staffSchema , prefixSchema , commandsSchema , filtersSchema , logSchema , commandCacheSchema , globalSettingsSchema , globalSettingsObject , CommandCache , userDoc , userSchema , UserWrapper , megalogDoc , megalogSchema , megalogFunctions , megalogObject , caseObject , caseDoc , caseSchema , pActionDoc , pActionSchema } from './schemas' ; import { setInterval } from 'timers' ; import { globalUpdateInterval , cleanInterval } from '../bot-config.json' ; import { Guild , DMChannel , GroupDMChannel , TextChannel , User } from 'discord.js' ; import { Bot } from '..' ; import { toNano } from '../utils/time' ; export class export class Database { type { { connection : mongoose . Connection ; guilds : mongoose . Model < guildDoc > ; staff : mongoose . Model < staffDoc > ; prefix : mongoose . Model < prefixDoc > ; commands : mongoose . Model < commandsDoc > ; filters : mongoose . Model < filtersDoc > ; logs : mongoose . Model < logDoc > ; commandCache : mongoose . Model < commandCacheDoc > ; } } memberof mainDB : { connection : mongoose . Connection ; guilds : mongoose . Model < guildDoc > ; staff : mongoose . Model < staffDoc > ; prefix : mongoose . Model < prefixDoc > ; commands : mongoose . Model < commandsDoc > ; filters : mongoose . Model < filtersDoc > ; logs : mongoose . Model < logDoc > ; commandCache : mongoose . Model < commandCacheDoc > ; users : mongoose . Model < userDoc > ; megalogs : mongoose . Model < megalogDoc > ; cases : mongoose . Model < caseDoc > ; pActions : mongoose . Model < pActionDoc > ; } ; type { { connection : mongoose . Connection ; settings : mongoose . Model < globalSettingsDoc > ; cache : globalSettingsObject ; } } memberof settingsDB : { connection : mongoose . Connection ; settings : mongoose . Model < globalSettingsDoc > ; cache : globalSettingsObject ; } ; param { { url : string , suffix : string } } clusterInfo memberof constructor ( clusterInfo ) { var mainCon = mongoose . createConnection ( clusterInfo . url + '/main' + clusterInfo . suffix , { useNewUrlParser : true } ) ; mainCon . on ( 'error' , error => { console . error ( 'connection error:' , error ) ; Bot . mStats . logError ( error ) ; } ) ; mainCon . once ( 'open' , function ( ) { console . log ( 'connected to /main database' ) ; Bot . database . mainDB = { connection : mainCon , guilds : mainCon . model ( 'guild' , guildSchema , 'guilds' ) , staff : mainCon . model ( 'staff' , staffSchema , 'staff' ) , prefix : mainCon . model ( 'prefix' , prefixSchema , 'prefix' ) , commands : mainCon . model ( 'commands' , commandsSchema , 'commands' ) , filters : mainCon . model ( 'filters' , filtersSchema , 'filters' ) , logs : mainCon . model ( 'log' , logSchema , 'logs' ) , commandCache : mainCon . model ( 'commandCache' , commandCacheSchema , 'commandCaches' ) , users : mainCon . model ( 'user' , userSchema , 'users' ) , megalogs : mainCon . model ( 'megalogSettings' , megalogSchema , 'megalogs' ) , cases : mainCon . model ( 'cases' , caseSchema , 'cases' ) , pActions : mainCon . model ( 'pActions' , pActionSchema , 'pAction' ) } ; setInterval ( async ( ) => { await Bot . database . cleanGuilds ( ) ; Bot . database . cleanCommandCaches ( ) ; Bot . database . cleanUsers ( ) ; Bot . database . cleanMegalogs ( ) ; } , cleanInterval ) ; console . info ( `cleaning database every ${ cleanInterval }ms` ) ; } ) ; var settingsCon = mongoose . createConnection ( clusterInfo . url + '/settings' + clusterInfo . suffix , { useNewUrlParser : true } ) ; settingsCon . on ( 'error' , error => { console . error ( 'connection error:' , error ) ; Bot . mStats . logError ( error ) ; } ) ; settingsCon . once ( 'open' , function ( ) { console . log ( 'connected to /settings database' ) ; Bot . database . settingsDB = { connection : settingsCon , settings : settingsCon . model ( 'globalSettings' , globalSettingsSchema , 'settings' ) , cache : undefined } ; Bot . database . updateGlobalSettings ( Bot . database . settingsDB ) ; setInterval ( ( ) => Bot . database . updateGlobalSettings ( Bot . database . settingsDB ) , globalUpdateInterval ) ; console . info ( `updating global cache every ${ globalUpdateInterval }ms` ) ; } ) ; } returns memberof async ping ( ) { var currentNano = process . hrtime ( ) ; await this . mainDB . connection . db . command ( { ping : 1 } ) ; return toNano ( process . hrtime ( currentNano ) ) ; } param { { connection : mongoose . Connection ; settings : mongoose . Model < globalSettingsDoc > ; cache : globalSettingsObject ; } } settingsDB returns memberof async updateGlobalSettings ( settingsDB ) { var settingsDoc = await settingsDB . settings . findOne ( ) . exec ( ) ; if ( ! settingsDoc ) { console . warn ( 'global settings doc not found' ) ; return ; } var settingsObject = settingsDoc . toObject ( ) ; if ( settingsDB . cache && ( settingsObject . presence != settingsDB . cache . presence || ! settingsObject . presence ) ) { if ( settingsObject . presence && ( settingsObject . presence . status || settingsObject . presence . game || settingsObject . presence . afk ) ) { Bot . client . user . setPresence ( settingsObject . presence ) ; } else { Bot . client . user . setActivity ( undefined ) ; Bot . client . user . setStatus ( 'online' ) ; } } settingsDB . cache = settingsObject ; } param { string } guildID param { string } prefix returns memberof async setPrefix ( guildID , prefix ? ) { var prefixDoc ; if ( prefix ) { prefixDoc = await this . mainDB . prefix . findOne ( { guild : guildID } ) . exec ( ) ; if ( prefixDoc ) return await prefixDoc . remove ( ) ; } prefixDoc = new this . mainDB . prefix ( { guild : guildID , prefix : prefix } ) ; return await prefixDoc . save ( ) ; } param { string } guildID returns memberof param { Guild } guild param { string } guildID returns { Promise < string > } memberof async getPrefix ( guild ? , guildID ? ) { if ( ! guildID && guild ) guildID = guild . id ; if ( guildID ) { var prefixDoc = await this . mainDB . prefix . findOne ( { guild : guildID } ) . exec ( ) ; if ( prefixDoc ) return prefixDoc . toObject ( ) . prefix ; if ( ! this . settingsDB . cache ) return '!?' ; } return this . settingsDB . cache . prefix ; } returns memberof getBotMasters ( ) { if ( ! this . settingsDB . cache ) return [ ] ; return this . settingsDB . cache . botMasters ; } param { string } command returns memberof getGlobalCommandSettings ( command ) { if ( ! this . settingsDB . cache || ! this . settingsDB . cache . commands [ command ] ) return undefined ; return this . settingsDB . cache . commands [ command ] ; } param { string } filter returns memberof getGlobalFilterSettings ( filter ) { if ( ! this . settingsDB . cache || ! this . settingsDB . cache . filters [ filter ] ) return undefined ; return this . settingsDB . cache . filters [ filter ] ; } param { string } guildID returns memberof findGuildDoc ( guildID , projection ? ) { return this . mainDB . guilds . findOne ( { guild : guildID } , projection ) . exec ( ) ; } param { * } guildID returns memberof async addGuild ( guildID ) { if ( ! ( await this . findStaffDoc ( guildID ) ) ) { var staffDoc = new this . mainDB . staff ( { guild : guildID , admins : { roles : [ ] , users : [ ] } , mods : { roles : [ ] , users : [ ] } , immune : { roles : [ ] , users : [ ] } } ) ; await staffDoc . save ( ) ; } var guildDoc = await this . findGuildDoc ( guildID ) ; if ( ! guildDoc ) { guildDoc = new this . mainDB . guilds ( { guild : guildID , logChannel : undefined , logs : [ ] , staff : staffDoc . id , webhooks : { youtube : [ ] } } ) ; } return await guildDoc . save ( ) ; } param { string } guildID memberof async removeGuild ( guildID ) { for ( const webhookDoc of await Bot . youtube . webhooks . find ( { guild : guildID } ) ) { Bot . youtube . deleteWebhook ( guildID , webhookDoc . toObject ( ) . channel , webhookDoc . toObject ( ) . feed ) ; } this . mainDB . guilds . deleteOne ( { guild : guildID } ) . exec ( ) ; this . mainDB . staff . deleteOne ( { guild : guildID } ) . exec ( ) ; this . mainDB . prefix . deleteOne ( { guild : guildID } ) . exec ( ) ; this . mainDB . commands . deleteOne ( { guild : guildID } ) . exec ( ) ; this . mainDB . filters . deleteOne ( { guild : guildID } ) . exec ( ) ; this . mainDB . logs . deleteMany ( { guild : guildID } ) . exec ( ) ; this . mainDB . megalogs . deleteOne ( { guild : guildID } ) . exec ( ) ; this . mainDB . cases . deleteMany ( { guild : guildID } ) . exec ( ) ; this . mainDB . pActions . deleteMany ( { 'info.guild' : guildID } ) . exec ( ) ; } memberof async cleanGuilds ( ) { let guildDocs = await this . mainDB . guilds . find ( { } , [ 'guild' ] ) ; for ( const guildDoc of guildDocs ) { if ( ! Bot . client . guilds . get ( guildDoc . guild ) ) { await this . removeGuild ( guildDoc . guild ) ; } } } param { string } guildID returns memberof findStaffDoc ( guildID ) { return this . mainDB . staff . findOne ( { guild : guildID } ) . exec ( ) ; } param { string } guildID param { ( 'admins' | 'mods' | 'immune' ) } rank param { string } roleID param { string } userID returns memberof async addToRank ( guildID , rank , roleID ? , userID ? ) { if ( ! roleID && ! userID ) return true ; var staffDoc = await this . findStaffDoc ( guildID ) ; if ( ! staffDoc ) { staffDoc = new this . mainDB . staff ( { guild : guildID , admins : { roles : [ ] , users : [ ] } , mods : { roles : [ ] , users : [ ] } , immune : { roles : [ ] , users : [ ] } } ) ; await staffDoc . save ( ) ; } if ( roleID && ! staffDoc [ rank ] . roles . includes ( roleID ) ) { staffDoc [ rank ] . roles . push ( roleID ) ; } else if ( userID && ! staffDoc [ rank ] . users . includes ( userID ) ) { staffDoc [ rank ] . users . push ( userID ) ; } else { return false ; } staffDoc . save ( ) ; return true ; } param { string } guildID param { ( 'admins' | 'mods' | 'immune' ) } rank param { string } roleID param { string } userID returns memberof async removeFromRank ( guildID , rank , roleID ? , userID ? ) { if ( ! roleID && ! userID ) return true ; var staffDoc = await this . findStaffDoc ( guildID ) ; if ( ! staffDoc ) return ; if ( roleID && staffDoc [ rank ] . roles . includes ( roleID ) ) { staffDoc [ rank ] . roles . splice ( staffDoc [ rank ] . roles . indexOf ( roleID ) , 1 ) ; } else if ( userID && staffDoc [ rank ] . users . includes ( userID ) ) { staffDoc [ rank ] . users . splice ( staffDoc [ rank ] . users . indexOf ( userID ) , 1 ) ; } else { return false ; } staffDoc . save ( ) ; return true ; } param { string } guildID returns memberof findCommandsDoc ( guildID ) { return this . mainDB . commands . findOne ( { guild : guildID } ) . exec ( ) ; } param { string } guildID param { string } command param { commandsDoc } doc returns memberof async getCommandSettings ( guildID , command , doc ? ) { var commandSettings = doc ; if ( ! commandSettings || commandSettings . guild != guildID ) commandSettings = await this . findCommandsDoc ( guildID ) ; if ( ! commandSettings ) return undefined ; commandSettings = commandSettings . toObject ( ) . commands ; if ( commandSettings [ command ] ) return commandSettings [ command ] ; return undefined ; } param { string } guildID param { string } command param { * } settings param { commandsDoc } doc returns memberof async setCommandSettings ( guildID , command , settings , doc ? ) { if ( ! doc || doc . guild != guildID ) { doc = await this . findCommandsDoc ( guildID ) ; } if ( ! doc ) { doc = new this . mainDB . commands ( { guild : guildID , commands : { } } ) ; } doc . commands [ command ] = settings ; doc . markModified ( 'commands.' + command ) ; return await doc . save ( ) ; } param { string } guildID returns memberof findFiltersDoc ( guildID ) { return this . mainDB . filters . findOne ( { guild : guildID } ) . exec ( ) ; } param { string } guildID param { string } filter param { filtersDoc } doc returns memberof async getFilterSettings ( guildID , filter , doc ? ) { var filterSettings = doc ; if ( ! filterSettings || filterSettings . guild != guildID ) filterSettings = await this . findFiltersDoc ( guildID ) ; if ( ! filterSettings ) return undefined ; filterSettings = filterSettings . toObject ( ) . filters ; if ( filter in filterSettings ) return filterSettings [ filter ] ; return undefined ; } param { string } guildID param { string } filter param { * } settings param { filtersDoc } doc returns memberof async setFilterSettings ( guildID , filter , settings , doc ? ) { if ( ! doc || doc . guild != guildID ) { doc = await this . findFiltersDoc ( guildID ) ; } if ( ! doc ) { doc = new this . mainDB . filters ( { guild : guildID , filters : { } } ) ; } doc . filters [ filter ] = settings ; doc . markModified ( 'filters.' + filter ) ; return await doc . save ( ) ; } param { string } channelID param { string } userID returns memberof findCommandCacheDoc ( channelID , userID , timestamp = Date . now ( ) ) { return this . mainDB . commandCache . findOne ( { channel : channelID , user : userID , delete : { $gt : timestamp } } ) . exec ( ) ; } param { ( DMChannel | GroupDMChannel | TextChannel ) } channel param { User } user param { string } command param { number } cacheTime param { * } cache returns memberof async getCommandCache ( channel , user , command ? , cacheTime ? , cache ? ) { let commandCacheDoc = await this . findCommandCacheDoc ( channel . id , user . id ) ; if ( ! commandCacheDoc ) { if ( cacheTime && command ) return new CommandCache ( undefined , channel , user , command , cacheTime , cache ) ; return undefined ; } return new CommandCache ( commandCacheDoc ) ; } returns memberof cleanCommandCaches ( ) { return this . mainDB . commandCache . deleteMany ( { delete : { $lt : Date . now ( ) } } ) . exec ( ) ; } param { string } userID returns memberof findUserDoc ( userID ) { return this . mainDB . users . findOne ( { user : userID } ) . exec ( ) ; } param { User } user param { boolean } create returns memberof async getUser ( user , create = false ) { var userDoc = await this . findUserDoc ( user . id ) ; if ( userDoc ) { return new UserWrapper ( userDoc , user ) ; } else if ( create ) return new UserWrapper ( undefined , user ) ; } memberof async cleanUsers ( ) { await this . mainDB . users . deleteMany ( { $or : [ { commandCooldown : null } , { commandCooldown : { } } ] } ) . exec ( ) ; let userDocs = await this . mainDB . users . find ( ) . exec ( ) ; let now = Date . now ( ) ; for ( const userDoc of userDocs ) { let useless = true ; let changed = false ; for ( const scopes in userDoc . commandCooldown ) { for ( const commands in userDoc . commandCooldown [ scopes ] ) { if ( userDoc . commandCooldown [ scopes ] [ commands ] < now ) { delete userDoc . commandCooldown [ scopes ] [ commands ] ; changed = true ; } else { useless = false ; } } } if ( useless || ! await Bot . client . fetchUser ( userDoc . toObject ( ) . user ) ) { userDoc . remove ( ) ; continue ; } if ( changed ) { userDoc . markModified ( 'commandCooldown' ) ; userDoc . save ( ) ; } } } param { string } guildID returns memberof findMegalogDoc ( guildID ) { return this . mainDB . megalogs . findOne ( { guild : guildID } ) . exec ( ) ; } param { string } guildID returns memberof async getMegalogDoc ( guildID ) { let megalogDoc = await this . findMegalogDoc ( guildID ) ; if ( ! megalogDoc ) { megalogDoc = new this . mainDB . megalogs ( { guild : guildID } ) ; await megalogDoc . save ( ) ; } return megalogDoc ; } memberof async cleanMegalogs ( ) { let deleteQuery = { } ; for ( const func of megalogFunctions . all ) { deleteQuery [ func ] = { $exists : false } ; } await this . mainDB . megalogs . deleteMany ( deleteQuery ) . exec ( ) ; let megalogDocs = await this . mainDB . megalogs . find ( ) ; for ( const megalogDoc of megalogDocs ) { let modified = false ; let megalogObject = megalogDoc . toObject ( ) ; let guild = Bot . client . guilds . get ( megalogObject . guild ) ; if ( ! guild ) { megalogDoc . remove ( ) ; continue ; } for ( const func of megalogFunctions . all ) { if ( megalogObject [ func ] && ! guild . channels . get ( megalogObject [ func ] ) ) { megalogDoc [ func ] = undefined ; modified = true ; } } if ( modified ) megalogDoc . save ( ) ; } } }