Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions Sources/SwitchRPC/source/discord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ void getRefreshTokenFromFile() {
std::ifstream file("sdmc:/config/switchrpc_token");
if (!file.is_open()) {
writeToLog("[Discord] No refresh token found at sdmc:/config/switchrpc_token");
return;
// keep any in-memory token: logout deletes the file first, and we still
// need to authenticate to tear the sessions down before forgetting it
return;
}

std::string line;
Expand Down Expand Up @@ -147,6 +149,10 @@ bool sendRequest(const char* url, const char* method, struct curl_slist* headers
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
// don't let a request hang forever - matters for the pre-sleep cleanup,
// it shouldn't hold up the console going to sleep
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);

// temporary fix to failing dns resolution
struct curl_slist *dns_cache = NULL;
Expand Down Expand Up @@ -319,7 +325,7 @@ void discordCleanupStaleSessions() {
}
}

void discordCreateHeadlessSession(u64 titleId, std::string titleName, const bool includeToken) {
void discordCreateHeadlessSession(u64 titleId, std::string titleName, u64 startEpochSec, const bool includeToken) {
writeToLog("[Discord] Creating/Updating session for TID: %016llX (%s)", (unsigned long long)titleId, titleName.c_str());

// tries to fetch eshop icon, else falls back to tinfoil media icon
Expand All @@ -341,7 +347,15 @@ void discordCreateHeadlessSession(u64 titleId, std::string titleName, const bool
json_object_object_add(json_activity, "platform", json_object_new_string("desktop"));
json_object_object_add(json_activity, "name", json_object_new_string(titleName.c_str()));
json_object_object_add(json_activity, "state", json_object_new_string("Nintendo Switch"));


// elapsed timer: discord shows (now - start). we pass a start moved forward
// to exclude sleep, so it counts real playtime. timestamps are in ms.
if (startEpochSec != 0) {
json_object* json_timestamps = json_object_new_object();
json_object_object_add(json_timestamps, "start", json_object_new_int64((int64_t)startEpochSec * 1000));
json_object_object_add(json_activity, "timestamps", json_timestamps);
}

json_object* json_assets = json_object_new_object();
json_object_object_add(json_assets, "large_image", json_object_new_string(iconUrl.c_str()));
json_object_object_add(json_activity, "assets", json_assets);
Expand All @@ -361,19 +375,21 @@ void discordCreateHeadlessSession(u64 titleId, std::string titleName, const bool

std::string response;
bool success = authenticatedRequest("https://discord.com/api/v9/users/@me/headless-sessions", "POST", headers, body, &response);

// free it once here - used to get freed twice on a failed no-token request
// and trash the heap
json_object_put(json_body);

if (!success) {
writeToLog("[Discord] Failed to create/update headless session!");
json_object_put(json_body);

// if the attempt happened with an existing session token, try again without it in case the token was the issue
if (includeToken) {
writeToLog("[Discord] Retrying headless session creation without session token...");
return discordCreateHeadlessSession(titleId, titleName, false);
return discordCreateHeadlessSession(titleId, titleName, startEpochSec, false);
}
return;
}

json_object_put(json_body);

json_object* json_response = json_tokener_parse(response.c_str());
if (json_response == NULL) {
writeToLog("[Discord] Failed to parse headless session response JSON!");
Expand Down Expand Up @@ -423,3 +439,17 @@ void discordDeleteHeadlessSession() {
sessionTokenExpiry = 0;
writeToLog("[Discord] Session deleted successfully.");
}

void discordLogout() {
writeToLog("[Discord] Logging out - dropping session and stored tokens.");
// delete the active session + any leftovers. these authenticate, which may
// refresh (and rewrite) the token file, so we still have valid auth here.
discordDeleteHeadlessSession();
discordCleanupStaleSessions();
// now forget everything and make sure we stay logged out even if a refresh
// just rewrote the token file
refreshToken = "";
authToken = "";
authTokenExpiry = 0;
remove("sdmc:/config/switchrpc_token");
}
11 changes: 9 additions & 2 deletions Sources/SwitchRPC/source/discord.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
* Creates or updates a Discord Headless Session to show the current game.
*
* @param titleId The Title ID of the currently running game.
* @param startEpochSec Unix time (seconds) the elapsed timer should count from; 0 to omit it.
* @param includeToken Whether to include the current sessionToken in the request.
*/
void discordCreateHeadlessSession(u64 titleId, std::string titleName, const bool includeToken);
void discordCreateHeadlessSession(u64 titleId, std::string titleName, u64 startEpochSec, const bool includeToken);

/**
* Deletes the active Discord Headless Session.
Expand All @@ -26,4 +27,10 @@ void discordDeleteHeadlessSession();
* Deletes all known sessions by clearing the sessions file.
* Call this on startup or when waking from sleep to clean up any sessions that should no longer be active.
*/
void discordCleanupStaleSessions();
void discordCleanupStaleSessions();

/**
* Tears down the active session and forgets all stored tokens.
* Call when the user logs out from the config app.
*/
void discordLogout();
2 changes: 1 addition & 1 deletion Sources/SwitchRPC/source/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ void writeToLog(const char* format, ...) {
}
TimeLocationName location;
timeGetDeviceLocationName(&location);
TimeZoneRule rule;
static TimeZoneRule rule; // 16kb, keep it off the tiny sysmodule stack
timeLoadTimeZoneRule(&location, &rule);
TimeCalendarTime t;
timeToCalendarTime(&rule, timestamp, &t, nullptr);
Expand Down
Loading
Loading