Skip to content

Add presence set-preferred and clear-preferred - #92

Open
aberoham wants to merge 3 commits into
osodevops:mainfrom
aberoham:feat/preferred-presence
Open

Add presence set-preferred and clear-preferred#92
aberoham wants to merge 3 commits into
osodevops:mainfrom
aberoham:feat/preferred-presence

Conversation

@aberoham

@aberoham aberoham commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

presence set-preferred and presence clear-preferred manage the user-preferred presence, which Microsoft Graph ranks above every presence session while one exists.

  • The Teams client's "Appear offline" is a preferred presence of Offline/OffWork. With it set, presence set returns success and the account stays offline, and no command could clear it.
  • set-preferred --availability A [--expiration D] calls setUserPreferredPresence. Graph accepts Available, Busy, DoNotDisturb, BeRightBack, Away and Offline, each with one fixed activity, so the activity is derived from the availability and reported back.
  • --expiration is checked as a positive ISO 8601 duration and not bounded. Graph documents defaults of one day for Busy and DoNotDisturb and seven days otherwise, rather than a range, so the PT5M to PT4H check on set does not apply here.
  • clear-preferred calls clearUserPreferredPresence with the documented {} body. A second clear with nothing to clear returns 200, so there is no 404 to translate.
  • Both commands reject an app-only token locally, as the other presence writes do.
  • README, command reference, man page, CHANGELOG and the command lists in CLAUDE.md and AGENTS.md are updated.

Sample output:

{"status": "preferred_presence_set", "availability": "Away", "activity": "Away", "expiration_duration": "PT1H"}
{"status": "preferred_presence_cleared"}

Verified against a live tenant on 2026-09-03: an account carrying Offline/OffWork with an open Available session read Available/Available after clear-preferred; set-preferred --availability Away read Away/Away through both presence get and presence get-batch; a second clear-preferred returned 200.

aberoham and others added 2 commits September 3, 2026 21:22
Microsoft Graph keeps a user-preferred presence above the presence sessions and ranks it first for as long as at least one session exists. The Teams client's "Appear offline" lives there, as `Offline`/`OffWork`, and nothing in the CLI could reach it: `presence set` answered success and the account stayed offline, because the override sat on top of the session it had just opened.

`set-preferred` calls `setUserPreferredPresence` and `clear-preferred` calls `clearUserPreferredPresence`, both under `/me/presence` like the other writes. Graph accepts six availabilities here, each with exactly one activity, so the command takes `--availability` alone, derives the activity, and reports both back rather than letting a caller send a pair Graph rejects. `--expiration` is checked as a positive ISO 8601 duration and left unbounded: Graph documents defaults of one day for `Busy` and `DoNotDisturb` and seven days otherwise, not a range, and the PT5M to PT4H limits in `parse_expiration` belong to sessions. The clear body is `{}` as documented, which a braced struct serializes to where a unit struct would send `null`; the wiremock test would catch the difference.

Verified against a live tenant: an account carrying `Offline`/`OffWork` with an open Available session read `Available`/`Available` after `clear-preferred`; `set-preferred --availability Away` read `Away`/`Away` through both the single-user and batch read paths; and a second clear with nothing left to clear returned 200 rather than 404, so there is no miss to translate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DD81KRhjKrJi3Gywivwv1
@aberoham

aberoham commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Adversarial review completed against base 979791f through the updated head 90466fe.

Findings fixed:

  • In 06a655f, the duration parser was fixed to reject malformed P1DT. A trailing T had been treated as an empty but valid time section, allowing an invalid value to reach setUserPreferredPresence despite the command promising local ISO 8601 validation. Unit and black-box regression coverage were added.
  • In 06a655f, exact v1.0 /me presence endpoint assertions were added. The previous Wiremock tests injected URLs and therefore did not cover the production endpoint builders.
  • In 06a655f, command-path coverage for app-only rejection and help-JSON coverage for both new subcommands and the required availability flag were added; the auth guide and PRD command documentation were completed.
  • In 06a655f, the docs were clarified to state that the CLI accepts positive whole-unit day/hour/minute/second durations rather than every possible ISO 8601 duration form.
  • In 90466fe, the locked optional rand dependency was updated from 0.9.2 to patched version 0.9.3, clearing informational unsoundness advisory RUSTSEC-2026-0097. The affected dependency was only present through the unused optional HTTP/3/Quinn path in Reqwest, and the CLI has no direct Rand use or custom log::Log implementation, so I found no reachable exploitation path; the update removes the advisory regardless.

Validation completed:

  • cargo audit: clean; 321 dependencies scanned
  • cargo fmt -- --check
  • cargo check --all-targets
  • cargo clippy --all-targets -- -D warnings
  • cargo test --all-targets: 250 unit tests and 69 black-box tests passed
  • cargo build --release
  • Direct probes confirmed malformed P1DT exits 2 while valid P1DT1S proceeds to authentication

I also checked the current Microsoft Graph v1.0 documentation for setUserPreferredPresence and clearUserPreferredPresence against the URL, request-body, pair, default-expiration, response, and delegated-permission behavior used here. I did not repeat the live-tenant writes already documented in the PR.

The fork-origin GitHub Actions run currently shows action_required with no jobs, so hosted Ubuntu/macOS/Windows CI remains pending approval.

No further code, dependency, or documentation findings remain. Merge-ready once hosted CI is approved and passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant