diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h index e628bd97f8..a4f99f7938 100644 --- a/include/dpp/appcommand.h +++ b/include/dpp/appcommand.h @@ -1591,4 +1591,4 @@ typedef std::unordered_map slashcommand_map; */ typedef std::unordered_map guild_command_permissions_map; -} // namespace dpp +} diff --git a/include/dpp/application.h b/include/dpp/application.h index eb7e8b32df..5fae5738d5 100644 --- a/include/dpp/application.h +++ b/include/dpp/application.h @@ -325,6 +325,11 @@ class DPP_EXPORT application : public managed, public json_interface application_map; -} // namespace dpp +} diff --git a/include/dpp/auditlog.h b/include/dpp/auditlog.h index 90339ec331..5af57abdb7 100644 --- a/include/dpp/auditlog.h +++ b/include/dpp/auditlog.h @@ -478,4 +478,4 @@ class DPP_EXPORT auditlog : public json_interface { virtual ~auditlog() = default; }; -} // namespace dpp +} diff --git a/include/dpp/automod.h b/include/dpp/automod.h index f01c81d821..05c194f47f 100644 --- a/include/dpp/automod.h +++ b/include/dpp/automod.h @@ -400,4 +400,4 @@ class DPP_EXPORT automod_rule : public managed, public json_interface automod_rule_map; -} // namespace dpp +} diff --git a/include/dpp/ban.h b/include/dpp/ban.h index 6a02352ed1..039aa90e31 100644 --- a/include/dpp/ban.h +++ b/include/dpp/ban.h @@ -66,4 +66,4 @@ class DPP_EXPORT ban : public json_interface { */ typedef std::unordered_map ban_map; -} // namespace dpp +} diff --git a/include/dpp/bignum.h b/include/dpp/bignum.h index 2966d7b8b0..6d13f8f472 100644 --- a/include/dpp/bignum.h +++ b/include/dpp/bignum.h @@ -98,4 +98,4 @@ class DPP_EXPORT bignumber { [[nodiscard]] std::vector get_binary() const; }; -} // namespace dpp +} diff --git a/include/dpp/cache.h b/include/dpp/cache.h index cdfa3788f8..f4bb884e98 100644 --- a/include/dpp/cache.h +++ b/include/dpp/cache.h @@ -270,5 +270,5 @@ cache_decl(role, find_role, get_role_cache, get_role_count); cache_decl(channel, find_channel, get_channel_cache, get_channel_count); cache_decl(emoji, find_emoji, get_emoji_cache, get_emoji_count); -} // namespace dpp +} diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 6b97b9e23a..4550bfc247 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -878,5 +878,5 @@ void to_json(nlohmann::json& j, const permission_overwrite& po); */ typedef std::unordered_map channel_map; -} // namespace dpp +} diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 5264d83d30..9c283e4a72 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -1334,17 +1334,6 @@ class DPP_EXPORT cluster { event_router_t on_voice_buffer_send; - /** - * @brief Called when a user is talking on a voice channel. - * - * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. - * - * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. - * The function signature for this event takes a single `const` reference of type voice_user_talking_t&, and returns void. - */ - event_router_t on_voice_user_talking; - - /** * @brief Called when a voice channel is connected and ready to send audio. * Note that this is not directly attached to the READY event of the websocket, @@ -3662,7 +3651,7 @@ class DPP_EXPORT cluster { /** * @brief Get all guild stickers - * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @see https://discord.com/developers/docs/resources/sticker#list-guild-stickers * @param guild_id Guild ID of the guild where the sticker is * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::sticker_map 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(). @@ -3671,7 +3660,7 @@ class DPP_EXPORT cluster { /** * @brief Get a list of available sticker packs - * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @see https://discord.com/developers/docs/resources/sticker#list-sticker-packs * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::sticker_pack_map 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(). */ @@ -3821,6 +3810,16 @@ class DPP_EXPORT cluster { */ void current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0, command_completion_event_t callback = utility::log_error()); + /** + * @brief Get the bot's voice state in a guild without a Gateway connection + * + * @see https://discord.com/developers/docs/resources/voice#get-current-user-voice-state + * @param guild_id Guild to get the voice state for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::voicestate 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 current_user_get_voice_state(snowflake guild_id, command_completion_event_t callback); + /** * @brief Set a user's voice state on a stage channel * @@ -3844,6 +3843,17 @@ class DPP_EXPORT cluster { */ void user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false, command_completion_event_t callback = utility::log_error()); + /** + * @brief Get a user's voice state in a guild without a Gateway connection + * + * @see https://discord.com/developers/docs/resources/voice#get-user-voice-state + * @param guild_id Guild to get the voice state for + * @param user_id The user to get the voice state of + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::voicestate 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 user_get_voice_state(snowflake guild_id, snowflake user_id, command_completion_event_t callback); + /** * @brief Get all auto moderation rules for a guild * @@ -3972,4 +3982,4 @@ class DPP_EXPORT cluster { }; -} // namespace dpp +} diff --git a/include/dpp/cluster_coro_calls.h b/include/dpp/cluster_coro_calls.h index 0ea93fc9b6..9077e9efa6 100644 --- a/include/dpp/cluster_coro_calls.h +++ b/include/dpp/cluster_coro_calls.h @@ -2009,7 +2009,7 @@ /** * @brief Get all guild stickers * @see dpp::cluster::guild_stickers_get - * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @see https://discord.com/developers/docs/resources/sticker#list-guild-stickers * @param guild_id Guild ID of the guild where the sticker is * @return sticker_map returned object on completion * \memberof dpp::cluster @@ -2029,7 +2029,7 @@ /** * @brief Get a list of available sticker packs * @see dpp::cluster::sticker_packs_get - * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @see https://discord.com/developers/docs/resources/sticker#list-sticker-packs * @return sticker_pack_map returned object on completion * \memberof dpp::cluster */ @@ -2367,6 +2367,17 @@ */ [[nodiscard]] async co_current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0); +/** + * @brief Get the bot's voice state in a guild without a Gateway connection + * + * @see dpp::cluster::current_user_get_voice_state + * @see https://discord.com/developers/docs/resources/voice#get-current-user-voice-state + * @param guild_id Guild to get the voice state for + * @return voicestate returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_get_voice_state(snowflake guild_id); + /** * @brief Set a user's voice state on a stage channel * @@ -2391,6 +2402,18 @@ */ [[nodiscard]] async co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false); +/** + * @brief Get a user's voice state in a guild without a Gateway connection + * + * @see dpp::cluster::user_get_voice_state + * @see https://discord.com/developers/docs/resources/voice#get-user-voice-state + * @param guild_id Guild to get the voice state for + * @param user_id The user to get the voice state of + * @return voicestate returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_get_voice_state(snowflake guild_id, snowflake user_id); + /** * @brief Get current user's connections (linked accounts, e.g. steam, xbox). * This call requires the oauth2 `connections` scope and cannot be executed diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 74db0bff7c..86ccd5fc02 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -2621,7 +2621,7 @@ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev /** * @brief Get all guild stickers * @see dpp::cluster::guild_stickers_get - * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @see https://discord.com/developers/docs/resources/sticker#list-guild-stickers * @param guild_id Guild ID of the guild where the sticker is * @return sticker_map returned object on completion * \memberof dpp::cluster @@ -2649,7 +2649,7 @@ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev /** * @brief Get a list of available sticker packs * @see dpp::cluster::sticker_packs_get - * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @see https://discord.com/developers/docs/resources/sticker#list-sticker-packs * @return sticker_pack_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function @@ -3095,6 +3095,21 @@ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev */ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation current_user_set_voice_state_sync(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0); +/** + * @brief Get the bot's voice state in a guild without a Gateway connection + * + * @see dpp::cluster::current_user_get_voice_state + * @see https://discord.com/developers/docs/resources/voice#get-current-user-voice-state + * @param guild_id Guild to get the voice state for + * @return voicestate returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") voicestate current_user_get_voice_state_sync(snowflake guild_id); + /** * @brief Set a user's voice state on a stage channel * @@ -3123,6 +3138,22 @@ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev */ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation user_set_voice_state_sync(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false); +/** + * @brief Get a user's voice state in a guild without a Gateway connection + * + * @see dpp::cluster::user_get_voice_state + * @see https://discord.com/developers/docs/resources/voice#get-user-voice-state + * @param guild_id Guild to get the voice state for + * @param user_id The user to get the voice state of + * @return voicestate returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") voicestate user_get_voice_state_sync(snowflake guild_id, snowflake user_id); + /** * @brief Get current user's connections (linked accounts, e.g. steam, xbox). * This call requires the oauth2 `connections` scope and cannot be executed diff --git a/include/dpp/collector.h b/include/dpp/collector.h index ac09bf95a6..7d25c1e659 100644 --- a/include/dpp/collector.h +++ b/include/dpp/collector.h @@ -470,4 +470,4 @@ class scheduled_event_collector : public scheduled_event_collector_t { virtual ~scheduled_event_collector() = default; }; -} // namespace dpp +} diff --git a/include/dpp/commandhandler.h b/include/dpp/commandhandler.h index 1958bc43d4..c8be5c9c97 100644 --- a/include/dpp/commandhandler.h +++ b/include/dpp/commandhandler.h @@ -425,4 +425,4 @@ class DPP_EXPORT DPP_DEPRECATED("commandhandler should not be used. Please consi }; -} // namespace dpp +} diff --git a/include/dpp/coro/async.h b/include/dpp/coro/async.h index 4295ae1218..2feac38d8a 100644 --- a/include/dpp/coro/async.h +++ b/include/dpp/coro/async.h @@ -184,6 +184,6 @@ class async : public awaitable { DPP_CHECK_ABI_COMPAT(async<>, async_dummy); -} // namespace dpp +} #endif /* DPP_CORO */ diff --git a/include/dpp/coro/coroutine.h b/include/dpp/coro/coroutine.h index a9e7a5d100..648ac2b79b 100644 --- a/include/dpp/coro/coroutine.h +++ b/include/dpp/coro/coroutine.h @@ -393,7 +393,7 @@ namespace detail::coroutine { DPP_CHECK_ABI_COMPAT(coroutine, coroutine_dummy) DPP_CHECK_ABI_COMPAT(coroutine, coroutine_dummy) -} // namespace dpp +} /** * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise type from a coroutine function. diff --git a/include/dpp/coro/task.h b/include/dpp/coro/task.h index 6625bb1f3e..d57bc8ce7f 100644 --- a/include/dpp/coro/task.h +++ b/include/dpp/coro/task.h @@ -433,7 +433,7 @@ std_coroutine::coroutine_handle<> final_awaiter::await_suspend(handle_t ha DPP_CHECK_ABI_COMPAT(task, task_dummy) DPP_CHECK_ABI_COMPAT(task, task_dummy) -} // namespace dpp +} /** * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise_t type from a coroutine function. diff --git a/include/dpp/discordclient.h b/include/dpp/discordclient.h index 872dbaa916..d57c2d775b 100644 --- a/include/dpp/discordclient.h +++ b/include/dpp/discordclient.h @@ -533,4 +533,4 @@ class DPP_EXPORT discord_client : public websocket_client voiceconn* get_voice(snowflake guild_id); }; -} // namespace dpp +} diff --git a/include/dpp/discordevents.h b/include/dpp/discordevents.h index 184294818b..c603239b61 100644 --- a/include/dpp/discordevents.h +++ b/include/dpp/discordevents.h @@ -229,4 +229,4 @@ std::string DPP_EXPORT base64_encode(unsigned char const* buf, unsigned int buff */ std::string DPP_EXPORT ts_to_string(time_t ts); -} // namespace dpp +} diff --git a/include/dpp/discordvoiceclient.h b/include/dpp/discordvoiceclient.h index 658ba6422a..ceb9d92829 100644 --- a/include/dpp/discordvoiceclient.h +++ b/include/dpp/discordvoiceclient.h @@ -240,6 +240,11 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ void cleanup(); + /** + * @brief A frame of silence packet + */ + static constexpr uint8_t silence_packet[3] = { 0xf8, 0xff, 0xfe }; + /** * @brief Mutex for outbound packet stream */ @@ -434,6 +439,13 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ bool paused; + /** + * @brief Whether has sent 5 frame of silence before stopping on pause. + * + * This is to avoid unintended Opus interpolation with subsequent transmissions. + */ + bool sent_stop_frames; + #ifdef HAVE_VOICE /** * @brief libopus encoder @@ -650,8 +662,10 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @param packet packet data * @param len length of packet * @param duration duration of opus packet + * @param send_now send this packet right away without buffering. + * Do NOT set send_now to true outside write_ready. */ - void send(const char* packet, size_t len, uint64_t duration); + void send(const char* packet, size_t len, uint64_t duration, bool send_now = false); /** * @brief Queue a message to be sent via the websocket @@ -962,6 +976,10 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @param duration Generally duration is 2.5, 5, 10, 20, 40 or 60 * if the timescale is 1000000 (1ms) * + * @param send_now Send this packet right away without buffering, + * this will skip duration calculation for the packet being sent + * and only safe to be set to true in write_ready. + * * @return discord_voice_client& Reference to self * * @note It is your responsibility to ensure that packets of data @@ -972,7 +990,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * * @throw dpp::voice_exception If data length is invalid or voice support not compiled into D++ */ - discord_voice_client& send_audio_opus(uint8_t* opus_packet, const size_t length, uint64_t duration); + discord_voice_client& send_audio_opus(const uint8_t* opus_packet, const size_t length, uint64_t duration, bool send_now = false); /** * @brief Send opus packets to the voice channel @@ -999,7 +1017,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * * @throw dpp::voice_exception If data length is invalid or voice support not compiled into D++ */ - discord_voice_client& send_audio_opus(uint8_t* opus_packet, const size_t length); + discord_voice_client& send_audio_opus(const uint8_t* opus_packet, const size_t length); /** * @brief Send silence to the voice channel @@ -1012,6 +1030,19 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ discord_voice_client& send_silence(const uint64_t duration); + /** + * @brief Send stop frames to the voice channel. + * + * @param send_now send this packet right away without buffering. + * Do NOT set send_now to true outside write_ready. + * Also make sure you're not locking stream_mutex if you + * don't set send_now to true. + * + * @return discord_voice_client& Reference to self + * @throw dpp::voice_exception if voice support is not compiled into D++ + */ + discord_voice_client& send_stop_frames(bool send_now = false); + /** * @brief Sets the audio type that will be sent with send_audio_* methods. * @@ -1240,5 +1271,5 @@ class DPP_EXPORT discord_voice_client : public websocket_client void process_mls_group_rosters(const std::map>& rmap); }; -} // namespace dpp +} diff --git a/include/dpp/dispatcher.h b/include/dpp/dispatcher.h index 151c2e8880..be08488b7b 100644 --- a/include/dpp/dispatcher.h +++ b/include/dpp/dispatcher.h @@ -1989,30 +1989,7 @@ struct DPP_EXPORT voice_buffer_send_t : public event_dispatch_t { }; /** - * @brief voice user talking - */ -struct DPP_EXPORT voice_user_talking_t : public event_dispatch_t { - using event_dispatch_t::event_dispatch_t; - using event_dispatch_t::operator=; - - /** - * @brief voice client where user is talking - */ - class discord_voice_client* voice_client = nullptr; - - /** - * @brief talking user id - */ - snowflake user_id = {}; - - /** - * @brief flags for talking user - */ - uint8_t talking_flags = 0; -}; - -/** - * @brief voice user talking + * @brief voice ready */ struct DPP_EXPORT voice_ready_t : public event_dispatch_t { using event_dispatch_t::event_dispatch_t; @@ -2221,5 +2198,5 @@ struct DPP_EXPORT entitlement_delete_t : public event_dispatch_t { entitlement deleted = {}; }; -} // namespace dpp +} diff --git a/include/dpp/dtemplate.h b/include/dpp/dtemplate.h index 098824e458..b0a2dc494b 100644 --- a/include/dpp/dtemplate.h +++ b/include/dpp/dtemplate.h @@ -112,4 +112,4 @@ class DPP_EXPORT dtemplate : public json_interface { */ typedef std::unordered_map dtemplate_map; -} // namespace dpp +} diff --git a/include/dpp/emoji.h b/include/dpp/emoji.h index 6249b4c9eb..7546ead720 100644 --- a/include/dpp/emoji.h +++ b/include/dpp/emoji.h @@ -249,4 +249,4 @@ class DPP_EXPORT emoji : public managed, public json_interface { */ typedef std::unordered_map emoji_map; -} // namespace dpp +} diff --git a/include/dpp/entitlement.h b/include/dpp/entitlement.h index 857b2691dd..fd181d6c5c 100644 --- a/include/dpp/entitlement.h +++ b/include/dpp/entitlement.h @@ -243,4 +243,4 @@ class DPP_EXPORT entitlement : public managed, public json_interface entitlement_map; -} // namespace dpp +} diff --git a/include/dpp/etf.h b/include/dpp/etf.h index 8e17ca3a96..f4e9f1d29b 100644 --- a/include/dpp/etf.h +++ b/include/dpp/etf.h @@ -708,4 +708,4 @@ class DPP_EXPORT etf_parser { std::string build(const nlohmann::json& j); }; -} // namespace dpp +} diff --git a/include/dpp/event.h b/include/dpp/event.h index 6921586755..1e34e1b365 100644 --- a/include/dpp/event.h +++ b/include/dpp/event.h @@ -156,4 +156,4 @@ event_decl(entitlement_create, ENTITLEMENT_CREATE); event_decl(entitlement_update, ENTITLEMENT_UPDATE); event_decl(entitlement_delete, ENTITLEMENT_DELETE); -} // namespace dpp::events +} diff --git a/include/dpp/event_router.h b/include/dpp/event_router.h index 2babc42e29..be8fc14d22 100644 --- a/include/dpp/event_router.h +++ b/include/dpp/event_router.h @@ -742,4 +742,4 @@ const T &awaitable::await_resume() { } #endif -} // namespace dpp +} diff --git a/include/dpp/exception.h b/include/dpp/exception.h index 1c60084240..3d5f41ff08 100644 --- a/include/dpp/exception.h +++ b/include/dpp/exception.h @@ -602,5 +602,5 @@ class exception : public std::exception # endif /* DPP_CORO */ #endif -} // namespace dpp +} diff --git a/include/dpp/guild.h b/include/dpp/guild.h index 86c9393f0b..b031371e20 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -2048,4 +2048,4 @@ typedef std::unordered_map guild_member_map; */ guild_member DPP_EXPORT find_guild_member(const snowflake guild_id, const snowflake user_id); -} // namespace dpp +} diff --git a/include/dpp/httpsclient.h b/include/dpp/httpsclient.h index 0090c68af4..5a0ff481cf 100644 --- a/include/dpp/httpsclient.h +++ b/include/dpp/httpsclient.h @@ -356,4 +356,4 @@ class DPP_EXPORT https_client : public ssl_client { }; -} // namespace dpp +} diff --git a/include/dpp/integration.h b/include/dpp/integration.h index 1e48c7be65..1dc78747fc 100644 --- a/include/dpp/integration.h +++ b/include/dpp/integration.h @@ -335,5 +335,5 @@ typedef std::unordered_map integration_map; */ typedef std::unordered_map connection_map; -} // namespace dpp +} diff --git a/include/dpp/intents.h b/include/dpp/intents.h index d225a9d5e1..f2e82b0c74 100644 --- a/include/dpp/intents.h +++ b/include/dpp/intents.h @@ -152,4 +152,4 @@ enum intents { i_unverified_default_intents = dpp::i_default_intents | dpp::i_message_content }; -} // namespace dpp +} diff --git a/include/dpp/invite.h b/include/dpp/invite.h index c20708056a..d1d9a939b9 100644 --- a/include/dpp/invite.h +++ b/include/dpp/invite.h @@ -242,4 +242,4 @@ class DPP_EXPORT invite : public json_interface { */ typedef std::unordered_map invite_map; -} // namespace dpp +} diff --git a/include/dpp/isa/avx.h b/include/dpp/isa/avx.h index b84d90b56a..6cae986288 100644 --- a/include/dpp/isa/avx.h +++ b/include/dpp/isa/avx.h @@ -107,6 +107,6 @@ namespace dpp { } }; -} // namespace dpp +} #endif \ No newline at end of file diff --git a/include/dpp/isa/avx2.h b/include/dpp/isa/avx2.h index 009127932a..29ed493e43 100644 --- a/include/dpp/isa/avx2.h +++ b/include/dpp/isa/avx2.h @@ -110,6 +110,6 @@ namespace dpp { } }; -} // namespace dpp +} #endif \ No newline at end of file diff --git a/include/dpp/isa/avx512.h b/include/dpp/isa/avx512.h index e5535da742..333a1a64f7 100644 --- a/include/dpp/isa/avx512.h +++ b/include/dpp/isa/avx512.h @@ -113,6 +113,6 @@ namespace dpp { } }; -} // namespace dpp +} #endif \ No newline at end of file diff --git a/include/dpp/isa/fallback.h b/include/dpp/isa/fallback.h index a7154233b2..5d246d4d29 100644 --- a/include/dpp/isa/fallback.h +++ b/include/dpp/isa/fallback.h @@ -76,4 +76,4 @@ namespace dpp { } }; -} // namespace dpp +} diff --git a/include/dpp/json_interface.h b/include/dpp/json_interface.h index 0fd209ed00..720b7a2eba 100644 --- a/include/dpp/json_interface.h +++ b/include/dpp/json_interface.h @@ -70,4 +70,4 @@ struct json_interface { } }; -} // namespace dpp +} diff --git a/include/dpp/managed.h b/include/dpp/managed.h index 4ccd2e5318..66d6b210a1 100644 --- a/include/dpp/managed.h +++ b/include/dpp/managed.h @@ -113,4 +113,4 @@ class DPP_EXPORT managed { } }; -} // namespace dpp +} diff --git a/include/dpp/message.h b/include/dpp/message.h index 44640af8d8..200f3f7a07 100644 --- a/include/dpp/message.h +++ b/include/dpp/message.h @@ -2651,4 +2651,4 @@ typedef std::unordered_map sticker_map; */ typedef std::unordered_map sticker_pack_map; -} // namespace dpp +} diff --git a/include/dpp/misc-enum.h b/include/dpp/misc-enum.h index 19fd2fcd9a..d9ac781f08 100644 --- a/include/dpp/misc-enum.h +++ b/include/dpp/misc-enum.h @@ -86,4 +86,4 @@ enum loglevel { ll_critical }; -} // namespace dpp +} diff --git a/include/dpp/once.h b/include/dpp/once.h index f81a02cfdb..ef24f205bb 100644 --- a/include/dpp/once.h +++ b/include/dpp/once.h @@ -43,4 +43,4 @@ template auto run_once() { return !std::exchange(called, true); }; -} // namespace dpp +} diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 22b1b555ba..4124e3cd80 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -456,4 +456,4 @@ class DPP_EXPORT permission { } }; -} // namespace dpp +} diff --git a/include/dpp/presence.h b/include/dpp/presence.h index 7515b14bac..3918bf2d60 100644 --- a/include/dpp/presence.h +++ b/include/dpp/presence.h @@ -595,4 +595,4 @@ class DPP_EXPORT presence : public json_interface { */ typedef std::unordered_map presence_map; -} // namespace dpp +} diff --git a/include/dpp/prune.h b/include/dpp/prune.h index 36e6275f6d..08b7592597 100644 --- a/include/dpp/prune.h +++ b/include/dpp/prune.h @@ -77,4 +77,4 @@ struct DPP_EXPORT prune : public json_interface { json to_json(bool with_id = false) const; // Intentional shadow of json_interface, mostly present for documentation }; -} // namespace dpp +} diff --git a/include/dpp/queues.h b/include/dpp/queues.h index 3597e78931..c502647936 100644 --- a/include/dpp/queues.h +++ b/include/dpp/queues.h @@ -671,4 +671,4 @@ class DPP_EXPORT request_queue { bool is_globally_ratelimited() const; }; -} // namespace dpp +} diff --git a/include/dpp/restrequest.h b/include/dpp/restrequest.h index 93fdd27071..2fcff85a77 100644 --- a/include/dpp/restrequest.h +++ b/include/dpp/restrequest.h @@ -294,4 +294,4 @@ template inline void rest_request_vector(dpp::cluster* c, const char* b } -} // namespace dpp +} diff --git a/include/dpp/restresults.h b/include/dpp/restresults.h index 3dd0ebb69c..a78b9e9dfe 100644 --- a/include/dpp/restresults.h +++ b/include/dpp/restresults.h @@ -156,6 +156,7 @@ typedef std::variant< ban_map, voiceregion, voiceregion_map, + voicestate, integration, integration_map, webhook, @@ -334,4 +335,4 @@ typedef std::function command_completion_e * @brief Automatically JSON encoded HTTP result */ typedef std::function json_encode_t; -} // namespace dpp +} diff --git a/include/dpp/role.h b/include/dpp/role.h index a3da13f367..1a76862823 100644 --- a/include/dpp/role.h +++ b/include/dpp/role.h @@ -994,5 +994,5 @@ typedef std::unordered_map role_map; */ typedef std::vector application_role_connection_metadata_list; -} // namespace dpp +} diff --git a/include/dpp/sku.h b/include/dpp/sku.h index f9d7c03908..7515a06260 100644 --- a/include/dpp/sku.h +++ b/include/dpp/sku.h @@ -173,4 +173,4 @@ class DPP_EXPORT sku : public managed, public json_interface { */ typedef std::unordered_map sku_map; -} // namespace dpp +} diff --git a/include/dpp/snowflake.h b/include/dpp/snowflake.h index 109f16a53a..871dca729b 100644 --- a/include/dpp/snowflake.h +++ b/include/dpp/snowflake.h @@ -269,7 +269,7 @@ class DPP_EXPORT snowflake final { } }; -} // namespace dpp +} template<> struct std::hash diff --git a/include/dpp/sslclient.h b/include/dpp/sslclient.h index e8e17e2c82..00371ef287 100644 --- a/include/dpp/sslclient.h +++ b/include/dpp/sslclient.h @@ -257,4 +257,4 @@ class DPP_EXPORT ssl_client virtual void log(dpp::loglevel severity, const std::string &msg) const; }; -} // namespace dpp +} diff --git a/include/dpp/stage_instance.h b/include/dpp/stage_instance.h index 6b0d2719a0..2bd90884a8 100644 --- a/include/dpp/stage_instance.h +++ b/include/dpp/stage_instance.h @@ -109,4 +109,4 @@ struct DPP_EXPORT stage_instance : public managed, public json_interface stage_instance_map; -} // namespace dpp +} diff --git a/include/dpp/stringops.h b/include/dpp/stringops.h index 9383fa4d06..cd99d9cc4f 100644 --- a/include/dpp/stringops.h +++ b/include/dpp/stringops.h @@ -220,4 +220,4 @@ template std::string leading_zeroes(T i, size_t width) return stream.str(); } -} // namespace dpp +} diff --git a/include/dpp/sync.h b/include/dpp/sync.h index 3c69f43a05..fdce669b2d 100644 --- a/include/dpp/sync.h +++ b/include/dpp/sync.h @@ -78,4 +78,4 @@ template T sync(class cluster* c, F func, Ts&& return _f.get(); } -} // namespace dpp +} diff --git a/include/dpp/timed_listener.h b/include/dpp/timed_listener.h index c5d7d0229b..d4dca777fc 100644 --- a/include/dpp/timed_listener.h +++ b/include/dpp/timed_listener.h @@ -102,4 +102,4 @@ template class timed_listene } }; -} // namespace dpp +} diff --git a/include/dpp/timer.h b/include/dpp/timer.h index e3e10943d1..c3dfbfa97e 100644 --- a/include/dpp/timer.h +++ b/include/dpp/timer.h @@ -129,4 +129,4 @@ class DPP_EXPORT oneshot_timer ~oneshot_timer(); }; -} // namespace dpp +} diff --git a/include/dpp/user.h b/include/dpp/user.h index cc7acadadd..57a8d73444 100644 --- a/include/dpp/user.h +++ b/include/dpp/user.h @@ -581,4 +581,4 @@ void from_json(const nlohmann::json& j, user_identified& u); */ typedef std::unordered_map user_map; -} // namespace dpp +} diff --git a/include/dpp/utility.h b/include/dpp/utility.h index 6654606fc3..cf6c417104 100644 --- a/include/dpp/utility.h +++ b/include/dpp/utility.h @@ -1066,4 +1066,4 @@ struct alignas(T) dummy { }; } // namespace utility -} // namespace dpp +} diff --git a/include/dpp/voiceregion.h b/include/dpp/voiceregion.h index 15d9306294..f3fd56055c 100644 --- a/include/dpp/voiceregion.h +++ b/include/dpp/voiceregion.h @@ -123,4 +123,4 @@ class DPP_EXPORT voiceregion : public json_interface { */ typedef std::unordered_map voiceregion_map; -} // namespace dpp +} diff --git a/include/dpp/voicestate.h b/include/dpp/voicestate.h index b5f9b55bcf..dc29652524 100644 --- a/include/dpp/voicestate.h +++ b/include/dpp/voicestate.h @@ -176,4 +176,4 @@ class DPP_EXPORT voicestate : public json_interface { /** A container of voicestates */ typedef std::unordered_map voicestate_map; -} // namespace dpp +} diff --git a/include/dpp/webhook.h b/include/dpp/webhook.h index 304565c26b..bef620814b 100644 --- a/include/dpp/webhook.h +++ b/include/dpp/webhook.h @@ -196,4 +196,4 @@ class DPP_EXPORT webhook : public managed, public json_interface { */ typedef std::unordered_map webhook_map; -} // namespace dpp +} diff --git a/include/dpp/wsclient.h b/include/dpp/wsclient.h index ee476c14f9..fbbf72dee7 100644 --- a/include/dpp/wsclient.h +++ b/include/dpp/wsclient.h @@ -237,4 +237,4 @@ class DPP_EXPORT websocket_client : public ssl_client { void send_close_packet(); }; -} // namespace dpp +} diff --git a/src/dpp/application.cpp b/src/dpp/application.cpp index 78908a03fe..1d2878d96d 100644 --- a/src/dpp/application.cpp +++ b/src/dpp/application.cpp @@ -21,10 +21,7 @@ ************************************************************************************/ #include #include -#include -#include #include -#include namespace dpp { @@ -94,6 +91,7 @@ application& application::fill_from_json_impl(nlohmann::json* j) { set_iconhash_not_null(j, "cover_image", cover_image); set_int32_not_null(j, "flags", flags); set_int64_not_null(j, "approximate_guild_count", approximate_guild_count); + set_int64_not_null(j, "approximate_user_install_count", approximate_user_install_count); if (j->contains("redirect_uris")) { for (const auto& uri : (*j)["redirect_uris"]) { @@ -167,5 +165,4 @@ std::string application::get_icon_url(uint16_t size, const image_type format) co return ""; } -} // namespace dpp - +} diff --git a/src/dpp/auditlog.cpp b/src/dpp/auditlog.cpp index 9abc95f121..2dcf8fb99f 100644 --- a/src/dpp/auditlog.cpp +++ b/src/dpp/auditlog.cpp @@ -74,5 +74,5 @@ auditlog& auditlog::fill_from_json_impl(nlohmann::json* j) { return *this; } -} // namespace dpp +} diff --git a/src/dpp/automod.cpp b/src/dpp/automod.cpp index 219d347859..0492f49a97 100644 --- a/src/dpp/automod.cpp +++ b/src/dpp/automod.cpp @@ -186,5 +186,5 @@ json automod_rule::to_json_impl(bool with_id) const { return j; } -} // namespace dpp +} diff --git a/src/dpp/ban.cpp b/src/dpp/ban.cpp index aee056b68e..a697b35c70 100644 --- a/src/dpp/ban.cpp +++ b/src/dpp/ban.cpp @@ -40,5 +40,5 @@ ban& ban::fill_from_json_impl(nlohmann::json* j) { return *this; } -} // namespace dpp +} diff --git a/src/dpp/cache.cpp b/src/dpp/cache.cpp index 715b829dd8..256e4e16f0 100644 --- a/src/dpp/cache.cpp +++ b/src/dpp/cache.cpp @@ -21,10 +21,8 @@ ************************************************************************************/ #include #include -#include #include #include -#include namespace dpp { @@ -85,4 +83,4 @@ cache_helper(role, role_cache, find_role, get_role_cache, get_role_count); cache_helper(guild, guild_cache, find_guild, get_guild_cache, get_guild_count); cache_helper(emoji, emoji_cache, find_emoji, get_emoji_cache, get_emoji_count); -} // namespace dpp +} diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index 4903b60f81..a12be0f59d 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -580,4 +579,4 @@ forum_layout_type channel::get_default_forum_layout() const { } -} // namespace dpp +} diff --git a/src/dpp/cluster.cpp b/src/dpp/cluster.cpp index 26e333d194..90d4cb12c0 100644 --- a/src/dpp/cluster.cpp +++ b/src/dpp/cluster.cpp @@ -21,17 +21,9 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include namespace dpp { diff --git a/src/dpp/cluster/appcommand.cpp b/src/dpp/cluster/appcommand.cpp index 80c3308447..ae131da0ab 100644 --- a/src/dpp/cluster/appcommand.cpp +++ b/src/dpp/cluster/appcommand.cpp @@ -191,4 +191,4 @@ void cluster::interaction_followup_get_original(const std::string &token, comman rest_request(this, API_PATH "/webhooks",std::to_string(me.id), utility::url_encode(token) + "/messages/@original", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/automod.cpp b/src/dpp/cluster/automod.cpp index f4584baab7..c0d2d534aa 100644 --- a/src/dpp/cluster/automod.cpp +++ b/src/dpp/cluster/automod.cpp @@ -43,4 +43,4 @@ void cluster::automod_rule_delete(snowflake guild_id, snowflake rule_id, command rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/auto-moderation/rules/" + std::to_string(rule_id), m_delete, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/channel.cpp b/src/dpp/cluster/channel.cpp index 4e0e41cc80..83e317f71c 100644 --- a/src/dpp/cluster/channel.cpp +++ b/src/dpp/cluster/channel.cpp @@ -101,4 +101,4 @@ void cluster::channel_set_voice_status(snowflake channel_id, const std::string& rest_request(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 +} diff --git a/src/dpp/cluster/confirmation.cpp b/src/dpp/cluster/confirmation.cpp index 07072ccaf2..6e48b138a2 100644 --- a/src/dpp/cluster/confirmation.cpp +++ b/src/dpp/cluster/confirmation.cpp @@ -161,4 +161,4 @@ error_info confirmation_callback_t::get_error() const { return {}; } -} // namespace dpp +} diff --git a/src/dpp/cluster/dm.cpp b/src/dpp/cluster/dm.cpp index cba8af9654..be895f7690 100644 --- a/src/dpp/cluster/dm.cpp +++ b/src/dpp/cluster/dm.cpp @@ -63,4 +63,4 @@ void cluster::gdm_remove(snowflake channel_id, snowflake user_id, command_comple rest_request(this, API_PATH "/channels", std::to_string(channel_id), "recipients/" + std::to_string(user_id), m_delete, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/emoji.cpp b/src/dpp/cluster/emoji.cpp index 626707d13c..9e2f2b8461 100644 --- a/src/dpp/cluster/emoji.cpp +++ b/src/dpp/cluster/emoji.cpp @@ -81,4 +81,4 @@ void cluster::application_emoji_delete(snowflake emoji_id, command_completion_ev rest_request(this, API_PATH "/applications", me.id.str(), "emojis/" + emoji_id.str(), m_delete, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/entitlement.cpp b/src/dpp/cluster/entitlement.cpp index d2b120772e..fa96e4c2b1 100644 --- a/src/dpp/cluster/entitlement.cpp +++ b/src/dpp/cluster/entitlement.cpp @@ -81,4 +81,4 @@ void cluster::entitlement_consume(const class snowflake entitlement_id, command_ rest_request(this, API_PATH "/applications", me.id.str(), "entitlements/" + entitlement_id.str() + "/consume", m_post, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/gateway.cpp b/src/dpp/cluster/gateway.cpp index 7da7a81dd4..85bcfd6ca1 100644 --- a/src/dpp/cluster/gateway.cpp +++ b/src/dpp/cluster/gateway.cpp @@ -26,4 +26,4 @@ void cluster::get_gateway_bot(command_completion_event_t callback) { rest_request(this, API_PATH "/gateway", "bot", "", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/guild.cpp b/src/dpp/cluster/guild.cpp index bf58372f69..75396c907b 100644 --- a/src/dpp/cluster/guild.cpp +++ b/src/dpp/cluster/guild.cpp @@ -169,4 +169,4 @@ void cluster::guild_edit_welcome_screen(snowflake guild_id, const struct welcome } -} // namespace dpp +} diff --git a/src/dpp/cluster/guild_member.cpp b/src/dpp/cluster/guild_member.cpp index 28e3b953b0..8ab690ea94 100644 --- a/src/dpp/cluster/guild_member.cpp +++ b/src/dpp/cluster/guild_member.cpp @@ -152,4 +152,4 @@ void cluster::guild_search_members(snowflake guild_id, const std::string& query, }); } -} // namespace dpp +} diff --git a/src/dpp/cluster/invite.cpp b/src/dpp/cluster/invite.cpp index 5d73f6aed7..1e03ed6555 100644 --- a/src/dpp/cluster/invite.cpp +++ b/src/dpp/cluster/invite.cpp @@ -35,4 +35,4 @@ void cluster::invite_get(const std::string &invite_code, command_completion_even rest_request(this, API_PATH "/invites", utility::url_encode(invite_code) + "?with_counts=true&with_expiration=true", "", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/json_interface.cpp b/src/dpp/cluster/json_interface.cpp index 76863321ce..3af8435841 100644 --- a/src/dpp/cluster/json_interface.cpp +++ b/src/dpp/cluster/json_interface.cpp @@ -25,4 +25,4 @@ namespace dpp { -} // namespace dpp +} diff --git a/src/dpp/cluster/message.cpp b/src/dpp/cluster/message.cpp index e1d4943b59..b1922902a1 100644 --- a/src/dpp/cluster/message.cpp +++ b/src/dpp/cluster/message.cpp @@ -208,4 +208,4 @@ void cluster::channel_pins_get(snowflake channel_id, command_completion_event_t rest_request_list(this, API_PATH "/channels", std::to_string(channel_id), "pins", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/role.cpp b/src/dpp/cluster/role.cpp index 2a2065a8f2..371c6256bb 100644 --- a/src/dpp/cluster/role.cpp +++ b/src/dpp/cluster/role.cpp @@ -70,4 +70,4 @@ void cluster::user_application_role_connection_update(snowflake application_id, rest_request(this, API_PATH "/users/@me/applications", std::to_string(application_id), "role-connection", m_put, connection.build_json(), callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/scheduled_event.cpp b/src/dpp/cluster/scheduled_event.cpp index ddc7ea16c6..fbf91ca18c 100644 --- a/src/dpp/cluster/scheduled_event.cpp +++ b/src/dpp/cluster/scheduled_event.cpp @@ -68,4 +68,4 @@ void cluster::guild_event_get(snowflake guild_id, snowflake event_id, command_co rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/scheduled-events/" + std::to_string(event_id) + "?with_user_count=true", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/sku.cpp b/src/dpp/cluster/sku.cpp index 20f23f3e77..2c44c30c01 100644 --- a/src/dpp/cluster/sku.cpp +++ b/src/dpp/cluster/sku.cpp @@ -27,4 +27,4 @@ void cluster::skus_get(command_completion_event_t callback) { rest_request_list(this, API_PATH "/applications", me.id.str(), "entitlements", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/stage_instance.cpp b/src/dpp/cluster/stage_instance.cpp index 9257f90a6d..ac9146d568 100644 --- a/src/dpp/cluster/stage_instance.cpp +++ b/src/dpp/cluster/stage_instance.cpp @@ -39,4 +39,4 @@ void cluster::stage_instance_delete(const snowflake channel_id, command_completi rest_request(this, API_PATH "/stage-instances", std::to_string(channel_id), "", m_delete, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/sticker.cpp b/src/dpp/cluster/sticker.cpp index 9f61521a44..bc5ba5a0b5 100644 --- a/src/dpp/cluster/sticker.cpp +++ b/src/dpp/cluster/sticker.cpp @@ -55,4 +55,4 @@ void cluster::sticker_packs_get(command_completion_event_t callback) { rest_request_list(this, API_PATH "/sticker-packs", "", "", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/template.cpp b/src/dpp/cluster/template.cpp index f0503ff83f..bf01b136a2 100644 --- a/src/dpp/cluster/template.cpp +++ b/src/dpp/cluster/template.cpp @@ -60,4 +60,4 @@ void cluster::template_get(const std::string &code, command_completion_event_t c rest_request(this, API_PATH "/guilds", "templates", code, m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/thread.cpp b/src/dpp/cluster/thread.cpp index b3c109406e..bf2205b51b 100644 --- a/src/dpp/cluster/thread.cpp +++ b/src/dpp/cluster/thread.cpp @@ -191,4 +191,4 @@ void cluster::thread_get(snowflake thread_id, command_completion_event_t callbac rest_request(this, API_PATH "/channels", std::to_string(thread_id), "", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/timer.cpp b/src/dpp/cluster/timer.cpp index e6d7df1814..a6520306f2 100644 --- a/src/dpp/cluster/timer.cpp +++ b/src/dpp/cluster/timer.cpp @@ -146,4 +146,4 @@ oneshot_timer::~oneshot_timer() { cancel(); } -} // namespace dpp +} diff --git a/src/dpp/cluster/user.cpp b/src/dpp/cluster/user.cpp index 671fdb64a9..616c5c101f 100644 --- a/src/dpp/cluster/user.cpp +++ b/src/dpp/cluster/user.cpp @@ -81,6 +81,10 @@ void cluster::current_user_set_voice_state(snowflake guild_id, snowflake channel rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/voice-states/@me", m_patch, j.dump(-1, ' ', false, json::error_handler_t::replace), callback); } +void cluster::current_user_get_voice_state(snowflake guild_id, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/voice-states/@me", m_get, "", callback); +} + void cluster::user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress, command_completion_event_t callback) { json j({ {"channel_id", channel_id}, @@ -89,6 +93,10 @@ void cluster::user_set_voice_state(snowflake user_id, snowflake guild_id, snowfl rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/voice-states/" + std::to_string(user_id), m_patch, j.dump(-1, ' ', false, json::error_handler_t::replace), callback); } +void cluster::user_get_voice_state(snowflake guild_id, snowflake user_id, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/voice-states/" + std::to_string(user_id), m_get, "", callback); +} + void cluster::current_user_connections_get(command_completion_event_t callback) { rest_request_list(this, API_PATH "/users", "@me", "connections", m_get, "", callback); } @@ -121,4 +129,4 @@ void cluster::user_get_cached(snowflake user_id, command_completion_event_t call rest_request(this, API_PATH "/users", std::to_string(user_id), "", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/voice.cpp b/src/dpp/cluster/voice.cpp index ea78a487a4..c99bc45303 100644 --- a/src/dpp/cluster/voice.cpp +++ b/src/dpp/cluster/voice.cpp @@ -32,4 +32,4 @@ void cluster::guild_get_voice_regions(snowflake guild_id, command_completion_eve rest_request_list(this, API_PATH "/guilds", std::to_string(guild_id), "regions", m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 5231d19e40..8ebf9370e4 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -117,4 +117,4 @@ void cluster::get_webhook_with_token(snowflake webhook_id, const std::string &to rest_request(this, API_PATH "/webhooks", std::to_string(webhook_id), utility::url_encode(token), m_get, "", callback); } -} // namespace dpp +} diff --git a/src/dpp/cluster_coro_calls.cpp b/src/dpp/cluster_coro_calls.cpp index d56b3b1013..3ec3aeff93 100644 --- a/src/dpp/cluster_coro_calls.cpp +++ b/src/dpp/cluster_coro_calls.cpp @@ -783,10 +783,18 @@ async cluster::co_current_user_set_voice_state(snowflak return async{ this, static_cast(&cluster::current_user_set_voice_state), guild_id, channel_id, suppress, request_to_speak_timestamp }; } +async cluster::co_current_user_get_voice_state(snowflake guild_id) { + return async{ this, static_cast(&cluster::current_user_get_voice_state), guild_id }; +} + async cluster::co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress) { return async{ this, static_cast(&cluster::user_set_voice_state), user_id, guild_id, channel_id, suppress }; } +async cluster::co_user_get_voice_state(snowflake guild_id, snowflake user_id) { + return async{ this, static_cast(&cluster::user_get_voice_state), guild_id, user_id }; +} + async cluster::co_current_user_connections_get() { return async{ this, static_cast(&cluster::current_user_connections_get) }; } diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index 8aa69aba77..c49c9d9a3e 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -781,10 +781,18 @@ confirmation cluster::current_user_set_voice_state_sync(snowflake guild_id, snow return dpp::sync(this, static_cast(&cluster::current_user_set_voice_state), guild_id, channel_id, suppress, request_to_speak_timestamp); } +voicestate cluster::current_user_get_voice_state_sync(snowflake guild_id) { + return dpp::sync(this, static_cast(&cluster::current_user_get_voice_state), guild_id); +} + confirmation cluster::user_set_voice_state_sync(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress) { return dpp::sync(this, static_cast(&cluster::user_set_voice_state), user_id, guild_id, channel_id, suppress); } +voicestate cluster::user_get_voice_state_sync(snowflake guild_id, snowflake user_id) { + return dpp::sync(this, static_cast(&cluster::user_get_voice_state), guild_id, user_id); +} + connection_map cluster::current_user_connections_get_sync() { return dpp::sync(this, static_cast(&cluster::current_user_connections_get)); } diff --git a/src/dpp/commandhandler.cpp b/src/dpp/commandhandler.cpp index e337b0eee9..9101bbd477 100644 --- a/src/dpp/commandhandler.cpp +++ b/src/dpp/commandhandler.cpp @@ -20,7 +20,6 @@ ************************************************************************************/ #include #include -#include #include #include #include @@ -437,4 +436,4 @@ void commandhandler::thonk(command_source source, command_completion_event_t cal thinking(source, callback); } -} // namespace dpp +} diff --git a/src/dpp/discordclient.cpp b/src/dpp/discordclient.cpp index 09e5258ece..cf43b76fca 100644 --- a/src/dpp/discordclient.cpp +++ b/src/dpp/discordclient.cpp @@ -20,7 +20,6 @@ * ************************************************************************************/ #include -#include #include #include #include @@ -30,18 +29,6 @@ #include #include #include -#ifdef _WIN32 - #include - #include - #include -#else - #include - #include - #include - #include - #include - #include -#endif #define PATH_UNCOMPRESSED_JSON "/?v=" DISCORD_API_VERSION "&encoding=json" #define PATH_COMPRESSED_JSON "/?v=" DISCORD_API_VERSION "&encoding=json&compress=zlib-stream" @@ -732,4 +719,4 @@ voiceconn& voiceconn::connect(snowflake guild_id) { } -} // namespace dpp +} diff --git a/src/dpp/discordevents.cpp b/src/dpp/discordevents.cpp index f3e2aebb68..fb58faf610 100644 --- a/src/dpp/discordevents.cpp +++ b/src/dpp/discordevents.cpp @@ -22,7 +22,7 @@ /* OpenBSD errors when xopen_source is defined. * We want to make sure that OpenBSD does not define it. */ -#if !defined(__OpenBSD__) +#ifndef __OpenBSD__ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE #endif @@ -35,11 +35,7 @@ #include #include #include -#include -#include -#include #include -#include #include #include @@ -439,4 +435,4 @@ void discord_client::handle_event(const std::string &event, json &j, const std:: } } -} // namespace dpp +} diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 1b8fe7b95f..5b241d1122 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -20,16 +20,7 @@ * ************************************************************************************/ -#ifdef _WIN32 - #include - #include - #include -#else - #include - #include - #include -#endif -#include +#include #include #include #include @@ -148,6 +139,9 @@ bool discord_voice_client::is_end_to_end_encrypted() const { discord_voice_client& discord_voice_client::pause_audio(bool pause) { this->paused = pause; + if (!this->paused) { + this->sent_stop_frames = false; + } return *this; } @@ -172,10 +166,13 @@ dpp::utility::uptime discord_voice_client::get_remaining() { } discord_voice_client& discord_voice_client::stop_audio() { - std::lock_guard lock(this->stream_mutex); - outbuf.clear(); - track_meta.clear(); - tracks = 0; + { + std::lock_guard lock(this->stream_mutex); + outbuf.clear(); + track_meta.clear(); + tracks = 0; + } + this->send_stop_frames(); return *this; } @@ -398,7 +395,6 @@ discord_voice_client& discord_voice_client::skip_to_next_marker() { } discord_voice_client& discord_voice_client::send_silence(const uint64_t duration) { - uint8_t silence_packet[3] = { 0xf8, 0xff, 0xfe }; send_audio_opus(silence_packet, 3, duration); return *this; } @@ -443,4 +439,15 @@ uint16_t discord_voice_client::get_iteration_interval() { return this->iteration_interval; } -} // namespace dpp +discord_voice_client& discord_voice_client::send_stop_frames(bool send_now) { + uint8_t silence_frames[sizeof(silence_packet) / sizeof(*silence_packet) * 5]; + for (size_t i = 0; i < sizeof(silence_frames) / sizeof(*silence_frames); i++) { + silence_frames[i] = silence_packet[i % 3]; + } + + this->send_audio_opus(silence_frames, sizeof(silence_frames) / sizeof(*silence_frames), 20, send_now); + + return *this; +} + +} diff --git a/src/dpp/dispatcher.cpp b/src/dpp/dispatcher.cpp index 6ac9775d40..db8ea2e888 100644 --- a/src/dpp/dispatcher.cpp +++ b/src/dpp/dispatcher.cpp @@ -286,4 +286,4 @@ void voice_receive_t::reassign(discord_voice_client* vc, snowflake _user_id, con audio_size = audio_data.size(); } -} // namespace dpp +} diff --git a/src/dpp/dns.cpp b/src/dpp/dns.cpp index 257b9b2315..42aed3ce8f 100644 --- a/src/dpp/dns.cpp +++ b/src/dpp/dns.cpp @@ -21,7 +21,6 @@ ************************************************************************************/ #include -#include #include #include #include diff --git a/src/dpp/dtemplate.cpp b/src/dpp/dtemplate.cpp index 8f67e0efd6..1ed6f53905 100644 --- a/src/dpp/dtemplate.cpp +++ b/src/dpp/dtemplate.cpp @@ -57,4 +57,4 @@ json dtemplate::to_json_impl(bool with_id) const { }; } -} // namespace dpp +} diff --git a/src/dpp/emoji.cpp b/src/dpp/emoji.cpp index b8d0350e20..4d90982e64 100644 --- a/src/dpp/emoji.cpp +++ b/src/dpp/emoji.cpp @@ -22,7 +22,6 @@ #include #include #include -#include namespace dpp { @@ -130,5 +129,5 @@ std::string emoji::get_url(uint16_t size, const dpp::image_type format, bool pre } -} // namespace dpp +} diff --git a/src/dpp/entitlement.cpp b/src/dpp/entitlement.cpp index f3ee034a04..b30871ce58 100644 --- a/src/dpp/entitlement.cpp +++ b/src/dpp/entitlement.cpp @@ -83,4 +83,4 @@ bool entitlement::is_consumed() const { return flags & entitlement_flags::ent_consumed; } -} // namespace dpp +} diff --git a/src/dpp/etf.cpp b/src/dpp/etf.cpp index a76f5d9c41..2b4c25f657 100644 --- a/src/dpp/etf.cpp +++ b/src/dpp/etf.cpp @@ -33,8 +33,6 @@ ************************************************************************************/ #include #include -#include -#include #include #include #include @@ -729,5 +727,5 @@ etf_buffer::etf_buffer(size_t initial) { etf_buffer::~etf_buffer() = default; -} // namespace dpp +} diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 7751d6abea..0cecaaea4c 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -20,9 +20,6 @@ ************************************************************************************/ #include #include -#include -#include -#include #include #include #include @@ -1196,4 +1193,4 @@ onboarding &onboarding::set_enabled(const bool is_enabled) { } -} // namespace dpp +} diff --git a/src/dpp/httpsclient.cpp b/src/dpp/httpsclient.cpp index 9ac8faa021..8c7cff3258 100644 --- a/src/dpp/httpsclient.cpp +++ b/src/dpp/httpsclient.cpp @@ -27,8 +27,6 @@ #include #include #include -#include -#include namespace dpp { @@ -358,4 +356,4 @@ http_connect_info https_client::get_host_info(std::string url) { return hci; } -} // namespace dpp +} diff --git a/src/dpp/integration.cpp b/src/dpp/integration.cpp index c8e870fde1..e2cd59b51f 100644 --- a/src/dpp/integration.cpp +++ b/src/dpp/integration.cpp @@ -25,8 +25,6 @@ #include #include - - namespace dpp { using json = nlohmann::json; @@ -150,4 +148,4 @@ connection& connection::fill_from_json_impl(nlohmann::json* j) { return *this; } -} // namespace dpp +} diff --git a/src/dpp/invite.cpp b/src/dpp/invite.cpp index c063ac4aed..b33435f6e9 100644 --- a/src/dpp/invite.cpp +++ b/src/dpp/invite.cpp @@ -23,8 +23,6 @@ #include #include - - namespace dpp { using json = nlohmann::json; @@ -117,4 +115,4 @@ invite &invite::set_unique(const bool is_unique) { return *this; } -} // namespace dpp +} diff --git a/src/dpp/message.cpp b/src/dpp/message.cpp index 8c7efa1fe4..cb96f4edaa 100644 --- a/src/dpp/message.cpp +++ b/src/dpp/message.cpp @@ -1548,4 +1548,4 @@ sticker& sticker::set_file_content(std::string_view fc) { } -} // namespace dpp +} diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp index 02dd8325d7..58eefaa3ce 100644 --- a/src/dpp/permissions.cpp +++ b/src/dpp/permissions.cpp @@ -27,4 +27,4 @@ permission::operator nlohmann::json() const { return std::to_string(value); } -} // namespace dpp +} diff --git a/src/dpp/presence.cpp b/src/dpp/presence.cpp index 34809b1c4c..7b40dc9a0d 100644 --- a/src/dpp/presence.cpp +++ b/src/dpp/presence.cpp @@ -21,12 +21,8 @@ ************************************************************************************/ #include #include -#include -#include #include - - namespace dpp { using json = nlohmann::json; @@ -35,8 +31,8 @@ std::string activity::get_large_asset_url(uint16_t size, const image_type format if (!this->assets.large_image.empty() && this->application_id && this->assets.large_image.find(':') == std::string::npos) { // make sure it's not a prefixed proxy image return utility::cdn_endpoint_url({ i_jpg, i_png, i_webp }, - "app-assets/" + std::to_string(this->application_id) + "/" + this->assets.large_image, - format, size); + "app-assets/" + std::to_string(this->application_id) + "/" + this->assets.large_image, + format, size); } else { return std::string(); } @@ -46,8 +42,8 @@ std::string activity::get_small_asset_url(uint16_t size, const image_type format if (!this->assets.small_image.empty() && this->application_id && this->assets.small_image.find(':') == std::string::npos) { // make sure it's not a prefixed proxy image return utility::cdn_endpoint_url({ i_jpg, i_png, i_webp }, - "app-assets/" + std::to_string(this->application_id) + "/" + this->assets.small_image, - format, size); + "app-assets/" + std::to_string(this->application_id) + "/" + this->assets.small_image, + format, size); } else { return std::string(); } @@ -308,4 +304,4 @@ presence_status presence::status() const { return (presence_status)((flags >> PF_SHIFT_MAIN) & PF_STATUS_MASK); } -} // namespace dpp +} diff --git a/src/dpp/prune.cpp b/src/dpp/prune.cpp index afd7f9ae52..befea860f0 100644 --- a/src/dpp/prune.cpp +++ b/src/dpp/prune.cpp @@ -51,4 +51,4 @@ json prune::to_json(bool with_id) const { return to_json_impl(with_id); } -} // namespace dpp +} diff --git a/src/dpp/queues.cpp b/src/dpp/queues.cpp index 8505606556..96aa1d5288 100644 --- a/src/dpp/queues.cpp +++ b/src/dpp/queues.cpp @@ -28,7 +28,6 @@ #include #include #include -#include namespace dpp { @@ -480,4 +479,4 @@ bool request_queue::is_globally_ratelimited() const return this->globally_ratelimited; } -} // namespace dpp +} diff --git a/src/dpp/role.cpp b/src/dpp/role.cpp index 89643f210f..877cf6d0a4 100644 --- a/src/dpp/role.cpp +++ b/src/dpp/role.cpp @@ -23,12 +23,8 @@ #include #include #include -#include -#include #include - - namespace dpp { using json = nlohmann::json; @@ -485,4 +481,4 @@ json application_role_connection::to_json_impl(bool with_id) const { } -} // namespace dpp +} diff --git a/src/dpp/scheduled_event.cpp b/src/dpp/scheduled_event.cpp index fe7e2c7a4c..2af52a4e5f 100644 --- a/src/dpp/scheduled_event.cpp +++ b/src/dpp/scheduled_event.cpp @@ -19,7 +19,6 @@ * ************************************************************************************/ #include -#include #include #include #include @@ -193,4 +192,4 @@ json scheduled_event::to_json_impl(bool with_id) const { return j; } -} // namespace dpp +} diff --git a/src/dpp/sku.cpp b/src/dpp/sku.cpp index 170a158159..52b0b62161 100644 --- a/src/dpp/sku.cpp +++ b/src/dpp/sku.cpp @@ -86,4 +86,4 @@ bool sku::is_user_subscription() const { return flags & sku_flags::sku_user_subscription; } -} // namespace dpp +} diff --git a/src/dpp/slashcommand.cpp b/src/dpp/slashcommand.cpp index 77dd4bfebe..782393ccde 100644 --- a/src/dpp/slashcommand.cpp +++ b/src/dpp/slashcommand.cpp @@ -20,7 +20,6 @@ ************************************************************************************/ #include #include -#include #include #include #include @@ -941,4 +940,4 @@ std::string command_interaction::get_mention() const { std::string slashcommand::get_mention() const { return dpp::utility::slashcommand_mention(id, name); } -} // namespace dpp +} diff --git a/src/dpp/snowflake.cpp b/src/dpp/snowflake.cpp index 72d566756c..2257b3158c 100644 --- a/src/dpp/snowflake.cpp +++ b/src/dpp/snowflake.cpp @@ -44,4 +44,4 @@ snowflake::operator json() const { return std::to_string(value); } -} // namespace dpp +} diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index 54698eb0e4..7cdf92150e 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -37,20 +37,13 @@ #else /* Anyting other than Windows (e.g. sane OSes) */ #include - #include - #include - #include #include - #include #include #endif -#include -#include -#include +#include #include #include -#include -#include +#include #include #include /* Windows specific OpenSSL symbol weirdness */ @@ -676,4 +669,4 @@ ssl_client::~ssl_client() cleanup(); } -} // namespace dpp +} diff --git a/src/dpp/stage_instance.cpp b/src/dpp/stage_instance.cpp index 4fde6c9dd3..dc11433e97 100644 --- a/src/dpp/stage_instance.cpp +++ b/src/dpp/stage_instance.cpp @@ -55,4 +55,4 @@ json stage_instance::to_json_impl(bool with_id) const { return j; } -} // namespace dpp +} diff --git a/src/dpp/thread.cpp b/src/dpp/thread.cpp index af661fe663..5ffc1f44f7 100644 --- a/src/dpp/thread.cpp +++ b/src/dpp/thread.cpp @@ -22,7 +22,6 @@ #include #include - namespace dpp { thread_member& thread_member::fill_from_json_impl(nlohmann::json* j) { diff --git a/src/dpp/user.cpp b/src/dpp/user.cpp index 64f21ae898..7f2d08cf4d 100644 --- a/src/dpp/user.cpp +++ b/src/dpp/user.cpp @@ -24,8 +24,8 @@ #include namespace dpp { -using json = nlohmann:: -json; + +using json = nlohmann::json; /* A mapping of discord's flag values to our bitmap (they're different bit positions to fit other stuff in) */ std::map usermap = { @@ -46,12 +46,7 @@ std::map usermap = { { 1 << 22, dpp::u_active_developer}, }; -user::user() : - managed(), - flags(0), - discriminator(0), - refcount(1) -{ +user::user() : managed(), flags(0), discriminator(0), refcount(1) { } std::string user::get_mention(const snowflake& id) { @@ -98,8 +93,8 @@ std::string user::get_avatar_url(uint16_t size, const image_type format, bool pr return get_default_avatar_url(); } else if (this->id) { return utility::cdn_endpoint_url_hash({ i_jpg, i_png, i_webp, i_gif }, - "avatars/" + std::to_string(this->id), this->avatar.to_string(), - format, size, prefer_animated, has_animated_icon()); + "avatars/" + std::to_string(this->id), this->avatar.to_string(), + format, size, prefer_animated, has_animated_icon()); } else { return std::string(); } @@ -108,12 +103,12 @@ std::string user::get_avatar_url(uint16_t size, const image_type format, bool pr std::string user::get_default_avatar_url() const { if (this->discriminator) { return utility::cdn_endpoint_url({ i_png }, - "embed/avatars/" + std::to_string(this->discriminator % 5), - i_png, 0); + "embed/avatars/" + std::to_string(this->discriminator % 5), + i_png, 0); } else if (this->id){ return utility::cdn_endpoint_url({ i_png }, - "embed/avatars/" + std::to_string((this->id >> 22) % 6), - i_png, 0); + "embed/avatars/" + std::to_string((this->id >> 22) % 6), + i_png, 0); } else { return std::string(); } @@ -122,8 +117,8 @@ std::string user::get_default_avatar_url() const { std::string user::get_avatar_decoration_url(uint16_t size) const { if (this->id) { return utility::cdn_endpoint_url_hash({ i_png }, - "avatar-decorations/" + std::to_string(this->id), this->avatar_decoration.to_string(), - i_png, size); + "avatar-decorations/" + std::to_string(this->id), this->avatar_decoration.to_string(), + i_png, size); } else { return std::string(); } @@ -253,8 +248,8 @@ bool user_identified::has_animated_banner() const { std::string user_identified::get_banner_url(uint16_t size, const image_type format, bool prefer_animated) const { if (!this->banner.to_string().empty() && this->id) { return utility::cdn_endpoint_url_hash({ i_jpg, i_png, i_webp, i_gif }, - "banners/" + std::to_string(this->id), this->banner.to_string(), - format, size, prefer_animated, has_animated_banner()); + "banners/" + std::to_string(this->id), this->banner.to_string(), + format, size, prefer_animated, has_animated_banner()); } else { return std::string(); } @@ -309,4 +304,4 @@ void from_json(const nlohmann::json& j, user& u) { } } -} // namespace dpp +} diff --git a/src/dpp/utility.cpp b/src/dpp/utility.cpp index b41be262fa..922fc4195e 100644 --- a/src/dpp/utility.cpp +++ b/src/dpp/utility.cpp @@ -21,7 +21,6 @@ ************************************************************************************/ #include #include -#include #include #include #include @@ -34,9 +33,6 @@ #include #include #include -#include -#include -#include #ifdef _WIN32 #include @@ -936,4 +932,4 @@ void set_thread_name(const std::string& name) { #endif } -} // namespace dpp::utility +} diff --git a/src/dpp/voice/enabled/audio_mix.cpp b/src/dpp/voice/enabled/audio_mix.cpp index 6d0f128e8c..c43d712313 100644 --- a/src/dpp/voice/enabled/audio_mix.cpp +++ b/src/dpp/voice/enabled/audio_mix.cpp @@ -21,14 +21,12 @@ ************************************************************************************/ #include -#include #include #include #include #include #include #include -#include #include namespace dpp { diff --git a/src/dpp/voice/enabled/cleanup.cpp b/src/dpp/voice/enabled/cleanup.cpp index 838000e884..5ae1d9c7e2 100644 --- a/src/dpp/voice/enabled/cleanup.cpp +++ b/src/dpp/voice/enabled/cleanup.cpp @@ -25,10 +25,8 @@ #include #include #include - #include #include "../../dave/encryptor.h" - #include "enabled.h" namespace dpp { diff --git a/src/dpp/voice/enabled/courier_loop.cpp b/src/dpp/voice/enabled/courier_loop.cpp index 6526c7f8ba..72ccccdbfc 100644 --- a/src/dpp/voice/enabled/courier_loop.cpp +++ b/src/dpp/voice/enabled/courier_loop.cpp @@ -21,6 +21,7 @@ ************************************************************************************/ #include +#include #include #include #include @@ -34,6 +35,7 @@ namespace dpp { void discord_voice_client::voice_courier_loop(discord_voice_client& client, courier_shared_state_t& shared_state) { utility::set_thread_name(std::string("vcourier/") + std::to_string(client.server_id)); + while (true) { std::this_thread::sleep_for(std::chrono::milliseconds{client.iteration_interval}); @@ -59,13 +61,17 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour bool has_payload_to_deliver = false; for (auto &[user_id, parking_lot]: shared_state.parked_voice_payloads) { has_payload_to_deliver = has_payload_to_deliver || !parking_lot.parked_payloads.empty(); - flush_data.push_back(flush_data_t{user_id, - parking_lot.range.min_seq, - std::move(parking_lot.parked_payloads), + + flush_data.push_back(flush_data_t{ + user_id, + parking_lot.range.min_seq, + std::move(parking_lot.parked_payloads), /* Quickly check if we already have a decoder and only take the pending ctls if so. */ - parking_lot.decoder ? std::move(parking_lot.pending_decoder_ctls) - : decltype(parking_lot.pending_decoder_ctls){}, - parking_lot.decoder}); + parking_lot.decoder ? std::move(parking_lot.pending_decoder_ctls) + : decltype(parking_lot.pending_decoder_ctls){}, + parking_lot.decoder + }); + parking_lot.range.min_seq = parking_lot.range.max_seq + 1; parking_lot.range.min_timestamp = parking_lot.range.max_timestamp + 1; } @@ -76,7 +82,24 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour break; } - shared_state.signal_iteration.wait(lk); + shared_state.signal_iteration.wait(lk, [&shared_state](){ + if (shared_state.terminating) { + return true; + } + + /* + * Actually check the state we're looking for instead of waking up + * everytime read_ready was called. + */ + for (auto &[user_id, parking_lot]: shared_state.parked_voice_payloads) { + if (parking_lot.parked_payloads.empty()) { + continue; + } + return true; + } + return false; + }); + /* * More data came or about to terminate, or just a spurious wake. * We need to collect the payloads again to determine what to do next. @@ -95,13 +118,14 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour /* This 32 bit PCM audio buffer is an upmixed version of the streams * combined for all users. This is a wider width audio buffer so that - * there is no clipping when there are many loud audio sources at once. + * there is no clipping when there are many loud audio sources at once. */ opus_int32 pcm_mix[23040] = {0}; size_t park_count = 0; int max_samples = 0; int samples = 0; + opus_int16 flush_data_pcm[23040]; for (auto &d: flush_data) { if (!d.decoder) { continue; @@ -109,37 +133,133 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour for (const auto &decoder_ctl: d.pending_decoder_ctls) { decoder_ctl(*d.decoder); } + for (rtp_seq_t seq = d.min_seq; !d.parked_payloads.empty(); ++seq) { - opus_int16 pcm[23040]; if (d.parked_payloads.top().seq != seq) { /* * Lost a packet with sequence number "seq", * But Opus decoder might be able to guess something. */ - if (int samples = opus_decode(d.decoder.get(), nullptr, 0, pcm, 5760, 0); - samples >= 0) { + if (int lost_packet_samples = opus_decode(d.decoder.get(), nullptr, 0, flush_data_pcm, 5760, 0); + lost_packet_samples >= 0) { /* * Since this sample comes from a lost packet, * we can only pretend there is an event, without any raw payload byte. */ - voice_receive_t vr(nullptr, "", &client, d.user_id, reinterpret_cast(pcm), - samples * opus_channel_count * sizeof(opus_int16)); + voice_receive_t vr(nullptr, "", &client, d.user_id, + reinterpret_cast(flush_data_pcm), + lost_packet_samples * opus_channel_count * sizeof(opus_int16)); - park_count = audio_mix(client, *client.mixer, pcm_mix, pcm, park_count, samples, max_samples); + park_count = audio_mix(client, *client.mixer, pcm_mix, flush_data_pcm, park_count, lost_packet_samples, max_samples); client.creator->on_voice_receive.call(vr); } } else { voice_receive_t &vr = *d.parked_payloads.top().vr; - if (vr.audio_data.size() > 0x7FFFFFFF) { + + /* + * We do decryption here to avoid blocking ssl_client and saving cpu time by doing it when needed only. + * + * NOTE: You do not want to send audio while also listening for on_voice_receive/on_voice_receive_combined. + * It will cause gaps in your recording, I have no idea why exactly. + */ + + constexpr size_t header_size = 12; + + uint8_t *buffer = vr.audio_data.data(); + size_t packet_size = vr.audio_data.size(); + + constexpr size_t nonce_size = sizeof(uint32_t); + /* Nonce is 4 byte at the end of payload with zero padding */ + uint8_t nonce[24] = { 0 }; + std::memcpy(nonce, buffer + packet_size - nonce_size, nonce_size); + + /* Get the number of CSRC in header */ + const size_t csrc_count = buffer[0] & 0b0000'1111; + /* Skip to the encrypted voice data */ + const ptrdiff_t offset_to_data = header_size + sizeof(uint32_t) * csrc_count; + size_t total_header_len = offset_to_data; + + uint8_t* ciphertext = buffer + offset_to_data; + size_t ciphertext_len = packet_size - offset_to_data - nonce_size; + + size_t ext_len = 0; + if ([[maybe_unused]] const bool uses_extension = (buffer[0] >> 4) & 0b0001) { + /** + * Get the RTP Extensions size, we only get the size here because + * the extension itself is encrypted along with the opus packet + */ + { + uint16_t ext_len_in_words; + memcpy(&ext_len_in_words, &ciphertext[2], sizeof(uint16_t)); + ext_len_in_words = ntohs(ext_len_in_words); + ext_len = sizeof(uint32_t) * ext_len_in_words; + } + constexpr size_t ext_header_len = sizeof(uint16_t) * 2; + ciphertext += ext_header_len; + ciphertext_len -= ext_header_len; + total_header_len += ext_header_len; + } + + uint8_t decrypted[65535] = { 0 }; + unsigned long long opus_packet_len = 0; + if (ssl_crypto_aead_xchacha20poly1305_ietf_decrypt( + decrypted, &opus_packet_len, + nullptr, + ciphertext, ciphertext_len, + buffer, + /** + * Additional Data: + * The whole header (including csrc list) + + * 4 byte extension header (magic 0xBEDE + 16-bit denoting extension length) + */ + total_header_len, + nonce, vr.voice_client->secret_key.data()) != 0) { + /* Invalid Discord RTP payload. */ + return; + } + + uint8_t *opus_packet = decrypted; + if (ext_len > 0) { + /* Skip previously encrypted RTP Header Extension */ + opus_packet += ext_len; + opus_packet_len -= ext_len; + } + + /** + * If DAVE is enabled, use the user's ratchet to decrypt the OPUS audio data + */ + std::vector decrypted_dave_frame; + if (vr.voice_client->is_end_to_end_encrypted()) { + auto decryptor = vr.voice_client->mls_state->decryptors.find(vr.user_id); + + if (decryptor != vr.voice_client->mls_state->decryptors.end()) { + decrypted_dave_frame.resize(decryptor->second->get_max_plaintext_byte_size(dave::media_type::media_audio, opus_packet_len)); + + size_t enc_len = decryptor->second->decrypt( + dave::media_type::media_audio, + dave::make_array_view(opus_packet, opus_packet_len), + dave::make_array_view(decrypted_dave_frame) + ); + + if (enc_len > 0) { + opus_packet = decrypted_dave_frame.data(); + opus_packet_len = enc_len; + } + } + } + + if (opus_packet_len > 0x7FFFFFFF) { throw dpp::length_exception(err_massive_audio, "audio_data > 2GB! This should never happen!"); } - if (samples = opus_decode(d.decoder.get(), vr.audio_data.data(), - static_cast(vr.audio_data.size() & 0x7FFFFFFF), pcm, 5760, 0); - samples >= 0) { - vr.reassign(&client, d.user_id, reinterpret_cast(pcm), - samples * opus_channel_count * sizeof(opus_int16)); + + samples = opus_decode(d.decoder.get(), opus_packet, static_cast(opus_packet_len & 0x7FFFFFFF), flush_data_pcm, 5760, 0); + + if (samples >= 0) { + vr.reassign(&client, d.user_id, reinterpret_cast(flush_data_pcm), samples * opus_channel_count * sizeof(opus_int16)); + client.end_gain = 1.0f / client.moving_average; - park_count = audio_mix(client, *client.mixer, pcm_mix, pcm, park_count, samples, max_samples); + park_count = audio_mix(client, *client.mixer, pcm_mix, flush_data_pcm, park_count, samples, max_samples); + client.creator->on_voice_receive.call(vr); } @@ -147,15 +267,14 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour } } } - /* If combined receive is bound, dispatch it */ if (park_count) { - /* Downsample the 32 bit samples back to 16 bit */ opus_int16 pcm_downsample[23040] = {0}; opus_int16 *pcm_downsample_ptr = pcm_downsample; opus_int32 *pcm_mix_ptr = pcm_mix; client.increment = (client.end_gain - client.current_gain) / static_cast(samples); + for (int64_t x = 0; x < (samples * opus_channel_count) / client.mixer->byte_blocks_per_register; ++x) { client.mixer->collect_single_register(pcm_mix_ptr, pcm_downsample_ptr, client.current_gain, client.increment); client.current_gain += client.increment * static_cast(client.mixer->byte_blocks_per_register); @@ -164,7 +283,7 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour } voice_receive_t vr(nullptr, "", &client, 0, reinterpret_cast(pcm_downsample), - max_samples * opus_channel_count * sizeof(opus_int16)); + max_samples * opus_channel_count * sizeof(opus_int16)); client.creator->on_voice_receive_combined.call(vr); } diff --git a/src/dpp/voice/enabled/handle_frame.cpp b/src/dpp/voice/enabled/handle_frame.cpp index 07423d3377..0b84cc1ca4 100644 --- a/src/dpp/voice/enabled/handle_frame.cpp +++ b/src/dpp/voice/enabled/handle_frame.cpp @@ -411,6 +411,11 @@ bool discord_voice_client::handle_frame(const std::string &data, ws_opcode opcod }); } this->reinit_dave_mls_group(); + + /* Ready now if there's no DAVE user waiting in the vc */ + if (dave_mls_user_list.empty()) { + ready_now = true; + } } } else { /* Non-DAVE ready immediately */ diff --git a/src/dpp/voice/enabled/opus.cpp b/src/dpp/voice/enabled/opus.cpp index d99a3776d0..a92886ac9c 100644 --- a/src/dpp/voice/enabled/opus.cpp +++ b/src/dpp/voice/enabled/opus.cpp @@ -71,14 +71,14 @@ discord_voice_client& discord_voice_client::send_audio_raw(uint16_t* audio_data, return *this; } -discord_voice_client& discord_voice_client::send_audio_opus(uint8_t* opus_packet, const size_t length) { +discord_voice_client& discord_voice_client::send_audio_opus(const uint8_t* opus_packet, const size_t length) { int samples = opus_packet_get_nb_samples(opus_packet, (opus_int32)length, opus_sample_rate_hz); uint64_t duration = (samples / 48) / (timescale / 1000000); - send_audio_opus(opus_packet, length, duration); + send_audio_opus(opus_packet, length, duration, false); return *this; } -discord_voice_client& discord_voice_client::send_audio_opus(uint8_t* opus_packet, const size_t length, uint64_t duration) { +discord_voice_client& discord_voice_client::send_audio_opus(const uint8_t* opus_packet, const size_t length, uint64_t duration, bool send_now) { int frame_size = (int)(48 * duration * (timescale / 1000000)); opus_int32 encoded_audio_max_length = (opus_int32)length; std::vector encoded_audio(encoded_audio_max_length); @@ -147,7 +147,7 @@ discord_voice_client& discord_voice_client::send_audio_opus(uint8_t* opus_packet /* Append the 4 byte nonce to the resulting payload */ std::memcpy(payload.data() + payload.size() - sizeof(noncel), &noncel, sizeof(noncel)); - this->send(reinterpret_cast(payload.data()), payload.size(), duration); + this->send(reinterpret_cast(payload.data()), payload.size(), duration, send_now); timestamp += frame_size; diff --git a/src/dpp/voice/enabled/read_ready.cpp b/src/dpp/voice/enabled/read_ready.cpp index 95ac598160..03c79b0486 100644 --- a/src/dpp/voice/enabled/read_ready.cpp +++ b/src/dpp/voice/enabled/read_ready.cpp @@ -20,6 +20,7 @@ * ************************************************************************************/ +#include #include #include #include @@ -66,8 +67,8 @@ void discord_voice_client::read_ready() } voice_payload vp{0, // seq, populate later - 0, // timestamp, populate later - std::make_unique(nullptr, std::string(reinterpret_cast(buffer), packet_size))}; + 0, // timestamp, populate later + std::make_unique(nullptr, std::string(reinterpret_cast(buffer), packet_size))}; vp.vr->voice_client = this; @@ -86,88 +87,7 @@ void discord_voice_client::read_ready() std::memcpy(&vp.timestamp, &buffer[4], sizeof(rtp_timestamp_t)); vp.timestamp = ntohl(vp.timestamp); - constexpr size_t nonce_size = sizeof(uint32_t); - /* Nonce is 4 byte at the end of payload with zero padding */ - uint8_t nonce[24] = { 0 }; - std::memcpy(nonce, buffer + packet_size - nonce_size, nonce_size); - - /* Get the number of CSRC in header */ - const size_t csrc_count = buffer[0] & 0b0000'1111; - /* Skip to the encrypted voice data */ - const ptrdiff_t offset_to_data = header_size + sizeof(uint32_t) * csrc_count; - size_t total_header_len = offset_to_data; - - uint8_t* ciphertext = buffer + offset_to_data; - size_t ciphertext_len = packet_size - offset_to_data - nonce_size; - - size_t ext_len = 0; - if ([[maybe_unused]] const bool uses_extension = (buffer[0] >> 4) & 0b0001) { - /** - * Get the RTP Extensions size, we only get the size here because - * the extension itself is encrypted along with the opus packet - */ - { - uint16_t ext_len_in_words; - memcpy(&ext_len_in_words, &ciphertext[2], sizeof(uint16_t)); - ext_len_in_words = ntohs(ext_len_in_words); - ext_len = sizeof(uint32_t) * ext_len_in_words; - } - constexpr size_t ext_header_len = sizeof(uint16_t) * 2; - ciphertext += ext_header_len; - ciphertext_len -= ext_header_len; - total_header_len += ext_header_len; - } - - uint8_t decrypted[65535] = { 0 }; - unsigned long long opus_packet_len = 0; - if (ssl_crypto_aead_xchacha20poly1305_ietf_decrypt( - decrypted, &opus_packet_len, - nullptr, - ciphertext, ciphertext_len, - buffer, - /** - * Additional Data: - * The whole header (including csrc list) + - * 4 byte extension header (magic 0xBEDE + 16-bit denoting extension length) - */ - total_header_len, - nonce, secret_key.data()) != 0) { - /* Invalid Discord RTP payload. */ - return; - } - - uint8_t *opus_packet = decrypted; - if (ext_len > 0) { - /* Skip previously encrypted RTP Header Extension */ - opus_packet += ext_len; - opus_packet_len -= ext_len; - } - - /** - * If DAVE is enabled, use the user's ratchet to decrypt the OPUS audio data - */ - std::vector frame; - if (is_end_to_end_encrypted()) { - auto decryptor = mls_state->decryptors.find(vp.vr->user_id); - if (decryptor != mls_state->decryptors.end()) { - frame.resize(decryptor->second->get_max_plaintext_byte_size(dave::media_type::media_audio, opus_packet_len)); - size_t enc_len = decryptor->second->decrypt( - dave::media_type::media_audio, - dave::make_array_view(opus_packet, opus_packet_len), - dave::make_array_view(frame) - ); - if (enc_len > 0) { - opus_packet = frame.data(); - opus_packet_len = enc_len; - } - } - } - - /* - * We're left with the decrypted, opus-encoded data. - * Park the payload and decode on the voice courier thread. - */ - vp.vr->audio_data.assign(opus_packet, opus_packet + opus_packet_len); + vp.vr->audio_data.assign(buffer, buffer + packet_size); { std::lock_guard lk(voice_courier_shared_state.mtx); @@ -183,7 +103,7 @@ void discord_voice_client::read_ready() int opus_error = 0; decoder.reset(opus_decoder_create(opus_sample_rate_hz, opus_channel_count, &opus_error), - &opus_decoder_destroy); + &opus_decoder_destroy); if (opus_error) { /** * NOTE: The -10 here makes the opus_error match up with values of exception_error_code, @@ -207,8 +127,8 @@ void discord_voice_client::read_ready() if (!voice_courier.joinable()) { /* Courier thread is not running, start it */ voice_courier = std::thread(&voice_courier_loop, - std::ref(*this), - std::ref(voice_courier_shared_state)); + std::ref(*this), + std::ref(voice_courier_shared_state)); } } diff --git a/src/dpp/voice/enabled/read_write.cpp b/src/dpp/voice/enabled/read_write.cpp index c24200b49b..52a09d5a39 100644 --- a/src/dpp/voice/enabled/read_write.cpp +++ b/src/dpp/voice/enabled/read_write.cpp @@ -30,7 +30,7 @@ namespace dpp { dpp::socket discord_voice_client::want_write() { std::lock_guard lock(this->stream_mutex); - if (!this->paused && !outbuf.empty()) { + if (!this->sent_stop_frames && !outbuf.empty()) { return fd; } return INVALID_SOCKET; @@ -42,13 +42,16 @@ dpp::socket discord_voice_client::want_read() { } -void discord_voice_client::send(const char* packet, size_t len, uint64_t duration) { - voice_out_packet frame; - frame.packet.assign(packet, packet + len); - frame.duration = duration; - { +void discord_voice_client::send(const char* packet, size_t len, uint64_t duration, bool send_now) { + if (!send_now) [[likely]] { + voice_out_packet frame; + frame.packet.assign(packet, packet + len); + frame.duration = duration; + std::lock_guard lock(this->stream_mutex); outbuf.emplace_back(frame); + } else [[unlikely]] { + this->udp_send(packet, len); } } @@ -68,4 +71,4 @@ int discord_voice_client::udp_recv(char* data, size_t max_length) return static_cast(recv(this->fd, data, static_cast(max_length), 0)); } -} \ No newline at end of file +} diff --git a/src/dpp/voice/enabled/write_ready.cpp b/src/dpp/voice/enabled/write_ready.cpp index 46c0307055..8287dea2a3 100644 --- a/src/dpp/voice/enabled/write_ready.cpp +++ b/src/dpp/voice/enabled/write_ready.cpp @@ -37,7 +37,14 @@ void discord_voice_client::write_ready() { send_audio_type_t type = satype_recorded_audio; { std::lock_guard lock(this->stream_mutex); - if (!this->paused && outbuf.size()) { + if (this->paused) { + if (!this->sent_stop_frames) { + this->send_stop_frames(true); + this->sent_stop_frames = true; + } + + /* Fallthrough if paused */ + } else if (!outbuf.empty()) { type = send_audio_type; if (outbuf[0].packet.size() == sizeof(uint16_t) && (*(reinterpret_cast(outbuf[0].packet.data()))) == AUDIO_TRACK_MARKER) { outbuf.erase(outbuf.begin()); @@ -46,7 +53,7 @@ void discord_voice_client::write_ready() { tracks--; } } - if (outbuf.size()) { + if (!outbuf.empty()) { if (this->udp_send(outbuf[0].packet.data(), outbuf[0].packet.length()) == (int)outbuf[0].packet.length()) { duration = outbuf[0].duration * timescale; bufsize = outbuf[0].packet.length(); diff --git a/src/dpp/voice/stub/stubs.cpp b/src/dpp/voice/stub/stubs.cpp index b184367d98..c027aa8b43 100644 --- a/src/dpp/voice/stub/stubs.cpp +++ b/src/dpp/voice/stub/stubs.cpp @@ -63,11 +63,11 @@ namespace dpp { return *this; } - discord_voice_client& discord_voice_client::send_audio_opus(uint8_t* opus_packet, const size_t length) { + discord_voice_client& discord_voice_client::send_audio_opus(const uint8_t* opus_packet, const size_t length, uint64_t duration, bool send_now) { return *this; } - discord_voice_client& discord_voice_client::send_audio_opus(uint8_t* opus_packet, const size_t length, uint64_t duration) { + discord_voice_client& discord_voice_client::send_audio_opus(const uint8_t* opus_packet, const size_t length) { return *this; } @@ -80,7 +80,7 @@ namespace dpp { } - void discord_voice_client::send(const char* packet, size_t len, uint64_t duration) { + void discord_voice_client::send(const char* packet, size_t len, uint64_t duration, bool send_now) { } int discord_voice_client::udp_send(const char* data, size_t length) { diff --git a/src/dpp/voiceregion.cpp b/src/dpp/voiceregion.cpp index e3c9fc6349..22410228d7 100644 --- a/src/dpp/voiceregion.cpp +++ b/src/dpp/voiceregion.cpp @@ -72,5 +72,4 @@ bool voiceregion::is_custom() const { return flags & v_custom; } -} // namespace dpp - +} diff --git a/src/dpp/voicestate.cpp b/src/dpp/voicestate.cpp index f0bcbd6e9f..9968890200 100644 --- a/src/dpp/voicestate.cpp +++ b/src/dpp/voicestate.cpp @@ -90,5 +90,4 @@ bool voicestate::is_suppressed() const { return flags & vs_suppress; } -} // namespace dpp - +} diff --git a/src/dpp/webhook.cpp b/src/dpp/webhook.cpp index 4c43c4df2c..cc5622fe06 100644 --- a/src/dpp/webhook.cpp +++ b/src/dpp/webhook.cpp @@ -22,7 +22,6 @@ #include #include #include -#include namespace dpp { @@ -101,5 +100,4 @@ webhook& webhook::load_image(const std::string &image_blob, const image_type typ return *this; } -} // namespace dpp - +} diff --git a/src/dpp/wsclient.cpp b/src/dpp/wsclient.cpp index 424c34d2b1..618c7e7282 100644 --- a/src/dpp/wsclient.cpp +++ b/src/dpp/wsclient.cpp @@ -331,4 +331,4 @@ void websocket_client::close() ssl_client::close(); } -} // namespace dpp +} diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 7c61570ab7..da96a28096 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -641,7 +641,6 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b DPP_CHECK_CONSTRUCT_ASSIGN(EVENT_CLASS, dpp::thread_member_update_t, success); DPP_CHECK_CONSTRUCT_ASSIGN(EVENT_CLASS, dpp::thread_members_update_t, success); DPP_CHECK_CONSTRUCT_ASSIGN(EVENT_CLASS, dpp::voice_buffer_send_t, success); - DPP_CHECK_CONSTRUCT_ASSIGN(EVENT_CLASS, dpp::voice_user_talking_t, success); DPP_CHECK_CONSTRUCT_ASSIGN(EVENT_CLASS, dpp::voice_ready_t, success); DPP_CHECK_CONSTRUCT_ASSIGN(EVENT_CLASS, dpp::voice_receive_t, success); DPP_CHECK_CONSTRUCT_ASSIGN(EVENT_CLASS, dpp::voice_client_speaking_t, success);