Skip to content

Commit

Permalink
feat: implement discord buttons
Browse files Browse the repository at this point in the history
This commit introduces buttons to the rich presences.
The JSON structure would be like this:
```"buttons": [
    {
        "label": "Global Button1",
        "url": "https://url"
    },
    {
        "label": "Global Button2",
        "url": "https://url"
    }
]```
The buttons array can contain 2 elements as a maximum.
Like other parameters, the buttons can be implemented globally and in each state.
  • Loading branch information
Kirollos committed Nov 15, 2024
1 parent 4acc4b1 commit 2e3b6c4
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "discord-rpc"]
path = discord-rpc
url = https://github.com/discord/discord-rpc.git
url = https://github.com/multitheftauto/discord-rpc.git
4 changes: 4 additions & 0 deletions DiscordEuroscope/ConfigData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ namespace DiscordEuroScope_Configuration
this->discord_presence_small_image_key = "";
this->sweatbox_bypass = false;
this->loaded_from_ese = false;
this->buttons[0] = Button();
this->buttons[1] = Button();
this->RadioCallsigns.clear();
for (int i = 0; i < 7; i++)
{
Expand All @@ -40,6 +42,8 @@ namespace DiscordEuroScope_Configuration
this->states[i].presence_large_image_text = "";
this->states[i].presence_small_image_key = "";
this->states[i].presence_small_image_text = "";
this->states[i].buttons[0] = Button();
this->states[i].buttons[1] = Button();
this->states[i].used = false;
}
}
Expand Down
22 changes: 22 additions & 0 deletions DiscordEuroscope/ConfigData.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ namespace DiscordEuroScope_Configuration
std::string radio_callsign;
} RadioCallsignElement_t;
typedef std::vector<RadioCallsignElement_t> RadioCallsigns_t;

struct Button
{
std::string label;
std::string url;

Button()
{
}

Button(std::string label, std::string url) : label{label}, url{url}
{
}

inline bool IsValid() const
{
return !label.empty() && !url.empty();
}
};

enum States_Enum
{
State_Idle,
Expand All @@ -53,6 +73,7 @@ namespace DiscordEuroScope_Configuration
std::string details;
std::string presence_small_image_text;
std::string presence_large_image_text;
Button buttons[2];
};

class ConfigData
Expand All @@ -62,6 +83,7 @@ namespace DiscordEuroScope_Configuration
std::string discord_presence_large_image_key;
std::string discord_presence_small_image_key;
bool sweatbox_bypass;
Button buttons[2];
State states[7];
RadioCallsigns_t RadioCallsigns;
bool loaded_from_ese = false;
Expand Down
48 changes: 48 additions & 0 deletions DiscordEuroscope/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ namespace DiscordEuroScope_Configuration
data.discord_presence_small_image_key = json_document["discord_presence_small_image_key"].GetString();
assert(json_document["sweatbox_bypass"].IsBool());
data.sweatbox_bypass = json_document["sweatbox_bypass"].GetBool();

if (json_document.HasMember("buttons") && json_document["buttons"].IsArray())
{
auto& arr = json_document["buttons"].GetArray();
int len = arr.Size();
if (len > 2) {
len = 2;
}
for (int i = 0; i < len; i++)
{
auto& element = arr[i];
assert(arr[i]["label"].IsString());
assert(arr[i]["url"].IsString());
data.buttons[i] = Button(arr[i]["label"].GetString(), arr[i]["url"].GetString());
}
}

assert(json_document["states"].IsObject());
assert(json_document["states"]["idle"].IsObject());
Expand All @@ -96,6 +112,22 @@ namespace DiscordEuroScope_Configuration
data.states[i].details = objects[i]["details"].GetString();
data.states[i].presence_small_image_text = objects[i]["presence_small_image_text"].GetString();
data.states[i].presence_large_image_text = objects[i]["presence_large_image_text"].GetString();

if (objects[i].HasMember("buttons") && objects[i]["buttons"].IsArray())
{
auto& arr = objects[i]["buttons"].GetArray();
int len = arr.Size();
if (len > 2) {
len = 2;
}
for (int j = 0; j < len; j++)
{
auto& element = arr[j];
assert(arr[j]["label"].IsString());
assert(arr[j]["url"].IsString());
data.states[i].buttons[j] = Button(arr[j]["label"].GetString(), arr[j]["url"].GetString());
}
}
}

for (int i = 4; i < 7; i++)
Expand Down Expand Up @@ -125,6 +157,22 @@ namespace DiscordEuroScope_Configuration
data.states[i].details = obj["details"].GetString();
data.states[i].presence_small_image_text = obj["presence_small_image_text"].GetString();
data.states[i].presence_large_image_text = obj["presence_large_image_text"].GetString();

if (obj.HasMember("buttons") && obj["buttons"].IsArray())
{
auto& arr = obj["buttons"].GetArray();
int len = arr.Size();
if (len > 2) {
len = 2;
}
for (int j = 0; j < len; j++)
{
auto& element = arr[j];
assert(arr[j]["label"].IsString());
assert(arr[j]["url"].IsString());
data.states[i].buttons[j] = Button(arr[j]["label"].GetString(), arr[j]["url"].GetString());
}
}
}
_ready = true;
}
Expand Down
6 changes: 6 additions & 0 deletions DiscordEuroscope/DefaultFileContent.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
}\r\
}\r\
},\r\
\"buttons\": [\r\
{\r\
\"label\": \"Callsign stats\",\r\
\"url\" : \"https://stats.vatsim.net/search/{callsign}\"\r\
},\r\
],\r\
\"radio_callsigns\": {\r\
\"config\": {\r\
\"load_from_ese\": false,\r\
Expand Down
84 changes: 84 additions & 0 deletions DiscordEuroscope/DiscordEuroscopeExt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent
discordPresence.largeImageKey = data.discord_presence_large_image_key.c_str();
discordPresence.smallImageKey = data.discord_presence_small_image_key.c_str();
discordPresence.startTimestamp = pMyPlugIn->EuroInittime;

DiscordButton buttons[2] = { nullptr, nullptr };

switch (pMyPlugIn->GetConnectionType())
{
Expand All @@ -112,6 +114,25 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent
discordPresence.smallImageKey = ConfigData::LocalOrGlobal(data.states[State_Idle].presence_small_image_key, data.discord_presence_small_image_key).c_str();
discordPresence.smallImageText = data.states[State_Idle].presence_small_image_text.c_str();
discordPresence.state = data.states[State_Idle].state.c_str();
if (data.buttons[0].IsValid()) {
buttons[0] =
{
ConfigData::LocalOrGlobal(data.states[State_Idle].buttons[0].label, data.buttons[0].label).c_str(),
ConfigData::LocalOrGlobal(data.states[State_Idle].buttons[0].url, data.buttons[0].url).c_str()
};
// Nested condition because there will never be a single button in index 1.
if (data.buttons[1].IsValid()) {
buttons[1] =
{
ConfigData::LocalOrGlobal(data.states[State_Idle].buttons[1].label, data.buttons[1].label).c_str(),
ConfigData::LocalOrGlobal(data.states[State_Idle].buttons[1].url, data.buttons[1].url).c_str()
};
}
else {
buttons[1] = { "" , "" };
}
discordPresence.buttons = buttons;
}
Discord_UpdatePresence(&discordPresence);
Discord_RunCallbacks();
return;
Expand All @@ -122,6 +143,25 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent
discordPresence.smallImageKey = ConfigData::LocalOrGlobal(data.states[State_Playback].presence_small_image_key, data.discord_presence_small_image_key).c_str();
discordPresence.smallImageText = data.states[State_Playback].presence_small_image_text.c_str();
discordPresence.state = data.states[State_Playback].state.c_str();
if (data.buttons[0].IsValid()) {
buttons[0] =
{
ConfigData::LocalOrGlobal(data.states[State_Playback].buttons[0].label, data.buttons[0].label).c_str(),
ConfigData::LocalOrGlobal(data.states[State_Playback].buttons[0].url, data.buttons[0].url).c_str()
};
// Nested condition because there will never be a single button in index 1.
if (data.buttons[1].IsValid()) {
buttons[1] =
{
ConfigData::LocalOrGlobal(data.states[State_Playback].buttons[1].label, data.buttons[1].label).c_str(),
ConfigData::LocalOrGlobal(data.states[State_Playback].buttons[1].url, data.buttons[1].url).c_str()
};
}
else {
buttons[1] = { "" , "" };
}
discordPresence.buttons = buttons;
}
Discord_UpdatePresence(&discordPresence);
Discord_RunCallbacks();
return;
Expand All @@ -134,6 +174,24 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent
discordPresence.smallImageKey = ConfigData::LocalOrGlobal(data.states[State_Sweatbox].presence_small_image_key, data.discord_presence_small_image_key).c_str();
discordPresence.smallImageText = data.states[State_Sweatbox].presence_small_image_text.c_str();
discordPresence.state = data.states[State_Sweatbox].state.c_str();
if (data.buttons[0].IsValid()) {
buttons[0] =
{
ConfigData::LocalOrGlobal(data.states[State_Sweatbox].buttons[0].label, data.buttons[0].label).c_str(),
ConfigData::LocalOrGlobal(data.states[State_Sweatbox].buttons[0].url, data.buttons[0].url).c_str()
};
if (data.buttons[1].IsValid()) {
buttons[1] =
{
ConfigData::LocalOrGlobal(data.states[State_Sweatbox].buttons[1].label, data.buttons[1].label).c_str(),
ConfigData::LocalOrGlobal(data.states[State_Sweatbox].buttons[1].url, data.buttons[1].url).c_str()
};
}
else {
buttons[1] = { "" , "" };
}
discordPresence.buttons = buttons;
}
Discord_UpdatePresence(&discordPresence);
Discord_RunCallbacks();
return;
Expand Down Expand Up @@ -195,6 +253,32 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent
presence_small_image_text = ConfigData::LocalOrGlobal(data.states[use_state].presence_small_image_text, data.states[State_Direct].presence_small_image_text);
presence_large_image_text = ConfigData::LocalOrGlobal(data.states[use_state].presence_large_image_text, data.states[State_Direct].presence_large_image_text);

if (data.buttons[0].IsValid()) {
std::string button1_label = ConfigData::LocalOrGlobal(ConfigData::LocalOrGlobal(data.states[use_state].buttons[0].label, data.states[State_Direct].buttons[0].label), data.buttons[0].label);
MessageFormatter::formatmap(button1_label, Dictionary);
std::string button1_url = ConfigData::LocalOrGlobal(ConfigData::LocalOrGlobal(data.states[use_state].buttons[0].url, data.states[State_Direct].buttons[0].url), data.buttons[0].url);
MessageFormatter::formatmap(button1_url, Dictionary);
buttons[0] = {
button1_label.c_str(),
button1_url.c_str()
};
if (data.buttons[1].IsValid()) {

std::string button2_label = ConfigData::LocalOrGlobal(ConfigData::LocalOrGlobal(data.states[use_state].buttons[1].label, data.states[State_Direct].buttons[1].label), data.buttons[1].label);
MessageFormatter::formatmap(button2_label, Dictionary);
std::string button2_url = ConfigData::LocalOrGlobal(ConfigData::LocalOrGlobal(data.states[use_state].buttons[1].url, data.states[State_Direct].buttons[1].url), data.buttons[1].url);
MessageFormatter::formatmap(button2_url, Dictionary);
buttons[1] = {
button2_label.c_str(),
button2_url.c_str()
};
}
else {
buttons[1] = { "" , "" };
}
discordPresence.buttons = buttons;
}

MessageFormatter::formatmap(state, Dictionary);
MessageFormatter::formatmap(details, Dictionary);
MessageFormatter::formatmap(presence_small_image_text, Dictionary);
Expand Down
9 changes: 9 additions & 0 deletions DiscordEuroscope/inc/discord_rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
extern "C" {
#endif

typedef struct DiscordButton {
const char* label; /* TODO: max 128 bytes? */
const char* url; /* TODO: would be reasonable to expect it to be >=128 bytes? */
} DiscordButton;

typedef struct DiscordRichPresence {
const char* state; /* max 128 bytes */
const char* details; /* max 128 bytes */
Expand All @@ -35,10 +40,12 @@ typedef struct DiscordRichPresence {
const char* partyId; /* max 128 bytes */
int partySize;
int partyMax;
int partyPrivacy;
const char* matchSecret; /* max 128 bytes */
const char* joinSecret; /* max 128 bytes */
const char* spectateSecret; /* max 128 bytes */
int8_t instance;
const DiscordButton* buttons;
} DiscordRichPresence;

typedef struct DiscordUser {
Expand All @@ -60,6 +67,8 @@ typedef struct DiscordEventHandlers {
#define DISCORD_REPLY_NO 0
#define DISCORD_REPLY_YES 1
#define DISCORD_REPLY_IGNORE 2
#define DISCORD_PARTY_PRIVATE 0
#define DISCORD_PARTY_PUBLIC 1

DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
Expand Down
Binary file modified DiscordEuroscope/lib/3.2/discord-rpc-rel.lib
Binary file not shown.
Binary file modified DiscordEuroscope/lib/3.2/discord-rpc.lib
Binary file not shown.
2 changes: 1 addition & 1 deletion discord-rpc

0 comments on commit 2e3b6c4

Please sign in to comment.