diff --git a/include/wolfram/agent.h b/include/wolfram/agent.h index 03086bd..20717f6 100644 --- a/include/wolfram/agent.h +++ b/include/wolfram/agent.h @@ -181,6 +181,13 @@ typedef struct wf_agent_profile { char *description; char *avatar_cid; char *following; /* viewer.following record URI, or NULL */ + /* viewer.blocking record URI, or NULL. Unlike `muted` below, blocking is + * a repo record (app.bsky.graph.block) an unblock deletes by URI, the + * same shape as `following`. */ + char *blocking; + /* viewer.muted. A plain account-level flag, not a record: muteActor/ + * unmuteActor flip it directly with no URI to hand back. */ + bool muted; int followers_count; int follows_count; int posts_count; diff --git a/src/agent/agent.c b/src/agent/agent.c index 8805e26..b6cfbcb 100644 --- a/src/agent/agent.c +++ b/src/agent/agent.c @@ -115,6 +115,7 @@ static void wf_agent_profile_reset(wf_agent_profile *profile) { free(profile->description); free(profile->avatar_cid); free(profile->following); + free(profile->blocking); memset(profile, 0, sizeof(*profile)); } @@ -680,6 +681,17 @@ static wf_status wf_agent_profile_from_response(const wf_response *res, status = wf_agent_set_string(&out->following, following->valuestring); } + + cJSON *blocking = cJSON_GetObjectItemCaseSensitive(viewer, "blocking"); + if (status == WF_OK && cJSON_IsString(blocking) && + blocking->valuestring) { + status = wf_agent_set_string(&out->blocking, blocking->valuestring); + } + + cJSON *muted = cJSON_GetObjectItemCaseSensitive(viewer, "muted"); + if (cJSON_IsBool(muted)) { + out->muted = cJSON_IsTrue(muted); + } } cJSON *followers_count = diff --git a/test/test_bsky_agent.c b/test/test_bsky_agent.c index 534fd6b..e95807a 100644 --- a/test/test_bsky_agent.c +++ b/test/test_bsky_agent.c @@ -118,17 +118,34 @@ static int test_parse_profile_viewer(void) { const char *json = "{\"did\":\"did:plc:alice\",\"handle\":\"alice.test\"," "\"avatar\":\"https://cdn.example/alice.png\"," "\"viewer\":{\"following\":" - "\"at://did:plc:me/app.bsky.graph.follow/one\"}}"; + "\"at://did:plc:me/app.bsky.graph.follow/one\"," + "\"blocking\":" + "\"at://did:plc:me/app.bsky.graph.block/two\"," + "\"muted\":true}}"; wf_agent_profile profile = {0}; if (wf_agent_parse_profile(json, strlen(json), &profile) != WF_OK || - !profile.avatar_cid || !profile.following || + !profile.avatar_cid || !profile.following || !profile.blocking || + !profile.muted || strcmp(profile.avatar_cid, "https://cdn.example/alice.png") != 0 || strcmp(profile.following, - "at://did:plc:me/app.bsky.graph.follow/one") != 0) { + "at://did:plc:me/app.bsky.graph.follow/one") != 0 || + strcmp(profile.blocking, "at://did:plc:me/app.bsky.graph.block/two") != + 0) { wf_agent_profile_free(&profile); return 0; } wf_agent_profile_free(&profile); + + /* Absent viewer.blocking/muted must not be fabricated as present. */ + const char *json2 = "{\"did\":\"did:plc:bob\",\"handle\":\"bob.test\"," + "\"viewer\":{}}"; + wf_agent_profile profile2 = {0}; + if (wf_agent_parse_profile(json2, strlen(json2), &profile2) != WF_OK || + profile2.blocking != NULL || profile2.muted != false) { + wf_agent_profile_free(&profile2); + return 0; + } + wf_agent_profile_free(&profile2); return 1; }