diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f29e1affe..89e025ae05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: - { arch: 'arm64', concurrency: 4, os: [self-hosted, linux, ARM64], package: g++-12, cpp: g++, version: 12, cmake-flags: '', cpack: 'yes', ctest: 'no', mold: 'yes' } steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -132,12 +132,10 @@ jobs: fail-fast: false # Don't fail everything if one fails. We want to test each OS/Compiler individually matrix: cfg: - - { arch: 'arm64', concurrency: 3, os: macos-latest, cpp: clang++, version: 16, cmake-flags: '', xcode-version: '16.0.0' } - { arch: 'arm64', concurrency: 3, os: macos-latest, cpp: clang++, version: 15, cmake-flags: '', xcode-version: '15.3' } - - { arch: 'arm64', concurrency: 3, os: macos-latest, cpp: clang++, version: 14, cmake-flags: '', xcode-version: '14.3.1' } steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -150,7 +148,7 @@ jobs: xcode-version: ${{ matrix.cfg.xcode-version }} - name: Install homebrew packages - run: brew install cmake make opus openssl pkg-config + run: brew install cmake make opus openssl - name: Generate CMake run: cmake -B build -DDPP_NO_VCPKG=ON -DCMAKE_BUILD_TYPE=Release -DDPP_CORO=ON -DAVX_TYPE=AVX0 @@ -191,7 +189,7 @@ jobs: runs-on: ${{matrix.cfg.os}} steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -256,7 +254,7 @@ jobs: runs-on: ${{matrix.cfg.os}} steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 05a46c9efe..ef46f0d798 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 + uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -64,6 +64,6 @@ jobs: run: cmake -B build -DDPP_NO_VCPKG=ON -DAVX_TYPE=AVX0 -DCMAKE_BUILD_TYPE=Release && cmake --build build -j4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 + uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/construct-vcpkg-info.yml b/.github/workflows/construct-vcpkg-info.yml index 862d212997..2ee3616628 100644 --- a/.github/workflows/construct-vcpkg-info.yml +++ b/.github/workflows/construct-vcpkg-info.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 955b3b3fb2..21a469b132 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,11 +17,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit - name: 'Checkout Repository' uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: 'Dependency Review' - uses: actions/dependency-review-action@4081bf99e2866ebe428fc0477b69eb4fcda7220a # v4.4.0 + uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4f69516150..b70eca4f58 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -22,7 +22,7 @@ jobs: cancel-in-progress: false steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -47,7 +47,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 + uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 with: push: true tags: brainboxdotcc/dpp diff --git a/.github/workflows/documentation-check.yml b/.github/workflows/documentation-check.yml index 2d47a833a0..b39a234d86 100644 --- a/.github/workflows/documentation-check.yml +++ b/.github/workflows/documentation-check.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index add9fa4c7a..f83e6c8e42 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/gitguardian.yml b/.github/workflows/gitguardian.yml index 9530677b2b..20f9287bf7 100644 --- a/.github/workflows/gitguardian.yml +++ b/.github/workflows/gitguardian.yml @@ -14,7 +14,7 @@ jobs: cancel-in-progress: true steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -23,7 +23,7 @@ jobs: with: fetch-depth: 0 # fetch all history so multiple commits can be scanned - name: GitGuardian scan - uses: GitGuardian/ggshield-action@0ebefb9aad794cfe4ea98897204db50e20ad5a00 # master + uses: GitGuardian/ggshield-action@4b450617504f2a3e1b58cbf0214f7ad3108cdab7 # master env: GITHUB_PUSH_BEFORE_SHA: ${{ github.event.before }} GITHUB_PUSH_BASE_SHA: ${{ github.event.base }} diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index c4cf52f566..340a0b6653 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 4134815d20..e739c1b709 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -72,6 +72,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 + uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: sarif_file: results.sarif diff --git a/.github/workflows/sitemap.yml b/.github/workflows/sitemap.yml index cfe156e5e1..a7ccaae9bf 100644 --- a/.github/workflows/sitemap.yml +++ b/.github/workflows/sitemap.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index fc5c9fb328..6fbb3cef90 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/target-master.yml b/.github/workflows/target-master.yml index 7c6c7c2d82..e80db5e2ed 100644 --- a/.github/workflows/target-master.yml +++ b/.github/workflows/target-master.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/test-docs-examples.yml b/.github/workflows/test-docs-examples.yml index c1bc9c8747..cc9a3238b0 100644 --- a/.github/workflows/test-docs-examples.yml +++ b/.github/workflows/test-docs-examples.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/docpages/dl.dpp.dev/index.php b/docpages/dl.dpp.dev/index.php index 31b38c68d9..4940aff62e 100644 --- a/docpages/dl.dpp.dev/index.php +++ b/docpages/dl.dpp.dev/index.php @@ -8,7 +8,7 @@ header("Status: 200 OK"); // Split up url and set defaults -list($version, $arch, $type) = explode('/', preg_replace('/https:\/\/dl\.dpp\.dev\//', '', $_SERVER['REDIRECT_SCRIPT_URI']), 3); +list($_, $version, $arch, $type) = explode('/', preg_replace('/https:\/\/dl\.dpp\.dev\//', '', $_SERVER['REDIRECT_URL'] ?? ''), 4); $version = !empty($version) ? $version : 'latest'; $arch = !empty($arch) ? $arch : 'linux-x64'; $type = !empty($type) ? $type : 'deb'; diff --git a/docpages/example_programs/misc/checking-member-permissions.md b/docpages/example_programs/misc/checking-member-permissions.md index 364234f7ad..b903ce113a 100644 --- a/docpages/example_programs/misc/checking-member-permissions.md +++ b/docpages/example_programs/misc/checking-member-permissions.md @@ -1,10 +1,10 @@ \page checking-member-permissions Checking Permissions -Of course most people do just iterate over the roles of a member to check for a permission. But there's a helper method for that: dpp::guild::base_permissions gets a member's permission taking into account the server owner and role permissions. +Of course most people typically iterate over the roles of a member to check for a permission. But there is a helper method for this: dpp::guild::base_permissions retrieves a member's permissions, taking into account role permissions **and** the server owner. For total member permissions including channel overwrites use either the dpp::channel::get_user_permissions or dpp::guild::permission_overwrites method. Both do the same under the hood. -They all return a dpp::permission class, which is a wrapper around a permission bitmask containing bits of the dpp::permissions enum. +They all return a dpp::permission class, which is a wrapper around a permission bitmask with several helpful methods for easier manipulation and checking of permissions. This bitmask contains flags from the dpp::permissions enum. Demonstration: @@ -15,11 +15,37 @@ if (c && c->get_user_permissions(member).can(dpp::p_send_messages)) { } ``` +### Role Hierarchy + +The recommended and correct way to compare for roles in the hierarchy is using the comparison operators (`<`, `>`) on the dpp::role objects themselves. Keep in mind that multiple roles can have the same position number. As a result, comparing roles by position alone can lead to subtle bugs when checking for role hierarchy. + +For example let's say you have a ban command, and want to make sure that any issuer of the command can only ban members of lower position than their own highest role: + +```cpp +bot.on_interaction_create([](const dpp::interaction_create_t& event) { + dpp::snowflake target_id = std::get(event.get_parameter("user")); + dpp::guild_member target = event.command.get_resolved_member(target_id); + + for (dpp::snowflake issuer_role_id : event.command.member.get_roles()) { + auto issuer_role = dpp::find_role(issuer_role_id); + if (issuer_role == nullptr) continue; + for (dpp::snowflake target_role_id : target.get_roles()) { + auto target_role = dpp::find_role(target_role_id); + if (target_role == nullptr) continue; + if (target_role > issuer_role) { + event.reply("You can't ban someone whose role is higher than yours!"); + return; + } + } + } +}); +``` + ## Permissions in Interaction Events ### Default Command Permissions -Discord's intended way to manage permissions for commands is through default member permissions. You set them using dpp::slashcommand::set_default_permissions when creating or updating a command to set the default permissions a user must have to use it. However, server administrators can then overwrite these permissions by their own restrictions. +Discord's intended way of managing permissions for commands is through "default member permissions". In a nutshell you tell Discord which permissions a user must have to use the command. Discord completely hides the command for members who don't have the required permissions. You set them using dpp::slashcommand::set_default_permissions when creating or updating a command. The corresponding code to create a command with default permissions would look something like this: @@ -34,9 +60,15 @@ command.add_option(dpp::command_option(dpp::co_string, "reason", "The reason for bot.global_command_create(command); ``` +You can set the default member permissions to "0" to disable the command for everyone except admins by default. + +For more customization for server owners, they can override these permissions by their own restrictions in the server settings. This is why they are referred to as "default" permissions. + ### Checking Permissions on Your Own -If you want to check permissions on your own, the easiest way to check if a member has certain permissions in interaction events is by using the dpp::interaction::get_resolved_permission function. The resolved list contains associated structures for the command and does not use the cache or require any extra API calls. Note that the permissions in the resolved set are pre-calculated by Discord and taking into account channel overwrites, roles and admin privileges. So no need to loop through roles or stuff like that. +When using default permissions you don't necessarily need to check the issuing user for any permissions in the interaction event as Discord handles all that for you. However, if you don't want server admins to be able to override the command restrictions, you can make those permission checks on your own. + +To check if a member has certain permissions during interaction events, the easiest way is to use the dpp::interaction::get_resolved_permission function. The resolved list contains associated structures for the command and does not rely on the cache or require any extra API calls. Additionally, the permissions in the resolved set are pre-calculated by Discord and taking into account channel overwrites, roles and admin privileges. So, there's no need to loop through roles or stuff like that. Let's imagine the following scenario: @@ -52,8 +84,6 @@ bot.on_interaction_create([](const dpp::interaction_create_t& event) { }); ``` -\note When using default permissions you don't necessarily need to check the issuing user for any permissions in the interaction event as Discord handles all that for you. But if you'd sleep better... - ### From Parameters The resolved set also contains the permissions of members from command parameters. @@ -85,3 +115,7 @@ bot.on_interaction_create([](const dpp::interaction_create_t& event) { } }); ``` + +### Things to Keep in Mind + +When replying to interactions using dpp::interaction_create_t::reply, you do **not** need to manually check whether the bot has permission to send messages. A bot always has permissions to reply to an interaction. diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h index 651c6b6ead..26c5a84b47 100644 --- a/include/dpp/appcommand.h +++ b/include/dpp/appcommand.h @@ -1556,7 +1556,8 @@ class DPP_EXPORT slashcommand : public managed, public json_interface { * @param allowed_permissions bitmask of dpp::permissions you want to allow for this user/role in this channel. Note: You can use the dpp::permission class * @param denied_permissions bitmask of dpp::permissions you want to deny for this user/role in this channel. Note: You can use the dpp::permission class * + * **Example:** + * + * ```cpp + * channel.add_permission_overwrite(388499352297406481, dpp::ot_role, dpp::p_manage_channels | dpp::p_manage_messages, 0); + * // Allows p_manage_channels and p_manage_messages permissions for the provided role. + * ``` + * * @return Reference to self, so these method calls may be chained */ channel& add_permission_overwrite(const snowflake target, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions); @@ -644,6 +651,13 @@ class DPP_EXPORT channel : public managed, public json_interface { * @param allowed_permissions bitmask of allowed dpp::permissions for this user/role in this channel. Note: You can use the dpp::permission class * @param denied_permissions bitmask of denied dpp::permissions for this user/role in this channel. Note: You can use the dpp::permission class * + * **Example:** + * + * ```cpp + * channel.set_permission_overwrite(388499352297406481, dpp::ot_role, dpp::p_manage_channels | dpp::p_manage_messages, 0); + * // Sets the allowed permissions to p_manage_channels and p_manage_messages and removes all denied permission flags for the provided role. + * ``` + * * @return Reference to self, so these method calls may be chained * * @note If both `allowed_permissions` and `denied_permissions` parameters are 0, the permission overwrite for the target will be removed diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 9c283e4a72..179be8f434 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2170,8 +2170,8 @@ class DPP_EXPORT cluster { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param c Channel to set permissions for * @param overwrite_id Overwrite to change (a user or role ID) - * @param allow allow permissions bitmask - * @param deny deny permissions bitmask + * @param allow Bitmask of allowed permissions (refer to enum dpp::permissions) + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) * @param member true if the overwrite_id is a user id, false if it is a channel id * @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(). @@ -2185,8 +2185,8 @@ class DPP_EXPORT cluster { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param channel_id ID of the channel to set permissions for * @param overwrite_id Overwrite to change (a user or role ID) - * @param allow allow permissions bitmask - * @param deny deny permissions bitmask + * @param allow Bitmask of allowed permissions (refer to enum dpp::permissions) + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) * @param member true if the overwrite_id is a user id, false if it is a channel id * @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(). diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 86ccd5fc02..461736b215 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -590,8 +590,8 @@ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param c Channel to set permissions for * @param overwrite_id Overwrite to change (a user or role ID) - * @param allow allow permissions bitmask - * @param deny deny permissions bitmask + * @param allow Bitmask of allowed permissions (refer to enum dpp::permissions) + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) * @param member true if the overwrite_id is a user id, false if it is a channel id * @return confirmation returned object on completion * \memberof dpp::cluster @@ -610,8 +610,8 @@ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param channel_id ID of the channel to set permissions for * @param overwrite_id Overwrite to change (a user or role ID) - * @param allow allow permissions bitmask - * @param deny deny permissions bitmask + * @param allow Bitmask of allowed permissions (refer to enum dpp::permissions) + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) * @param member true if the overwrite_id is a user id, false if it is a channel id * @return confirmation returned object on completion * \memberof dpp::cluster diff --git a/include/dpp/event_router.h b/include/dpp/event_router.h index be8fc14d22..7a352f0453 100644 --- a/include/dpp/event_router.h +++ b/include/dpp/event_router.h @@ -512,7 +512,6 @@ template class event_router_t { * @brief Obtain an awaitable object that refers to any event. * It can be co_await-ed to wait for the next event. * - * Example: * @details Example: @code{cpp} * dpp::task<> my_handler(const dpp::slashcommand_t& event) { * co_await event.co_reply(dpp::message().add_component(dpp::component().add_component().set_label("click me!").set_id("test"))); diff --git a/include/dpp/role.h b/include/dpp/role.h index 1a76862823..42119c1569 100644 --- a/include/dpp/role.h +++ b/include/dpp/role.h @@ -120,6 +120,10 @@ class DPP_EXPORT role : public managed, public json_interface { /** * @brief Role position. + * + * @warning multiple roles can have the same position number. + * As a result, comparing roles by position alone can lead to subtle bugs when checking for role hierarchy. + * The recommended and correct way to compare for roles in the hierarchy is using the comparison operators on the role objects themselves. */ uint8_t position{0}; @@ -322,6 +326,11 @@ class DPP_EXPORT role : public managed, public json_interface { * @return true if lhs is less than rhs */ friend inline bool operator< (const role& lhs, const role& rhs) { + if (lhs.position == rhs.position) { + // the higher the IDs are, the lower the position + return lhs.id > rhs.id; + } + return lhs.position < rhs.position; } @@ -333,7 +342,7 @@ class DPP_EXPORT role : public managed, public json_interface { * @return true if lhs is greater than rhs */ friend inline bool operator> (const role& lhs, const role& rhs) { - return lhs.position > rhs.position; + return !(lhs < rhs); } /** @@ -343,7 +352,7 @@ class DPP_EXPORT role : public managed, public json_interface { * @return true if is equal to other */ inline bool operator== (const role& other) const { - return this->position == other.position; + return this->id == other.id; } /** @@ -353,7 +362,7 @@ class DPP_EXPORT role : public managed, public json_interface { * @return true if is not equal to other */ inline bool operator!= (const role& other) const { - return this->position != other.position; + return this->id != other.id; } /** diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index c9714b0083..497a2defaf 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -411,17 +411,20 @@ if(DPP_FORMATTERS) endif() if (NOT BUILD_SHARED_LIBS) - add_library(dppstatic STATIC + if (HAVE_VOICE) + add_library(dppstatic STATIC $ $ $ $ $ $ - ) - if (HAVE_VOICE) + ) target_link_libraries(dppstatic ${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES} ${OPUS_LIBRARIES} -static-libgcc -static-libstdc++) else() + add_library(dppstatic STATIC + $ + ) target_link_libraries(dppstatic ${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES}) endif() endif() diff --git a/mlspp/CMakeLists.txt b/mlspp/CMakeLists.txt index 52f26b5ba6..3e0cb45977 100755 --- a/mlspp/CMakeLists.txt +++ b/mlspp/CMakeLists.txt @@ -16,9 +16,10 @@ if(MLS_NAMESPACE_SUFFIX) set(MLS_CXX_NAMESPACE "mls_${MLS_NAMESPACE_SUFFIX}" CACHE STRING "Top-level Namespace for CXX") set(MLS_EXPORT_NAMESPACE "MLSPP${MLS_NAMESPACE_SUFFIX}" CACHE STRING "Namespace for CMake Export") else() - set(MLS_CXX_NAMESPACE "../include/dpp/mlspp/mls" CACHE STRING "Top-level Namespace for CXX") + set(MLS_CXX_NAMESPACE "mls" CACHE STRING "Top-level Namespace for CXX") set(MLS_EXPORT_NAMESPACE "MLSPP" CACHE STRING "Namespace for CMake Export") endif() + message(STATUS "CXX Namespace: ${MLS_CXX_NAMESPACE}") message(STATUS "CMake Export Namespace: ${MLS_EXPORT_NAMESPACE}") diff --git a/src/dpp/cluster.cpp b/src/dpp/cluster.cpp index 90d4cb12c0..e39bc75b1e 100644 --- a/src/dpp/cluster.cpp +++ b/src/dpp/cluster.cpp @@ -319,6 +319,7 @@ void cluster::shutdown() { delete t.second; } timer_list.clear(); + next_timer.clear(); /* Terminate shards */ for (const auto& sh : shards) { log(ll_info, "Terminating shard id " + std::to_string(sh.second->shard_id)); diff --git a/src/dpp/cluster/guild_member.cpp b/src/dpp/cluster/guild_member.cpp index 8ab690ea94..5880cd61b2 100644 --- a/src/dpp/cluster/guild_member.cpp +++ b/src/dpp/cluster/guild_member.cpp @@ -30,7 +30,7 @@ void cluster::guild_add_member(const guild_member& gm, const std::string &access void cluster::guild_edit_member(const guild_member& gm, command_completion_event_t callback) { - this->post_rest(API_PATH "/guilds", std::to_string(gm.guild_id), "members/" + std::to_string(gm.user_id), m_patch, gm.build_json(), [this, &gm, callback](json &j, const http_request_completion_t& http) { + this->post_rest(API_PATH "/guilds", std::to_string(gm.guild_id), "members/" + std::to_string(gm.user_id), m_patch, gm.build_json(), [this, gm, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, guild_member().fill_from_json(&j, gm.guild_id, gm.user_id), http)); } diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index da96a28096..b85b9386ab 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -342,11 +342,28 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b set_test(TIMESTAMPTOSTRING, false); set_test(TIMESTAMPTOSTRING, dpp::ts_to_string(1642611864) == "2022-01-19T17:04:24Z"); - set_test(ROLE_COMPARE, false); - dpp::role role_1, role_2; - role_1.position = 1; - role_2.position = 2; - set_test(ROLE_COMPARE, role_1 < role_2 && role_1 != role_2); + { + set_test(ROLE_COMPARE, false); + dpp::role role_1, role_2; + role_1.id = 99; + role_1.position = 1; + role_1.guild_id = 123; + role_1.id = 98; + role_2.position = 2; + role_2.guild_id = 123; + set_test(ROLE_COMPARE, role_1 < role_2 && !(role_1 > role_2) && role_1 != role_2); + } + { + set_test(ROLE_COMPARE_CONSIDERING_ID, false); + dpp::role role_1, role_2; + role_1.id = 99; + role_1.position = 2; + role_1.guild_id = 123; + role_2.id = 98; + role_2.position = 2; + role_2.guild_id = 123; + set_test(ROLE_COMPARE_CONSIDERING_ID, role_1 < role_2 && !(role_1 > role_2)); + } set_test(WEBHOOK, false); try { diff --git a/src/unittest/test.h b/src/unittest/test.h index 97637b7711..8c0d1e1192 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -206,6 +206,7 @@ DPP_TEST(UTILITY_CDN_ENDPOINT_URL_HASH, "utility::cdn_endpoint_url_hash", tf_off DPP_TEST(STICKER_GET_URL, "sticker::get_url aka utility::cdn_endpoint_url_sticker", tf_offline); DPP_TEST(EMOJI_GET_URL, "emoji::get_url", tf_offline); DPP_TEST(ROLE_COMPARE, "role::operator<", tf_offline); +DPP_TEST(ROLE_COMPARE_CONSIDERING_ID, "role::operator<", tf_offline); DPP_TEST(ROLE_CREATE, "cluster::role_create", tf_online); DPP_TEST(ROLE_EDIT, "cluster::role_edit", tf_online); DPP_TEST(ROLE_DELETE, "cluster::role_delete", tf_online);