Skip to content

Commit

Permalink
FIX: QOL: non fatal utf8 handling. (#1147)
Browse files Browse the repository at this point in the history
  • Loading branch information
braindigitalis authored May 10, 2024
1 parent 709a2c2 commit 1edf816
Show file tree
Hide file tree
Showing 28 changed files with 184 additions and 79 deletions.
2 changes: 1 addition & 1 deletion doxygen-awesome-css
64 changes: 64 additions & 0 deletions include/dpp/cluster_sync_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,70 @@ message_map messages_get_sync(snowflake channel_id, snowflake around, snowflake
*/
confirmation message_unpin_sync(snowflake channel_id, snowflake message_id);

/**
* @brief Get a list of users that voted for this specific answer.
*
* @param m Message that contains the poll to retrieve the answers from
* @param answer_id ID of the answer to retrieve votes from (see poll_answer::answer_id)
* @param after Users after this ID should be retrieved if this is set to non-zero
* @param limit This number of users maximum should be returned, up to 100
* @return user_map returned object on completion
* @see dpp::cluster::poll_get_answer_voters
* @see https://discord.com/developers/docs/resources/poll#get-answer-voters
* \memberof dpp::cluster
* @throw dpp::rest_exception upon failure to execute REST function
* @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.
* Avoid direct use of this function inside an event handler.
*/
user_map poll_get_answer_voters_sync(const message& m, uint32_t answer_id, snowflake after, uint64_t limit);

/**
* @brief Get a list of users that voted for this specific answer.
*
* @param message_id ID of the message with the poll to retrieve the answers from
* @param channel_id ID of the channel with the poll to retrieve the answers from
* @param answer_id ID of the answer to retrieve votes from (see poll_answer::answer_id)
* @param after Users after this ID should be retrieved if this is set to non-zero
* @param limit This number of users maximum should be returned, up to 100
* @return user_map returned object on completion
* @see dpp::cluster::poll_get_answer_voters
* @see https://discord.com/developers/docs/resources/poll#get-answer-voters
* \memberof dpp::cluster
* @throw dpp::rest_exception upon failure to execute REST function
* @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.
* Avoid direct use of this function inside an event handler.
*/
user_map poll_get_answer_voters_sync(snowflake message_id, snowflake channel_id, uint32_t answer_id, snowflake after, uint64_t limit);

/**
* @brief Immediately end a poll.
*
* @param m Message that contains the poll
* @return message returned object on completion
* @see dpp::cluster::poll_end
* @see https://discord.com/developers/docs/resources/poll#end-poll
* \memberof dpp::cluster
* @throw dpp::rest_exception upon failure to execute REST function
* @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.
* Avoid direct use of this function inside an event handler.
*/
message poll_end_sync(const message &m);

/**
* @brief Immediately end a poll.
*
* @param message_id ID of the message with the poll to end
* @param channel_id ID of the channel with the poll to end
* @return message returned object on completion
* @see dpp::cluster::poll_end
* @see https://discord.com/developers/docs/resources/poll#end-poll
* \memberof dpp::cluster
* @throw dpp::rest_exception upon failure to execute REST function
* @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.
* Avoid direct use of this function inside an event handler.
*/
message poll_end_sync(snowflake message_id, snowflake channel_id);

/**
* @brief Get a channel's pins
* @see dpp::cluster::channel_pins_get
Expand Down
4 changes: 2 additions & 2 deletions include/dpp/json_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

#pragma once
#include <dpp/export.h>
#include <dpp/json_fwd.h>
#include <dpp/json.h>

namespace dpp {

Expand Down Expand Up @@ -66,7 +66,7 @@ struct json_interface {
*/
template <typename U = T, typename = decltype(std::declval<U&>().to_json_impl(bool{}))>
std::string build_json(bool with_id = false) const {
return to_json(with_id).dump();
return to_json(with_id).dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace);
}
};

Expand Down
2 changes: 1 addition & 1 deletion include/dpp/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24300,7 +24300,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
NLOHMANN_BASIC_JSON_TPL_DECLARATION
std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)
{
return j.dump();
return j.dump(-1, ' ', false, json::error_handler_t::ignore);
}

inline namespace literals
Expand Down
4 changes: 2 additions & 2 deletions src/dpp/auditlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ audit_entry &audit_entry::fill_from_json_impl(nlohmann::json *j) {
audit_change ac;
ac.key = string_not_null(&change, "key");
if (change.find("new_value") != change.end()) {
ac.new_value = change["new_value"].dump();
ac.new_value = change["new_value"].dump(-1, ' ', false, json::error_handler_t::replace);
}
if (change.find("old_value") != change.end()) {
ac.old_value = change["old_value"].dump();
ac.old_value = change["old_value"].dump(-1, ' ', false, json::error_handler_t::replace);
}
this->changes.push_back(ac);
}
Expand Down
2 changes: 1 addition & 1 deletion src/dpp/cluster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ json error_response(const std::string& message, http_request_completion_t& rv)
}},
{"message", message}
});
rv.body = j.dump();
rv.body = j.dump(-1, ' ', false, json::error_handler_t::replace);
return j;
}

Expand Down
8 changes: 4 additions & 4 deletions src/dpp/cluster/appcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void cluster::global_bulk_command_create(const std::vector<slashcommand> &comman
for (auto & s : commands) {
j.push_back(s.to_json(false));
}
rest_request_list<slashcommand>(this, API_PATH "/applications", std::to_string(commands.size() > 0 && commands[0].application_id ? commands[0].application_id : me.id), "commands", m_put, j.dump(), callback);
rest_request_list<slashcommand>(this, API_PATH "/applications", std::to_string(commands.size() > 0 && commands[0].application_id ? commands[0].application_id : me.id), "commands", m_put, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::global_bulk_command_delete(command_completion_event_t callback) {
Expand Down Expand Up @@ -60,7 +60,7 @@ void cluster::guild_bulk_command_create(const std::vector<slashcommand> &command
for (auto & s : commands) {
j.push_back(s.to_json(false));
}
rest_request_list<slashcommand>(this, API_PATH "/applications", std::to_string(commands.size() > 0 && commands[0].application_id ? commands[0].application_id : me.id), "guilds/" + std::to_string(guild_id) + "/commands", m_put, j.dump(), callback);
rest_request_list<slashcommand>(this, API_PATH "/applications", std::to_string(commands.size() > 0 && commands[0].application_id ? commands[0].application_id : me.id), "guilds/" + std::to_string(guild_id) + "/commands", m_put, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::guild_bulk_command_delete(snowflake guild_id, command_completion_event_t callback) {
Expand All @@ -85,7 +85,7 @@ void cluster::guild_bulk_command_edit_permissions(const std::vector<slashcommand
}
j.push_back(jcommand);
}
rest_request_list<guild_command_permissions>(this, API_PATH "/applications", std::to_string(me.id), "guilds/" + std::to_string(guild_id) + "/commands/permissions", m_put, j.dump(), callback);
rest_request_list<guild_command_permissions>(this, API_PATH "/applications", std::to_string(me.id), "guilds/" + std::to_string(guild_id) + "/commands/permissions", m_put, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::guild_command_create(const slashcommand &s, snowflake guild_id, command_completion_event_t callback) {
Expand Down Expand Up @@ -116,7 +116,7 @@ void cluster::guild_command_edit_permissions(const slashcommand &s, snowflake gu
j["permissions"].push_back(jperm);
}
}
rest_request<confirmation>(this, API_PATH "/applications", std::to_string(s.application_id ? s.application_id : me.id), "guilds/" + std::to_string(guild_id) + "/commands/" + std::to_string(s.id) + "/permissions", m_put, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/applications", std::to_string(s.application_id ? s.application_id : me.id), "guilds/" + std::to_string(guild_id) + "/commands/" + std::to_string(s.id) + "/permissions", m_put, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::guild_command_get(snowflake id, snowflake guild_id, command_completion_event_t callback) {
Expand Down
8 changes: 4 additions & 4 deletions src/dpp/cluster/channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void cluster::channel_edit_permissions(const class channel &c, const snowflake o

void cluster::channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member, command_completion_event_t callback) {
json j({ {"allow", std::to_string(allow)}, {"deny", std::to_string(deny)}, {"type", member ? 1 : 0} });
rest_request<confirmation>(this, API_PATH "/channels", std::to_string(channel_id), "permissions/" + std::to_string(overwrite_id), m_put, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/channels", std::to_string(channel_id), "permissions/" + std::to_string(overwrite_id), m_put, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::channel_edit_positions(const std::vector<channel> &c, command_completion_event_t callback) {
Expand All @@ -61,7 +61,7 @@ void cluster::channel_edit_positions(const std::vector<channel> &c, command_comp
}
j.push_back(cj);
}
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(c[0].guild_id), "channels/" + std::to_string(c[0].id), m_patch, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(c[0].guild_id), "channels/" + std::to_string(c[0].id), m_patch, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::channel_edit(const class channel &c, command_completion_event_t callback) {
Expand All @@ -70,7 +70,7 @@ void cluster::channel_edit(const class channel &c, command_completion_event_t ca

void cluster::channel_follow_news(const class channel &c, snowflake target_channel_id, command_completion_event_t callback) {
json j({ {"webhook_channel_id", target_channel_id} });
rest_request<confirmation>(this, API_PATH "/channels", std::to_string(c.id), "followers", m_post, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/channels", std::to_string(c.id), "followers", m_post, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::channel_get(snowflake c, command_completion_event_t callback) {
Expand Down Expand Up @@ -100,7 +100,7 @@ void cluster::channels_get(snowflake guild_id, command_completion_event_t callba

void cluster::channel_set_voice_status(snowflake channel_id, const std::string& status, command_completion_event_t callback) {
json j({ {"status", status} });
rest_request<confirmation>(this, API_PATH "/channels", std::to_string(channel_id), "voice-status", m_put, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/channels", std::to_string(channel_id), "voice-status", m_put, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

} // namespace dpp
61 changes: 33 additions & 28 deletions src/dpp/cluster/confirmation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,39 +121,44 @@ std::vector<error_detail> find_errors_in_object(const std::string& obj, const st

error_info confirmation_callback_t::get_error() const {
if (is_error()) {
json j = json::parse(this->http_info.body);
error_info e;

set_int32_not_null(&j, "code", e.code);
set_string_not_null(&j, "message", e.message);
json& errors = j["errors"];
for (auto obj = errors.begin(); obj != errors.end(); ++obj) {
std::vector<error_detail> sub_errors;
std::string field = isdigit(*obj.key().c_str()) ? "<array>[" + obj.key() + "]" : obj.key();

sub_errors = find_errors_in_object({}, field, *obj);

if (!sub_errors.empty()) {
e.errors.reserve(e.errors.capacity() + sub_errors.size());
std::move(sub_errors.begin(), sub_errors.end(), std::back_inserter(e.errors));
try {
json j = json::parse(this->http_info.body);
error_info e;

set_int32_not_null(&j, "code", e.code);
set_string_not_null(&j, "message", e.message);
json &errors = j["errors"];
for (auto obj = errors.begin(); obj != errors.end(); ++obj) {
std::vector<error_detail> sub_errors;
std::string field = isdigit(*obj.key().c_str()) ? "<array>[" + obj.key() + "]" : obj.key();

sub_errors = find_errors_in_object({}, field, *obj);

if (!sub_errors.empty()) {
e.errors.reserve(e.errors.capacity() + sub_errors.size());
std::move(sub_errors.begin(), sub_errors.end(), std::back_inserter(e.errors));
}
}
}

e.human_readable = std::to_string(e.code) + ": " + e.message;
std::string prefix = e.errors.size() == 1 ? " " : "\n\t";
for (const auto& error : e.errors) {
if (error.object.empty()) {
/* A singular field with an error in an unnamed object */
e.human_readable += prefix + "- " + error.field + ": " + error.reason + " (" + error.code + ")";
} else {
/* An object field that caused an error */
e.human_readable += prefix + "- " + error.object + '.' + error.field + ": " + error.reason + " (" + error.code + ")";
e.human_readable = std::to_string(e.code) + ": " + e.message;
std::string prefix = e.errors.size() == 1 ? " " : "\n\t";
for (const auto &error: e.errors) {
if (error.object.empty()) {
/* A singular field with an error in an unnamed object */
e.human_readable += prefix + "- " + error.field + ": " + error.reason + " (" + error.code + ")";
} else {
/* An object field that caused an error */
e.human_readable += prefix + "- " + error.object + '.' + error.field + ": " + error.reason + " (" + error.code + ")";
}
}
}

return e;
return e;
}
catch (const std::exception &e) {
return {};
}
}
return error_info();
return {};
}

} // namespace dpp
4 changes: 2 additions & 2 deletions src/dpp/cluster/dm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
namespace dpp {

void cluster::create_dm_channel(snowflake user_id, command_completion_event_t callback) {
rest_request<channel>(this, API_PATH "/users", "@me", "channels", m_post, json({{"recipient_id", std::to_string(user_id)}}).dump(), callback);
rest_request<channel>(this, API_PATH "/users", "@me", "channels", m_post, json({{"recipient_id", std::to_string(user_id)}}).dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::current_user_get_dms(command_completion_event_t callback) {
Expand Down Expand Up @@ -56,7 +56,7 @@ void cluster::gdm_add(snowflake channel_id, snowflake user_id, const std::string
json params;
params["access_token"] = access_token;
params["nick"] = nick;
rest_request<confirmation>(this, API_PATH "/channels", std::to_string(channel_id), "recipients/" + std::to_string(user_id), m_put, params.dump(), callback);
rest_request<confirmation>(this, API_PATH "/channels", std::to_string(channel_id), "recipients/" + std::to_string(user_id), m_put, params.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::gdm_remove(snowflake channel_id, snowflake user_id, command_completion_event_t callback) {
Expand Down
8 changes: 4 additions & 4 deletions src/dpp/cluster/guild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
namespace dpp {

void cluster::guild_current_member_edit(snowflake guild_id, const std::string &nickname, command_completion_event_t callback) {
std::string o = (nickname.empty() ? json({{"nick", json::value_t::null }}) : json({{"nick", nickname }})).dump();
std::string o = (nickname.empty() ? json({{"nick", json::value_t::null }}) : json({{"nick", nickname }})).dump(-1, ' ', false, json::error_handler_t::replace);
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(guild_id), "members/@me", m_patch, o, callback);
}

Expand Down Expand Up @@ -52,7 +52,7 @@ void cluster::guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t dele
}
}
}
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(guild_id), "bans/" + std::to_string(user_id), m_put, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(guild_id), "bans/" + std::to_string(user_id), m_put, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}


Expand Down Expand Up @@ -140,7 +140,7 @@ void cluster::guild_begin_prune(snowflake guild_id, const struct prune& pruneinf


void cluster::guild_set_nickname(snowflake guild_id, const std::string &nickname, command_completion_event_t callback) {
std::string o = (nickname.empty() ? json({{"nick", json::value_t::null }}) : json({{"nick", nickname }})).dump();
std::string o = (nickname.empty() ? json({{"nick", json::value_t::null }}) : json({{"nick", nickname }})).dump(-1, ' ', false, json::error_handler_t::replace);
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(guild_id), "members/@me/nick", m_patch, o, callback);
}

Expand All @@ -165,7 +165,7 @@ void cluster::guild_get_welcome_screen(snowflake guild_id, command_completion_ev
void cluster::guild_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled, command_completion_event_t callback) {
json j = welcome_screen.to_json();
j["enabled"] = enabled;
rest_request<dpp::welcome_screen>(this, API_PATH "/guilds", std::to_string(guild_id), "welcome-screen", m_patch, j.dump(), callback);
rest_request<dpp::welcome_screen>(this, API_PATH "/guilds", std::to_string(guild_id), "welcome-screen", m_patch, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}


Expand Down
8 changes: 4 additions & 4 deletions src/dpp/cluster/guild_member.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace dpp {
void cluster::guild_add_member(const guild_member& gm, const std::string &access_token, command_completion_event_t callback) {
json j = gm.to_json();
j["access_token"] = access_token;
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(gm.guild_id), "members/" + std::to_string(gm.user_id), m_put, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(gm.guild_id), "members/" + std::to_string(gm.user_id), m_put, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}


Expand Down Expand Up @@ -93,13 +93,13 @@ void cluster::guild_member_timeout(snowflake guild_id, snowflake user_id, time_t
j["communication_disabled_until"] = json::value_t::null;
}

rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(guild_id), "members/" + std::to_string(user_id), m_patch, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(guild_id), "members/" + std::to_string(user_id), m_patch, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}

void cluster::guild_member_timeout_remove(snowflake guild_id, snowflake user_id, command_completion_event_t callback) {
json j;
j["communication_disabled_until"] = json::value_t::null;
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(guild_id), "members/" + std::to_string(user_id), m_patch, j.dump(), callback);
rest_request<confirmation>(this, API_PATH "/guilds", std::to_string(guild_id), "members/" + std::to_string(user_id), m_patch, j.dump(-1, ' ', false, json::error_handler_t::replace), callback);
}


Expand All @@ -122,7 +122,7 @@ void cluster::guild_member_move(const snowflake channel_id, const snowflake guil
j["channel_id"] = json::value_t::null;
}

this->post_rest(API_PATH "/guilds", std::to_string(guild_id), "members/" + std::to_string(user_id), m_patch, j.dump(), [this, guild_id, user_id, callback](json &j, const http_request_completion_t& http) {
this->post_rest(API_PATH "/guilds", std::to_string(guild_id), "members/" + std::to_string(user_id), m_patch, j.dump(-1, ' ', false, json::error_handler_t::replace), [this, guild_id, user_id, callback](json &j, const http_request_completion_t& http) {
if (callback) {
callback(confirmation_callback_t(this, guild_member().fill_from_json(&j, guild_id, user_id), http));
}
Expand Down
Loading

0 comments on commit 1edf816

Please sign in to comment.