diff --git a/DESCRIPTION b/DESCRIPTION index 3130f3c..e36256b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: mx.api Type: Package Title: Minimal Matrix Client-Server API -Version: 0.3.0 +Version: 0.3.0.1 Date: 2026-06-10 Authors@R: c( person("Troy", "Hernandez", role = c("aut", "cre"), diff --git a/NAMESPACE b/NAMESPACE index 9d59b04..b9cfaf4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -44,3 +44,5 @@ export(mx_sync) export(mx_typing) export(mx_upload) export(mx_whoami) + +S3method(print,mx_session) diff --git a/NEWS.md b/NEWS.md index 08837ba..a3c7edf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,10 @@ +# mx.api 0.3.0.1 + +* `print()` method for `mx_session`: masks the access `token` as + `` (or `` when empty) instead of falling through to the + default list print, which showed it verbatim. Every other field stays + visible; `unclass()` still exposes the raw token when needed. + # mx.api 0.3.0 * Generic room-event and state plumbing: `mx_send_event()` sends any diff --git a/R/account.R b/R/account.R index cde3bd9..35841dd 100644 --- a/R/account.R +++ b/R/account.R @@ -16,10 +16,8 @@ #' } #' @export mx_get_account_data <- function(session, type, user_id = session$user_id) { - path <- sprintf( - "/_matrix/client/v3/user/%s/account_data/%s", - mx_encode_id(user_id), mx_encode_id(type) - ) + path <- sprintf("/_matrix/client/v3/user/%s/account_data/%s", + mx_encode_id(user_id), mx_encode_id(type)) tryCatch( mx_http(session$server, "GET", path, token = session$token), mx_error_M_NOT_FOUND = function(e) NULL @@ -43,11 +41,8 @@ mx_get_account_data <- function(session, type, user_id = session$user_id) { #' @export mx_set_account_data <- function(session, type, content, user_id = session$user_id) { - path <- sprintf( - "/_matrix/client/v3/user/%s/account_data/%s", - mx_encode_id(user_id), mx_encode_id(type) - ) - mx_http(session$server, "PUT", path, body = content, - token = session$token) + path <- sprintf("/_matrix/client/v3/user/%s/account_data/%s", + mx_encode_id(user_id), mx_encode_id(type)) + mx_http(session$server, "PUT", path, body = content, token = session$token) invisible(TRUE) } diff --git a/R/canonical_json.R b/R/canonical_json.R index 0bdc960..56b2584 100644 --- a/R/canonical_json.R +++ b/R/canonical_json.R @@ -154,9 +154,8 @@ mx_cj_number <- function(x) { # [-(2^53)+1, (2^53)-1] (spec: "Float values are not permitted by # this encoding"). if (x != trunc(x)) { - stop(sprintf( - "mx_canonical_json: non-integer number %s disallowed", x - ), call. = FALSE) + stop(sprintf("mx_canonical_json: non-integer number %s disallowed", x), + call. = FALSE) } if (abs(x) > 2 ^ 53 - 1) { stop(sprintf( @@ -187,4 +186,3 @@ mx_cj_string <- function(s) { } paste0("\"", s, "\"") } - diff --git a/R/devices.R b/R/devices.R index 318bb3b..b22477c 100644 --- a/R/devices.R +++ b/R/devices.R @@ -45,11 +45,7 @@ mx_delete_device <- function(session, device_id, auth = NULL) { } else { list(auth = auth) } - path <- sprintf( - "/_matrix/client/v3/devices/%s", - mx_encode_id(device_id) - ) - mx_http(session$server, "DELETE", path, body = body, - token = session$token) + path <- sprintf("/_matrix/client/v3/devices/%s", mx_encode_id(device_id)) + mx_http(session$server, "DELETE", path, body = body, token = session$token) invisible(TRUE) } diff --git a/R/http.R b/R/http.R index 91d8c8b..3c74265 100644 --- a/R/http.R +++ b/R/http.R @@ -28,10 +28,8 @@ mx_http <- function(base_url, method, path, body = NULL, query = NULL, resp <- curl::curl_fetch_memory(url, handle = h) raw <- rawToChar(resp$content) parsed <- if (nzchar(raw)) { - tryCatch( - jsonlite::fromJSON(raw, simplifyVector = FALSE), - error = function(e) list(raw = raw) - ) + tryCatch(jsonlite::fromJSON(raw, simplifyVector = FALSE), + error = function(e) list(raw = raw)) } else { list() } @@ -53,16 +51,15 @@ mx_http <- function(base_url, method, path, body = NULL, query = NULL, # carry the structured details. mx_raise <- function(errcode, msg, status = NULL, body = NULL) { cond <- structure( - class = c(paste0("mx_error_", errcode), "mx_error", - "error", "condition"), + class = c(paste0("mx_error_", errcode), "mx_error", "error", + "condition"), list( - message = sprintf("Matrix error [%s]: %s", - errcode, msg), + message = sprintf("Matrix error [%s]: %s", errcode, msg), call = NULL, errcode = errcode, status = status, body = body - ) + ) ) stop(cond) } @@ -77,4 +74,3 @@ mx_txn_id <- function() { mx_encode_id <- function(x) utils::URLencode(x, reserved = TRUE) mx_empty_body <- function() stats::setNames(list(), character()) - diff --git a/R/keys.R b/R/keys.R index cdb036e..7eec178 100644 --- a/R/keys.R +++ b/R/keys.R @@ -166,4 +166,3 @@ mx_send_to_device <- function(session, event_type, messages, txn_id = NULL) { body = list(messages = messages), token = session$token) invisible(NULL) } - diff --git a/R/media.R b/R/media.R index 5a2bc16..ce6ab06 100644 --- a/R/media.R +++ b/R/media.R @@ -25,11 +25,9 @@ mx_upload <- function(session, path, content_type = NULL, filename = NULL) { filename <- basename(path) } - url <- paste0( - sub("/$", "", session$server), + url <- paste0(sub("/$", "", session$server), "/_matrix/media/v3/upload?filename=", - utils::URLencode(filename, reserved = TRUE) - ) + utils::URLencode(filename, reserved = TRUE)) # Stream from disk rather than reading the whole file into RAM: # upload mode + a readfunction lets curl pull chunks as it sends, @@ -57,8 +55,7 @@ mx_upload <- function(session, path, content_type = NULL, filename = NULL) { # text); don't let the parse error mask the real failure -- fall # through to mx_raise with whatever body came back. parsed <- tryCatch( - jsonlite::fromJSON(rawToChar(resp$content), - simplifyVector = FALSE), + jsonlite::fromJSON(rawToChar(resp$content), simplifyVector = FALSE), error = function(e) list(raw = rawToChar(resp$content)) ) @@ -91,12 +88,10 @@ mx_download <- function(session, mxc_url, dest) { server_name <- m[2] media_id <- m[3] - url <- paste0( - sub("/$", "", session$server), + url <- paste0(sub("/$", "", session$server), "/_matrix/client/v1/media/download/", utils::URLencode(server_name, reserved = TRUE), "/", - utils::URLencode(media_id, reserved = TRUE) - ) + utils::URLencode(media_id, reserved = TRUE)) h <- curl::new_handle() curl::handle_setheaders(h, Authorization = paste("Bearer", session$token)) @@ -118,17 +113,15 @@ mx_download <- function(session, mxc_url, dest) { #' @export mx_guess_mime <- function(path) { ext <- tolower(tools::file_ext(path)) - table <- c( - txt = "text/plain", md = "text/markdown", csv = "text/csv", + table <- c(txt = "text/plain", md = "text/markdown", csv = "text/csv", json = "application/json", pdf = "application/pdf", html = "text/html", xml = "application/xml", png = "image/png", jpg = "image/jpeg", jpeg = "image/jpeg", gif = "image/gif", webp = "image/webp", svg = "image/svg+xml", mp3 = "audio/mpeg", wav = "audio/wav", ogg = "audio/ogg", - mp4 = "video/mp4", webm = "video/webm", mov = "video/quicktime", - zip = "application/zip", gz = "application/gzip", - tar = "application/x-tar" - ) + mp4 = "video/mp4", webm = "video/webm", + mov = "video/quicktime", zip = "application/zip", + gz = "application/gzip", tar = "application/x-tar") hit <- unname(table[ext]) # A named-vector miss is NA, not NULL, so %||% alone won't catch it. if (length(hit) != 1L || is.na(hit)) { @@ -137,7 +130,6 @@ mx_guess_mime <- function(path) { hit } - #' Send a media file to a room #' #' Uploads \code{path} to the media repository and posts an @@ -163,8 +155,7 @@ mx_guess_mime <- function(path) { #' } #' @export mx_send_media <- function(session, room_id, path, body = basename(path), - msgtype = NULL, content_type = NULL, - info = list()) { + msgtype = NULL, content_type = NULL, info = list()) { if (is.null(content_type)) { content_type <- mx_guess_mime(path) } @@ -245,8 +236,7 @@ mx_msgtype_for_mime <- function(content_type) { #' @export mx_media_config <- function(session) { tryCatch( - mx_http(session$server, "GET", - "/_matrix/client/v1/media/config", + mx_http(session$server, "GET", "/_matrix/client/v1/media/config", token = session$token), error = function(e) { mx_http(session$server, "GET", "/_matrix/media/v3/config", diff --git a/R/messages.R b/R/messages.R index 2915d0b..6b89eb4 100644 --- a/R/messages.R +++ b/R/messages.R @@ -21,10 +21,8 @@ mx_send <- function(session, room_id, body, msgtype = "m.text", extra = NULL) { content <- utils::modifyList(content, extra) } - path <- sprintf( - "/_matrix/client/v3/rooms/%s/send/m.room.message/%s", - mx_encode_id(room_id), mx_encode_id(mx_txn_id()) - ) + path <- sprintf("/_matrix/client/v3/rooms/%s/send/m.room.message/%s", + mx_encode_id(room_id), mx_encode_id(mx_txn_id())) resp <- mx_http( session$server, "PUT", path, body = content, token = session$token @@ -55,11 +53,9 @@ mx_send_event <- function(session, room_id, event_type, content, if (is.null(txn_id)) { txn_id <- mx_txn_id() } - path <- sprintf( - "/_matrix/client/v3/rooms/%s/send/%s/%s", + path <- sprintf("/_matrix/client/v3/rooms/%s/send/%s/%s", mx_encode_id(room_id), mx_encode_id(event_type), - mx_encode_id(txn_id) - ) + mx_encode_id(txn_id)) resp <- mx_http( session$server, "PUT", path, body = content, token = session$token @@ -87,11 +83,9 @@ mx_send_event <- function(session, room_id, event_type, content, #' @export mx_set_state <- function(session, room_id, event_type, content, state_key = "") { - path <- sprintf( - "/_matrix/client/v3/rooms/%s/state/%s/%s", + path <- sprintf("/_matrix/client/v3/rooms/%s/state/%s/%s", mx_encode_id(room_id), mx_encode_id(event_type), - mx_encode_id(state_key) - ) + mx_encode_id(state_key)) resp <- mx_http( session$server, "PUT", path, body = content, token = session$token @@ -118,11 +112,9 @@ mx_set_state <- function(session, room_id, event_type, content, #' } #' @export mx_get_state <- function(session, room_id, event_type, state_key = "") { - path <- sprintf( - "/_matrix/client/v3/rooms/%s/state/%s/%s", + path <- sprintf("/_matrix/client/v3/rooms/%s/state/%s/%s", mx_encode_id(room_id), mx_encode_id(event_type), - mx_encode_id(state_key) - ) + mx_encode_id(state_key)) tryCatch( mx_http(session$server, "GET", path, token = session$token), mx_error_M_NOT_FOUND = function(e) NULL @@ -152,10 +144,8 @@ mx_messages <- function(session, room_id, from = NULL, dir = "b", limit = 50L) { query$from <- from } - path <- sprintf( - "/_matrix/client/v3/rooms/%s/messages", - mx_encode_id(room_id) - ) + path <- sprintf("/_matrix/client/v3/rooms/%s/messages", + mx_encode_id(room_id)) mx_http(session$server, "GET", path, query = query, token = session$token) } @@ -180,12 +170,9 @@ mx_messages <- function(session, room_id, from = NULL, dir = "b", limit = 50L) { mx_read_receipt <- function(session, room_id, event_id, receipt_type = c("m.read", "m.read.private")) { receipt_type <- match.arg(receipt_type) - path <- sprintf( - "/_matrix/client/v3/rooms/%s/receipt/%s/%s", - mx_encode_id(room_id), - mx_encode_id(receipt_type), - mx_encode_id(event_id) - ) + path <- sprintf("/_matrix/client/v3/rooms/%s/receipt/%s/%s", + mx_encode_id(room_id), mx_encode_id(receipt_type), + mx_encode_id(event_id)) mx_http( session$server, "POST", path, body = mx_empty_body(), token = session$token @@ -212,11 +199,8 @@ mx_read_receipt <- function(session, room_id, event_id, #' @export mx_react <- function(session, room_id, event_id, key) { content <- list( - `m.relates_to` = list( - rel_type = "m.annotation", - event_id = event_id, - key = key - ) + `m.relates_to` = list(rel_type = "m.annotation", event_id = event_id, + key = key) ) path <- sprintf( "/_matrix/client/v3/rooms/%s/send/m.reaction/%s", @@ -257,13 +241,10 @@ mx_sync <- function(session, since = NULL, timeout = 0L, filter = NULL) { query$filter <- filter } - mx_http( - session$server, "GET", "/_matrix/client/v3/sync", - query = query, token = session$token - ) + mx_http(session$server, "GET", "/_matrix/client/v3/sync", query = query, + token = session$token) } - #' Redact an event #' #' Removes the content of a message, reaction, or other event. This is @@ -290,11 +271,9 @@ mx_redact <- function(session, room_id, event_id, reason = NULL, } else { list(reason = reason) } - path <- sprintf( - "/_matrix/client/v3/rooms/%s/redact/%s/%s", + path <- sprintf("/_matrix/client/v3/rooms/%s/redact/%s/%s", mx_encode_id(room_id), mx_encode_id(event_id), - mx_encode_id(txn_id) - ) + mx_encode_id(txn_id)) resp <- mx_http(session$server, "PUT", path, body = body, token = session$token) resp$event_id @@ -323,11 +302,8 @@ mx_typing <- function(session, room_id, typing = TRUE, timeout = 30000L) { if (isTRUE(typing)) { body$timeout <- as.integer(timeout) } - path <- sprintf( - "/_matrix/client/v3/rooms/%s/typing/%s", - mx_encode_id(room_id), mx_encode_id(session$user_id) - ) - mx_http(session$server, "PUT", path, body = body, - token = session$token) + path <- sprintf("/_matrix/client/v3/rooms/%s/typing/%s", + mx_encode_id(room_id), mx_encode_id(session$user_id)) + mx_http(session$server, "PUT", path, body = body, token = session$token) invisible(TRUE) } diff --git a/R/mx.api-package.R b/R/mx.api-package.R index c18836e..4fa15e1 100644 --- a/R/mx.api-package.R +++ b/R/mx.api-package.R @@ -8,4 +8,3 @@ #' @name mx.api-package #' @aliases mx.api "_PACKAGE" - diff --git a/R/profile.R b/R/profile.R index 9c028fd..9c7b814 100644 --- a/R/profile.R +++ b/R/profile.R @@ -27,10 +27,8 @@ mx_profile <- function(session, user_id = session$user_id) { #' } #' @export mx_set_displayname <- function(session, displayname) { - path <- sprintf( - "/_matrix/client/v3/profile/%s/displayname", - mx_encode_id(session$user_id) - ) + path <- sprintf("/_matrix/client/v3/profile/%s/displayname", + mx_encode_id(session$user_id)) mx_http(session$server, "PUT", path, body = list(displayname = displayname), token = session$token) invisible(TRUE) @@ -49,10 +47,8 @@ mx_set_displayname <- function(session, displayname) { #' } #' @export mx_set_avatar_url <- function(session, avatar_url) { - path <- sprintf( - "/_matrix/client/v3/profile/%s/avatar_url", - mx_encode_id(session$user_id) - ) + path <- sprintf("/_matrix/client/v3/profile/%s/avatar_url", + mx_encode_id(session$user_id)) mx_http(session$server, "PUT", path, body = list(avatar_url = avatar_url), token = session$token) invisible(TRUE) diff --git a/R/rooms.R b/R/rooms.R index 0c88758..3650c57 100644 --- a/R/rooms.R +++ b/R/rooms.R @@ -11,10 +11,8 @@ #' } #' @export mx_rooms <- function(session) { - resp <- mx_http( - session$server, "GET", "/_matrix/client/v3/joined_rooms", - token = session$token - ) + resp <- mx_http(session$server, "GET", "/_matrix/client/v3/joined_rooms", + token = session$token) unlist(resp$joined_rooms, use.names = FALSE) } @@ -51,10 +49,8 @@ mx_room_create <- function(session, name = NULL, topic = NULL, body$invite <- as.list(invite) } - resp <- mx_http( - session$server, "POST", "/_matrix/client/v3/createRoom", - body = body, token = session$token - ) + resp <- mx_http(session$server, "POST", "/_matrix/client/v3/createRoom", + body = body, token = session$token) resp$room_id } @@ -71,10 +67,8 @@ mx_room_create <- function(session, name = NULL, topic = NULL, #' @export mx_room_join <- function(session, room) { path <- sprintf("/_matrix/client/v3/join/%s", mx_encode_id(room)) - resp <- mx_http( - session$server, "POST", path, - body = mx_empty_body(), token = session$token - ) + resp <- mx_http(session$server, "POST", path, body = mx_empty_body(), + token = session$token) resp$room_id } @@ -129,10 +123,8 @@ mx_room_members <- function(session, room_id) { #' } #' @export mx_room_name <- function(session, room_id) { - path <- sprintf( - "/_matrix/client/v3/rooms/%s/state/m.room.name", - mx_encode_id(room_id) - ) + path <- sprintf("/_matrix/client/v3/rooms/%s/state/m.room.name", + mx_encode_id(room_id)) resp <- tryCatch( mx_http(session$server, "GET", path, token = session$token), error = function(e) NULL @@ -158,10 +150,8 @@ mx_room_name <- function(session, room_id) { #' } #' @export mx_room_topic <- function(session, room_id) { - path <- sprintf( - "/_matrix/client/v3/rooms/%s/state/m.room.topic", - mx_encode_id(room_id) - ) + path <- sprintf("/_matrix/client/v3/rooms/%s/state/m.room.topic", + mx_encode_id(room_id)) resp <- tryCatch( mx_http(session$server, "GET", path, token = session$token), error = function(e) NULL @@ -172,7 +162,6 @@ mx_room_topic <- function(session, room_id) { resp$topic } - #' Invite a user to a room #' #' Invitation at creation time is covered by \code{mx_room_create()}; @@ -189,11 +178,8 @@ mx_room_topic <- function(session, room_id) { #' } #' @export mx_room_invite <- function(session, room_id, user_id) { - path <- sprintf( - "/_matrix/client/v3/rooms/%s/invite", - mx_encode_id(room_id) - ) - mx_http(session$server, "POST", path, - body = list(user_id = user_id), token = session$token) + path <- sprintf("/_matrix/client/v3/rooms/%s/invite", mx_encode_id(room_id)) + mx_http(session$server, "POST", path, body = list(user_id = user_id), + token = session$token) invisible(TRUE) } diff --git a/R/session.R b/R/session.R index e847521..91ee3a7 100644 --- a/R/session.R +++ b/R/session.R @@ -29,12 +29,9 @@ mx_register <- function(server, username, password, device_id = NULL, initial_device_display_name = NULL, inhibit_login = FALSE) { - body <- list( - username = username, - password = password, + body <- list(username = username, password = password, auth = list(type = "m.login.dummy"), - inhibit_login = isTRUE(inhibit_login) - ) + inhibit_login = isTRUE(inhibit_login)) if (!is.null(device_id)) { body$device_id <- device_id } @@ -77,11 +74,8 @@ mx_login <- function(server, user, password, device_id = NULL) { } else { list(type = "m.id.user", user = user) } - body <- list( - type = "m.login.password", - identifier = identifier, - password = password - ) + body <- list(type = "m.login.password", identifier = identifier, + password = password) if (!is.null(device_id)) { body$device_id <- device_id } @@ -113,16 +107,56 @@ mx_login <- function(server, user, password, device_id = NULL) { #' @export mx_session <- function(server, token, user_id, device_id) { structure( - list( - server = sub("/$", "", server), - token = token, - user_id = user_id, - device_id = device_id - ), + list(server = sub("/$", "", server), token = token, + user_id = user_id, device_id = device_id), class = "mx_session" ) } +#' Print a Matrix session +#' +#' Prints the session with the access token masked, so that an +#' interactive session, a screenshot, or a pasted bug report does not +#' leak it. The token shows as \code{} when set and +#' \code{} when empty; every other field prints as-is. Use +#' \code{unclass(x)} when the raw token is genuinely needed. +#' +#' @param x An "mx_session" object. +#' @param ... Ignored. +#' +#' @return \code{x}, invisibly. +#' @examples +#' s <- mx_session( +#' server = "https://matrix.example", +#' token = "syt_secret_value", +#' user_id = "@alice:matrix.example", +#' device_id = "ABC123" +#' ) +#' s +#' @export +print.mx_session <- function(x, ...) { + cat("\n") + fields <- names(x) + width <- max(nchar(fields)) + 1L + for (f in fields) { + value <- x[[f]] + set <- length(value) > 0L && any(nzchar(as.character(value))) + text <- if (identical(f, "token")) { + if (set) { + "" + } else { + "" + } + } else if (set) { + paste(as.character(value), collapse = ", ") + } else { + "" + } + cat(sprintf(" %-*s %s\n", width, paste0(f, ":"), text)) + } + invisible(x) +} + #' Log out of a Matrix session #' #' Invalidates the access token on the homeserver. @@ -136,10 +170,8 @@ mx_session <- function(server, token, user_id, device_id) { #' } #' @export mx_logout <- function(session) { - mx_http( - session$server, "POST", "/_matrix/client/v3/logout", - body = mx_empty_body(), token = session$token - ) + mx_http(session$server, "POST", "/_matrix/client/v3/logout", + body = mx_empty_body(), token = session$token) invisible(NULL) } @@ -154,10 +186,7 @@ mx_logout <- function(session) { #' } #' @export mx_whoami <- function(session) { - resp <- mx_http( - session$server, "GET", "/_matrix/client/v3/account/whoami", - token = session$token - ) + resp <- mx_http(session$server, "GET", + "/_matrix/client/v3/account/whoami", token = session$token) list(user_id = resp$user_id, device_id = resp$device_id) } - diff --git a/inst/tinytest/test_session.R b/inst/tinytest/test_session.R index 9f0da6d..48c6104 100644 --- a/inst/tinytest/test_session.R +++ b/inst/tinytest/test_session.R @@ -31,3 +31,24 @@ if (at_home() && nzchar(Sys.getenv("MX_TEST_SERVER"))) { expect_equal(wid$user_id, s$user_id) mx.api::mx_logout(s) } + +# --- print.mx_session masks the access token --- + +sess <- mx.api::mx_session("https://matrix.example", "syt_supersecret", + "@alice:matrix.example", "ABC123") +out <- paste(capture.output(print(sess)), collapse = "\n") +expect_false(grepl("syt_supersecret", out, fixed = TRUE)) +expect_true(grepl("token:\\s+", out)) +# everything else stays readable +expect_true(grepl("@alice:matrix.example", out, fixed = TRUE)) +expect_true(grepl("ABC123", out, fixed = TRUE)) +expect_true(grepl("https://matrix.example", out, fixed = TRUE)) +# an empty token reads as unset, so "is my token loaded?" stays answerable +empty <- mx.api::mx_session("https://matrix.example", "", + "@alice:matrix.example", "ABC123") +expect_true(grepl("token:\\s+", paste(capture.output(print(empty)), + collapse = "\n"))) +# returns the session unchanged, and invisibly +capture.output(vis <- withVisible(print(sess))) +expect_false(vis$visible) +expect_identical(vis$value, sess) diff --git a/man/print.mx_session.Rd b/man/print.mx_session.Rd new file mode 100644 index 0000000..ab6183f --- /dev/null +++ b/man/print.mx_session.Rd @@ -0,0 +1,31 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{print.mx_session} +\alias{print.mx_session} +\title{Print a Matrix session} +\usage{ +\method{print}{mx_session}(x, ...) +} +\arguments{ +\item{x}{An "mx_session" object.} + +\item{...}{Ignored.} +} +\value{ +\code{x}, invisibly. +} +\description{ +Prints the session with the access token masked, so that an +interactive session, a screenshot, or a pasted bug report does not +leak it. The token shows as \code{} when set and +\code{} when empty; every other field prints as-is. Use +\code{unclass(x)} when the raw token is genuinely needed. +} +\examples{ +s <- mx_session( + server = "https://matrix.example", + token = "syt_secret_value", + user_id = "@alice:matrix.example", + device_id = "ABC123" +) +s +}