Skip to content
Open
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
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -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"),
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,5 @@ export(mx_sync)
export(mx_typing)
export(mx_upload)
export(mx_whoami)

S3method(print,mx_session)
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# mx.api 0.3.0.1

* `print()` method for `mx_session`: masks the access `token` as
`<hidden>` (or `<unset>` 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
Expand Down
15 changes: 5 additions & 10 deletions R/account.R
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
6 changes: 2 additions & 4 deletions R/canonical_json.R
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -187,4 +186,3 @@ mx_cj_string <- function(s) {
}
paste0("\"", s, "\"")
}

8 changes: 2 additions & 6 deletions R/devices.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
16 changes: 6 additions & 10 deletions R/http.R
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand All @@ -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)
}
Expand All @@ -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())

1 change: 0 additions & 1 deletion R/keys.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

32 changes: 11 additions & 21 deletions R/media.R
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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))
)

Expand Down Expand Up @@ -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))
Expand All @@ -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)) {
Expand All @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -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",
Expand Down
68 changes: 22 additions & 46 deletions R/messages.R
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
}

Expand All @@ -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
Expand All @@ -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",
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
}
1 change: 0 additions & 1 deletion R/mx.api-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@
#' @name mx.api-package
#' @aliases mx.api
"_PACKAGE"

12 changes: 4 additions & 8 deletions R/profile.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
Loading
Loading