Thursday, September 19, 2024 9:25:32 PM
> settings

Customize


Authenticate

> esm.rb
# frozen_string_literal: true

# Welcome to Exile Server Manager!
#   I hope you enjoy your stay.
#
# Just fyi, this file is laid out in a particular order so
#   I can access ESM.env and ESM.config when all other files load

[
  # This contains a check for the existence of the Rails class.
  # One of the action/active gems defines Rails, so this needs to be loaded first
  "sucker_punch",

  "action_view",
  "action_view/helpers",
  "active_record",
  "active_support",
  "active_support/all",
  "activerecord-import",
  "base64",
  "colorize",
  "concurrent",
  "discordrb",
  "dotenv",
  "dotiw",
  "drb",
  "eventmachine",
  "fast_jsonparser",
  "faye/websocket",
  "httparty",
  "i18n",
  "neatjson",
  "openssl",
  "puma",
  "puma/events",
  "pry",
  "redis",
  "securerandom",
  "semantic",
  "socket",
  "steam_web_api",
  "steam-condenser",
  "terminal-table",
  "yaml",
  "zeitwerk"
].each { |gem| require gem }

#############################
# Load extensions and other useful classes to have
Dir["#{__dir__}/esm/extension/**/*.rb"].sort.each { |extension| require extension }

require "otr-activerecord" if ENV["ESM_ENV"] != "production"

# Load Dotenv variables; overwriting any that already exist
Dotenv.overload
Dotenv.overload(".env.test") if ENV["ESM_ENV"] == "test"
Dotenv.overload(".env.prod") if ENV["ESM_ENV"] == "production"

# Default timezone to UTC
Time.zone_default = Time.find_zone!("UTC")

#################################
# Logging methods!
#################################
[:trace, :debug, :info, :warn, :error].each do |severity|
  define_method(:"#{severity}!") do |content = {}|
    __log(severity, caller_locations(1, 1).first, content)
  end
end

# Used internally by logging methods. Do not call manually
def __log(severity, caller_data, content)
  if content.is_a?(Hash) && content[:error].is_a?(StandardError)
    e = content[:error]

    content[:error] = {
      class: e.class,
      message: e.message,
      backtrace: ESM.backtrace_cleaner.clean(e.backtrace)
    }
  end

  caller_class = caller_data
    .path
    .sub("#{__dir__}/", "")
    .sub(".rb", "")
    .classify

  caller_method = caller_data.label.gsub("block in ", "")

  ESM.logger.send(severity, "#{caller_class}##{caller_method}:#{caller_data.lineno}") do
    if content.is_a?(Hash)
      ESM::JSON.pretty_generate(content).presence || ""
    else
      content || ""
    end
  end
end
#################################

module ESM
  REDIS_OPTS = {
    host: ENV.fetch("REDIS_HOST", "localhost"),
    reconnect_attempts: 10
  }.freeze

  class << self
    def bot
      @bot ||= ESM::Bot.new
    end

    def run!(async: false, **)
      require_relative "post_init"

      # Start the bot
      bot.run(async:, **)
    end

    # Load everything right meow
    def load!
      loader.setup
      loader.eager_load
    end

    def root
      @root ||= Pathname.new(File.expand_path("."))
    end

    def logger
      @logger ||= begin
        logger = Logger.new("log/#{env}.log", "daily")
        logger.level = Logger::INFO

        logger.formatter = proc do |severity, datetime, progname = "N/A", msg|
          header = "#{severity} [#{datetime.utc.strftime("%F %H:%M:%S:%L")}] (#{progname})"
          body = "\n\t#{msg.to_s.gsub("\n", "\n\t")}\n\n"

          if config.print_to_stdout
            styled_header =
              case severity
              when "TRACE"
                header.colorize(:cyan)
              when "INFO"
                header.colorize(:light_blue)
              when "DEBUG"
                header.colorize(:magenta)
              when "WARN"
                header.colorize(:yellow)
              when "ERROR", "FATAL"
                header.colorize(:red)
              else
                header
              end

            styled_body =
              case severity
              when "WARN"
                body.colorize(:yellow)
              when "ERROR", "FATAL"
                body.colorize(:red)
              else
                body
              end

            puts "#{styled_header}#{styled_body}" # rubocop:disable Rails/Output
          end

          "#{header}#{body}"
        end

        logger
      end
    end

    def redis
      @redis ||= ConnectionPool::Wrapper.new do
        Redis.new(**REDIS_OPTS)
      end
    end

    def cache
      @cache ||= ActiveSupport::Cache::RedisCacheStore.new(namespace: "esm_bot", redis: redis)
    end

    def env
      @env ||= Inquirer.new(:production, :staging, :test, :development).set(ENV["ESM_ENV"].presence || :development)
    end

    def config
      @config ||= begin
        config = YAML.safe_load(
          ERB.new(File.read(File.expand_path("config/config.yml"))).result,
          aliases: true
        )[env.to_s]

        config.to_struct
      end
    end

    def loader
      @loader ||= begin
        Zeitwerk::Loader.attr_predicate(:setup, :eager_loaded)
        Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
      end
    end

    def backtrace_cleaner
      @backtrace_cleaner ||= begin
        cleaner = ActiveSupport::BacktraceCleaner.new
        cleaner.add_filter { |line| line.gsub(root.to_s, "") }
        cleaner.add_silencer { |line| /\/ruby.gems/.match?(line) }
        cleaner
      end
    end

    #
    # Handles the connection between the bot and all of the A3 servers
    #
    def connection_server
      @connection_server ||= ESM::Connection::Server.new
    end
  end
end

# Required ahead of time, ignored in autoloader
require_relative "esm/database"
ESM::Database.connect!

# Run pre_init (Throwback to Exile)
require_relative "pre_init"
require_relative "pre_init_dev" if ESM.env.development?
All opinions represented herein are my own
- © 2024 itsthedevman
- build 3c15a1b