Skip to content

Commit c5eb6ae

Browse files
committed
Implement Discord Social SDK to display rich presence for the user. Enabled by default, includes option to disable.
1 parent fd019c0 commit c5eb6ae

File tree

9 files changed

+14092
-3
lines changed

9 files changed

+14092
-3
lines changed

3rdParty/discord_social_sdk/cdiscord.h

Lines changed: 1827 additions & 0 deletions
Large diffs are not rendered by default.

3rdParty/discord_social_sdk/discordpp.h

Lines changed: 12001 additions & 0 deletions
Large diffs are not rendered by default.
93.4 MB
Binary file not shown.

SerialPrograms/CMakeLists.txt

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ set(CMAKE_AUTOMOC ON)
1616
set(CMAKE_AUTORCC ON)
1717
set(CMAKE_AUTOUIC ON)
1818

19+
# Set root dir for convenience
20+
set(PROJECT_ROOT_DIR "${CMAKE_SOURCE_DIR}/..")
21+
get_filename_component(PROJECT_ROOT_DIR "${CMAKE_SOURCE_DIR}/.." ABSOLUTE)
22+
1923
add_custom_target(build-time-make-directory ALL
2024
COMMAND ${CMAKE_COMMAND} -E make_directory Assembly/)
2125

@@ -732,6 +736,8 @@ file(GLOB MAIN_SOURCES
732736
Source/Integrations/DiscordWebhookSettings.h
733737
Source/Integrations/DppIntegration/DppClient.cpp
734738
Source/Integrations/DppIntegration/DppClient.h
739+
Source/Integrations/DiscordSocial/DiscordSocial.cpp
740+
Source/Integrations/DiscordSocial/DiscordSocial.h
735741
Source/Integrations/DppIntegration/DppCommandHandler.cpp
736742
Source/Integrations/DppIntegration/DppCommandHandler.h
737743
Source/Integrations/DppIntegration/DppUtility.cpp
@@ -2450,12 +2456,17 @@ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../Internal/SerialPrograms/Internal0.
24502456
target_sources(SerialPrograms PRIVATE ../../Internal/SerialPrograms/Internal1.cpp)
24512457
endif()
24522458

2453-
#extract opencv_world4110d.dll from archive on Windows Debug builds
2459+
#extract opencv_world4110d.dll from archive on Windows Debug build, extract Discord Social SDK
24542460
if (WIN32)
24552461
file(ARCHIVE_EXTRACT
24562462
INPUT ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/opencv_world4110d.zip
24572463
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/
24582464
)
2465+
2466+
file(ARCHIVE_EXTRACT
2467+
INPUT ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/discord_social_sdk.zip
2468+
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/
2469+
)
24592470
endif()
24602471

24612472
#add include directory
@@ -2504,29 +2515,44 @@ if (WIN32)
25042515

25052516
add_library(dpp_lib IMPORTED UNKNOWN)
25062517
set_target_properties(dpp_lib PROPERTIES
2507-
IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dpp.lib
2508-
IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dppd.lib)
2518+
IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dpp.lib
2519+
IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dppd.lib)
25092520
set_target_properties(dpp_lib PROPERTIES
25102521
MAP_IMPORTED_CONFIG_DEBUG DEBUG
25112522
MAP_IMPORTED_CONFIG_RELEASE RELEASE
25122523
MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE
25132524
MAP_IMPORTED_CONFIG_MINSIZEREL RELEASE
25142525
)
25152526

2527+
add_library(discord_lib IMPORTED UNKNOWN)
2528+
target_include_directories(SerialPrograms SYSTEM PRIVATE ../3rdParty/discord_social_sdk/)
2529+
set_target_properties(discord_lib PROPERTIES
2530+
IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/discord_social_sdk/lib/release/discord_partner_sdk.lib
2531+
IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/discord_social_sdk/lib/debug/discord_partner_sdk.lib)
2532+
set_target_properties(discord_lib PROPERTIES
2533+
MAP_IMPORTED_CONFIG_DEBUG DEBUG
2534+
MAP_IMPORTED_CONFIG_RELEASE RELEASE
2535+
MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE
2536+
MAP_IMPORTED_CONFIG_MINSIZEREL RELEASE
2537+
)
2538+
25162539
target_link_libraries(
25172540
SerialPrograms PRIVATE
25182541
tesseractPA.lib
25192542
OpenCV_lib
25202543
ONNX_lib
25212544
ONNX_Providers_lib
25222545
dpp_lib
2546+
discord_lib
25232547
)
2548+
25242549
target_compile_definitions(
25252550
SerialPrograms PRIVATE
25262551
_CRT_SECURE_NO_WARNINGS
25272552
_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
25282553
PA_TESSERACT
25292554
PA_DPP
2555+
PA_SOCIAL_SDK
25302556
)
25312557

25322558
target_compile_options(SerialPrograms PRIVATE /FAs /FaAssembly/ /MP /W4 /WX /external:anglebrackets /external:W0 /utf-8)
@@ -2771,6 +2797,16 @@ file(GLOB MY_DLLS
27712797
"../3rdPartyBinaries/*.dll"
27722798
)
27732799
file(COPY ${MY_DLLS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
2800+
2801+
# Copy discord_social_sdk DLL based on build type
2802+
add_custom_command(
2803+
TARGET SerialPrograms
2804+
POST_BUILD
2805+
COMMAND ${CMAKE_COMMAND} -E copy
2806+
$<IF:$<CONFIG:Debug>,"${PROJECT_ROOT_DIR}/3rdPartyBinaries/discord_social_sdk/bin/debug/discord_partner_sdk.dll","${PROJECT_ROOT_DIR}/3rdPartyBinaries/discord_social_sdk/bin/release/discord_partner_sdk.dll">
2807+
$<TARGET_FILE_DIR:SerialPrograms>/Binaries64/discord_partner_sdk.dll
2808+
COMMENT "Copying Discord SDK DLL to Binaries64"
2809+
)
27742810
endif()
27752811

27762812
if (QT_MAJOR GREATER_EQUAL 6)

SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ GlobalSettings::GlobalSettings()
157157
"online documentation"
158158
) + ")</font>"
159159
)
160+
, RICH_PRESENCE(
161+
"<b>Enable Rich Presence:</b><br>Will display program activity and status under your Discord user.",
162+
LockMode::UNLOCK_WHILE_RUNNING,
163+
true
164+
)
160165
, ALL_STATS(
161166
"<b>All Stats:</b><br>Include all-time stats for notifications.",
162167
LockMode::UNLOCK_WHILE_RUNNING,
@@ -223,6 +228,7 @@ GlobalSettings::GlobalSettings()
223228
#endif
224229

225230
PA_ADD_STATIC(m_discord_settings);
231+
PA_ADD_OPTION(RICH_PRESENCE);
226232
PA_ADD_OPTION(ALL_STATS);
227233
PA_ADD_OPTION(DISCORD);
228234

SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class GlobalSettings : public BatchOption, private ConfigOption::Listener{
103103
Pimpl<SleepSuppressOptions> SLEEP_SUPPRESS;
104104

105105
SectionDividerOption m_discord_settings;
106+
BooleanCheckBoxOption RICH_PRESENCE;
106107
BooleanCheckBoxOption ALL_STATS;
107108
Pimpl<Integration::DiscordSettingsOption> DISCORD;
108109

SerialPrograms/Source/CommonFramework/Main.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//#include <QTextStream>
66
#include <QMessageBox>
77
#include <Integrations/DppIntegration/DppClient.h>
8+
#include <Integrations/DiscordSocial/DiscordSocial.h>
89
#include "Common/Cpp/Exceptions.h"
910
#include "Common/Cpp/ImageResolution.h"
1011
#include "PersistentSettings.h"
@@ -115,6 +116,12 @@ int main(int argc, char *argv[]){
115116
discord_settings.on_config_value_changed(nullptr);
116117
}
117118

119+
if (GlobalSettings::instance().RICH_PRESENCE){
120+
#ifdef PA_SOCIAL_SDK
121+
Integration::DiscordSocialSDK::DiscordSocial::instance().run();
122+
#endif
123+
}
124+
118125
set_working_directory();
119126

120127
// Run this asynchronously to we don't block startup.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#ifdef PA_SOCIAL_SDK
2+
#define DISCORDPP_IMPLEMENTATION
3+
4+
#include <thread>
5+
#include "DiscordSocial.h"
6+
#include "CommonFramework/Logging/Logger.h"
7+
#include "Integrations/ProgramTracker.h"
8+
#include "../../CommonFramework/GlobalSettingsPanel.h"
9+
10+
using namespace discordpp;
11+
namespace PokemonAutomation{
12+
namespace Integration{
13+
namespace DiscordSocialSDK{
14+
15+
DiscordSocial& DiscordSocial::instance(){
16+
static DiscordSocial instance;
17+
return instance;
18+
}
19+
20+
void DiscordSocial::run(){
21+
m_client = std::make_shared<Client>();
22+
if (!m_client){
23+
log("Failed to initialize DiscordSocialSDK.", "run()", LoggingSeverity::Error);
24+
m_client.reset();
25+
return;
26+
}
27+
28+
m_client->SetApplicationId(m_app_id);
29+
m_client->AddLogCallback([&](auto message, auto severity){
30+
log(message, "Internal", severity);
31+
}, m_log_level);
32+
33+
std::thread(&DiscordSocial::thread_loop, this).detach();
34+
}
35+
36+
void DiscordSocial::thread_loop(){
37+
log("Starting Discord Rich Presence update thread...", "thread_loop()", LoggingSeverity::Info);
38+
m_running.store(true, std::memory_order_release);
39+
m_activity = create_activity();
40+
m_client->UpdateRichPresence(m_activity, [&](ClientResult result){
41+
if (!result.Successful()){
42+
log("Rich Presence initial update failed.", "thread_loop()", LoggingSeverity::Warning);
43+
}
44+
});
45+
46+
while (m_running.load(std::memory_order_relaxed)){
47+
try{
48+
if (!m_running.load(std::memory_order_relaxed) || !GlobalSettings::instance().RICH_PRESENCE){
49+
break;
50+
}
51+
52+
RunCallbacks();
53+
update_rich_presence();
54+
std::this_thread::sleep_for(std::chrono::seconds(1));
55+
}catch (const std::exception& e){
56+
log("Exception: " + std::string(e.what()), "thread_loop()", LoggingSeverity::Error);
57+
break;
58+
}
59+
}
60+
61+
log("Discord Rich Presence update thread exiting...", "thread_loop()", LoggingSeverity::Info);
62+
m_running.store(false, std::memory_order_release);
63+
m_client.reset();
64+
}
65+
66+
void DiscordSocial::update_rich_presence(){
67+
try{
68+
std::string details = m_activity.Details().value();
69+
std::string state = m_activity.State().value();
70+
71+
discordpp::ActivityAssets assets{};
72+
assets.SetLargeImage("rotom");
73+
assets.SetLargeText("Pok\u00E9mon Automation");
74+
75+
for (const auto& item : ProgramTracker::instance().all_programs()){
76+
switch (item.second.state) {
77+
case ProgramState::RUNNING:{
78+
assets.SetSmallImage("running");
79+
assets.SetSmallText("Running");
80+
state = "Running";
81+
m_activity.SetAssets(assets); break;
82+
}
83+
case ProgramState::STOPPED:{
84+
assets.SetSmallImage("stopped");
85+
assets.SetSmallText("Stopped");
86+
state = "Stopped";
87+
m_activity.SetAssets(assets); break;
88+
}
89+
default: {
90+
assets.SetSmallImage("idle");
91+
assets.SetSmallText("Idle");
92+
state = "Idle";
93+
m_activity.SetAssets(assets); break;
94+
}
95+
}
96+
97+
if (item.second.program_name != details){
98+
details = item.second.program_name;
99+
}
100+
101+
m_timestamps.SetStart(std::chrono::duration_cast<std::chrono::seconds>(item.second.start_time.time_since_epoch()).count());
102+
}
103+
104+
m_activity.SetTimestamps(m_timestamps);
105+
m_activity.SetDetails(details);
106+
m_activity.SetState(state);
107+
m_client->UpdateRichPresence(m_activity, [&](ClientResult result){
108+
if (!result.Successful()){
109+
log("Rich Presence update failed.", "update_rp()", LoggingSeverity::Warning);
110+
}
111+
});
112+
}catch (const std::exception& e){
113+
log("Exception: " + std::string(e.what()), "update_rp()", LoggingSeverity::Error);
114+
m_running.store(false, std::memory_order_release);
115+
}
116+
}
117+
118+
Logger& DiscordSocial::logger(){
119+
static TaggedLogger logger(global_logger_raw(), "DiscordSocialSDK");
120+
return logger;
121+
}
122+
123+
void DiscordSocial::log(const std::string& message, const std::string& identity, const LoggingSeverity& severity){
124+
if (severity < m_log_level){
125+
return;
126+
}
127+
128+
std::string log = identity + ": " + message;
129+
Color color;
130+
switch (severity){
131+
case LoggingSeverity::Verbose: color = COLOR_GRAY; break;
132+
case LoggingSeverity::Info: color = COLOR_CYAN; break;
133+
case LoggingSeverity::Warning: color = COLOR_ORANGE; break;
134+
case LoggingSeverity::Error: color = COLOR_MAGENTA; break;
135+
default: color = COLOR_PURPLE; break;
136+
};
137+
138+
logger().log(log, color);
139+
}
140+
141+
Activity DiscordSocial::create_activity(){
142+
Activity activity{};
143+
activity.SetType(ActivityTypes::Playing);
144+
activity.SetDetails("ComputerControl: Preparing to automate...");
145+
activity.SetState("Idle");
146+
147+
ActivityButton button1{};
148+
button1.SetLabel("Download from GitHub");
149+
button1.SetUrl("https://github.com/PokemonAutomation/ComputerControl/releases/latest");
150+
activity.AddButton(button1);
151+
152+
ActivityButton button2{};
153+
button2.SetLabel("Join our Discord");
154+
button2.SetUrl("https://discord.gg/pokemonautomation");
155+
activity.AddButton(button2);
156+
157+
discordpp::ActivityAssets assets{};
158+
assets.SetLargeImage("rotom");
159+
assets.SetLargeText("Pok\u00E9mon Automation");
160+
assets.SetSmallImage("idle");
161+
assets.SetSmallText("Idle");
162+
activity.Assets().emplace(assets);
163+
return activity;
164+
}
165+
166+
167+
}
168+
}
169+
}
170+
#endif
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#pragma once
2+
#ifndef DISCORD_SOCIAL_H
3+
#define DISCORD_SOCIAL_H
4+
5+
#include <atomic>
6+
#include <discord_social_sdk/discordpp.h>
7+
#include "Common/Cpp/AbstractLogger.h"
8+
9+
namespace PokemonAutomation{
10+
namespace Integration{
11+
namespace DiscordSocialSDK{
12+
class DiscordSocial{
13+
public:
14+
DiscordSocial() : m_running(false) {}
15+
~DiscordSocial(){
16+
m_running.store(false, std::memory_order_release);
17+
if (m_client) m_client.reset();
18+
}
19+
20+
static DiscordSocial& instance();
21+
void run();
22+
23+
private:
24+
Logger& logger();
25+
void log(const std::string& message, const std::string& identity, const discordpp::LoggingSeverity& severity);
26+
discordpp::Activity create_activity();
27+
void update_rich_presence();
28+
void thread_loop();
29+
30+
private:
31+
std::shared_ptr<discordpp::Client> m_client = nullptr;
32+
std::atomic<bool> m_running;
33+
const uint64_t m_app_id = 1406867596585865326;
34+
discordpp::Activity m_activity;
35+
discordpp::ActivityTimestamps m_timestamps{};
36+
discordpp::LoggingSeverity m_log_level = discordpp::LoggingSeverity::Error;
37+
};
38+
}
39+
}
40+
}
41+
#endif

0 commit comments

Comments
 (0)