From 094c817206944122eb89fc430873920798df40ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Sat, 4 Apr 2026 15:37:08 +0200 Subject: [PATCH] [Application] Add discord integration shenanigans Took 21 seconds Took 16 seconds --- cockatrice/CMakeLists.txt | 42 +++++++++ .../integrations/discord_social_client.cpp | 90 +++++++++++++++++++ .../integrations/discord_social_client.h | 13 +++ .../integrations/discord_social_manager.cpp | 7 ++ .../integrations/discord_social_manager.h | 24 +++++ cockatrice/src/main.cpp | 5 +- 6 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 cockatrice/src/client/network/integrations/discord_social_client.cpp create mode 100644 cockatrice/src/client/network/integrations/discord_social_client.h create mode 100644 cockatrice/src/client/network/integrations/discord_social_manager.cpp create mode 100644 cockatrice/src/client/network/integrations/discord_social_manager.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 5ab37ecf9..d9bd710c1 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -325,6 +325,10 @@ set(cockatrice_SOURCES src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h + src/client/network/integrations/discord_social_manager.cpp + src/client/network/integrations/discord_social_manager.h + src/client/network/integrations/discord_social_client.cpp + src/client/network/integrations/discord_social_client.h src/single_instance_manager.cpp src/single_instance_manager.h ) @@ -337,6 +341,12 @@ configure_file( set(cockatrice_RESOURCES cockatrice.qrc) +# Define some handy Discord Social SDK variables +set(DISCORD_SDK_ROOT "${CMAKE_SOURCE_DIR}/external/discord_social_sdk") +set(DISCORD_SDK_LIB_DIR "${DISCORD_SDK_ROOT}/lib/release") +set(DISCORD_SDK_BIN_DIR "${DISCORD_SDK_ROOT}/bin/release") +set(DISCORD_SDK_INCLUDE_DIR "${DISCORD_SDK_ROOT}/include") + if(UPDATE_TRANSLATIONS) # Cockatrice main sources file(GLOB_RECURSE translate_cockatrice_SRCS ${CMAKE_SOURCE_DIR}/cockatrice/src/*.cpp @@ -427,6 +437,19 @@ elseif(Qt5_FOUND) endif() endif() +target_include_directories(cockatrice PRIVATE ${DISCORD_SDK_INCLUDE_DIR}) + +if(WIN32) + set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/discord_partner_sdk.lib") + set(DISCORD_SHARED_LIB "${DISCORD_SDK_BIN_DIR}/discord_partner_sdk.dll") +elseif(APPLE) + set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.dylib") + set(DISCORD_SHARED_LIB "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.dylib") +else() # Linux + set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.so") + set(DISCORD_SHARED_LIB "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.so") +endif() + if(Qt5_FOUND) target_link_libraries( cockatrice @@ -439,6 +462,7 @@ if(Qt5_FOUND) libcockatrice_rng libcockatrice_settings ${COCKATRICE_QT_MODULES} + ${DISCORD_LIB_PATH} ) else() target_link_libraries( @@ -452,9 +476,27 @@ else() libcockatrice_rng libcockatrice_settings ${COCKATRICE_QT_MODULES} + ${DISCORD_LIB_PATH} ) endif() +if(UNIX) + # Use RPATH when building + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + # Set the RPATH to use the lib directory relative to the executable + set(CMAKE_INSTALL_RPATH "$ORIGIN") + if(APPLE) + set(CMAKE_INSTALL_RPATH "@executable_path") + endif() +endif() + +# Copy Social SDK shared library to output directory, so it's available at runtime. +add_custom_command( + TARGET cockatrice + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${DISCORD_SHARED_LIB}" $ +) + if(UNIX) if(APPLE) set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") diff --git a/cockatrice/src/client/network/integrations/discord_social_client.cpp b/cockatrice/src/client/network/integrations/discord_social_client.cpp new file mode 100644 index 000000000..be2ed363a --- /dev/null +++ b/cockatrice/src/client/network/integrations/discord_social_client.cpp @@ -0,0 +1,90 @@ +#include "discord_social_client.h" + +#define DISCORDPP_IMPLEMENTATION +#include "discordpp.h" + +#include +#include + +const uint64_t APPLICATION_ID = 1489873419875913832; + +DiscordSocialClient::DiscordSocialClient(QObject *parent) +{ + Q_UNUSED(parent); + + auto client = std::make_shared(); + client->AddLogCallback( + [](auto message, auto severity) { qInfo() << "[" << EnumToString(severity) << "] " << message; }, + discordpp::LoggingSeverity::Info); + + client->SetStatusChangedCallback( + [client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) { + qInfo() << "🔄 Status changed: " << discordpp::Client::StatusToString(status); + + if (status == discordpp::Client::Status::Ready) { + qInfo() << "✅ Client is ready! You can now call SDK functions.\n"; + qInfo() << "👥 Friends Count: " << client->GetRelationships().size(); + // Configure rich presence details + discordpp::Activity activity; + activity.SetType(discordpp::ActivityTypes::Playing); + activity.SetState("Coding features"); + activity.SetDetails("Working on integrating Discord"); + + // Update rich presence + client->UpdateRichPresence(activity, [](discordpp::ClientResult result) { + if (result.Successful()) { + qInfo() << "🎮 Rich Presence updated successfully!\n"; + } else { + qInfo() << "❌ Rich Presence update failed"; + } + }); + } else if (error != discordpp::Client::Error::None) { + qInfo() << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) + << " - Details: " << errorDetail; + } + }); + + // Generate OAuth2 code verifier for authentication + auto codeVerifier = client->CreateAuthorizationCodeVerifier(); + + // Set up authentication arguments + discordpp::AuthorizationArgs args{}; + args.SetClientId(APPLICATION_ID); + args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + args.SetCodeChallenge(codeVerifier.Challenge()); + + // Begin authentication process + client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) { + if (!result.Successful()) { + qInfo() << "❌ Authentication Error: " << result.Error(); + return; + } else { + qInfo() << "✅ Authorization successful! Getting access token...\n"; + + // Exchange auth code for access token + client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, + [client](discordpp::ClientResult result, std::string accessToken, std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, int32_t expiresIn, + std::string scope) { + Q_UNUSED(result); + Q_UNUSED(expiresIn); + Q_UNUSED(refreshToken); + Q_UNUSED(tokenType); + Q_UNUSED(scope); + qInfo() << "🔓 Access token received! Establishing connection...\n"; + client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, + [client](discordpp::ClientResult result) { + if (result.Successful()) { + qInfo() << "🔑 Token updated, connecting to Discord...\n"; + client->Connect(); + } + }); + }); + } + }); + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, []() { discordpp::RunCallbacks(); }); + + timer->start(10); +} \ No newline at end of file diff --git a/cockatrice/src/client/network/integrations/discord_social_client.h b/cockatrice/src/client/network/integrations/discord_social_client.h new file mode 100644 index 000000000..25c805937 --- /dev/null +++ b/cockatrice/src/client/network/integrations/discord_social_client.h @@ -0,0 +1,13 @@ +#ifndef COCKATRICE_DISCORD_SOCIAL_CLIENT_H +#define COCKATRICE_DISCORD_SOCIAL_CLIENT_H +#include "discordpp.h" + +#include + +class DiscordSocialClient : public QObject +{ +public: + DiscordSocialClient(QObject *parent = nullptr); +}; + +#endif // COCKATRICE_DISCORD_SOCIAL_CLIENT_H diff --git a/cockatrice/src/client/network/integrations/discord_social_manager.cpp b/cockatrice/src/client/network/integrations/discord_social_manager.cpp new file mode 100644 index 000000000..cda8dc83b --- /dev/null +++ b/cockatrice/src/client/network/integrations/discord_social_manager.cpp @@ -0,0 +1,7 @@ +#include "discord_social_manager.h" + +DiscordSocialClient *DiscordSocialManager::getInstance() +{ + static DiscordSocialClient instance(nullptr); + return &instance; +} \ No newline at end of file diff --git a/cockatrice/src/client/network/integrations/discord_social_manager.h b/cockatrice/src/client/network/integrations/discord_social_manager.h new file mode 100644 index 000000000..ae153c839 --- /dev/null +++ b/cockatrice/src/client/network/integrations/discord_social_manager.h @@ -0,0 +1,24 @@ +#ifndef COCKATRICE_DISCORD_SOCIAL_MANAGER_H +#define COCKATRICE_DISCORD_SOCIAL_MANAGER_H +#include "discord_social_client.h" + +class DiscordSocialManager +{ +public: + DiscordSocialManager(const DiscordSocialManager &) = delete; + + DiscordSocialManager &operator=(const DiscordSocialManager &) = delete; + + /** + * @brief Returns the singleton CardDatabase instance. + * @return Pointer to the global CardDatabase. + */ + static DiscordSocialClient *getInstance(); + +private: + DiscordSocialManager() = default; + + ~DiscordSocialManager() = default; +}; + +#endif // COCKATRICE_DISCORD_SOCIAL_MANAGER_H diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index c9866b508..60f0b285d 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -24,6 +24,7 @@ #include "client/settings/cache_settings.h" #include "client/sound_engine.h" #include "database/interface/settings_card_preference_provider.h" +#include "integrations/discord_social_manager.h" #include "interface/logger.h" #include "interface/pixel_map_generator.h" #include "interface/theme_manager.h" @@ -258,6 +259,8 @@ int main(int argc, char *argv[]) CardDatabaseManager::setCardDatabasePathProvider(&SettingsCache::instance()); CardDatabaseManager::setCardSetPriorityController(SettingsCache::instance().cardDatabase()); + DiscordSocialManager::getInstance(); + qCInfo(MainLog) << "Starting main program"; MainWindow ui; @@ -348,5 +351,5 @@ int main(int argc, char *argv[]) CountryPixmapGenerator::clear(); UserLevelPixmapGenerator::clear(); - return 0; + return ret; }