2021-06-20 13:11:21 +02:00
const fs = require ( 'fs' ) ;
2022-01-25 10:06:47 +01:00
const pathUtil = require ( 'path' ) ;
2021-10-22 09:35:04 +02:00
const CacheHandler = require ( './abstractions/CacheHandler' ) ;
2021-06-20 13:11:21 +02:00
2021-10-22 09:35:04 +02:00
class JsonCache extends CacheHandler {
2021-06-20 13:11:21 +02:00
2021-10-22 09:35:04 +02:00
constructor ( client ) {
super ( client ) ;
2021-06-20 13:11:21 +02:00
const opts = client . _options ;
this . logger = client . logger ;
this . saveInterval = opts . saveInterval ;
this . _ready = false ;
2021-07-19 21:30:31 +02:00
// Data that gets stored to persistent cache
2021-06-20 13:11:21 +02:00
this . queue = [ ] ;
this . channels = { } ;
this . lastActivity = { } ;
2021-07-19 21:30:31 +02:00
this . misc = { } ; // Random misc data, should not be non-primitive data types
2021-06-20 13:11:21 +02:00
2021-07-19 21:30:31 +02:00
// Stored separately if at all
2021-06-20 13:11:21 +02:00
this . modmail = { } ;
this . updatedThreads = [ ] ;
}
2021-10-22 09:35:04 +02:00
load ( ) {
2021-06-20 13:11:21 +02:00
if ( this . _ready ) return ;
if ( fs . existsSync ( './persistent_cache.json' ) ) {
this . logger . info ( 'Loading cache' ) ;
const raw = JSON . parse ( fs . readFileSync ( './persistent_cache.json' , { encoding : 'utf-8' } ) ) ;
const entries = Object . entries ( raw ) ;
2021-10-22 09:35:04 +02:00
for ( const [ key , val ] of entries ) this [ key ] = val ;
2021-06-20 13:11:21 +02:00
} else {
this . logger . info ( 'Cache file missing, creating...' ) ;
2021-10-22 09:35:04 +02:00
this . savePersistentCache ( ) ;
2021-06-20 13:11:21 +02:00
}
2021-10-22 09:35:04 +02:00
this . cacheSaveInterval = setInterval ( this . savePersistentCache . bind ( this ) , 10 * 60 * 1000 ) ;
2021-06-21 16:54:05 +02:00
this . modmailSaveInterval = setInterval ( this . saveModmailHistory . bind ( this ) , this . saveInterval * 60 * 1000 , this . client . modmail ) ;
2021-06-20 13:11:21 +02:00
this . _ready = true ;
}
2021-10-22 09:35:04 +02:00
savePersistentCache ( ) {
2021-06-20 13:11:21 +02:00
this . logger . debug ( 'Saving cache' ) ;
2022-08-28 10:25:15 +02:00
fs . writeFileSync ( './persistent_cache.json' , JSON . stringify ( this . json , null , 4 ) ) ;
2021-06-20 13:11:21 +02:00
}
2021-10-22 09:35:04 +02:00
saveModmailHistory ( ) {
2021-06-20 13:11:21 +02:00
if ( ! this . updatedThreads . length ) return ;
2021-10-22 09:35:04 +02:00
const toSave = [ ... this . updatedThreads ] ;
2021-06-20 13:11:21 +02:00
this . updatedThreads = [ ] ;
this . client . logger . debug ( ` Saving modmail data ` ) ;
if ( ! fs . existsSync ( './modmail_cache' ) ) fs . mkdirSync ( './modmail_cache' ) ;
for ( const id of toSave ) {
const path = ` ./modmail_cache/ ${ id } .json ` ;
try {
2022-08-28 10:25:15 +02:00
fs . writeFileSync ( path , JSON . stringify ( this . modmail [ id ] , null , 4 ) ) ;
2021-06-20 13:11:21 +02:00
} catch ( err ) {
this . client . logger . error ( ` Error during saving of history \n ${ id } \n ${ JSON . stringify ( this . modmail ) } \n ${ err . stack } ` ) ;
}
}
}
2021-10-22 09:35:04 +02:00
loadModmailHistory ( userId ) {
2021-06-20 13:11:21 +02:00
return new Promise ( ( resolve , reject ) => {
if ( this . modmail [ userId ] ) return resolve ( this . modmail [ userId ] ) ;
const path = ` ./modmail_cache/ ${ userId } .json ` ;
if ( ! fs . existsSync ( path ) ) {
this . modmail [ userId ] = [ ] ;
return resolve ( this . modmail [ userId ] ) ;
}
fs . readFile ( path , { encoding : 'utf-8' } , ( err , data ) => {
if ( err ) reject ( err ) ;
const parsed = JSON . parse ( data ) ;
this . modmail [ userId ] = parsed ;
resolve ( parsed ) ;
} ) ;
} ) ;
}
2022-01-12 17:46:06 +01:00
async verifyQueue ( ) {
2022-01-12 17:29:52 +01:00
this . client . logger . info ( ` Verifying modmail queue. ` ) ;
2022-01-12 17:46:06 +01:00
for ( const entry of this . queue ) {
2022-01-25 10:06:47 +01:00
const path = ` ./modmail_cache/ ${ entry } .json ` ;
if ( ! fs . existsSync ( pathUtil . resolve ( path ) ) ) this . client . logger . warn ( ` User ${ entry } is in queue but is missing history. Attempting to recover history. ` ) ;
2022-01-12 17:29:52 +01:00
const user = await this . client . resolveUser ( entry ) ;
const dm = await user . createDM ( ) ;
let messages = await dm . messages . fetch ( ) ;
2022-01-12 22:20:28 +01:00
messages = messages . sort ( ( a , b ) => a . createdTimestamp - b . createdTimestamp ) . map ( msg => msg ) ; // .filter(msg => msg.author.id !== this.client.user.id)
const amt = messages . length ;
2022-01-12 17:29:52 +01:00
const history = await this . loadModmailHistory ( entry ) ;
2022-01-12 22:20:28 +01:00
if ( history . length ) { // Sync user's past messages with the bot's cache if one exists
const last = history [ history . length - 1 ] ;
let index = amt - 1 ;
for ( index ; index >= 0 ; index -- ) { // Find the most recent message that is also in the user's history
const msg = messages [ index ] ;
if ( msg . content === last . content || msg . embeds . length && msg . author . bot ) {
messages = messages . slice ( index + 1 ) . filter ( m => ! m . author . bot ) ;
break ;
}
}
if ( messages . length ) this . client . logger . warn ( ` User ${ entry } has previous history but is out of sync, attempting sync. ${ messages . length } messages out of sync. ` ) ;
else continue ;
}
history . push ( { timestamp : Date . now ( ) , author : this . client . user . id , content : 'Attempted a recovery of missing messages at this point, messages may look out of place if something went wrong.' } ) ;
for ( const { author , content , createdTimestamp , attachments } of messages ) {
if ( author . bot ) continue ;
2022-01-12 17:46:06 +01:00
history . push ( { attachments : attachments . map ( att => att . url ) , author : author . id , content , timestamp : createdTimestamp } ) ;
}
this . updatedThreads . push ( entry ) ;
2022-01-12 17:29:52 +01:00
2022-01-12 17:46:06 +01:00
}
2022-01-12 17:29:52 +01:00
this . client . logger . info ( ` Queue verified. ` ) ;
2022-01-12 17:46:06 +01:00
this . saveModmailHistory ( ) ;
2022-01-12 17:29:52 +01:00
}
2021-10-22 09:35:04 +02:00
get json ( ) {
2021-06-20 13:11:21 +02:00
return {
queue : this . queue ,
channels : this . channels ,
2021-07-19 21:30:31 +02:00
lastActivity : this . lastActivity ,
misc : this . misc
2021-06-20 13:11:21 +02:00
} ;
}
}
2021-10-22 09:35:04 +02:00
module . exports = JsonCache ;