<commit_before>/* Misc */
const { oneLine, stripIndents } = require('common-tags')
const pluralize = require('pluralize')

/* Files */
process.chdir(__dirname)
const fs = require('fs')
const path = require('path')
const yaml = require('js-yaml')
const clientConfig = yaml.safeLoad(fs.readFileSync('./config/client.yml', 'utf8'))
const sqlConfig = yaml.safeLoad(fs.readFileSync('./config/sql.yml', 'utf8'))

/* Database */
const sqlite = require('sqlite')
const MySQL = require('mysql2/promise')
const MySQLProvider = require('discord.js-commando-mysqlprovider')

/* **************************************************************************************************** *\
Load Commando Client
\* **************************************************************************************************** */
const { CommandoClient, SQLiteProvider } = require('discord.js-commando')
const client = new CommandoClient({
  selfbot: false,
  commandPrefix: clientConfig.commandPrefix,
  commandEditableDuration: clientConfig.commandEditableDuration,
  nonCommandEditable: clientConfig.nonCommandEditable,
  unknownCommandResponse: clientConfig.unknownCommandResponse,
  owner: clientConfig.owner,
  invite: clientConfig.invite
})

/* **************************************************************************************************** *\
Load SQL Provider
\* **************************************************************************************************** */
if (sqlConfig.useMySQL === false) {
  sqlite.open(path.join(__dirname, './config/database.sqlite')).then((db) => {
    client.setProvider(new SQLiteProvider(db))
    setInterval(async () => {
      await client.provider.destroy()
      await client.provider.init(client)
      client.log.debug(`Synced Database`, client.provider.constructor.name)
    }, sqlConfig.sqlSync)
  })
} else {
  MySQL.createConnection({
    host: sqlConfig.host,
    user: sqlConfig.user,
    password: <PASSWORD>,
    database: sqlConfig.database
  }).then((db) => {
    client.setProvider(new MySQLProvider(db))
    setInterval(async () => {
      await client.provider.destroy()
      await client.provider.init(client)
      client.log.debug(`Synced Database`, client.provider.constructor.name)
    }, sqlConfig.sqlSync)
  })
}

/* **************************************************************************************************** *\
Functions
\* **************************************************************************************************** */
/**
 * Lists the files in a directory.
 * @param {string} dir The directory that you want to get a list of files from.
 * @param {string} filter The file type you want to get. IE: `js`
 * @return {array} Returns all the file names.
 */
var listFiles = (dir, filter) => {
  var files = fs.readdirSync(dir).filter(f => fs.statSync(path.join(dir, f)).isFile())
  if (filter) {
    return files.filter(file => file.endsWith(`.${filter}`))
  } else {
    return files
  }
}

/**
 * Lists the directories in a directory.
 * @param {string} dir The directory that you want to get a list of directories from.
 * @return {array} Returns all the folder names.
 */
var listDirs = dir => {
  return fs.readdirSync(dir).filter(f => fs.statSync(path.join(dir, f)).isDirectory())
}

/**
 * Capitalizes a word.
 * @param {string} char The word you want to capitalize.
 * @return {string} The new capitalized word
 */
var capitalize = char => {
  return char.charAt(0).toUpperCase() + char.slice(1)
}

/**
 * Checks to see if the file exists.
 * @param {string} source The file.
 * @return {boolean} Whether the file exists or not.
 */
const isFile = source => fs.lstatSync(source).isFile()

/**
 * Gets all files in a given directory.
 * @param {string} source The directory.
 * @return {string[]} List of the files in the directory.
 */
let getFiles = source => {
  let files = fs.readdirSync(__dirname + source).map(name => path.join(__dirname + source, name)).filter(isFile) // eslint-disable-line no-path-concat
  for (let file in files) {
    files[file] = files[file].slice(__dirname.length)
  }
  return files
}

/* **************************************************************************************************** *\
Automatic Group Loading System
\* **************************************************************************************************** */
/**
 * Converts something like `bot-staff` to `Bot Staff`.
 * @param {string} str The string you want to convert to a group name.
 * @return {string} The string after it was converted to a group name.
 */
var createGroup = str => {
  var arr = str.split('-')
  var temp = []
  arr.forEach(char => {
    char = capitalize(char)
    temp.push(char)
  })
  str = temp.join(' ')
  return str
}

var groups = []
listDirs('./commands').forEach(group => {
  groups.push([group, createGroup(group)])
})

/* **************************************************************************************************** *\
Load Client Registry
\* **************************************************************************************************** */
client.registry
  .registerDefaultTypes()
  .registerTypesIn(path.join(__dirname, './src/types'))
  .registerGroups(groups)
  .registerCommandsIn(path.join(__dirname, 'commands'))

/* **************************************************************************************************** *\
Load Modules
\* **************************************************************************************************** */
for (let file of getFiles('/modules')) {
  const moduleName = file.split('.')[0].substring(9)
  const moduleFile = require(`./${file}`)
  client[moduleName] = moduleFile
  delete require.cache[require.resolve(`./${file}`)]
}
client.log.info(oneLine`
  Initialized ${getFiles('/modules').length} ${pluralize('module', getFiles('/modules').length, false)}!
`, 'Module Initializer')

/* **************************************************************************************************** *\
Load Client Configuration
\* **************************************************************************************************** */
client.config = {}
for (let file of getFiles('/config')) {
  const configFileName = file.split('.')[0].substring(8)
  if (configFileName !== 'database') {
    const configFileContents = yaml.safeLoad(fs.readFileSync(`./${file}`, 'utf8'))
    client.config[configFileName] = configFileContents
    delete require.cache[require.resolve(`./${file}`)]
  }
}

/* **************************************************************************************************** *\
Load All Events
\* **************************************************************************************************** */
var events = []
// You will have to manually add event emitters here because apparently you can use `[event.type]on( //etc )`.
let eventEmitters = { client, process }

listDirs('./src/events').forEach(directory => {
  events.push({
    type: directory,
    location: `./src/events/${directory}/`
  })
})

events.forEach(event => {
  var files = listFiles(event.location, 'js')
  var type = capitalize(event.type)
  files.forEach(file => {
    var eventName = file.split('.')[0]
    var eventFile = require(`${event.location}${file}`)
    eventEmitters[event.type].on(eventName, eventFile.bind(null, client))
    delete require.cache[require.resolve(`${event.location}${file}`)]
  })
  client.log.info(oneLine`
    Initialized ${files.length} ${type}
    ${pluralize('event', files.length, false)}!
    `, `${type} Event Initializer`)
})

/* **************************************************************************************************** *\<commit_msg>Core: Add new module loader.<commit_after>/* Misc */
const { oneLine, stripIndents } = require('common-tags')
const pluralize = require('pluralize')

/* Files */
process.chdir(__dirname)
const fs = require('fs')
const path = require('path')
const yaml = require('js-yaml')
const clientConfig = yaml.safeLoad(fs.readFileSync('./config/client.yml', 'utf8'))
const sqlConfig = yaml.safeLoad(fs.readFileSync('./config/sql.yml', 'utf8'))

/* Database */
const sqlite = require('sqlite')
const MySQL = require('mysql2/promise')
const MySQLProvider = require('discord.js-commando-mysqlprovider')

/* **************************************************************************************************** *\
Load Commando Client
\* **************************************************************************************************** */
const { CommandoClient, SQLiteProvider } = require('discord.js-commando')
const client = new CommandoClient({
  selfbot: false,
  commandPrefix: clientConfig.commandPrefix,
  commandEditableDuration: clientConfig.commandEditableDuration,
  nonCommandEditable: clientConfig.nonCommandEditable,
  unknownCommandResponse: clientConfig.unknownCommandResponse,
  owner: clientConfig.owner,
  invite: clientConfig.invite
})

/* **************************************************************************************************** *\
Load SQL Provider
\* **************************************************************************************************** */
if (sqlConfig.useMySQL === false) {
  sqlite.open(path.join(__dirname, './config/database.sqlite')).then((db) => {
    client.setProvider(new SQLiteProvider(db))
    setInterval(async () => {
      await client.provider.destroy()
      await client.provider.init(client)
      client.log.debug(`Synced Database`, client.provider.constructor.name)
    }, sqlConfig.sqlSync)
  })
} else {
  MySQL.createConnection({
    host: sqlConfig.host,
    user: sqlConfig.user,
    password: <PASSWORD>,
    database: sqlConfig.database
  }).then((db) => {
    client.setProvider(new MySQLProvider(db))
    setInterval(async () => {
      await client.provider.destroy()
      await client.provider.init(client)
      client.log.debug(`Synced Database`, client.provider.constructor.name)
    }, sqlConfig.sqlSync)
  })
}

/* **************************************************************************************************** *\
Functions
\* **************************************************************************************************** */
/**
 * Lists the files in a directory.
 * @param {string} dir The directory that you want to get a list of files from.
 * @param {string} filter The file type you want to get. IE: `js`
 * @return {array} Returns all the file names.
 */
var listFiles = (dir, filter) => {
  var files = fs.readdirSync(dir).filter(f => fs.statSync(path.join(dir, f)).isFile())
  if (filter) {
    return files.filter(file => file.endsWith(`.${filter}`))
  } else {
    return files
  }
}

/**
 * Lists the directories in a directory.
 * @param {string} dir The directory that you want to get a list of directories from.
 * @return {array} Returns all the folder names.
 */
var listDirs = dir => {
  return fs.readdirSync(dir).filter(f => fs.statSync(path.join(dir, f)).isDirectory())
}

/**
 * Capitalizes a word.
 * @param {string} char The word you want to capitalize.
 * @return {string} The new capitalized word
 */
var capitalize = char => {
  return char.charAt(0).toUpperCase() + char.slice(1)
}

/**
 * Checks to see if the file exists.
 * @param {string} source The file.
 * @return {boolean} Whether the file exists or not.
 */
const isFile = source => fs.lstatSync(source).isFile()

/**
 * Gets all files in a given directory.
 * @param {string} source The directory.
 * @return {string[]} List of the files in the directory.
 */
let getFiles = source => {
  let files = fs.readdirSync(__dirname + source).map(name => path.join(__dirname + source, name)).filter(isFile) // eslint-disable-line no-path-concat
  for (let file in files) {
    files[file] = files[file].slice(__dirname.length)
  }
  return files
}

/* **************************************************************************************************** *\
Automatic Group Loading System
\* **************************************************************************************************** */
/**
 * Converts something like `bot-staff` to `Bot Staff`.
 * @param {string} str The string you want to convert to a group name.
 * @return {string} The string after it was converted to a group name.
 */
var createGroup = str => {
  var arr = str.split('-')
  var temp = []
  arr.forEach(char => {
    char = capitalize(char)
    temp.push(char)
  })
  str = temp.join(' ')
  return str
}

var groups = []
listDirs('./commands').forEach(group => {
  groups.push([group, createGroup(group)])
})

/* **************************************************************************************************** *\
Load Client Registry
\* **************************************************************************************************** */
client.registry
  .registerDefaultTypes()
  .registerTypesIn(path.join(__dirname, './src/types'))
  .registerGroups(groups)
  .registerCommandsIn(path.join(__dirname, 'commands'))

/* **************************************************************************************************** *\
Load Modules
\* **************************************************************************************************** */
listFiles('./src/modules', 'js').forEach(clientModule => {
  client[clientModule.split('.')[0]] = require(`./src/modules/${clientModule}`)
  delete require.cache[require.resolve(`./src/modules/${clientModule}`)]
})

client.log.info(oneLine`
  Initialized ${listFiles('./src/modules', 'js').length}
  ${pluralize('module', listFiles('./src/modules', 'js').length, false)}!
`, 'Module Initializer')

/* **************************************************************************************************** *\
Load Client Configuration
\* **************************************************************************************************** */
client.config = {}
for (let file of getFiles('/config')) {
  const configFileName = file.split('.')[0].substring(8)
  if (configFileName !== 'database') {
    const configFileContents = yaml.safeLoad(fs.readFileSync(`./${file}`, 'utf8'))
    client.config[configFileName] = configFileContents
    delete require.cache[require.resolve(`./${file}`)]
  }
}

/* **************************************************************************************************** *\
Load All Events
\* **************************************************************************************************** */
var events = []
// You will have to manually add event emitters here because apparently you can use `[event.type]on( //etc )`.
let eventEmitters = { client, process }

listDirs('./src/events').forEach(directory => {
  events.push({
    type: directory,
    location: `./src/events/${directory}/`
  })
})

events.forEach(event => {
  var files = listFiles(event.location, 'js')
  var type = capitalize(event.type)
  files.forEach(file => {
    eventEmitters[event.type].on(file.split('.')[0], require(`${event.location}${file}`).bind(null, client))
    delete require.cache[require.resolve(`${event.location}${file}`)]
  })
  client.log.info(oneLine`
    Initialized ${files.length} ${type}
    ${pluralize('event', files.length, false)}!
    `, `${type} Event Initializer`)
})

/* **************************************************************************************************** *\
{'fraction_non_alphanumeric': 0.35089906811917576, 'fraction_numerical': 0.0013781336133350834, 'mean_word_length': 5.3522300958732805, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6373955', 'n_tokens_mistral': 4002, 'n_tokens_neox': 3849, 'n_words': 917}