diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 0e064ce27d..6298cc3abd 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -3509,11 +3509,16 @@ class DPP_EXPORT cluster { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @see https://discord.com/developers/docs/resources/guild#modify-current-member * @param guild_id Guild ID to change on - * @param nickname New nickname, or empty string to clear nickname + * @param nickname New nickname, or empty string to clear nickname. + * @param banner_blob New banner, or empty string to clear banner. + * @param banner_type Type of image for new banner. + * @param avatar_blob New avatar, or empty string to clear avatar. + * @param avatar_type Type of image for new avatar. + * @param bio New bio, or empty string to clear bio * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void guild_current_member_edit(snowflake guild_id, const std::string &nickname, command_completion_event_t callback = utility::log_error()); + void guild_current_member_edit(snowflake guild_id, const std::string& nickname, const std::string& banner_blob, const image_type banner_type, const std::string& avatar_blob, const image_type avatar_type, const std::string& bio, command_completion_event_t callback = utility::log_error()); /** * @brief Get current user's connections (linked accounts, e.g. steam, xbox). diff --git a/include/dpp/cluster_coro_calls.h b/include/dpp/cluster_coro_calls.h index 3e2e57385c..9337a06da9 100644 --- a/include/dpp/cluster_coro_calls.h +++ b/include/dpp/cluster_coro_calls.h @@ -835,11 +835,16 @@ * @see dpp::cluster::guild_current_member_edit * @see https://discord.com/developers/docs/resources/guild#modify-current-member * @param guild_id Guild ID to change on - * @param nickname New nickname, or empty string to clear nickname + * @param nickname New nickname, or empty string to clear nickname. + * @param banner_blob New banner, or empty string to clear banner. + * @param banner_type Type of image for new banner. + * @param avatar_blob New avatar, or empty string to clear avatar. + * @param avatar_type Type of image for new avatar. + * @param bio New bio, or empty string to clear bio * @return confirmation returned object on completion * \memberof dpp::cluster */ -[[nodiscard]] async co_guild_current_member_edit(snowflake guild_id, const std::string &nickname); +[[nodiscard]] async co_guild_current_member_edit(snowflake guild_id, const std::string& nickname, const std::string& banner_blob, const image_type banner_type, const std::string& avatar_blob, const image_type avatar_type, const std::string& bio); /** * @brief Get the audit log for a guild diff --git a/src/dpp/cluster/guild.cpp b/src/dpp/cluster/guild.cpp index 75396c907b..6e47d5dda1 100644 --- a/src/dpp/cluster/guild.cpp +++ b/src/dpp/cluster/guild.cpp @@ -23,9 +23,49 @@ 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(-1, ' ', false, json::error_handler_t::replace); - rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "members/@me", m_patch, o, callback); +void cluster::guild_current_member_edit(snowflake guild_id, const std::string& nickname, const std::string& banner_blob, const image_type banner_type, const std::string& avatar_blob, const image_type avatar_type, const std::string& bio, command_completion_event_t callback) { + json j = json::object(); + + // Cannot use ternary operator, json null isn't compatible with std::string. + if (nickname.empty()) { + j["nick"] = json::value_t::null; + } else { + j["nick"] = nickname; + } + + static const std::map mimetypes = { + { i_gif, "image/gif" }, + { i_jpg, "image/jpeg" }, + { i_png, "image/png" }, + { i_webp, "image/webp" }, /* Whilst webp isn't supported (as of 13/07/24, confirmed again in 06/10/25, UK date), best to keep this here for when Discord support webp */ + }; + + if (banner_blob.empty()) { + j["banner"] = json::value_t::null; + } else { + if (banner_blob.size() > MAX_AVATAR_SIZE) { + throw dpp::length_exception(err_icon_size, "Banner file exceeds discord limit of 10240 kilobytes"); + } + j["banner"] = "data:" + mimetypes.find(banner_type)->second + ";base64," + base64_encode((unsigned char const*)banner_blob.data(), static_cast(banner_blob.length())); + } + + if (avatar_blob.empty()) { + j["avatar"] = json::value_t::null; + } else { + if (avatar_blob.size() > MAX_AVATAR_SIZE) { // Avatar limit is 10240 kb. + throw dpp::length_exception(err_icon_size, "Avatar file exceeds discord limit of 10240 kilobytes"); + } + j["avatar"] = "data:" + mimetypes.find(avatar_type)->second + ";base64," + base64_encode((unsigned char const*)avatar_blob.data(), static_cast(avatar_blob.length())); + } + + if (bio.empty()) { + j["bio"] = json::value_t::null; + } else { + j["bio"] = bio; + } + + std::string post_data = j.dump(-1, ' ', false, json::error_handler_t::replace); + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "members/@me", m_patch, std::move(post_data), std::move(callback)); } diff --git a/src/dpp/cluster_coro_calls.cpp b/src/dpp/cluster_coro_calls.cpp index 69df5537c2..2eeb705ab5 100644 --- a/src/dpp/cluster_coro_calls.cpp +++ b/src/dpp/cluster_coro_calls.cpp @@ -303,8 +303,8 @@ async cluster::co_get_gateway_bot() { return async{ this, static_cast(&cluster::get_gateway_bot) }; } -async cluster::co_guild_current_member_edit(snowflake guild_id, const std::string &nickname) { - return async{ this, static_cast(&cluster::guild_current_member_edit), guild_id, nickname }; +async cluster::co_guild_current_member_edit(snowflake guild_id, const std::string& nickname, const std::string& banner_blob, const image_type banner_type, const std::string& avatar_blob, const image_type avatar_type, const std::string& bio) { + return async{ this, static_cast(&cluster::guild_current_member_edit), guild_id, nickname, banner_blob, banner_type, avatar_blob, avatar_type, bio }; } async cluster::co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit) {