Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking, fix: guild_member state tracking #909

Merged
merged 13 commits into from
Oct 2, 2023
Merged
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,
braindigitalis marked this conversation as resolved.
Show resolved Hide resolved
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);

braindigitalis marked this conversation as resolved.
Show resolved Hide resolved
};

/**
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