2021-04-07 10:58:09 +00:00
// Contains all the code handling dynamic JSON data. Has a one-to-one connection with each file generated, for example, `Config` which calls `super("config")` meaning it writes to `data/config.json`.
2021-04-05 04:35:12 +00:00
import FileManager from "./modules/storage" ;
2021-03-31 02:19:04 +00:00
import { select , GenericJSON , GenericStructure } from "./lib" ;
2020-12-15 01:44:28 +00:00
import { watch } from "fs" ;
2020-12-31 16:10:56 +00:00
import { Guild as DiscordGuild , Snowflake } from "discord.js" ;
2020-10-15 09:23:24 +00:00
2021-04-03 04:11:18 +00:00
// Maybe use getters and setters to auto-save on set?
2020-10-15 09:23:24 +00:00
class ConfigStructure extends GenericStructure {
2020-12-15 01:44:28 +00:00
public token : string ;
public prefix : string ;
public owner : string ;
public admins : string [ ] ;
public support : string [ ] ;
2021-04-03 04:11:18 +00:00
public systemLogsChannel : string | null ;
2020-12-15 01:44:28 +00:00
constructor ( data : GenericJSON ) {
super ( "config" ) ;
this . token = select ( data . token , "<ENTER YOUR TOKEN HERE>" , String ) ;
this . prefix = select ( data . prefix , "$" , String ) ;
this . owner = select ( data . owner , "" , String ) ;
this . admins = select ( data . admins , [ ] , String , true ) ;
this . support = select ( data . support , [ ] , String , true ) ;
2021-04-03 04:11:18 +00:00
this . systemLogsChannel = select ( data . systemLogsChannel , null , String ) ;
2020-12-15 01:44:28 +00:00
}
2020-07-25 08:15:26 +00:00
}
2020-10-15 09:23:24 +00:00
class User {
2020-12-15 01:44:28 +00:00
public money : number ;
public lastReceived : number ;
2020-12-16 07:24:26 +00:00
public lastMonday : number ;
2021-01-24 14:07:58 +00:00
public timezone : number | null ; // This is for the standard timezone only, not the daylight savings timezone
public daylightSavingsRegion : "na" | "eu" | "sh" | null ;
2021-03-31 01:40:29 +00:00
public todoList : { [ timestamp : string ] : string } ;
2021-04-06 06:02:52 +00:00
public ecoBetInsurance : number ;
2020-10-15 09:23:24 +00:00
2020-12-15 01:44:28 +00:00
constructor ( data? : GenericJSON ) {
this . money = select ( data ? . money , 0 , Number ) ;
this . lastReceived = select ( data ? . lastReceived , - 1 , Number ) ;
2020-12-16 07:24:26 +00:00
this . lastMonday = select ( data ? . lastMonday , - 1 , Number ) ;
2021-01-24 14:07:58 +00:00
this . timezone = data ? . timezone ? ? null ;
this . daylightSavingsRegion = /^((na)|(eu)|(sh))$/ . test ( data ? . daylightSavingsRegion )
? data ? . daylightSavingsRegion
: null ;
2021-03-31 01:40:29 +00:00
this . todoList = { } ;
2021-04-07 06:43:39 +00:00
this . ecoBetInsurance = select ( data ? . ecoBetInsurance , 0 , Number ) ;
2021-03-31 01:40:29 +00:00
if ( data ) {
for ( const timestamp in data . todoList ) {
const note = data . todoList [ timestamp ] ;
if ( typeof note === "string" ) {
this . todoList [ timestamp ] = note ;
}
}
}
2020-12-15 01:44:28 +00:00
}
2020-07-25 08:15:26 +00:00
}
2020-10-15 09:23:24 +00:00
class Guild {
2020-12-15 01:44:28 +00:00
public prefix : string | null ;
2021-04-01 13:41:49 +00:00
public welcomeType : "none" | "text" | "graphical" ;
public welcomeChannel : string | null ;
2021-04-03 04:11:18 +00:00
public welcomeMessage : string | null ;
2021-04-04 02:35:55 +00:00
public streamingChannel : string | null ;
2020-10-15 09:23:24 +00:00
2020-12-15 01:44:28 +00:00
constructor ( data? : GenericJSON ) {
this . prefix = select ( data ? . prefix , null , String ) ;
2021-04-01 13:41:49 +00:00
this . welcomeChannel = select ( data ? . welcomeChannel , null , String ) ;
2021-04-03 04:11:18 +00:00
this . welcomeMessage = select ( data ? . welcomeMessage , null , String ) ;
2021-04-04 02:35:55 +00:00
this . streamingChannel = select ( data ? . streamingChannel , null , String ) ;
2021-04-01 13:41:49 +00:00
switch ( data ? . welcomeType ) {
case "text" :
this . welcomeType = "text" ;
break ;
case "graphical" :
this . welcomeType = "graphical" ;
break ;
default :
this . welcomeType = "none" ;
break ;
}
2020-12-15 01:44:28 +00:00
}
2020-07-25 08:15:26 +00:00
}
2020-10-15 09:23:24 +00:00
class StorageStructure extends GenericStructure {
2020-12-15 01:44:28 +00:00
public users : { [ id : string ] : User } ;
public guilds : { [ id : string ] : Guild } ;
constructor ( data : GenericJSON ) {
super ( "storage" ) ;
this . users = { } ;
this . guilds = { } ;
2021-04-10 04:06:16 +00:00
for ( let id in data . users ) if ( /\d{17,}/g . test ( id ) ) this . users [ id ] = new User ( data . users [ id ] ) ;
for ( let id in data . guilds ) if ( /\d{17,}/g . test ( id ) ) this . guilds [ id ] = new Guild ( data . guilds [ id ] ) ;
2020-10-15 09:23:24 +00:00
}
2020-12-15 01:44:28 +00:00
/** Gets a user's profile if they exist and generate one if not. */
public getUser ( id : string ) : User {
2021-04-10 04:06:16 +00:00
if ( ! /\d{17,}/g . test ( id ) )
2021-03-30 08:58:21 +00:00
console . warn ( ` " ${ id } " is not a valid user ID! It will be erased when the data loads again. ` ) ;
2020-12-15 01:44:28 +00:00
if ( id in this . users ) return this . users [ id ] ;
else {
const user = new User ( ) ;
this . users [ id ] = user ;
return user ;
}
}
/** Gets a guild's settings if they exist and generate one if not. */
public getGuild ( id : string ) : Guild {
2021-04-10 04:06:16 +00:00
if ( ! /\d{17,}/g . test ( id ) )
2021-03-30 08:58:21 +00:00
console . warn ( ` " ${ id } " is not a valid guild ID! It will be erased when the data loads again. ` ) ;
2020-12-15 01:44:28 +00:00
if ( id in this . guilds ) return this . guilds [ id ] ;
else {
const guild = new Guild ( ) ;
this . guilds [ id ] = guild ;
return guild ;
}
2020-10-15 09:23:24 +00:00
}
2020-07-25 08:15:26 +00:00
}
// Exports instances. Don't worry, importing it from different files will load the same instance.
2020-12-15 01:44:28 +00:00
export let Config = new ConfigStructure ( FileManager . read ( "config" ) ) ;
export let Storage = new StorageStructure ( FileManager . read ( "storage" ) ) ;
2020-07-25 08:15:26 +00:00
// This part will allow the user to manually edit any JSON files they want while the program is running which'll update the program's cache.
// However, fs.watch is a buggy mess that should be avoided in production. While it helps test out stuff for development, it's not a good idea to have it running outside of development as it causes all sorts of issues.
2021-03-30 08:58:21 +00:00
if ( IS_DEV_MODE ) {
2020-12-15 01:44:28 +00:00
watch ( "data" , ( event , filename ) = > {
2021-03-30 08:58:21 +00:00
console . debug ( "File Watcher:" , event , filename ) ;
2020-12-15 01:44:28 +00:00
const header = filename . substring ( 0 , filename . indexOf ( ".json" ) ) ;
switch ( header ) {
case "config" :
Config = new ConfigStructure ( FileManager . read ( "config" ) ) ;
break ;
case "storage" :
Storage = new StorageStructure ( FileManager . read ( "storage" ) ) ;
break ;
}
} ) ;
2020-08-14 09:43:45 +00:00
}
2021-04-07 10:58:09 +00:00
/ * *
* Get the current prefix of the guild or the bot ' s prefix if none is found .
* /
2020-10-15 09:23:24 +00:00
export function getPrefix ( guild : DiscordGuild | null ) : string {
2021-01-25 01:12:43 +00:00
let prefix = Config . prefix ;
if ( guild ) {
const possibleGuildPrefix = Storage . getGuild ( guild . id ) . prefix ;
// Here, lossy comparison works in our favor because you wouldn't want an empty string to trigger the prefix.
if ( possibleGuildPrefix ) {
prefix = possibleGuildPrefix ;
}
}
return prefix ;
2020-10-15 09:23:24 +00:00
}
2020-12-31 16:10:56 +00:00
export interface EmoteRegistryDumpEntry {
2021-01-03 02:16:15 +00:00
ref : string ;
2020-12-31 16:10:56 +00:00
id : Snowflake ;
name : string ;
requires_colons : boolean ;
animated : boolean ;
url : string ;
2021-01-03 02:16:15 +00:00
guild_id : Snowflake ;
2020-12-31 16:10:56 +00:00
guild_name : string ;
}
export interface EmoteRegistryDump {
version : number ;
list : EmoteRegistryDumpEntry [ ] ;
}