Skip to content

Commit

Permalink
breaking, fix: guild_member state tracking (#909)
Browse files Browse the repository at this point in the history
  • Loading branch information
braindigitalis authored Oct 2, 2023
1 parent a49d867 commit 35159e2
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 24 deletions.
59 changes: 56 additions & 3 deletions include/dpp/guild.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,25 @@ enum guild_member_flags : uint16_t {
gm_bypasses_verification = 0b0000000010000000,
/** Member has started onboarding */
gm_started_onboarding = 0b0000000100000000,
gm_roles_action = 0b0000001000000000,
gm_nickname_action = 0b0000010000000000,
};

/**
* @brief Represents dpp::user membership upon a dpp::guild.
* This contains the user's nickname, guild roles, and any other guild-specific flags.
*/
class DPP_EXPORT guild_member {
public:
protected:
/** Nickname, or empty string if they don't have a nickname on this guild */
std::string nickname;
/** List of roles this user has on this guild */
std::vector<snowflake> roles;
/** A set of flags built from the bitmask defined by dpp::guild_member_flags */
uint16_t flags;

friend void from_json(const nlohmann::json& j, guild_member& gm);
public:
/** Guild id */
snowflake guild_id;
/** User id */
Expand All @@ -204,8 +211,6 @@ class DPP_EXPORT guild_member {
time_t joined_at;
/** Boosting since */
time_t premium_since;
/** A set of flags built from the bitmask defined by dpp::guild_member_flags */
uint16_t flags;

/** Default constructor */
guild_member();
Expand Down Expand Up @@ -322,6 +327,20 @@ class DPP_EXPORT guild_member {
*/
guild_member& set_nickname(const std::string& nick);

/**
* @brief Get the nickname
*
* @return std::string nickname
*/
std::string get_nickname() const;

/**
* @brief Get the roles
*
* @return std::vector<dpp::snowflake> roles
*/
const std::vector<dpp::snowflake>& get_roles() const;

/**
* @brief Find the dpp::user object for this member. This is an alias for dpp::find_user
* @return dpp::user* Pointer to the user object. If not in cache, it returns nullptr
Expand Down Expand Up @@ -378,6 +397,40 @@ class DPP_EXPORT guild_member {
* @return std::string mention
*/
std::string get_mention() const;

/**
* @brief Add a role to this member
* @note This call sets the role change bit, which causes the new role
* list to be sent if this is passed to dpp::cluster::guild_edit_member
* or dpp::cluster::guild_add_member
*
* @param role_id Role ID to add
* @return guild_member& Reference to self
*/
guild_member& add_role(dpp::snowflake role_id);

/**
* @brief Remove a role from this member
* @note This call sets the role change bit, which causes the new role
* list to be sent if this is passed to dpp::cluster::guild_edit_member
* or dpp::cluster::guild_add_member
*
* @param role_id Role ID to remove
* @return guild_member& Reference to self
*/
guild_member& remove_role(dpp::snowflake role_id);

/**
* @brief Set a new role list for this member
* @note This call sets the role change bit, which causes the new role
* list to be sent if this is passed to dpp::cluster::guild_edit_member
* or dpp::cluster::guild_add_member
*
* @param role_ids Roles to set
* @return guild_member& Reference to self
*/
guild_member& set_roles(const std::vector<dpp::snowflake> &role_ids);

};

/**
Expand Down
58 changes: 43 additions & 15 deletions src/dpp/guild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ guild::guild() :


guild_member::guild_member() :
flags(0),
guild_id(0),
user_id(0),
communication_disabled_until(0),
joined_at(0),
premium_since(0),
flags(0)
premium_since(0)
{
}

Expand All @@ -112,6 +112,34 @@ std::string guild_member::get_mention() const {

guild_member& guild_member::set_nickname(const std::string& nick) {
this->nickname = nick;
this->flags |= gm_nickname_action;
return *this;
}

guild_member& guild_member::add_role(dpp::snowflake role_id) {
roles.emplace_back(role_id);
flags |= gm_roles_action;
return *this;
}

guild_member& guild_member::remove_role(dpp::snowflake role_id) {
roles.erase(std::remove(roles.begin(), roles.end(), role_id), roles.end());
flags |= gm_roles_action;
return *this;
}

std::string guild_member::get_nickname() const {
return nickname;
}

const std::vector<dpp::snowflake>& guild_member::get_roles() const {
return roles;
}


guild_member& guild_member::set_roles(const std::vector<dpp::snowflake> &role_ids) {
roles = role_ids;
flags |= gm_roles_action;
return *this;
}

Expand Down Expand Up @@ -189,8 +217,8 @@ void from_json(const nlohmann::json& j, guild_member& gm) {
std::string guild_member::get_avatar_url(uint16_t size, const image_type format, bool prefer_animated) const {
if (this->guild_id && this->user_id && !this->avatar.to_string().empty()) {
return utility::cdn_endpoint_url_hash({ i_jpg, i_png, i_webp, i_gif },
"guilds/" + std::to_string(this->guild_id) + "/" + std::to_string(this->user_id), this->avatar.to_string(),
format, size, prefer_animated, has_animated_guild_avatar());
"guilds/" + std::to_string(this->guild_id) + "/" + std::to_string(this->user_id), this->avatar.to_string(),
format, size, prefer_animated, has_animated_guild_avatar());
} else {
return std::string();
}
Expand All @@ -211,19 +239,19 @@ std::string guild_member::build_json(bool with_id) const {
}
}

if (!this->nickname.empty()) {
j["nick"] = this->nickname;
} else {
j["nick"] = json::value_t::null;
if (this->flags & gm_nickname_action) {
if (!this->nickname.empty()) {
j["nick"] = this->nickname;
} else {
j["nick"] = json::value_t::null;
}
}

if (!this->roles.empty()) {
if (this->flags & gm_roles_action) {
j["roles"] = {};
for (auto & role : this->roles) {
for (const auto & role : this->roles) {
j["roles"].push_back(std::to_string(role));
}
} else {
j["roles"] = {};
}

if (this->flags & gm_voice_action) {
Expand Down Expand Up @@ -718,7 +746,7 @@ permission guild::base_permissions(const guild_member &member) const {

permission permissions = everyone->permissions;

for (auto& rid : member.roles) {
for (auto& rid : member.get_roles()) {
role* r = dpp::find_role(rid);
if (r) {
permissions |= r->permissions;
Expand Down Expand Up @@ -765,7 +793,7 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u
uint64_t allow = 0;
uint64_t deny = 0;

for (auto& rid : gm.roles) {
for (auto& rid : gm.get_roles()) {

/* Skip \@everyone role to not break the hierarchy. It's calculated above */
if (rid == this->id) {
Expand Down Expand Up @@ -821,7 +849,7 @@ permission guild::permission_overwrites(const guild_member &member, const channe
uint64_t allow = 0;
uint64_t deny = 0;

for (auto& rid : member.roles) {
for (auto& rid : member.get_roles()) {

/* Skip \@everyone role to not break the hierarchy. It's calculated above */
if (rid == this->id) {
Expand Down
9 changes: 5 additions & 4 deletions src/dpp/role.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,9 @@ members_container role::get_members() const {
}
for (auto & m : g->members) {
/* Iterate all members and use std::find on their role list to see who has this role */
auto i = std::find(m.second.roles.begin(), m.second.roles.end(), this->id);
if (i != m.second.roles.end()) {
const auto& r = m.second.get_roles();
auto i = std::find(r.begin(), r.end(), this->id);
if (i != r.end()) {
gm[m.second.user_id] = m.second;
}
}
Expand All @@ -425,8 +426,8 @@ members_container role::get_members() const {
std::string role::get_icon_url(uint16_t size, const image_type format) const {
if (!this->icon.to_string().empty() && this->id) {
return utility::cdn_endpoint_url({ i_jpg, i_png, i_webp },
"role-icons/" + std::to_string(this->id) + "/" + this->icon.to_string(),
format, size);
"role-icons/" + std::to_string(this->id) + "/" + this->icon.to_string(),
format, size);
} else {
return std::string();
}
Expand Down
2 changes: 1 addition & 1 deletion src/unittest/coro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ void event_handler_test(dpp::cluster *bot) {
if (!pair.first.has_value()) {
co_return {};
}
const std::string& member_nick = pair.second.has_value() ? pair.second->nickname : "";
const std::string& member_nick = pair.second.has_value() ? pair.second->get_nickname() : "";
const std::string& user_nick = pair.first->username;
result = co_await bot->co_message_edit(msg.set_content("coro " + (member_nick.empty() ? user_nick : member_nick) + " " + std::to_string(i)));
co_return result.is_error() ? dpp::snowflake{} : std::get<dpp::message>(result.value).id;
Expand Down
3 changes: 2 additions & 1 deletion src/unittest/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ DPP_TEST(OPTCHOICE_STRING, "command_option_choice::fill_from_json: string", tf_o
DPP_TEST(HOSTINFO, "https_client::get_host_info()", tf_offline);
DPP_TEST(HTTPS, "https_client HTTPS request", tf_online);
DPP_TEST(HTTP, "https_client HTTP request", tf_offline);
DPP_TEST(MULTIHEADER, "multiheader cookie test", tf_offline);
DPP_TEST(RUNONCE, "run_once<T>", tf_offline);
DPP_TEST(WEBHOOK, "webhook construct from URL", tf_offline);
DPP_TEST(MD_ESC_1, "Markdown escaping (ignore code block contents)", tf_offline);
Expand Down Expand Up @@ -221,6 +220,8 @@ DPP_TEST(INVITE_DELETE, "cluster::invite_delete", tf_online);

/* Extended set -- Less important, skipped on the master branch due to rate limits and GitHub actions limitations*/
/* To execute, run unittests with "full" command line argument */
DPP_TEST(MULTIHEADER, "multiheader cookie test", tf_offline | tf_extended); // Fails in the EU as cookies are not sent without acceptance

DPP_TEST(VOICECONN, "Connect to voice channel", tf_online | tf_extended);
DPP_TEST(VOICESEND, "Send audio to voice channel", tf_online | tf_extended); // udp unreliable on gitbub
DPP_TEST(MESSAGEPIN, "Pinning a channel message", tf_online | tf_extended);
Expand Down

0 comments on commit 35159e2

Please sign in to comment.