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?
2021-04-10 12:51:32 +00:00
// And maybe use Collections/Maps instead of objects?
2021-04-03 04:11:18 +00:00
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-05-21 11:54:20 +00:00
public lavalink : boolean | null ;
2021-11-03 12:45:58 +00:00
public wolfram : string | null ;
2021-04-03 04:11:18 +00:00
public systemLogsChannel : string | null ;
2021-05-06 13:30:51 +00:00
public webhooks : { [ id : string ] : string } ; // id-token pairs
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-05-21 11:54:20 +00:00
this . lavalink = select ( data . lavalink , null , Boolean ) ;
2021-11-03 12:45:58 +00:00
this . wolfram = select ( data . wolfram , null , String ) ;
2021-04-03 04:11:18 +00:00
this . systemLogsChannel = select ( data . systemLogsChannel , null , String ) ;
2021-05-06 13:30:51 +00:00
this . webhooks = { } ;
for ( const id in data . webhooks ) {
const token = data . webhooks [ id ] ;
if ( /\d{17,}/g . test ( id ) && typeof token === "string" ) {
this . webhooks [ id ] = token ;
}
}
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
}
2021-04-12 17:43:13 +00:00
class Member {
public streamCategory : string | null ;
constructor ( data? : GenericJSON ) {
this . streamCategory = select ( data ? . streamCategory , null , String ) ;
}
}
2020-10-15 09:23:24 +00:00
class Guild {
2020-12-15 01:44:28 +00:00
public prefix : string | null ;
2021-08-21 10:31:47 +00:00
public messageEmbeds : boolean | 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-08-15 20:42:27 +00:00
public autoRoles : string [ ] | null ; // StringArray of role IDs
2021-04-04 02:35:55 +00:00
public streamingChannel : string | null ;
2021-04-12 17:43:13 +00:00
public streamingRoles : { [ role : string ] : string } ; // Role ID: Category Name
2021-04-26 11:16:37 +00:00
public channelNames : { [ channel : string ] : string } ;
2021-04-12 17:43:13 +00:00
public members : { [ id : string ] : Member } ;
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-08-21 10:31:47 +00:00
this . messageEmbeds = select ( data ? . messageLinks , true , Boolean ) ;
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-08-15 20:42:27 +00:00
this . autoRoles = select ( data ? . autoRoles , null , String , true ) ;
2021-04-04 02:35:55 +00:00
this . streamingChannel = select ( data ? . streamingChannel , null , String ) ;
2021-04-12 17:43:13 +00:00
this . streamingRoles = { } ;
2021-04-26 11:16:37 +00:00
this . channelNames = { } ;
2021-04-12 17:43:13 +00:00
this . members = { } ;
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 ;
}
2021-04-12 17:43:13 +00:00
if ( data ? . streamingRoles ) {
for ( const id in data . streamingRoles ) {
const category = data . streamingRoles [ id ] ;
if ( /\d{17,}/g . test ( id ) && typeof category === "string" ) {
this . streamingRoles [ id ] = category ;
}
}
}
2021-04-26 11:16:37 +00:00
if ( data ? . channelNames ) {
for ( const id in data . channelNames ) {
const name = data . channelNames [ id ] ;
if ( /\d{17,}/g . test ( id ) && typeof name === "string" ) {
this . channelNames [ id ] = name ;
}
}
}
2021-04-12 17:43:13 +00:00
if ( data ? . members ) {
for ( let id in data . members ) {
if ( /\d{17,}/g . test ( id ) ) {
this . members [ id ] = new Member ( data . members [ id ] ) ;
}
}
}
}
/** Gets a member's profile if they exist and generate one if not. */
public getMember ( id : string ) : Member {
if ( ! /\d{17,}/g . test ( id ) )
2021-05-08 13:32:45 +00:00
console . warn (
"[structures]" ,
` " ${ id } " is not a valid user ID! It will be erased when the data loads again. `
) ;
2021-04-12 17:43:13 +00:00
if ( id in this . members ) return this . members [ id ] ;
else {
const member = new Member ( ) ;
this . members [ id ] = member ;
return member ;
}
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-05-08 13:32:45 +00:00
console . warn (
"[structures]" ,
` " ${ 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-05-08 13:32:45 +00:00
console . warn (
"[structures]" ,
` " ${ 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 ) {
2021-05-08 13:32:45 +00:00
watch ( "data" , ( _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-05-17 22:12:14 +00:00
ref : string | null ;
2020-12-31 16:10:56 +00:00
id : Snowflake ;
2021-05-17 22:12:14 +00:00
name : string | null ;
2020-12-31 16:10:56 +00:00
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 [ ] ;
}