// Discord Social SDK bindings for Cell #include "cell.h" #include #include #include #include "cdiscord.h" // Global state static Discord_Client *discord_client = NULL; static JSContext *stored_js_ctx = NULL; static uint64_t discord_application_id = 0; // Stored JS callbacks static JSValue js_status_cb = JS_UNINITIALIZED; static JSValue js_log_cb = JS_UNINITIALIZED; static JSValue js_auth_cb = JS_UNINITIALIZED; static JSValue js_token_cb = JS_UNINITIALIZED; static JSValue js_update_token_cb = JS_UNINITIALIZED; static JSValue js_presence_cb = JS_UNINITIALIZED; // Stored code verifier for OAuth flow static Discord_AuthorizationCodeVerifier *stored_verifier = NULL; // Helper: Discord_String to JS string static JSValue dstr_to_js(JSContext *js, Discord_String *str) { if (!str || !str->ptr || str->size == 0) return JS_NULL; return JS_NewStringLen(js, (const char*)str->ptr, str->size); } // Helper: JS string to Discord_String static Discord_String js_to_dstr(JSContext *js, JSValue val) { Discord_String str = {0}; if (JS_IsNull(val)) return str; size_t len; const char *cstr = JS_ToCStringLen(js, &len, val); if (cstr) { str.ptr = (uint8_t*)Discord_Alloc(len + 1); if (str.ptr) { memcpy(str.ptr, cstr, len); str.ptr[len] = 0; str.size = len; } JS_FreeCString(js, cstr); } return str; } // Helper: Free Discord_String static void free_dstr(Discord_String *str) { if (str && str->ptr) { Discord_Free(str->ptr); str->ptr = NULL; str->size = 0; } } // Helper: JS value (string or number) to uint64_t static uint64_t js_to_u64(JSContext *js, JSValueConst v) { if (JS_IsString(v)) { uint64_t out = 0; const char *s = JS_ToCString(js, v); if (s) { out = strtoull(s, NULL, 10); JS_FreeCString(js, s); } return out; } return (uint64_t)js2number(js, v); } // Helper: free stored JS callback value static void free_cb(JSContext *js, JSValue *cb) { if (!JS_IsUninitialized(*cb)) { JS_FreeValue(js, *cb); *cb = JS_UNINITIALIZED; } } // Helper: UserHandle to JS object static JSValue user_to_js(JSContext *js, Discord_UserHandle *h) { if (!h) return JS_NULL; JSValue u = JS_NewObject(js); { char buf[32]; snprintf(buf, sizeof(buf), "%llu", (unsigned long long)Discord_UserHandle_Id(h)); JS_SetPropertyStr(js, u, "id", JS_NewString(js, buf)); } Discord_String s; Discord_UserHandle_Username(h, &s); JS_SetPropertyStr(js, u, "username", dstr_to_js(js, &s)); Discord_UserHandle_DisplayName(h, &s); JS_SetPropertyStr(js, u, "display_name", dstr_to_js(js, &s)); if (Discord_UserHandle_GlobalName(h, &s)) JS_SetPropertyStr(js, u, "global_name", dstr_to_js(js, &s)); Discord_UserHandle_AvatarUrl(h, Discord_UserHandle_AvatarType_Png, Discord_UserHandle_AvatarType_Png, &s); JS_SetPropertyStr(js, u, "avatar_url", dstr_to_js(js, &s)); Discord_StatusType st = Discord_UserHandle_Status(h); const char *ss = "unknown"; if (st == Discord_StatusType_Online) ss = "online"; else if (st == Discord_StatusType_Offline) ss = "offline"; else if (st == Discord_StatusType_Idle) ss = "idle"; else if (st == Discord_StatusType_Dnd) ss = "dnd"; JS_SetPropertyStr(js, u, "status", JS_NewString(js, ss)); JS_SetPropertyStr(js, u, "is_provisional", JS_NewBool(js, Discord_UserHandle_IsProvisional(h))); return u; } // Helper: RelationshipHandle to JS object static JSValue rel_to_js(JSContext *js, Discord_RelationshipHandle *h) { if (!h) return JS_NULL; JSValue r = JS_NewObject(js); { char buf[32]; snprintf(buf, sizeof(buf), "%llu", (unsigned long long)Discord_RelationshipHandle_Id(h)); JS_SetPropertyStr(js, r, "id", JS_NewString(js, buf)); } Discord_RelationshipType t = Discord_RelationshipHandle_DiscordRelationshipType(h); const char *ts = "none"; if (t == Discord_RelationshipType_Friend) ts = "friend"; else if (t == Discord_RelationshipType_Blocked) ts = "blocked"; else if (t == Discord_RelationshipType_PendingIncoming) ts = "pending_incoming"; else if (t == Discord_RelationshipType_PendingOutgoing) ts = "pending_outgoing"; JS_SetPropertyStr(js, r, "type", JS_NewString(js, ts)); Discord_UserHandle uh; if (Discord_RelationshipHandle_User(h, &uh)) JS_SetPropertyStr(js, r, "user", user_to_js(js, &uh)); return r; } // Callback: Status changed static void cb_status(Discord_Client_Status status, Discord_Client_Error error, int32_t detail, void* ud) { if (!stored_js_ctx || JS_IsUninitialized(js_status_cb)) return; JSContext *js = stored_js_ctx; const char *ss = "unknown"; if (status == Discord_Client_Status_Disconnected) ss = "disconnected"; else if (status == Discord_Client_Status_Connecting) ss = "connecting"; else if (status == Discord_Client_Status_Connected) ss = "connected"; else if (status == Discord_Client_Status_Ready) ss = "ready"; else if (status == Discord_Client_Status_Reconnecting) ss = "reconnecting"; const char *es = "none"; if (error == Discord_Client_Error_ConnectionFailed) es = "connection_failed"; else if (error == Discord_Client_Error_UnexpectedClose) es = "unexpected_close"; JSValue args[3] = { JS_NewString(js, ss), JS_NewString(js, es), JS_NewInt32(js, detail) }; JSValue res = JS_Call(js, js_status_cb, JS_NULL, 3, args); JS_FreeValue(js, args[0]); JS_FreeValue(js, args[1]); JS_FreeValue(js, args[2]); JS_FreeValue(js, res); } // Callback: Log static void cb_log(Discord_String msg, Discord_LoggingSeverity sev, void* ud) { if (!stored_js_ctx || JS_IsUninitialized(js_log_cb)) return; JSContext *js = stored_js_ctx; const char *ss = "info"; if (sev == Discord_LoggingSeverity_Verbose) ss = "verbose"; else if (sev == Discord_LoggingSeverity_Warning) ss = "warning"; else if (sev == Discord_LoggingSeverity_Error) ss = "error"; JSValue args[2] = { dstr_to_js(js, &msg), JS_NewString(js, ss) }; JSValue res = JS_Call(js, js_log_cb, JS_NULL, 2, args); JS_FreeValue(js, args[0]); JS_FreeValue(js, args[1]); JS_FreeValue(js, res); } // Callback: Authorization static void cb_auth(Discord_ClientResult* result, Discord_String code, Discord_String uri, void* ud) { if (!stored_js_ctx || JS_IsUninitialized(js_auth_cb)) return; JSContext *js = stored_js_ctx; bool ok = Discord_ClientResult_Successful(result); JSValue args[4]; args[0] = JS_NewBool(js, ok); if (ok) { args[1] = dstr_to_js(js, &code); args[2] = dstr_to_js(js, &uri); args[3] = JS_NULL; } else { args[1] = JS_NULL; args[2] = JS_NULL; Discord_String e; Discord_ClientResult_Error(result, &e); args[3] = dstr_to_js(js, &e); } JSValue res = JS_Call(js, js_auth_cb, JS_NULL, 4, args); for (int i = 0; i < 4; i++) JS_FreeValue(js, args[i]); JS_FreeValue(js, res); } // Callback: Token exchange static void cb_token(Discord_ClientResult* result, Discord_String access, Discord_String refresh, Discord_AuthorizationTokenType type, int32_t expires, Discord_String scope, void* ud) { if (!stored_js_ctx || JS_IsUninitialized(js_token_cb)) return; JSContext *js = stored_js_ctx; bool ok = Discord_ClientResult_Successful(result); JSValue obj = JS_NewObject(js); JS_SetPropertyStr(js, obj, "success", JS_NewBool(js, ok)); if (ok) { JS_SetPropertyStr(js, obj, "access_token", dstr_to_js(js, &access)); JS_SetPropertyStr(js, obj, "refresh_token", dstr_to_js(js, &refresh)); JS_SetPropertyStr(js, obj, "token_type", JS_NewString(js, type == Discord_AuthorizationTokenType_Bearer ? "bearer" : "user")); JS_SetPropertyStr(js, obj, "expires_in", JS_NewInt32(js, expires)); JS_SetPropertyStr(js, obj, "scope", dstr_to_js(js, &scope)); } else { Discord_String e; Discord_ClientResult_Error(result, &e); JS_SetPropertyStr(js, obj, "error", dstr_to_js(js, &e)); } JSValue args[1] = { obj }; JSValue res = JS_Call(js, js_token_cb, JS_NULL, 1, args); JS_FreeValue(js, obj); JS_FreeValue(js, res); } // Callback: Update token static void cb_update_token(Discord_ClientResult* result, void* ud) { if (!stored_js_ctx || JS_IsUninitialized(js_update_token_cb)) return; JSContext *js = stored_js_ctx; bool ok = Discord_ClientResult_Successful(result); JSValue args[2]; args[0] = JS_NewBool(js, ok); if (!ok) { Discord_String e; Discord_ClientResult_Error(result, &e); args[1] = dstr_to_js(js, &e); } else args[1] = JS_NULL; JSValue res = JS_Call(js, js_update_token_cb, JS_NULL, 2, args); JS_FreeValue(js, args[0]); JS_FreeValue(js, args[1]); JS_FreeValue(js, res); } // Callback: Rich presence static void cb_presence(Discord_ClientResult* result, void* ud) { if (!stored_js_ctx || JS_IsUninitialized(js_presence_cb)) return; JSContext *js = stored_js_ctx; bool ok = Discord_ClientResult_Successful(result); JSValue args[2]; args[0] = JS_NewBool(js, ok); if (!ok) { Discord_String e; Discord_ClientResult_Error(result, &e); args[1] = dstr_to_js(js, &e); } else args[1] = JS_NULL; JSValue res = JS_Call(js, js_presence_cb, JS_NULL, 2, args); JS_FreeValue(js, args[0]); JS_FreeValue(js, args[1]); JS_FreeValue(js, res); } extern "C" { // ============================================================================ // CORE API // ============================================================================ // discord.init(application_id) - Initialize the Discord client JSC_CCALL(discord_init, if (discord_client) return JS_ThrowInternalError(js, "Discord client already initialized"); uint64_t app_id = 0; if (argc > 0) app_id = js_to_u64(js, argv[0]); if (app_id == 0) return JS_ThrowTypeError(js, "Application ID is required"); discord_application_id = app_id; stored_js_ctx = js; discord_client = (Discord_Client*)Discord_Alloc(sizeof(Discord_Client)); if (!discord_client) return JS_ThrowOutOfMemory(js); Discord_Client_Init(discord_client); Discord_Client_SetApplicationId(discord_client, app_id); return JS_TRUE; ) // discord.run_callbacks() - Process Discord callbacks JSC_CCALL(discord_run_callbacks, Discord_RunCallbacks(); return JS_NULL; ) // discord.shutdown() - Disconnect and clean up JSC_CCALL(discord_shutdown, if (discord_client) { Discord_Client_Disconnect(discord_client); Discord_Client_Drop(discord_client); discord_client = NULL; } if (stored_verifier) { Discord_AuthorizationCodeVerifier_Drop(stored_verifier); Discord_Free(stored_verifier); stored_verifier = NULL; } free_cb(js, &js_status_cb); free_cb(js, &js_log_cb); free_cb(js, &js_auth_cb); free_cb(js, &js_token_cb); free_cb(js, &js_update_token_cb); free_cb(js, &js_presence_cb); stored_js_ctx = NULL; discord_application_id = 0; return JS_NULL; ) // discord.get_status() - Get current connection status JSC_CCALL(discord_get_status, if (!discord_client) return JS_NewString(js, "not_initialized"); Discord_Client_Status st = Discord_Client_GetStatus(discord_client); const char *s = "unknown"; if (st == Discord_Client_Status_Disconnected) s = "disconnected"; else if (st == Discord_Client_Status_Connecting) s = "connecting"; else if (st == Discord_Client_Status_Connected) s = "connected"; else if (st == Discord_Client_Status_Ready) s = "ready"; else if (st == Discord_Client_Status_Reconnecting) s = "reconnecting"; return JS_NewString(js, s); ) // discord.is_authenticated() JSC_CCALL(discord_is_authenticated, if (!discord_client) return JS_FALSE; return JS_NewBool(js, Discord_Client_IsAuthenticated(discord_client)); ) // discord.connect() JSC_CCALL(discord_connect, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); Discord_Client_Connect(discord_client); return JS_NULL; ) // discord.disconnect() JSC_CCALL(discord_disconnect, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); Discord_Client_Disconnect(discord_client); return JS_NULL; ) // ============================================================================ // CALLBACK REGISTRATION // ============================================================================ // discord.on_status_changed(callback) JSC_CCALL(discord_on_status_changed, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); if (!JS_IsUninitialized(js_status_cb)) JS_FreeValue(js, js_status_cb); if (JS_IsFunction(js, argv[0])) { js_status_cb = JS_DupValue(js, argv[0]); Discord_Client_SetStatusChangedCallback(discord_client, cb_status, NULL, NULL); } else { js_status_cb = JS_UNINITIALIZED; Discord_Client_SetStatusChangedCallback(discord_client, NULL, NULL, NULL); } return JS_NULL; ) // discord.on_log(callback, min_severity) JSC_CCALL(discord_on_log, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); if (!JS_IsUninitialized(js_log_cb)) JS_FreeValue(js, js_log_cb); if (JS_IsFunction(js, argv[0])) { js_log_cb = JS_DupValue(js, argv[0]); Discord_LoggingSeverity sev = Discord_LoggingSeverity_Info; if (argc > 1 && JS_IsString(argv[1])) { const char *s = JS_ToCString(js, argv[1]); if (strcmp(s, "verbose") == 0) sev = Discord_LoggingSeverity_Verbose; else if (strcmp(s, "warning") == 0) sev = Discord_LoggingSeverity_Warning; else if (strcmp(s, "error") == 0) sev = Discord_LoggingSeverity_Error; JS_FreeCString(js, s); } Discord_Client_AddLogCallback(discord_client, cb_log, NULL, NULL, sev); } else js_log_cb = JS_UNINITIALIZED; return JS_NULL; ) // ============================================================================ // AUTHENTICATION API // ============================================================================ // discord.get_default_scopes() JSC_CCALL(discord_get_default_scopes, Discord_String scopes; Discord_Client_GetDefaultPresenceScopes(&scopes); return dstr_to_js(js, &scopes); ) // discord.authorize(callback) JSC_CCALL(discord_authorize, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); if (!JS_IsUninitialized(js_auth_cb)) JS_FreeValue(js, js_auth_cb); js_auth_cb = JS_DupValue(js, argv[0]); if (stored_verifier) { Discord_AuthorizationCodeVerifier_Drop(stored_verifier); Discord_Free(stored_verifier); } stored_verifier = (Discord_AuthorizationCodeVerifier*)Discord_Alloc(sizeof(Discord_AuthorizationCodeVerifier)); Discord_Client_CreateAuthorizationCodeVerifier(discord_client, stored_verifier); Discord_AuthorizationCodeChallenge challenge; Discord_AuthorizationCodeVerifier_Challenge(stored_verifier, &challenge); Discord_AuthorizationArgs args; Discord_AuthorizationArgs_Init(&args); Discord_AuthorizationArgs_SetClientId(&args, discord_application_id); Discord_String scopes; Discord_Client_GetDefaultPresenceScopes(&scopes); Discord_AuthorizationArgs_SetScopes(&args, scopes); Discord_AuthorizationArgs_SetCodeChallenge(&args, &challenge); Discord_Client_Authorize(discord_client, &args, cb_auth, NULL, NULL); Discord_AuthorizationArgs_Drop(&args); return JS_NULL; ) // discord.get_token(code, redirect_uri, callback) JSC_CCALL(discord_get_token, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); if (!stored_verifier) return JS_ThrowInternalError(js, "No code verifier - call authorize first"); Discord_String code = js_to_dstr(js, argv[0]); Discord_String uri = js_to_dstr(js, argv[1]); if (!JS_IsUninitialized(js_token_cb)) JS_FreeValue(js, js_token_cb); js_token_cb = JS_DupValue(js, argv[2]); Discord_String verifier; Discord_AuthorizationCodeVerifier_Verifier(stored_verifier, &verifier); Discord_Client_GetToken(discord_client, discord_application_id, code, verifier, uri, cb_token, NULL, NULL); free_dstr(&code); free_dstr(&uri); return JS_NULL; ) // discord.update_token(token_type, token, callback) JSC_CCALL(discord_update_token, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); const char *ts = JS_ToCString(js, argv[0]); Discord_AuthorizationTokenType tt = Discord_AuthorizationTokenType_Bearer; if (ts && strcmp(ts, "user") == 0) tt = Discord_AuthorizationTokenType_User; JS_FreeCString(js, ts); Discord_String token = js_to_dstr(js, argv[1]); if (!JS_IsUninitialized(js_update_token_cb)) JS_FreeValue(js, js_update_token_cb); js_update_token_cb = JS_DupValue(js, argv[2]); Discord_Client_UpdateToken(discord_client, tt, token, cb_update_token, NULL, NULL); free_dstr(&token); return JS_NULL; ) // discord.refresh_token(refresh_token, callback) JSC_CCALL(discord_refresh_token, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); Discord_String rt = js_to_dstr(js, argv[0]); if (!JS_IsUninitialized(js_token_cb)) JS_FreeValue(js, js_token_cb); js_token_cb = JS_DupValue(js, argv[1]); Discord_Client_RefreshToken(discord_client, discord_application_id, rt, cb_token, NULL, NULL); free_dstr(&rt); return JS_NULL; ) // ============================================================================ // USER API // ============================================================================ // discord.get_current_user() JSC_CCALL(discord_get_current_user, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); Discord_UserHandle user; if (!Discord_Client_GetCurrentUserV2(discord_client, &user)) return JS_NULL; return user_to_js(js, &user); ) // discord.get_user(user_id) JSC_CCALL(discord_get_user, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); uint64_t uid = 0; uid = js_to_u64(js, argv[0]); Discord_UserHandle user; if (!Discord_Client_GetUser(discord_client, uid, &user)) return JS_NULL; return user_to_js(js, &user); ) // ============================================================================ // RELATIONSHIPS API // ============================================================================ // discord.get_relationships() JSC_CCALL(discord_get_relationships, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); Discord_RelationshipHandleSpan rels; Discord_Client_GetRelationships(discord_client, &rels); JSValue arr = JS_NewArray(js); for (size_t i = 0; i < rels.size; i++) JS_SetPropertyUint32(js, arr, i, rel_to_js(js, &rels.ptr[i])); return arr; ) // discord.get_friends_count() JSC_CCALL(discord_get_friends_count, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); Discord_RelationshipHandleSpan rels; Discord_Client_GetRelationships(discord_client, &rels); return JS_NewUint32(js, (uint32_t)rels.size); ) // ============================================================================ // RICH PRESENCE API // ============================================================================ // discord.update_rich_presence(activity, callback) JSC_CCALL(discord_update_rich_presence, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); Discord_Activity activity; Discord_Activity_Init(&activity); JSValue obj = argv[0]; // Type JSValue tv = JS_GetPropertyStr(js, obj, "type"); if (!JS_IsNull(tv)) { const char *ts = JS_ToCString(js, tv); Discord_ActivityTypes t = Discord_ActivityTypes_Playing; if (ts) { if (strcmp(ts, "streaming") == 0) t = Discord_ActivityTypes_Streaming; else if (strcmp(ts, "listening") == 0) t = Discord_ActivityTypes_Listening; else if (strcmp(ts, "watching") == 0) t = Discord_ActivityTypes_Watching; else if (strcmp(ts, "competing") == 0) t = Discord_ActivityTypes_Competing; JS_FreeCString(js, ts); } Discord_Activity_SetType(&activity, t); } JS_FreeValue(js, tv); // State JSValue sv = JS_GetPropertyStr(js, obj, "state"); if (JS_IsString(sv)) { Discord_String s = js_to_dstr(js, sv); Discord_Activity_SetState(&activity, &s); free_dstr(&s); } JS_FreeValue(js, sv); // Details JSValue dv = JS_GetPropertyStr(js, obj, "details"); if (JS_IsString(dv)) { Discord_String s = js_to_dstr(js, dv); Discord_Activity_SetDetails(&activity, &s); free_dstr(&s); } JS_FreeValue(js, dv); // Assets JSValue li = JS_GetPropertyStr(js, obj, "large_image"); JSValue lt = JS_GetPropertyStr(js, obj, "large_text"); JSValue si = JS_GetPropertyStr(js, obj, "small_image"); JSValue st = JS_GetPropertyStr(js, obj, "small_text"); if (JS_IsString(li) || JS_IsString(si)) { Discord_ActivityAssets assets; Discord_ActivityAssets_Init(&assets); if (JS_IsString(li)) { Discord_String s = js_to_dstr(js, li); Discord_ActivityAssets_SetLargeImage(&assets, &s); free_dstr(&s); } if (JS_IsString(lt)) { Discord_String s = js_to_dstr(js, lt); Discord_ActivityAssets_SetLargeText(&assets, &s); free_dstr(&s); } if (JS_IsString(si)) { Discord_String s = js_to_dstr(js, si); Discord_ActivityAssets_SetSmallImage(&assets, &s); free_dstr(&s); } if (JS_IsString(st)) { Discord_String s = js_to_dstr(js, st); Discord_ActivityAssets_SetSmallText(&assets, &s); free_dstr(&s); } Discord_Activity_SetAssets(&activity, &assets); Discord_ActivityAssets_Drop(&assets); } JS_FreeValue(js, li); JS_FreeValue(js, lt); JS_FreeValue(js, si); JS_FreeValue(js, st); // Timestamps JSValue tsv = JS_GetPropertyStr(js, obj, "start_timestamp"); JSValue tev = JS_GetPropertyStr(js, obj, "end_timestamp"); if (!JS_IsNull(tsv) || !JS_IsNull(tev)) { Discord_ActivityTimestamps ts; Discord_ActivityTimestamps_Init(&ts); if (!JS_IsNull(tsv)) Discord_ActivityTimestamps_SetStart(&ts, (uint64_t)js2number(js, tsv)); if (!JS_IsNull(tev)) Discord_ActivityTimestamps_SetEnd(&ts, (uint64_t)js2number(js, tev)); Discord_Activity_SetTimestamps(&activity, &ts); Discord_ActivityTimestamps_Drop(&ts); } JS_FreeValue(js, tsv); JS_FreeValue(js, tev); // Party JSValue piv = JS_GetPropertyStr(js, obj, "party_id"); if (JS_IsString(piv)) { Discord_ActivityParty party; Discord_ActivityParty_Init(&party); Discord_String s = js_to_dstr(js, piv); Discord_ActivityParty_SetId(&party, s); free_dstr(&s); JSValue psv = JS_GetPropertyStr(js, obj, "party_size"); JSValue pmv = JS_GetPropertyStr(js, obj, "party_max"); if (!JS_IsNull(psv)) Discord_ActivityParty_SetCurrentSize(&party, (int32_t)js2number(js, psv)); if (!JS_IsNull(pmv)) Discord_ActivityParty_SetMaxSize(&party, (int32_t)js2number(js, pmv)); JS_FreeValue(js, psv); JS_FreeValue(js, pmv); Discord_Activity_SetParty(&activity, &party); Discord_ActivityParty_Drop(&party); } JS_FreeValue(js, piv); // Secrets JSValue jsv = JS_GetPropertyStr(js, obj, "join_secret"); if (JS_IsString(jsv)) { Discord_ActivitySecrets secrets; Discord_ActivitySecrets_Init(&secrets); Discord_String s = js_to_dstr(js, jsv); Discord_ActivitySecrets_SetJoin(&secrets, s); free_dstr(&s); Discord_Activity_SetSecrets(&activity, &secrets); Discord_ActivitySecrets_Drop(&secrets); } JS_FreeValue(js, jsv); // Callback if (argc > 1 && JS_IsFunction(js, argv[1])) { if (!JS_IsUninitialized(js_presence_cb)) JS_FreeValue(js, js_presence_cb); js_presence_cb = JS_DupValue(js, argv[1]); } Discord_Client_UpdateRichPresence(discord_client, &activity, cb_presence, NULL, NULL); Discord_Activity_Drop(&activity); return JS_NULL; ) // discord.clear_rich_presence() JSC_CCALL(discord_clear_rich_presence, if (!discord_client) return JS_ThrowInternalError(js, "Discord client not initialized"); Discord_Client_ClearRichPresence(discord_client); return JS_NULL; ) // ============================================================================ // FUNCTION LIST AND MODULE EXPORT // ============================================================================ static const JSCFunctionListEntry js_discord_funcs[] = { MIST_FUNC_DEF(discord, init, 1), MIST_FUNC_DEF(discord, run_callbacks, 0), MIST_FUNC_DEF(discord, shutdown, 0), MIST_FUNC_DEF(discord, get_status, 0), MIST_FUNC_DEF(discord, is_authenticated, 0), MIST_FUNC_DEF(discord, connect, 0), MIST_FUNC_DEF(discord, disconnect, 0), MIST_FUNC_DEF(discord, on_status_changed, 1), MIST_FUNC_DEF(discord, on_log, 2), MIST_FUNC_DEF(discord, get_default_scopes, 0), MIST_FUNC_DEF(discord, authorize, 1), MIST_FUNC_DEF(discord, get_token, 3), MIST_FUNC_DEF(discord, update_token, 3), MIST_FUNC_DEF(discord, refresh_token, 2), MIST_FUNC_DEF(discord, get_current_user, 0), MIST_FUNC_DEF(discord, get_user, 1), MIST_FUNC_DEF(discord, get_relationships, 0), MIST_FUNC_DEF(discord, get_friends_count, 0), MIST_FUNC_DEF(discord, update_rich_presence, 2), MIST_FUNC_DEF(discord, clear_rich_presence, 0), }; CELL_USE_FUNCS(js_discord_funcs) } // extern "C"