turtle-wow-source-kinda/Dumps/Source Code/16 - Development_server/patch_1172/dep/dpp/utility.cpp
Brian Oost a1d5bb70b2 Init
2024-08-06 18:06:40 +02:00

527 lines
14 KiB
C++

/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/utility.h>
#include <dpp/stringops.h>
#include <dpp/exception.h>
#include <dpp/version.h>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <thread>
#include <functional>
#include <chrono>
#include <ctime>
#include <algorithm>
#include <fstream>
#include <streambuf>
#include <array>
#include <dpp/cluster.h>
#include <dpp/dispatcher.h>
#ifdef _WIN32
#include <stdio.h>
#include <stdlib.h>
#define popen _popen
#define pclose _pclose
#endif
#ifdef HAVE_PRCTL
#include <sys/prctl.h>
#endif
using namespace std::literals;
namespace dpp {
namespace utility {
double time_f()
{
using namespace std::chrono;
auto tp = system_clock::now() + 0ns;
return tp.time_since_epoch().count() / 1000000000.0;
}
bool has_voice() {
#if HAVE_VOICE
return true;
#else
return false;
#endif
}
std::string current_date_time() {
#ifdef _WIN32
std::time_t curr_time = time(nullptr);
return trim(std::ctime(&curr_time));
#else
auto t = std::time(nullptr);
struct tm timedata;
localtime_r(&t, &timedata);
std::stringstream s;
s << std::put_time(&timedata, "%Y-%m-%d %H:%M:%S");
return trim(s.str());
#endif
}
std::string loglevel(dpp::loglevel in) {
switch (in) {
case dpp::ll_trace: return "TRACE";
case dpp::ll_debug: return "DEBUG";
case dpp::ll_info: return "INFO";
case dpp::ll_warning: return "WARN";
case dpp::ll_error: return "ERROR";
case dpp::ll_critical: return "CRIT";
default: return "???";
}
}
uptime::uptime() : days(0), hours(0), mins(0), secs(0) {
}
uptime::uptime(double diff) : uptime((time_t)diff) {
}
uptime::uptime(time_t diff) : uptime() {
days = (uint16_t)(diff / (3600 * 24));
hours = (uint8_t)(diff % (3600 * 24) / 3600);
mins = (uint8_t)(diff % 3600 / 60);
secs = (uint8_t)(diff % 60);
}
std::string uptime::to_string() const {
char print_buffer[64];
if (hours == 0 && days == 0) {
snprintf(print_buffer, 64, "%02d:%02d", mins, secs);
return print_buffer;
} else {
char print_buffer[64];
std::string daystr;
if (days) {
daystr = std::to_string(days) + " day" + (days > 1 ? "s, " : ", ");
}
snprintf(print_buffer, 64, "%s%02d:%02d:%02d", daystr.c_str(), hours, mins, secs);
return print_buffer;
}
}
uint64_t uptime::to_secs() const {
return secs + (mins * 60) + (hours * 60 * 60) + (days * 60 * 60 * 24);
}
uint64_t uptime::to_msecs() const {
return to_secs() * 1000;
}
iconhash::iconhash(uint64_t _first, uint64_t _second) : first(_first), second(_second) {
}
iconhash::iconhash(const iconhash&) = default;
iconhash::~iconhash() = default;
void iconhash::set(const std::string &hash) {
std::string clean_hash(hash);
if (hash.empty()) { // Clear values if empty hash
first = second = 0;
return;
}
if (hash.length() == 34 && hash.substr(0, 2) == "a_") {
/* Someone passed in an animated icon. numpty.
* Clean that mess up!
*/
clean_hash = hash.substr(2);
}
if (clean_hash.length() != 32)
throw std::length_error("iconhash must be exactly 32 characters in length, passed value is: '" + clean_hash + "'");
this->first = from_string<uint64_t>(clean_hash.substr(0, 16), std::hex);
this->second = from_string<uint64_t>(clean_hash.substr(16, 16), std::hex);
}
iconhash::iconhash(const std::string &hash) {
set(hash);
}
iconhash& iconhash::operator=(const std::string &assignment) {
set(assignment);
return *this;
}
bool iconhash::operator==(const iconhash& other) const {
return other.first == first && other.second == second;
}
std::string iconhash::to_string() const {
if (first == 0 && second == 0) {
return "";
} else {
return to_hex(this->first) + to_hex(this->second);
}
}
std::string debug_dump(uint8_t* data, size_t length) {
std::ostringstream out;
size_t addr = (size_t)data;
size_t extra = addr % 16;
if (extra != 0) {
addr -= extra;
out << to_hex(addr);
}
for (size_t n = 0; n < extra; ++n) {
out << "-- ";
}
std::string ascii;
for (uint8_t* ptr = data; ptr < data + length; ++ptr) {
if (((size_t)ptr % 16) == 0) {
out << ascii << "\n[" << to_hex((size_t)ptr) << "] : ";
ascii.clear();
}
ascii.push_back(*ptr >= ' ' && *ptr <= '~' ? *ptr : '.');
out << to_hex(*ptr);
}
out << " " << ascii;
out << "\n";
return out.str();
}
std::string bytes(uint64_t c) {
char print_buffer[64] = { 0 };
if (c > 1099511627776) { // 1TB
snprintf(print_buffer, 64, "%.2fT", (c / 1099511627776.0));
} else if (c > 1073741824) { // 1GB
snprintf(print_buffer, 64, "%.2fG", (c / 1073741824.0));
} else if (c > 1048576) { // 1MB
snprintf(print_buffer, 64, "%.2fM", (c / 1048576.0));
} else if (c > 1024) { // 1KB
snprintf(print_buffer, 64, "%.2fK", (c / 1024.0));
} else { // Bytes
return std::to_string(c);
}
return print_buffer;
}
uint32_t rgb(float red, float green, float blue) {
return (((uint32_t)(red * 255)) << 16) | (((uint32_t)(green * 255)) << 8) | ((uint32_t)(blue * 255));
}
/* NOTE: Parameters here are `int` instead of `uint32_t` or `uint8_t` to prevent ambiguity error with rgb(float, float, float) */
uint32_t rgb(int red, int green, int blue) {
return ((uint32_t)red << 16) | ((uint32_t)green << 8) | (uint32_t)blue;
}
void exec(const std::string& cmd, std::vector<std::string> parameters, cmd_result_t callback) {
auto t = std::thread([cmd, parameters, callback]() {
utility::set_thread_name("async_exec");
std::array<char, 128> buffer;
std::vector<std::string> my_parameters = parameters;
std::string result;
std::stringstream cmd_and_parameters;
cmd_and_parameters << cmd;
for (auto & parameter : my_parameters) {
cmd_and_parameters << " " << std::quoted(parameter);
}
/* Capture stderr */
cmd_and_parameters << " 2>&1";
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd_and_parameters.str().c_str(), "r"), pclose);
if (!pipe) {
return;
}
while (fgets(buffer.data(), (int)buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
if (callback) {
callback(result);
}
});
t.detach();
}
size_t utf8len(const std::string &str)
{
size_t i = 0, iBefore = 0, count = 0;
const char* s = str.c_str();
if (*s == 0)
return 0;
while (s[i] > 0) {
ascii:
i++;
}
count += i - iBefore;
while (s[i]) {
if (s[i] > 0) {
iBefore = i;
goto ascii;
} else {
switch (0xF0 & s[i]) {
case 0xE0:
i += 3;
break;
case 0xF0:
i += 4;
break;
default:
i += 2;
break;
}
}
count++;
}
return count;
}
std::string utf8substr(const std::string& str, std::string::size_type start, std::string::size_type leng)
{
if (leng == 0) {
return "";
}
if (start == 0 && leng >= utf8len(str)) {
return str;
}
std::string::size_type i, ix, q, min = std::string::npos, max = std::string::npos;
for (q = 0, i = 0, ix = str.length(); i < ix; i++, q++)
{
if (q == start)
min = i;
if (q <= start + leng || leng == std::string::npos)
max = i;
unsigned char c = (unsigned char)str[i];
if (c < 0x80)
i += 0;
else if ((c & 0xE0) == 0xC0)
i += 1;
else if ((c & 0xF0) == 0xE0)
i += 2;
else if ((c & 0xF8) == 0xF0)
i += 3;
else
return ""; //invalid utf8
}
if (q <= start + leng || leng == std::string::npos)
max = i;
if (min == std::string::npos || max == std::string::npos)
return "";
return str.substr(min, max);
}
std::string read_file(const std::string& filename)
{
try {
std::ifstream ifs(filename, std::ios::binary);
return std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
}
catch (const std::exception& e) {
/* Rethrow as dpp::file_exception */
throw dpp::file_exception(e.what());
}
}
std::string validate(const std::string& value, size_t _min, size_t _max, const std::string& exception_message) {
if (utf8len(value) < _min) {
throw dpp::length_exception(exception_message);
} else if (utf8len(value) > _max) {
return utf8substr(value, 0, _max);
}
return value;
}
std::string timestamp(time_t ts, time_format tf) {
char format[2] = { (char)tf, 0 };
return "<t:" + std::to_string(ts) + ":" + format + ">";
}
std::string avatar_size(uint32_t size) {
if (size) {
return "?size=" + std::to_string(size);
}
return std::string();
}
std::vector<std::string> tokenize(std::string const &in, const char* sep) {
std::string::size_type b = 0;
std::vector<std::string> result;
while ((b = in.find_first_not_of(sep, b)) != std::string::npos) {
auto e = in.find(sep, b);
result.push_back(in.substr(b, e-b));
b = e;
}
return result;
}
std::string bot_invite_url(const snowflake bot_id, const uint64_t permissions, const std::vector<std::string>& scopes) {
std::string scope;
if (scopes.size()) {
for (auto& s : scopes) {
scope += s + "+";
}
scope = scope.substr(0, scope.length() - 1);
}
return "https://discord.com/oauth2/authorize?client_id=" + std::to_string(bot_id) + "&permissions=" + std::to_string(permissions) + "&scope=" + scope;
}
std::function<void(const dpp::log_t&)> cout_logger() {
return [](const dpp::log_t& event) {
if (event.severity > dpp::ll_trace) {
std::cout << "[" << dpp::utility::current_date_time() << "] " << dpp::utility::loglevel(event.severity) << ": " << event.message << "\n";
}
};
}
std::function<void(const dpp::confirmation_callback_t& detail)> log_error() {
return [](const dpp::confirmation_callback_t& detail) {
if (detail.is_error()) {
if (detail.bot) {
detail.bot->log(
dpp::ll_error,
"Error " + std::to_string(detail.get_error().code) + " [" +
detail.get_error().message + "] on API request, returned content was: " + detail.http_info.body
);
}
}
};
}
/* Hexadecimal sequence for URL encoding */
static const char* hex = "0123456789ABCDEF";
std::string url_encode(const std::string &value) {
// Reserve worst-case encoded length of string, input length * 3
std::string escaped(value.length() * 3, '\0');
char* data = escaped.data();
for (auto i = value.begin(); i != value.end(); ++i) {
unsigned char c = (unsigned char)(*i);
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
// Keep alphanumeric and other accepted characters intact
*data++ = c;
} else {
// Any other characters are percent-encoded
*data++ = '%';
*data++ = hex[c >> 4];
*data++ = hex[c & 0x0f];
}
}
*data = 0;
return escaped.data();
}
std::string slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand) {
return "</" + command_name + (!subcommand.empty() ? (" " + subcommand) : "") + ":" + std::to_string(command_id) + ">";
}
std::string slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand_group, const std::string &subcommand) {
return "</" + command_name + " " + subcommand_group + " " + subcommand + ":" + std::to_string(command_id) + ">";
}
std::string make_url_parameters(const std::map<std::string, std::string>& parameters) {
std::string output;
for(auto& [k, v] : parameters) {
if (!k.empty() && !v.empty()) {
output.append("&").append(k).append("=").append(url_encode(v));
}
}
if (!output.empty()) {
output[0] = '?';
}
return output;
}
std::string make_url_parameters(const std::map<std::string, uint64_t>& parameters) {
std::map<std::string, std::string> params;
for(auto& [k, v] : parameters) {
if (v != 0) {
params[k] = std::to_string(v);
}
}
return make_url_parameters(params);
}
std::string markdown_escape(const std::string& text, bool escape_code_blocks) {
/**
* @brief Represents the current state of the finite state machine
* for the markdown_escape function.
*/
enum md_state {
/// normal text
md_normal = 0,
/// a paragraph code block, represented by three backticks
md_big_code_block = 1,
/// an inline code block, represented by one backtick
md_small_code_block = 2,
};
md_state state = md_normal;
std::string output;
const std::string markdown_chars("\\*_|~[]()>");
for (size_t n = 0; n < text.length(); ++n) {
if (text.substr(n, 3) == "```") {
/* Start/end a paragraph code block */
output += (escape_code_blocks ? "\\`\\`\\`" : "```");
n += 2;
state = (state == md_normal) ? md_big_code_block : md_normal;
} else if (text[n] == '`' && (escape_code_blocks || state != md_big_code_block)) {
/* Start/end of an inline code block */
output += (escape_code_blocks ? "\\`" : "`");
state = (state == md_normal) ? md_small_code_block : md_normal;
} else {
/* Normal text */
if (escape_code_blocks || state == md_normal) {
/* Markdown sequence characters */
if (markdown_chars.find(text[n]) != std::string::npos) {
output += "\\";
}
}
output += text[n];
}
}
return output;
}
std::string version() {
return DPP_VERSION_TEXT;
}
void set_thread_name(const std::string& name) {
#ifdef HAVE_PRCTL
prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name.substr(0, 15).c_str()), NULL, NULL, NULL);
#else
#if HAVE_PTHREAD_SETNAME_NP
#if HAVE_SINGLE_PARAMETER_SETNAME_NP
pthread_setname_np(name.substr(0, 15).c_str());
#endif
#if HAVE_TWO_PARAMETER_SETNAME_NP
pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
#endif
#endif
#endif
}
};
};