diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h index 96f7b2cd66..10d49456dc 100644 --- a/include/dpp/appcommand.h +++ b/include/dpp/appcommand.h @@ -426,6 +426,10 @@ struct DPP_EXPORT interaction_modal_response : public interaction_response, publ virtual json to_json_impl(bool with_id = false) const; public: + using json_interface::fill_from_json; + using json_interface::to_json; + using json_interface::build_json; + /** * @brief Custom ID for the modal form */ diff --git a/include/dpp/application.h b/include/dpp/application.h index e5941df3d5..33e0ff27f1 100644 --- a/include/dpp/application.h +++ b/include/dpp/application.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -127,27 +128,48 @@ class DPP_EXPORT application : public managed, public json_interface tags; //!< Up to 5 tags describing the content and functionality of the application - application_install_params install_params; //!< Settings for the application's default in-app authorization link, if enabled - std::string custom_install_url; //!< The application's default custom authorization link, if enabled - std::string role_connections_verification_url; //!< The application's role connection verification entry point, which when configured will render the app as a verification method in the guild role verification configuration + std::string name; //!< the name of the app + utility::iconhash icon; //!< the icon hash of the app (may be empty) + std::string description; //!< the description of the app + std::vector rpc_origins; //!< Optional: an array of rpc origin urls, if rpc is enabled + bool bot_public; //!< when false only app owner can join the app's bot to guilds + bool bot_require_code_grant; //!< when true the app's bot will only join upon completion of the full oauth2 code grant flow + user bot; //!< Optional: Partial user object for the bot user associated with the app. + std::string terms_of_service_url; //!< Optional: the url of the app's terms of service + std::string privacy_policy_url; //!< Optional: the url of the app's privacy policy + user owner; //!< Optional: partial user object containing info on the owner of the application + std::string summary; //!< if this application is a game sold on Discord, this field will be the summary field for the store page of its primary sku @deprecated Will be removed in v11 + std::string verify_key; //!< the hex encoded key for verification in interactions and the GameSDK's GetTicket + app_team team; //!< if the application belongs to a team, this will be a list of the members of that team (may be empty) + snowflake guild_id; //!< Optional: if this application is a game sold on Discord, this field will be the guild to which it has been linked + guild guild_obj; //!< Optional: Partial object of the associated guild + snowflake primary_sku_id; //!< Optional: if this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists + std::string slug; //!< Optional: if this application is a game sold on Discord, this field will be the URL slug that links to the store page + utility::iconhash cover_image; //!< Optional: the application's default rich presence invite cover image hash + uint32_t flags; //!< Optional: the application's public flags + uint64_t approximate_guild_count; //!< Optional: Approximate count of guilds the app has been added to + std::vector redirect_uris; //!< Optional: Array of redirect URIs for the app + std::string interactions_endpoint_url; //!< Optional: Interactions endpoint URL for the app + std::string role_connections_verification_url; //!< The application's role connection verification entry point, which when configured will render the app as a verification method in the guild role verification configuration + std::vector tags; //!< Up to 5 tags describing the content and functionality of the application + application_install_params install_params; //!< Settings for the application's default in-app authorization link, if enabled + std::string custom_install_url; //!< The application's default custom authorization link, if enabled + + uint8_t discoverability_state; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint32_t discovery_eligibility_flags; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint8_t explicit_content_filter; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint8_t creator_monetization_state; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + bool integration_public; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + bool integration_require_code_grant; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + std::vector interactions_event_types; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint8_t interactions_version; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + bool is_monetized; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint32_t monetization_eligibility_flags; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint8_t monetization_state; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + bool hook; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint8_t rpc_application_state; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint8_t store_application_state; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + uint8_t verification_state; //!< @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. /** Constructor */ application(); diff --git a/include/dpp/discordevents.h b/include/dpp/discordevents.h index 60ef5006b2..0154d4a44b 100644 --- a/include/dpp/discordevents.h +++ b/include/dpp/discordevents.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,13 @@ std::string DPP_EXPORT string_not_null(const nlohmann::json* j, const char *keyn */ void DPP_EXPORT set_string_not_null(const nlohmann::json* j, const char *keyname, std::string &v); +/** @brief This is a repeat of set_string_not_null, but takes in a iconhash. + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_iconhash_not_null(const nlohmann::json* j, const char *keyname, utility::iconhash &v); + /** @brief Returns a double from a json field value, if defined, else returns 0. * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value diff --git a/include/dpp/integration.h b/include/dpp/integration.h index 6310c78bdb..98d5cb507d 100644 --- a/include/dpp/integration.h +++ b/include/dpp/integration.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace dpp { @@ -47,34 +48,30 @@ enum integration_type { * @brief Integration flags */ enum integration_flags { - /// Integration enabled - if_enabled = 0b00000001, - /// Integration syncing - if_syncing = 0b00000010, - /// Emoji integration - if_emoticons = 0b00000100, - /// Integration revoked - if_revoked = 0b00001000, - /// Kick users when their subscription expires - if_expire_kick = 0b00010000, + if_enabled = 0b00000001, //!< is this integration enabled + if_syncing = 0b00000010, //!< is this integration syncing @warning This is not provided for discord bot integrations. + if_emoticons = 0b00000100, //!< whether emoticons should be synced for this integration (twitch only currently) @warning This is not provided for discord bot integrations. + if_revoked = 0b00001000, //!< has this integration been revoked @warning This is not provided for discord bot integrations. + if_expire_kick = 0b00010000, //!< kick user when their subscription expires, otherwise only remove the role that is specified by `role_id`. @warning This is not provided for discord bot integrations. }; /** * @brief An application that has been integrated */ struct DPP_EXPORT integration_app { - /// Integration id - snowflake id; - /// Name - std::string name; - /// Icon - std::string icon; - /// Description - std::string description; - /// Integration summary @deprecated Removed by Discord - std::string summary; - /// Pointer to bot user - class user* bot; + snowflake id; //!< the id of the app + std::string name; //!< the name of the app + utility::iconhash icon; //!< the icon hash of the app + std::string description; //!< the description of the app + class user* bot; //!< the bot associated with this application +}; + +/** + * @brief The account information for an integration. + */ +struct DPP_EXPORT integration_account { + snowflake id; //!< id of the account + std::string name; //!< name of the account }; /** @@ -97,28 +94,17 @@ class DPP_EXPORT integration : public managed, public json_interface scopes; //!< the scopes the application has been authorized for /** Default constructor */ integration(); @@ -126,15 +112,35 @@ class DPP_EXPORT integration : public managed, public json_interface #include #include +#include namespace dpp { @@ -38,40 +39,25 @@ application::~application() = default; application& application::fill_from_json_impl(nlohmann::json* j) { set_snowflake_not_null(j, "id", id); set_string_not_null(j, "name", name); - std::string ic = string_not_null(j, "icon"); - if (!ic.empty()) { - icon = ic; - } + set_iconhash_not_null(j, "icon", icon); set_string_not_null(j, "description", description); - set_string_not_null(j, "rpc_origins", rpc_origins); + + if (j->contains("rpc_origins")) { + for (const auto& rpc : (*j)["rpc_origins"]) { + this->rpc_origins.push_back(to_string(rpc)); + } + } + set_bool_not_null(j, "bot_public", bot_public); set_bool_not_null(j, "bot_require_code_grant", bot_require_code_grant); + bot = user().fill_from_json(&((*j)["bot"])); set_string_not_null(j, "terms_of_service_url", terms_of_service_url); set_string_not_null(j, "privacy_policy_url", privacy_policy_url); owner = user().fill_from_json(&((*j)["owner"])); + // TODO: Remove the setting of 'summary' when v11 is released. set_string_not_null(j, "summary", summary); set_string_not_null(j, "verify_key", verify_key); - set_snowflake_not_null(j, "guild_id", guild_id); - set_snowflake_not_null(j, "primary_sku_id", primary_sku_id); - set_string_not_null(j, "slug", slug); - std::string ci = string_not_null(j, "cover_image"); - if (!ci.empty()) { - cover_image = ci; - } - set_int32_not_null(j, "flags", flags); - if (j->contains("tags")) { - for (const auto& tag : (*j)["tags"]) { - this->tags.push_back(to_string(tag)); - } - } - if (j->contains("install_params")) { - json& p = (*j)["install_params"]; - set_snowflake_not_null(&p, "permissions", this->install_params.permissions); - for (const auto& scope : p["scopes"]) { - this->install_params.scopes.push_back(to_string(scope)); - } - } - set_string_not_null(j, "custom_install_url", custom_install_url); + if (j->contains("team")) { json& t = (*j)["team"]; std::string i = string_not_null(&t, "icon"); @@ -100,28 +86,85 @@ application& application::fill_from_json_impl(nlohmann::json* j) { this->team.members.emplace_back(tm); } } + + set_snowflake_not_null(j, "guild_id", guild_id); + guild_obj = guild().fill_from_json(&((*j)["guild"])); + set_snowflake_not_null(j, "primary_sku_id", primary_sku_id); + set_string_not_null(j, "slug", slug); + 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); + + if (j->contains("redirect_uris")) { + for (const auto& uri : (*j)["redirect_uris"]) { + this->redirect_uris.push_back(to_string(uri)); + } + } + + set_string_not_null(j, "interactions_endpoint_url", interactions_endpoint_url); set_string_not_null(j, "role_connections_verification_url", role_connections_verification_url); + + if (j->contains("tags")) { + for (const auto& tag : (*j)["tags"]) { + this->tags.push_back(to_string(tag)); + } + } + + if (j->contains("install_params")) { + json& p = (*j)["install_params"]; + set_snowflake_not_null(&p, "permissions", this->install_params.permissions); + for (const auto& scope : p["scopes"]) { + this->install_params.scopes.push_back(to_string(scope)); + } + } + + set_string_not_null(j, "custom_install_url", custom_install_url); + + // TODO: Investigate https://discord.com/developers/docs/resources/application#application-resource when v11 releases. See if the variables below are documented. + + set_int8_not_null(j, "discoverability_state", discoverability_state); + set_int32_not_null(j, "discovery_eligibility_flags", discovery_eligibility_flags); + set_int8_not_null(j, "explicit_content_filter", explicit_content_filter); + set_int8_not_null(j, "creator_monetization_state", creator_monetization_state); + set_bool_not_null(j, "integration_public", integration_public); + set_bool_not_null(j, "integration_require_code_grant", integration_require_code_grant); + + if (j->contains("interactions_event_types")) { + for (const auto& event_type : (*j)["interactions_event_types"]) { + this->interactions_event_types.push_back(to_string(event_type)); + } + } + + set_int8_not_null(j, "interactions_version", interactions_version); + set_bool_not_null(j, "is_monetized", is_monetized); + set_int32_not_null(j, "monetization_eligibility_flags", monetization_eligibility_flags); + set_int8_not_null(j, "monetization_state", monetization_state); + set_bool_not_null(j, "hook", hook); + set_int8_not_null(j, "rpc_application_state", rpc_application_state); + set_int8_not_null(j, "store_application_state", store_application_state); + set_int8_not_null(j, "verification_state", verification_state); + return *this; } std::string application::get_cover_image_url(uint16_t size, const image_type format) const { if (!this->cover_image.to_string().empty() && this->id) { return utility::cdn_endpoint_url({ i_jpg, i_png, i_webp }, - "app-icons/" + std::to_string(this->id) + "/" + this->cover_image.to_string(), - format, size); - } else { - return std::string(); + "app-icons/" + std::to_string(this->id) + "/" + this->cover_image.to_string(), + format, size); } + + return ""; } std::string application::get_icon_url(uint16_t size, const image_type format) const { if (!this->icon.to_string().empty() && this->id) { return utility::cdn_endpoint_url({ i_jpg, i_png, i_webp }, - "app-icons/" + std::to_string(this->id) + "/" + this->icon.to_string(), - format, size); - } else { - return std::string(); + "app-icons/" + std::to_string(this->id) + "/" + this->icon.to_string(), + format, size); } + + return ""; } } // namespace dpp diff --git a/src/dpp/discordevents.cpp b/src/dpp/discordevents.cpp index 353515b6c7..7ca54a0d04 100644 --- a/src/dpp/discordevents.cpp +++ b/src/dpp/discordevents.cpp @@ -128,6 +128,14 @@ void set_string_not_null(const json* j, const char *keyname, std::string &v) { } } +void set_iconhash_not_null(const json* j, const char *keyname, utility::iconhash &v) { + /* Returns empty string if the value is not a string, or is null or not defined */ + auto k = j->find(keyname); + if (k != j->end()) { + v = !k->is_null() && k->is_string() ? k->get() : ""; + } +} + double double_not_null(const json* j, const char *keyname) { auto k = j->find(keyname); if (k != j->end()) { diff --git a/src/dpp/integration.cpp b/src/dpp/integration.cpp index 7f0570e8a7..f17239dd8a 100644 --- a/src/dpp/integration.cpp +++ b/src/dpp/integration.cpp @@ -31,18 +31,7 @@ namespace dpp { using json = nlohmann::json; -integration::integration() : - managed(), - type(i_twitch), - flags(0), - role_id(0), - user_id(0), - expire_grace_period(0), - synced_at(0), - subscriber_count(0) -{ - app.id = 0; - app.bot = nullptr; +integration::integration() : managed(), type(i_twitch), flags(0), role_id(0), expire_grace_period(0), synced_at(0), subscriber_count(0) { } integration& integration::fill_from_json_impl(nlohmann::json* j) @@ -54,51 +43,74 @@ integration& integration::fill_from_json_impl(nlohmann::json* j) { "discord", i_discord }, { "guild_subscription", i_guild_subscription } }; - this->id = snowflake_not_null(j, "id"); - this->name = string_not_null(j, "name"); + + set_snowflake_not_null(j, "id", id); + set_string_not_null(j, "name", name); this->type = type_map[string_not_null(j, "type")]; + if (bool_not_null(j, "enabled")) { this->flags |= if_enabled; } if (bool_not_null(j, "syncing")) { this->flags |= if_syncing; } + + set_snowflake_not_null(j, "role_id", role_id); + if (bool_not_null(j, "enable_emoticons")) { this->flags |= if_emoticons; } - if (bool_not_null(j, "revoked")) { - this->flags |= if_revoked; - } if (int8_not_null(j, "expire_behavior")) { this->flags |= if_expire_kick; } - this->expire_grace_period = int32_not_null(j, "expire_grace_period"); - if (j->contains("user")) { - auto t = (*j)["user"]; - this->user_id = snowflake_not_null(&t, "user_id"); + + set_int32_not_null(j, "expire_grace_period", expire_grace_period); + + if(j->contains("user")) { + user_obj = user().fill_from_json(&((*j)["user"])); + } + + /* Should never be null, but best to check in-case they maybe change it */ + if(j->contains("account")) { + auto & ac = (*j)["account"]; + set_snowflake_not_null(&ac, "id", account.id); + set_string_not_null(&ac, "name", account.name); } + + set_ts_not_null(j, "synced_at", synced_at); + set_int32_not_null(j, "subscriber_count", subscriber_count); + + if (bool_not_null(j, "revoked")) { + this->flags |= if_revoked; + } + if (j->contains("application")) { auto & t = (*j)["application"]; - this->app.id = snowflake_not_null(&t, "id"); + set_snowflake_not_null(&t, "id", app.id); + set_string_not_null(&t, "name", app.name); + set_string_not_null(&t, "description", app.description); + set_iconhash_not_null(&t, "icon", app.icon); if (t.find("bot") != t.end()) { auto & b = t["bot"]; this->app.bot = dpp::find_user(snowflake_not_null(&b, "id")); } } - this->subscriber_count = int32_not_null(j, "subscriber_count"); - this->account_id = string_not_null(&((*j)["account"]), "id"); - this->account_name = string_not_null(&((*j)["account"]), "name"); + if(j->contains("scopes")) { + for (const auto& scope : (*j)["scopes"]) { + this->scopes.push_back(to_string(scope)); + } + } return *this; } json integration::to_json_impl(bool with_id) const { return json({ - { "expire_behavior", (flags & if_expire_kick) ? 1 : 0 }, - { "expire_grace_period", expire_grace_period }, - { "enable_emoticons", emoticons_enabled() } - }).dump(); + { "expire_behavior", (flags & if_expire_kick) ? 1 : 0 }, + { "expire_grace_period", expire_grace_period }, + { "enable_emoticons", emoticons_enabled() } + }).dump(); } bool integration::emoticons_enabled() const { @@ -138,5 +150,4 @@ connection& connection::fill_from_json_impl(nlohmann::json* j) { return *this; } - } // namespace dpp