text: #include "wumpus-pp/utils.hpp"
  
  #include "wumpus-pp/bot.hpp"
  
  #ifndef WUMPUS_PP_THREADS_AMOUNT
      #define WUMPUS_PP_THREADS_AMOUNT std::max(std::thread::hardware_concurrency(), 4U)
  #endif
  
  discord::bot* discord::detail::instance = nullptr;
  discord::thread_pool discord::detail::tasks_pool = discord::thread_pool(WUMPUS_PP_THREADS_AMOUNT);
  
  const std::unordered_map<std::string, std::string> discord::utils::get_default_headers() {
      return {
          { "Authorization", fmt::format("Bot {}", discord::detail::instance->token) },
          { "User-Agent", "DiscordBot (https://www.github.com/Rynnya/wumpus-pp, 0.1)" }
      };
  }
  
  std::pair<std::string, std::string> discord::utils::split_endpoint(const std::string& endpoint) {
      auto offset = std::find(endpoint.begin(), endpoint.end(), '.');
      auto ptr = std::find(offset, endpoint.end(), '/');
  
      return { endpoint.substr(0, *ptr), endpoint.substr(*ptr) };
  }
  
  std::vector<std::string> discord::utils::split(const std::string& str, char delim) {
      size_t pos = str.find(delim);
      size_t offset = 0;
      std::vector<std::string> ret_vec = {};
  
      while (pos != std::string::npos) {
          ret_vec.push_back(str.substr(offset, pos - offset));
          offset = pos + 1;
  
          pos = str.find(delim, offset);
      }
  
      ret_vec.push_back(str.substr(offset, std::min(pos, str.size()) - offset + 1));
      return ret_vec;
  }
  
  std::string discord::utils::image_url_from_type(int32_t asset_t, snowflake id, std::string hash, bool is_animated) {
      switch (asset_t) {
      case enums::asset_type::custom_emoji:
          return fmt::format(is_animated ? "{}/emojis/{}.gif" : "{}/emojis/{}.png", get_cdn_url(), id);
  
      case enums::asset_type::guild_icon:
          return fmt::format("{}/icons/{}/{}.png", get_cdn_url(), id, hash);
  
      case enums::asset_type::guild_splash:
          return fmt::format("{}/splashes/{}/{}.png", get_cdn_url(), id, hash);
  
      case enums::asset_type::guild_banner:
          return fmt::format("{}/banners/{}/{}.png", get_cdn_url(), id, hash);
  
      case enums::asset_type::default_user_avatar:
          return fmt::format("{}/embed/avatars/{}.png", get_cdn_url(), id % 5);
  
      case enums::asset_type::user_avatar:
          return fmt::format(is_animated ? "{}/avatars/{}/{}.gif" : "{}/avatars/{}/{}.png", get_cdn_url(), id, hash);
  
      case enums::asset_type::application_icon:
          return fmt::format("{}/app-cons/{}/{}.png", get_cdn_url(), id, hash);
  
      case enums::asset_type::application_asset:
          return fmt::format("{}/app-assets/{}/{}.png", get_cdn_url(), id, hash);
  
      default:
          return fmt::format("{}/embed/avatars/{}.png", get_cdn_url(), id % 5);
      }
  }
  
  std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> discord::utils::time_from_discord_string(const std::string& str) {
      if (str.empty()) {
          return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
      }
  
      std::tm tm;
      std::stringstream ss(str);
      ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
      return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::from_time_t(std::mktime(&tm)));
  }
  
  std::string discord::utils::get_os_name() {
  #ifdef _WIN64
      return "Windows 64-bit";
  #elif _WIN32
      return "Windows 32-bit";
  #elif __unix || __unix__
      return "Unix";
  #elif __APPLE__ || __MACH__
      return "Mac OSX";
  #elif __FreeBSD__
      return "FreeBSD";
  #else
      return "Other";
  #endif
  }
  
  std::pair<std::string, std::string> discord::utils::generate_form_data(std::vector<std::pair<std::ifstream, std::string>>& strm, nlohmann::json const& json_payload) {
      std::stringstream data;
  
      std::string boundary{};
      for (int i = 0; i < 50; i++) {
          boundary += (rand() % 26) + 'A';
      }
  
      for (std::pair<std::ifstream, std::string>& pair : strm) {
          std::string filename = "file";
          data << "--" << boundary << "\r\n"
              << "Content-Disposition: form-data; name=\"file\"; filename=\""
              << pair.second << "\"\r\nContent-Type: application/octet-stream\r\n\r\n"
              << read_stream(pair.first)
              << "\r\n\r\n";
      }
  
      if (json_payload != nullptr) {
          data << "--" << boundary << "\r\nContent-Disposition: form-data; name=\"payload_json\"\r\n\r\n" << json_payload.dump() << "\r\n";
      }
  
      data << "--" << boundary << "--";
  
      return { boundary, data.str() };
  }
  
  std::string discord::utils::get_file_extension(const std::string& f_name) {
      std::string extension = f_name.substr(f_name.find_last_of('.') + 1);
      std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
      return extension;
  }
  
  bool discord::utils::is_image_or_gif(const std::string& f_name) {
      const std::string& extension = get_file_extension(f_name);
      return (extension == "png" || extension == "jpeg" || extension == "jpg" || extension == "webp" || extension == "gif");
  }
  