From 483949066ce23275b2c2fdb88c6a99c226547272 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Wed, 5 Feb 2025 06:33:18 -0600 Subject: [PATCH 1/9] Manual tweaks before bk. Deleted unused pieces of the package. --- NAMESPACE | 12 -- R/910-call.R | 92 ---------- R/930-responses.R | 55 ------ R/parse_channel.R | 18 -- R/parse_members.R | 78 --------- R/paths-api.R | 22 --- R/paths-auth.R | 36 ---- R/paths-conversations.R | 277 ------------------------------- R/paths-users.R | 134 --------------- R/users-info.R | 28 ---- data-raw/forLydia.R | 2 + data-raw/help_stats.R | 47 ++++-- man/conversations_archive.Rd | 23 --- man/conversations_close.Rd | 21 --- man/conversations_create.Rd | 21 --- man/conversations_info.Rd | 34 ---- man/conversations_invite.Rd | 21 --- man/conversations_join.Rd | 21 --- man/conversations_kick.Rd | 21 --- man/conversations_leave.Rd | 21 --- man/conversations_mark.Rd | 21 --- man/conversations_open.Rd | 21 --- man/conversations_rename.Rd | 21 --- man/conversations_set_purpose.Rd | 21 --- man/conversations_set_topic.Rd | 21 --- man/conversations_unarchive.Rd | 21 --- man/dot-slack_req_perform.Rd | 41 ----- man/slack_api_test.Rd | 23 --- man/slack_auth_revoke.Rd | 21 --- man/slack_auth_test.Rd | 19 --- man/slack_call_api.Rd | 75 --------- man/slack_response_parser.Rd | 19 --- man/users_conversations.Rd | 36 ---- man/users_delete_photo.Rd | 19 --- man/users_get_presence.Rd | 21 --- man/users_identity.Rd | 19 --- man/users_info.Rd | 41 ----- man/users_lookup_by_email.Rd | 21 --- man/users_set_active.Rd | 19 --- man/users_set_photo.Rd | 19 --- man/users_set_presence.Rd | 19 --- 41 files changed, 34 insertions(+), 1488 deletions(-) delete mode 100644 R/910-call.R delete mode 100644 R/930-responses.R delete mode 100644 R/parse_channel.R delete mode 100644 R/parse_members.R delete mode 100644 R/paths-api.R delete mode 100644 R/paths-auth.R delete mode 100644 R/paths-conversations.R delete mode 100644 R/paths-users.R delete mode 100644 R/users-info.R delete mode 100644 man/conversations_archive.Rd delete mode 100644 man/conversations_close.Rd delete mode 100644 man/conversations_create.Rd delete mode 100644 man/conversations_info.Rd delete mode 100644 man/conversations_invite.Rd delete mode 100644 man/conversations_join.Rd delete mode 100644 man/conversations_kick.Rd delete mode 100644 man/conversations_leave.Rd delete mode 100644 man/conversations_mark.Rd delete mode 100644 man/conversations_open.Rd delete mode 100644 man/conversations_rename.Rd delete mode 100644 man/conversations_set_purpose.Rd delete mode 100644 man/conversations_set_topic.Rd delete mode 100644 man/conversations_unarchive.Rd delete mode 100644 man/dot-slack_req_perform.Rd delete mode 100644 man/slack_api_test.Rd delete mode 100644 man/slack_auth_revoke.Rd delete mode 100644 man/slack_auth_test.Rd delete mode 100644 man/slack_call_api.Rd delete mode 100644 man/slack_response_parser.Rd delete mode 100644 man/users_conversations.Rd delete mode 100644 man/users_delete_photo.Rd delete mode 100644 man/users_get_presence.Rd delete mode 100644 man/users_identity.Rd delete mode 100644 man/users_info.Rd delete mode 100644 man/users_lookup_by_email.Rd delete mode 100644 man/users_set_active.Rd delete mode 100644 man/users_set_photo.Rd delete mode 100644 man/users_set_presence.Rd diff --git a/NAMESPACE b/NAMESPACE index d8c1e97..9b9e0c2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,19 +8,7 @@ export(conversations_history) export(conversations_list) export(conversations_members) export(conversations_replies) -export(slack_api_test) -export(slack_auth_revoke) -export(slack_auth_test) -export(slack_response_parser) -export(users_conversations) -export(users_delete_photo) -export(users_get_presence) -export(users_identity) export(users_list) -export(users_lookup_by_email) -export(users_set_active) -export(users_set_photo) -export(users_set_presence) importFrom(rlang,"%||%") importFrom(rlang,.data) importFrom(tibblify,tib_chr) diff --git a/R/910-call.R b/R/910-call.R deleted file mode 100644 index 8449fc5..0000000 --- a/R/910-call.R +++ /dev/null @@ -1,92 +0,0 @@ -#' Call the Slack Web API -#' -#' Generate and perform request to a Slack Web API method. -#' -#' @inheritParams nectar::req_modify -#' @inheritParams .slack_req_perform -#' @inheritParams rlang::args_error_context -#' @param response_parser (`function`) A function to parse the server response. -#' Defaults to [slack_response_parser()]. Set this to `NULL` to return the raw -#' response from [httr2::req_perform()]. -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. -#' -#' @return A tibble with the results of the API call. -#' @keywords internal -slack_call_api <- function(path, - query = list(), - body = NULL, - method = NULL, - pagination_fn = NULL, - max_results = Inf, - max_reqs = Inf, - response_parser = slack_response_parser, - token = Sys.getenv("SLACK_API_TOKEN"), - call = rlang::caller_env()) { - stop("You shouldn't be here") - # Don't pass token in query or body if provided as parameters; we'll instead - # pass it in the header. - token <- token %||% body$token %||% query$token - if (length(body)) { - body$token <- NULL - } - if (length(query)) { - query$token <- NULL - } - - req <- nectar::req_modify( - nectar::req_init("https://slack.com/api"), - path = path, - query = query, - body = body, - method = method - ) - req <- .req_auth(req, token = token) - - resps <- .slack_req_perform( - req, - pagination_fn = pagination_fn, - max_results = max_results, - max_reqs = max_reqs, - call = call - ) - - nectar::resp_parse(resps, response_parser = response_parser) -} - -#' Choose and apply pagination strategy -#' -#' @inheritParams rlang::args_error_context -#' @inheritParams nectar::req_perform_opinionated -#' @inheritParams .shared-params -#' @param req (`httr2_request`) The request object to modify. -#' @param max_results (`integer` or `Inf`) The maximum number of results to -#' return. Note that slightly more results may be returned if `max_results` is -#' not evenly divisible by 100. -#' -#' @inherit nectar::req_perform_opinionated return -#' @keywords internal -.slack_req_perform <- function(req, - pagination_fn = NULL, - max_results, - max_reqs, - call) { - if (max_reqs > 1 && !is.null(pagination_fn)) { - # Use Slack's recommended limit when paginating. - per_req <- 200L - max_reqs <- min(max_reqs, ceiling(max_results / per_req)) - req <- httr2::req_url_query(req, limit = per_req) - } - - # nectar respects it if we put our own retry mechanism on. Slack has retry - # tiers, so it MIGHT make sense to implement those specifically. Dunno if - # Slack sends back info around that, need to check; it seems to always wait 10 - # seconds. - - nectar::req_perform_opinionated( - req, - next_req_fn = pagination_fn, - max_reqs = max_reqs - ) -} diff --git a/R/930-responses.R b/R/930-responses.R deleted file mode 100644 index 13f17f3..0000000 --- a/R/930-responses.R +++ /dev/null @@ -1,55 +0,0 @@ -#' Basic response parsing -#' -#' This function attempts to parse a Slack response into a tidy tibble. It will -#' gradually be replaced as I implement specific parsers, and eventually will -#' not be exported. -#' -#' @inheritParams httr2::resp_body_json -#' -#' @return A tibble of results. -#' @export -slack_response_parser <- function(resp) { - # This will only work for a few things right now. We need more complete - # parsing. - # - # I intentionally left the targeting of specific parsers in here for other - # endpoints that return those same types of objects. - results <- httr2::resp_body_json(resp) - if (length(results)) { - if ("messages" %in% names(results)) { - if (length(results$messages)) { - return( - # TODO: If I (even ~naively) specify the spec, I think I can silence - # the messages about unspecified. - tibblify::tibblify( - results$messages, unspecified = "list" - ) - ) - } else { - return(NULL) - } - } - if ("channels" %in% names(results)) { - return(.parse_channel(results$channels)) - } - if ("user" %in% names(results)) { - obj <- results[["user"]] - profile <- obj$profile - obj$profile <- NULL - profile$real_name <- NULL - to_return <- tibble::as_tibble(obj) |> - dplyr::mutate( - profile = list(profile) - ) |> - tidyr::unnest_wider(profile) - return(to_return) - } - if ("members" %in% names(results)) { - return(.parse_members(results$members)) - } - } - cli::cli_abort(c( - "Don't know how to parse this response.", - i = "Response pieces: {names(results)}" - )) -} diff --git a/R/parse_channel.R b/R/parse_channel.R deleted file mode 100644 index b9a5a88..0000000 --- a/R/parse_channel.R +++ /dev/null @@ -1,18 +0,0 @@ -.parse_channel <- function(resp) { - results <- httr2::resp_body_json(resp)$channels - tibblify::tibblify( - results, - spec = tspec_slack_channels(), - unspecified = "list" - ) |> - dplyr::mutate( - channel_type = dplyr::case_when( - is_im ~ "im", - is_mpim ~ "mpim", - is_private ~ "private_channel", - .default = "public_channel" - ), - .after = "channel_name", - .keep = "unused" - ) -} diff --git a/R/parse_members.R b/R/parse_members.R deleted file mode 100644 index 76aa948..0000000 --- a/R/parse_members.R +++ /dev/null @@ -1,78 +0,0 @@ -.parse_members <- function(resp) { - results <- httr2::resp_body_json(resp)$members - tibblify::tibblify( - results, - spec = .slack_response_members_spec(), - unspecified = "list" - ) -} - -.slack_response_members_spec <- function() { - tspec_df( - user_id = tib_chr("id"), - tib_chr("team_id", required = FALSE), - user_name = tib_chr("name", required = FALSE), - is_deleted = tib_lgl("deleted", required = FALSE), - user_color = tib_chr("color", required = FALSE), - real_name = tib_chr("real_name", required = FALSE), - time_zone = tib_chr("tz", required = FALSE), - time_zone_label = tib_chr("tz_label", required = FALSE), - time_zone_offset_seconds = tib_int("tz_offset", required = FALSE), - tib_row( - "profile", .required = FALSE, - profile_title = tib_chr("title", required = FALSE), - tib_chr("phone", required = FALSE), - tib_chr("skype", required = FALSE), - profile_real_name = tib_chr("real_name", required = FALSE), - tib_chr("real_name_normalized", required = FALSE), - tib_chr("display_name", required = FALSE), - tib_chr("display_name_normalized", required = FALSE), - profile_fields = tib_unspecified("fields", required = FALSE), - tib_chr("status_text", required = FALSE), - tib_chr("status_emoji", required = FALSE), - tib_df( - "status_emoji_display_info", .required = FALSE, - status_emoji_name = tib_chr("emoji_name", required = FALSE), - status_emoji_display_url = tib_chr("display_url", required = FALSE), - status_emoji_unicode = tib_chr("unicode", required = FALSE) - ), - status_expiration_ts = tib_int("status_expiration", required = FALSE), - tib_chr("avatar_hash", required = FALSE), - profile_image_original = tib_chr("image_original", required = FALSE), - profile_image_is_custom = tib_lgl("is_custom_image", required = FALSE), - profile_image_24 = tib_chr("image_24", required = FALSE), - profile_image_32 = tib_chr("image_32", required = FALSE), - profile_image_48 = tib_chr("image_48", required = FALSE), - profile_image_72 = tib_chr("image_72", required = FALSE), - profile_image_192 = tib_chr("image_192", required = FALSE), - profile_image_512 = tib_chr("image_512", required = FALSE), - profile_image_1024 = tib_chr("image_1024", required = FALSE), - tib_chr("status_text_canonical", required = FALSE), - profile_team = tib_chr("team", required = FALSE), - tib_chr("huddle_state", required = FALSE), - tib_int("huddle_state_expiration_ts", required = FALSE), - tib_chr("first_name", required = FALSE), - tib_chr("last_name", required = FALSE), - tib_chr("pronouns", required = FALSE), - profile_who_can_share_contact_card = tib_chr("who_can_share_contact_card", required = FALSE), - profile_api_app_id = tib_chr("api_app_id", required = FALSE), - tib_lgl("always_active", required = FALSE), - profile_bot_id = tib_chr("bot_id", required = FALSE) - ), - tib_lgl("is_admin", required = FALSE), - tib_lgl("is_owner", required = FALSE), - tib_lgl("is_primary_owner", required = FALSE), - tib_lgl("is_restricted", required = FALSE), - tib_lgl("is_ultra_restricted", required = FALSE), - tib_lgl("is_bot", required = FALSE), - tib_lgl("is_app_user", required = FALSE), - user_updated_ts = tib_int("updated", required = FALSE), - tib_lgl("is_email_confirmed", required = FALSE), - tib_lgl("has_2fa", required = FALSE), - tib_chr("who_can_share_contact_card", required = FALSE), - user_locale = tib_chr("locale", required = FALSE), - tib_lgl("is_invited_user", required = FALSE), - tib_chr("two_factor_type", required = FALSE), - tib_lgl("is_forgotten", required = FALSE) - ) -} diff --git a/R/paths-api.R b/R/paths-api.R deleted file mode 100644 index ee7578f..0000000 --- a/R/paths-api.R +++ /dev/null @@ -1,22 +0,0 @@ -# These functions were generated by the {beekeeper} package, based on the paths -# element from the source API description. You should carefully review these -# functions. Missing documentation is tagged with "BKTODO" to make it easier for -# you to search for issues. - -#' Get api test -#' -#' Checks API calling code. -#' -#' @inheritParams slack_call_api -#' @param error Error response to return -#' @param foo example property to return -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -slack_api_test <- function(error, foo, token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/api.test", - method = "get", - token = token, - query = list(error = error, foo = foo) - ) -} diff --git a/R/paths-auth.R b/R/paths-auth.R deleted file mode 100644 index b9c9997..0000000 --- a/R/paths-auth.R +++ /dev/null @@ -1,36 +0,0 @@ -# These functions were generated by the {beekeeper} package, based on the paths -# element from the source API description. You should carefully review these -# functions. Missing documentation is tagged with "BKTODO" to make it easier for -# you to search for issues. - -#' Get auth revoke -#' -#' Revokes a token. -#' -#' @inheritParams slack_call_api -#' @param test Setting this parameter to `1` triggers a _testing mode_ where the specified token will not actually be revoked. -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -slack_auth_revoke <- function(test, token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/auth.revoke", - method = "get", - token = token, - query = list(test = test) - ) -} - -#' Get auth test -#' -#' Checks authentication & identity. -#' -#' @inheritParams slack_call_api -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -slack_auth_test <- function(token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/auth.test", - method = "get", - token = token - ) -} diff --git a/R/paths-conversations.R b/R/paths-conversations.R deleted file mode 100644 index 9c1bbec..0000000 --- a/R/paths-conversations.R +++ /dev/null @@ -1,277 +0,0 @@ -# These functions were generated by the {beekeeper} package, based on the paths -# element from the source API description. You should carefully review these -# functions. Missing documentation is tagged with "BKTODO" to make it easier for -# you to search for issues. - -#' Post conversations archive -#' -#' Archives a conversation. -#' -#' @param channel ID of conversation to archive -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return `TRUE` (invisibly) for success. -#' @keywords internal -conversations_archive <- function(channel, token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.archive", - method = "post", - token = token, - body = list(channel = channel) - ) - stop("Implement proper return") -} - -#' Post conversations close -#' -#' Closes a direct message or multi-person direct message. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_close <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.close", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations create -#' -#' Initiates a public or private channel-based conversation -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_create <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.create", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Get conversations info -#' -#' Retrieve information about a conversation. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:read` -#' @param channel Conversation ID to learn more about -#' @param include_locale Set this to `true` to receive the locale for this -#' conversation. Defaults to `false` -#' @param include_num_members Set to `true` to include the member count for the -#' specified conversation. Defaults to `false` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_info <- function(channel, include_locale, include_num_members, token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.info", - method = "get", - token = token, - query = list(channel = channel, include_locale = include_locale, include_num_members = include_num_members) - ) -} - -#' Post conversations invite -#' -#' Invites users to a channel. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_invite <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.invite", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations join -#' -#' Joins an existing conversation. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `channels:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_join <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.join", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations kick -#' -#' Removes a user from a conversation. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_kick <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.kick", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations leave -#' -#' Leaves a conversation. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_leave <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.leave", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations mark -#' -#' Sets the read cursor in a channel. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_mark <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.mark", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations open -#' -#' Opens or resumes a direct message or multi-person direct message. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_open <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.open", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations rename -#' -#' Renames a conversation. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_rename <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.rename", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations set purpose -#' -#' Sets the purpose for a conversation. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_set_purpose <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.setPurpose", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations set topic -#' -#' Sets the topic for a conversation. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_set_topic <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.setTopic", - method = "post", - token = token, - body = list(token = token) - ) -} - -#' Post conversations unarchive -#' -#' Reverses conversation archival. -#' -#' @param token (`character`) A bearer token provided by Slack. A later -#' enhancement will add the ability to generate this token. Slack token are -#' long-lasting, and should be carefully guarded. Requires scope: -#' `conversations:write` -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @keywords internal -conversations_unarchive <- function(token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/conversations.unarchive", - method = "post", - token = token, - body = list(token = token) - ) -} diff --git a/R/paths-users.R b/R/paths-users.R deleted file mode 100644 index 5037d06..0000000 --- a/R/paths-users.R +++ /dev/null @@ -1,134 +0,0 @@ -# These functions were generated by the {beekeeper} package, based on the paths -# element from the source API description. You should carefully review these -# functions. Missing documentation is tagged with "BKTODO" to make it easier for -# you to search for issues. - -#' Get users conversations -#' -#' List conversations the calling user may access. -#' -#' @inheritParams slack_call_api -#' @param user Browse conversations by a specific user ID's membership. Non-public channels are restricted to those where the calling user shares membership. -#' @param types Mix and match channel types by providing a comma-separated list of any combination of `public_channel`, `private_channel`, `mpim`, `im` -#' @param exclude_archived Set to `true` to exclude archived channels from the list -#' @param limit The maximum number of items to return. Fewer than the requested number of items may be returned, even if the end of the list hasn't been reached. Must be an integer no larger than 1000. -#' @param cursor Paginate through collections of data by setting the `cursor` parameter to a `next_cursor` attribute returned by a previous request's `response_metadata`. Default value fetches the first "page" of the collection. See [pagination](/docs/pagination) for more detail. -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -users_conversations <- function(user, types, exclude_archived, limit, cursor, token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/users.conversations", - method = "get", - token = token, - query = list(user = user, types = types, exclude_archived = exclude_archived, limit = limit, cursor = cursor) - ) -} - -#' Post users delete photo -#' -#' Delete the user profile photo -#' -#' @inheritParams slack_call_api -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -users_delete_photo <- function(token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/users.deletePhoto", - method = "post", - token = token - ) -} - -#' Get users get presence -#' -#' Gets user presence information. -#' -#' @inheritParams slack_call_api -#' @param user User to get presence info on. Defaults to the authed user. -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -users_get_presence <- function(user, token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/users.getPresence", - method = "get", - token = token, - query = list(user = user) - ) -} - -#' Get users identity -#' -#' Get a user's identity. -#' -#' @inheritParams slack_call_api -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -users_identity <- function(token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/users.identity", - method = "get", - token = token - ) -} - -#' Get users lookup by email -#' -#' Find a user with an email address. -#' -#' @inheritParams slack_call_api -#' @param email An email address belonging to a user in the workspace -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -users_lookup_by_email <- function(email, token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/users.lookupByEmail", - method = "get", - token = token, - query = list(email = email) - ) -} - -#' Post users set active -#' -#' Marked a user as active. Deprecated and non-functional. -#' -#' @inheritParams slack_call_api -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -users_set_active <- function(token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/users.setActive", - method = "post", - token = token - ) -} - -#' Post users set photo -#' -#' Set the user profile photo -#' -#' @inheritParams slack_call_api -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -users_set_photo <- function(token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/users.setPhoto", - method = "post", - token = token - ) -} - -#' Post users set presence -#' -#' Manually sets user presence. -#' -#' @inheritParams slack_call_api -#' @return BKTODO: Return descriptions are not yet implemented in beekeeper -#' @export -users_set_presence <- function(token = Sys.getenv("SLACK_TOKEN")) { - slack_call_api( - path = "/users.setPresence", - method = "post", - token = token - ) -} diff --git a/R/users-info.R b/R/users-info.R deleted file mode 100644 index 4e6baf0..0000000 --- a/R/users-info.R +++ /dev/null @@ -1,28 +0,0 @@ -#' Get users info -#' -#' Gets information about a user. -#' -#' @inheritParams slack_call_api -#' @param user (`character`) User to get info on. -#' @param include_locale (`logical`) Set this to `TRUE` to receive the locale -#' for users. Defaults to `FALSE`. -#' -#' @return This method returns information about a member of a workspace. -#' @keywords internal -users_info <- function(user, - include_locale = FALSE, - max_results = Inf, - max_reqs = Inf, - token = Sys.getenv("SLACK_API_TOKEN")) { - slack_call_api( - path = "/users.info", - method = "get", - token = token, - query = list( - user = user, - include_locale = include_locale - ), - max_results = max_results, - max_reqs = max_reqs - ) -} diff --git a/data-raw/forLydia.R b/data-raw/forLydia.R index 1a4b378..26868df 100644 --- a/data-raw/forLydia.R +++ b/data-raw/forLydia.R @@ -1,3 +1,5 @@ +stop("Requires a re-made `users_info()` function.") + convos <- conversations_history("C06DGLX8U4V") |> dplyr::arrange(ts) |> tidyr::unnest(pinned_info) |> diff --git a/data-raw/help_stats.R b/data-raw/help_stats.R index ee77e37..025cc8f 100644 --- a/data-raw/help_stats.R +++ b/data-raw/help_stats.R @@ -150,19 +150,36 @@ answer_stats <- answer_tags |> answer_stats -answerable <- answer_tags |> - dplyr::filter( - message_datetime > lubridate::now() - lubridate::days(90), - !tagged_answered, - !waiting_for_asker - ) |> - dplyr::mutate(url = glue::glue("https://dslcio.slack.com/archives/{channel_id}/p{ts}")) |> - dplyr::select(channel_name, reply_count, url) - -answerable +# answerable <- answer_tags |> +# dplyr::filter( +# message_datetime > lubridate::now() - lubridate::days(90), +# !tagged_answered, +# !waiting_for_asker +# ) |> +# dplyr::mutate(url = glue::glue("https://dslcio.slack.com/archives/{.data$channel_id}/p{.data$ts}")) |> +# dplyr::select("channel_name", "reply_count", "url") +# +# answerable +# +# target_n <- 0L +# target_n <- target_n + 1L +# answerable |> +# dplyr::slice(target_n) |> +# dplyr::pull(url) |> +# browseURL() +# +# rm(answerable, tartget_n) -target_n <- 1L -answerable |> - dplyr::slice(target_n) |> - dplyr::pull(url) |> - browseURL() +rm( + answer_stats, + answer_tags, + channels, + convos_all, + help_convos, + threads_all, + threads_nested, + year_counts, + mentor_channel_id, + mentor_ids, + has_reaction +) diff --git a/man/conversations_archive.Rd b/man/conversations_archive.Rd deleted file mode 100644 index c6f7220..0000000 --- a/man/conversations_archive.Rd +++ /dev/null @@ -1,23 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_archive} -\alias{conversations_archive} -\title{Post conversations archive} -\usage{ -conversations_archive(channel, token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{channel}{ID of conversation to archive} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -\code{TRUE} (invisibly) for success. -} -\description{ -Archives a conversation. -} -\keyword{internal} diff --git a/man/conversations_close.Rd b/man/conversations_close.Rd deleted file mode 100644 index 63583f9..0000000 --- a/man/conversations_close.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_close} -\alias{conversations_close} -\title{Post conversations close} -\usage{ -conversations_close(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Closes a direct message or multi-person direct message. -} -\keyword{internal} diff --git a/man/conversations_create.Rd b/man/conversations_create.Rd deleted file mode 100644 index 55b78aa..0000000 --- a/man/conversations_create.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_create} -\alias{conversations_create} -\title{Post conversations create} -\usage{ -conversations_create(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Initiates a public or private channel-based conversation -} -\keyword{internal} diff --git a/man/conversations_info.Rd b/man/conversations_info.Rd deleted file mode 100644 index 5389aa6..0000000 --- a/man/conversations_info.Rd +++ /dev/null @@ -1,34 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_info} -\alias{conversations_info} -\title{Get conversations info} -\usage{ -conversations_info( - channel, - include_locale, - include_num_members, - token = Sys.getenv("SLACK_API_TOKEN") -) -} -\arguments{ -\item{channel}{Conversation ID to learn more about} - -\item{include_locale}{Set this to \code{true} to receive the locale for this -conversation. Defaults to \code{false}} - -\item{include_num_members}{Set to \code{true} to include the member count for the -specified conversation. Defaults to \code{false}} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:read}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Retrieve information about a conversation. -} -\keyword{internal} diff --git a/man/conversations_invite.Rd b/man/conversations_invite.Rd deleted file mode 100644 index a50c27c..0000000 --- a/man/conversations_invite.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_invite} -\alias{conversations_invite} -\title{Post conversations invite} -\usage{ -conversations_invite(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Invites users to a channel. -} -\keyword{internal} diff --git a/man/conversations_join.Rd b/man/conversations_join.Rd deleted file mode 100644 index e33f71c..0000000 --- a/man/conversations_join.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_join} -\alias{conversations_join} -\title{Post conversations join} -\usage{ -conversations_join(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{channels:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Joins an existing conversation. -} -\keyword{internal} diff --git a/man/conversations_kick.Rd b/man/conversations_kick.Rd deleted file mode 100644 index 6284508..0000000 --- a/man/conversations_kick.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_kick} -\alias{conversations_kick} -\title{Post conversations kick} -\usage{ -conversations_kick(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Removes a user from a conversation. -} -\keyword{internal} diff --git a/man/conversations_leave.Rd b/man/conversations_leave.Rd deleted file mode 100644 index 39ba9bf..0000000 --- a/man/conversations_leave.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_leave} -\alias{conversations_leave} -\title{Post conversations leave} -\usage{ -conversations_leave(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Leaves a conversation. -} -\keyword{internal} diff --git a/man/conversations_mark.Rd b/man/conversations_mark.Rd deleted file mode 100644 index 17c1fa6..0000000 --- a/man/conversations_mark.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_mark} -\alias{conversations_mark} -\title{Post conversations mark} -\usage{ -conversations_mark(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Sets the read cursor in a channel. -} -\keyword{internal} diff --git a/man/conversations_open.Rd b/man/conversations_open.Rd deleted file mode 100644 index da6fe06..0000000 --- a/man/conversations_open.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_open} -\alias{conversations_open} -\title{Post conversations open} -\usage{ -conversations_open(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Opens or resumes a direct message or multi-person direct message. -} -\keyword{internal} diff --git a/man/conversations_rename.Rd b/man/conversations_rename.Rd deleted file mode 100644 index d0df783..0000000 --- a/man/conversations_rename.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_rename} -\alias{conversations_rename} -\title{Post conversations rename} -\usage{ -conversations_rename(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Renames a conversation. -} -\keyword{internal} diff --git a/man/conversations_set_purpose.Rd b/man/conversations_set_purpose.Rd deleted file mode 100644 index 969e146..0000000 --- a/man/conversations_set_purpose.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_set_purpose} -\alias{conversations_set_purpose} -\title{Post conversations set purpose} -\usage{ -conversations_set_purpose(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Sets the purpose for a conversation. -} -\keyword{internal} diff --git a/man/conversations_set_topic.Rd b/man/conversations_set_topic.Rd deleted file mode 100644 index 43e83c8..0000000 --- a/man/conversations_set_topic.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_set_topic} -\alias{conversations_set_topic} -\title{Post conversations set topic} -\usage{ -conversations_set_topic(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Sets the topic for a conversation. -} -\keyword{internal} diff --git a/man/conversations_unarchive.Rd b/man/conversations_unarchive.Rd deleted file mode 100644 index 3ce8e5b..0000000 --- a/man/conversations_unarchive.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-conversations.R -\name{conversations_unarchive} -\alias{conversations_unarchive} -\title{Post conversations unarchive} -\usage{ -conversations_unarchive(token = Sys.getenv("SLACK_API_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded. Requires scope: -\code{conversations:write}} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Reverses conversation archival. -} -\keyword{internal} diff --git a/man/dot-slack_req_perform.Rd b/man/dot-slack_req_perform.Rd deleted file mode 100644 index c37f613..0000000 --- a/man/dot-slack_req_perform.Rd +++ /dev/null @@ -1,41 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/910-call.R -\name{.slack_req_perform} -\alias{.slack_req_perform} -\title{Choose and apply pagination strategy} -\usage{ -.slack_req_perform(req, pagination_fn = NULL, max_results, max_reqs, call) -} -\arguments{ -\item{req}{(\code{httr2_request}) The request object to modify.} - -\item{pagination_fn}{(\code{function}) The pagination scheme to use. Currently -either \code{NULL} (no pagination, the default) or \code{slack_pagination_cursor} to -use \code{\link[=slack_pagination_cursor]{slack_pagination_cursor()}} for \code{cursor}-based pagination. See -\href{https://api.slack.com/apis/pagination}{Pagination through collections} in -the Slack API documentation. We do not currently support "Classic -pagination".} - -\item{max_results}{(\code{integer} or \code{Inf}) The maximum number of results to -return. Note that slightly more results may be returned if \code{max_results} is -not evenly divisible by 100.} - -\item{max_reqs}{(\verb{length-1 integer}) The maximum number of separate requests -to perform. Passed to the max_reqs argument of -\code{\link[httr2:req_perform_iterative]{httr2::req_perform_iterative()}} when \code{next_req} is supplied. You will -mostly likely want to change the default value (\code{2}) to \code{Inf} after you -validate that the request works.} - -\item{call}{The execution environment of a currently -running function, e.g. \code{caller_env()}. The function will be -mentioned in error messages as the source of the error. See the -\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.} -} -\value{ -A list of \code{\link[httr2:response]{httr2::response()}} objects, one for each request -performed. The list has additional class \code{nectar_responses}. -} -\description{ -Choose and apply pagination strategy -} -\keyword{internal} diff --git a/man/slack_api_test.Rd b/man/slack_api_test.Rd deleted file mode 100644 index 707dd09..0000000 --- a/man/slack_api_test.Rd +++ /dev/null @@ -1,23 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-api.R -\name{slack_api_test} -\alias{slack_api_test} -\title{Get api test} -\usage{ -slack_api_test(error, foo, token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{error}{Error response to return} - -\item{foo}{example property to return} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Checks API calling code. -} diff --git a/man/slack_auth_revoke.Rd b/man/slack_auth_revoke.Rd deleted file mode 100644 index 35f3d14..0000000 --- a/man/slack_auth_revoke.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-auth.R -\name{slack_auth_revoke} -\alias{slack_auth_revoke} -\title{Get auth revoke} -\usage{ -slack_auth_revoke(test, token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{test}{Setting this parameter to \code{1} triggers a \emph{testing mode} where the specified token will not actually be revoked.} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Revokes a token. -} diff --git a/man/slack_auth_test.Rd b/man/slack_auth_test.Rd deleted file mode 100644 index 21acc40..0000000 --- a/man/slack_auth_test.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-auth.R -\name{slack_auth_test} -\alias{slack_auth_test} -\title{Get auth test} -\usage{ -slack_auth_test(token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Checks authentication & identity. -} diff --git a/man/slack_call_api.Rd b/man/slack_call_api.Rd deleted file mode 100644 index 70c7e15..0000000 --- a/man/slack_call_api.Rd +++ /dev/null @@ -1,75 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/910-call.R -\name{slack_call_api} -\alias{slack_call_api} -\title{Call the Slack Web API} -\usage{ -slack_call_api( - path, - query = list(), - body = NULL, - method = NULL, - pagination_fn = NULL, - max_results = Inf, - max_reqs = Inf, - response_parser = slack_response_parser, - token = Sys.getenv("SLACK_API_TOKEN"), - call = rlang::caller_env() -) -} -\arguments{ -\item{path}{(\code{character} or \code{list}) The route to an API endpoint. Optionally, -a list or character vector with the path as one or more unnamed arguments -(which will be concatenated with "/") plus named arguments to -\code{\link[glue:glue]{glue::glue()}} into the path.} - -\item{query}{(\code{character} or \code{list}) An optional list or character vector of -parameters to pass in the query portion of the request. Can also include a -\code{.multi} argument to pass to \code{\link[httr2:req_url]{httr2::req_url_query()}} to control how -elements containing multiple values are handled.} - -\item{body}{(multiple types) An object to use as the body of the request. If -any component of the body is a path, pass it through \code{\link[fs:path]{fs::path()}} or -otherwise give it the class "fs_path" to indicate that it is a path.} - -\item{method}{(\verb{length-1 character}, optional) If the method is something -other than \code{GET} or \code{POST}, supply it. Case is ignored.} - -\item{pagination_fn}{(\code{function}) The pagination scheme to use. Currently -either \code{NULL} (no pagination, the default) or \code{slack_pagination_cursor} to -use \code{\link[=slack_pagination_cursor]{slack_pagination_cursor()}} for \code{cursor}-based pagination. See -\href{https://api.slack.com/apis/pagination}{Pagination through collections} in -the Slack API documentation. We do not currently support "Classic -pagination".} - -\item{max_results}{(\code{integer} or \code{Inf}) The maximum number of results to -return. Note that slightly more results may be returned if \code{max_results} is -not evenly divisible by 100.} - -\item{max_reqs}{(\verb{length-1 integer}) The maximum number of separate requests -to perform. Passed to the max_reqs argument of -\code{\link[httr2:req_perform_iterative]{httr2::req_perform_iterative()}} when \code{next_req} is supplied. You will -mostly likely want to change the default value (\code{2}) to \code{Inf} after you -validate that the request works.} - -\item{response_parser}{(\code{function}) A function to parse the server response. -Defaults to \code{\link[=slack_response_parser]{slack_response_parser()}}. Set this to \code{NULL} to return the raw -response from \code{\link[httr2:req_perform]{httr2::req_perform()}}.} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} - -\item{call}{(\code{environment}) The environment from which a function was called, -e.g. \code{\link[rlang:stack]{rlang::caller_env()}} (the default). The environment will be mentioned -in error messages as the source of the error. This argument is particularly -useful for functions that are intended to be called as utilities inside -other functions.} -} -\value{ -A tibble with the results of the API call. -} -\description{ -Generate and perform request to a Slack Web API method. -} -\keyword{internal} diff --git a/man/slack_response_parser.Rd b/man/slack_response_parser.Rd deleted file mode 100644 index 0d67d54..0000000 --- a/man/slack_response_parser.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/930-responses.R -\name{slack_response_parser} -\alias{slack_response_parser} -\title{Basic response parsing} -\usage{ -slack_response_parser(resp) -} -\arguments{ -\item{resp}{A httr2 \link[httr2]{response} object, created by \code{\link[httr2:req_perform]{req_perform()}}.} -} -\value{ -A tibble of results. -} -\description{ -This function attempts to parse a Slack response into a tidy tibble. It will -gradually be replaced as I implement specific parsers, and eventually will -not be exported. -} diff --git a/man/users_conversations.Rd b/man/users_conversations.Rd deleted file mode 100644 index a780dce..0000000 --- a/man/users_conversations.Rd +++ /dev/null @@ -1,36 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-users.R -\name{users_conversations} -\alias{users_conversations} -\title{Get users conversations} -\usage{ -users_conversations( - user, - types, - exclude_archived, - limit, - cursor, - token = Sys.getenv("SLACK_TOKEN") -) -} -\arguments{ -\item{user}{Browse conversations by a specific user ID's membership. Non-public channels are restricted to those where the calling user shares membership.} - -\item{types}{Mix and match channel types by providing a comma-separated list of any combination of \code{public_channel}, \code{private_channel}, \code{mpim}, \code{im}} - -\item{exclude_archived}{Set to \code{true} to exclude archived channels from the list} - -\item{limit}{The maximum number of items to return. Fewer than the requested number of items may be returned, even if the end of the list hasn't been reached. Must be an integer no larger than 1000.} - -\item{cursor}{Paginate through collections of data by setting the \code{cursor} parameter to a \code{next_cursor} attribute returned by a previous request's \code{response_metadata}. Default value fetches the first "page" of the collection. See \href{/docs/pagination}{pagination} for more detail.} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -List conversations the calling user may access. -} diff --git a/man/users_delete_photo.Rd b/man/users_delete_photo.Rd deleted file mode 100644 index 3daaff4..0000000 --- a/man/users_delete_photo.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-users.R -\name{users_delete_photo} -\alias{users_delete_photo} -\title{Post users delete photo} -\usage{ -users_delete_photo(token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Delete the user profile photo -} diff --git a/man/users_get_presence.Rd b/man/users_get_presence.Rd deleted file mode 100644 index 3b169c2..0000000 --- a/man/users_get_presence.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-users.R -\name{users_get_presence} -\alias{users_get_presence} -\title{Get users get presence} -\usage{ -users_get_presence(user, token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{user}{User to get presence info on. Defaults to the authed user.} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Gets user presence information. -} diff --git a/man/users_identity.Rd b/man/users_identity.Rd deleted file mode 100644 index 09f5982..0000000 --- a/man/users_identity.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-users.R -\name{users_identity} -\alias{users_identity} -\title{Get users identity} -\usage{ -users_identity(token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Get a user's identity. -} diff --git a/man/users_info.Rd b/man/users_info.Rd deleted file mode 100644 index 0cf6344..0000000 --- a/man/users_info.Rd +++ /dev/null @@ -1,41 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/users-info.R -\name{users_info} -\alias{users_info} -\title{Get users info} -\usage{ -users_info( - user, - include_locale = FALSE, - max_results = Inf, - max_reqs = Inf, - token = Sys.getenv("SLACK_API_TOKEN") -) -} -\arguments{ -\item{user}{(\code{character}) User to get info on.} - -\item{include_locale}{(\code{logical}) Set this to \code{TRUE} to receive the locale -for users. Defaults to \code{FALSE}.} - -\item{max_results}{(\code{integer} or \code{Inf}) The maximum number of results to -return. Note that slightly more results may be returned if \code{max_results} is -not evenly divisible by 100.} - -\item{max_reqs}{(\verb{length-1 integer}) The maximum number of separate requests -to perform. Passed to the max_reqs argument of -\code{\link[httr2:req_perform_iterative]{httr2::req_perform_iterative()}} when \code{next_req} is supplied. You will -mostly likely want to change the default value (\code{2}) to \code{Inf} after you -validate that the request works.} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -This method returns information about a member of a workspace. -} -\description{ -Gets information about a user. -} -\keyword{internal} diff --git a/man/users_lookup_by_email.Rd b/man/users_lookup_by_email.Rd deleted file mode 100644 index 76e287a..0000000 --- a/man/users_lookup_by_email.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-users.R -\name{users_lookup_by_email} -\alias{users_lookup_by_email} -\title{Get users lookup by email} -\usage{ -users_lookup_by_email(email, token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{email}{An email address belonging to a user in the workspace} - -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Find a user with an email address. -} diff --git a/man/users_set_active.Rd b/man/users_set_active.Rd deleted file mode 100644 index cf8a547..0000000 --- a/man/users_set_active.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-users.R -\name{users_set_active} -\alias{users_set_active} -\title{Post users set active} -\usage{ -users_set_active(token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Marked a user as active. Deprecated and non-functional. -} diff --git a/man/users_set_photo.Rd b/man/users_set_photo.Rd deleted file mode 100644 index 4519314..0000000 --- a/man/users_set_photo.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-users.R -\name{users_set_photo} -\alias{users_set_photo} -\title{Post users set photo} -\usage{ -users_set_photo(token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Set the user profile photo -} diff --git a/man/users_set_presence.Rd b/man/users_set_presence.Rd deleted file mode 100644 index 0138109..0000000 --- a/man/users_set_presence.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/paths-users.R -\name{users_set_presence} -\alias{users_set_presence} -\title{Post users set presence} -\usage{ -users_set_presence(token = Sys.getenv("SLACK_TOKEN")) -} -\arguments{ -\item{token}{(\code{character}) A bearer token provided by Slack. A later -enhancement will add the ability to generate this token. Slack token are -long-lasting, and should be carefully guarded.} -} -\value{ -BKTODO: Return descriptions are not yet implemented in beekeeper -} -\description{ -Manually sets user presence. -} From ab9836053c30a70a8308a08bb14249b14c02aa27 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Wed, 12 Feb 2025 07:55:09 -0600 Subject: [PATCH 2/9] Rename auth func. --- R/010-prepare.R | 2 +- R/020-auth.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/010-prepare.R b/R/010-prepare.R index 69c7b53..d71baaf 100644 --- a/R/010-prepare.R +++ b/R/010-prepare.R @@ -25,7 +25,7 @@ slack_req_prepare <- function(path, query = query, body = body, method = method, - auth_fn = .req_auth, + auth_fn = .slack_req_auth, auth_args = list(token = token), tidy_fn = tidy_fn, pagination_fn = pagination_fn, diff --git a/R/020-auth.R b/R/020-auth.R index 0e13bc8..61a7f43 100644 --- a/R/020-auth.R +++ b/R/020-auth.R @@ -1,4 +1,4 @@ -.req_auth <- function(req, token = NULL) { +.slack_req_auth <- function(req, token = NULL) { if (!is.null(token)) { req <- httr2::req_auth_bearer_token(req, token) } From e25ae889cca92d91af9b5545bc0548778561206c Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Fri, 23 May 2025 05:39:23 -0500 Subject: [PATCH 3/9] TODO: Clear PII in archives on request. --- data-raw/archive-users.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data-raw/archive-users.R b/data-raw/archive-users.R index 5191aff..d0b3e6a 100644 --- a/data-raw/archive-users.R +++ b/data-raw/archive-users.R @@ -1,5 +1,8 @@ pkgload::load_all(".", helpers = FALSE, attach_testthat = FALSE) +# TODO: Clear out archived PII for deleted users. User "U084PAXBFJR" has +# requested deletion (via Slack), for example. + users <- users_list(include_locale = TRUE) |> tidyr::unnest(profile) |> dplyr::select( From 4c948ccff32f27a235fe8bb9653e5af2de2be11f Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Thu, 26 Mar 2026 05:15:57 -0500 Subject: [PATCH 4/9] fix: update stbl scalar calls --- .Rbuildignore | 4 ++ .gitignore | 1 + .vscode/extensions.json | 5 ++ .vscode/settings.json | 10 ++++ R/050-validation.R | 56 ++++++++++++------ R/paths-conversations-conversations_history.R | 54 ++++++++--------- R/paths-conversations-conversations_list.R | 54 ++++++++--------- R/paths-conversations-conversations_members.R | 28 +++++---- R/paths-conversations-conversations_replies.R | 58 ++++++++----------- R/paths-users-users_list.R | 33 +++++------ air.toml | 0 data-raw/archive-convos.R | 11 ++-- 12 files changed, 171 insertions(+), 143 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 air.toml diff --git a/.Rbuildignore b/.Rbuildignore index 7c34331..affcd29 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -11,3 +11,7 @@ ^data-raw$ ^_beekeeper_rapid\.rds$ ^_beekeeper\.yml$ +^\.positai$ +^\.claude$ +^[.]?air[.]toml$ +^\.vscode$ diff --git a/.gitignore b/.gitignore index 457525e..b4c1d84 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .DS_Store .quarto docs +.positai diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..344f76e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "Posit.air-vscode" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a9f69fe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "[r]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "Posit.air-vscode" + }, + "[quarto]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "quarto.quarto" + } +} diff --git a/R/050-validation.R b/R/050-validation.R index e95818f..9a4021e 100644 --- a/R/050-validation.R +++ b/R/050-validation.R @@ -1,27 +1,41 @@ -as_slack_ts <- function(x, - arg = rlang::caller_arg(x), - call = rlang::caller_env()) { +as_slack_ts <- function( + x, + arg = rlang::caller_arg(x), + call = rlang::caller_env() +) { UseMethod("as_slack_ts") } #' @export -as_slack_ts.POSIXct <- function(x, - arg = rlang::caller_arg(x), - call = rlang::caller_env()) { +as_slack_ts.POSIXct <- function( + x, + arg = rlang::caller_arg(x), + call = rlang::caller_env() +) { as_slack_ts(as.numeric(x), arg = arg, call = call) } #' @export -as_slack_ts.numeric <- function(x, - arg = rlang::caller_arg(x), - call = rlang::caller_env()) { - stbl::stabilize_chr_scalar(x, allow_na = FALSE, x_arg = arg, call = call) +as_slack_ts.numeric <- function( + x, + arg = rlang::caller_arg(x), + call = rlang::caller_env() +) { + stbl::stabilize_chr_scalar( + x, + allow_null = TRUE, + allow_na = FALSE, + x_arg = arg, + call = call + ) } #' @export -as_slack_ts.character <- function(x, - arg = rlang::caller_arg(x), - call = rlang::caller_env()) { +as_slack_ts.character <- function( + x, + arg = rlang::caller_arg(x), + call = rlang::caller_env() +) { # TODO: Ideally this should detect whether this is datetime-y or double-y. It # should also probably use a stbl::to_dbl() function that doesn't exist yet, # to give better errors about NA. @@ -31,13 +45,21 @@ as_slack_ts.character <- function(x, # special class or attribute when we convert these to datetimes? # # as_slack_ts(as.double(x), arg = arg, call = call) - stbl::stabilize_chr_scalar(x, allow_na = FALSE, x_arg = arg, call = call) + stbl::stabilize_chr_scalar( + x, + allow_null = TRUE, + allow_na = FALSE, + x_arg = arg, + call = call + ) } #' @export -as_slack_ts.default <- function(x, - arg = rlang::caller_arg(x), - call = rlang::caller_env()) { +as_slack_ts.default <- function( + x, + arg = rlang::caller_arg(x), + call = rlang::caller_env() +) { cli::cli_abort( c( "Cannot convert object to Slack timestamp", diff --git a/R/paths-conversations-conversations_history.R b/R/paths-conversations-conversations_history.R index 191339c..06eb2c1 100644 --- a/R/paths-conversations-conversations_history.R +++ b/R/paths-conversations-conversations_history.R @@ -15,15 +15,17 @@ #' #' @returns `conversations_history()`: A channel's messages as a tibble. #' @export -conversations_history <- function(channel, - latest = lubridate::now(), - oldest = 0, - inclusive = TRUE, - include_all_metadata = FALSE, - per_req = 200L, - max_reqs = Inf, - max_tries_per_req = 3, - token = Sys.getenv("SLACK_API_TOKEN")) { +conversations_history <- function( + channel, + latest = lubridate::now(), + oldest = 0, + inclusive = TRUE, + include_all_metadata = FALSE, + per_req = 200L, + max_reqs = Inf, + max_tries_per_req = 3, + token = Sys.getenv("SLACK_API_TOKEN") +) { req <- req_conversations_history( channel = channel, latest = latest, @@ -44,32 +46,22 @@ conversations_history <- function(channel, #' @rdname conversations_history #' @returns `req_conversations_history()`: A `httr2_request` request object to #' fetch a conversation's history of messages and events. -req_conversations_history <- function(channel, - latest = lubridate::now(), - oldest = 0, - inclusive = TRUE, - include_all_metadata = FALSE, - per_req = 200L, - token = Sys.getenv("SLACK_API_TOKEN")) { - channel <- stbl::to_chr_scalar( - channel, - allow_null = FALSE, - allow_zero_length = FALSE - ) +req_conversations_history <- function( + channel, + latest = lubridate::now(), + oldest = 0, + inclusive = TRUE, + include_all_metadata = FALSE, + per_req = 200L, + token = Sys.getenv("SLACK_API_TOKEN") +) { + channel <- stbl::to_chr_scalar(channel, allow_null = TRUE) latest <- as_slack_ts(latest) oldest <- as_slack_ts(oldest) - inclusive <- stbl::to_lgl_scalar( - inclusive, - allow_null = FALSE - ) - include_all_metadata <- stbl::to_lgl_scalar( - include_all_metadata, - allow_null = FALSE - ) + inclusive <- stbl::to_lgl_scalar(inclusive) + include_all_metadata <- stbl::to_lgl_scalar(include_all_metadata) per_req <- stbl::stabilize_int_scalar( per_req, - allow_null = FALSE, - allow_zero_length = FALSE, allow_na = FALSE, min_value = 1L, max_value = 999L diff --git a/R/paths-conversations-conversations_list.R b/R/paths-conversations-conversations_list.R index ce459dd..72ee6bd 100644 --- a/R/paths-conversations-conversations_list.R +++ b/R/paths-conversations-conversations_list.R @@ -11,18 +11,20 @@ #' @returns `conversations_list()`: A tibble of information about channels and #' channel-like conversations. #' @export -conversations_list <- function(exclude_archived = FALSE, - per_req = 200L, - team_id = NULL, - types = c( - "public_channel", - "private_channel", - "mpim", - "im" - ), - max_reqs = Inf, - max_tries_per_req = 3, - token = Sys.getenv("SLACK_API_TOKEN")) { +conversations_list <- function( + exclude_archived = FALSE, + per_req = 200L, + team_id = NULL, + types = c( + "public_channel", + "private_channel", + "mpim", + "im" + ), + max_reqs = Inf, + max_tries_per_req = 3, + token = Sys.getenv("SLACK_API_TOKEN") +) { req <- req_conversations_list( exclude_archived = exclude_archived, per_req = per_req, @@ -41,26 +43,26 @@ conversations_list <- function(exclude_archived = FALSE, #' @rdname conversations_list #' @returns `req_conversations_list()`: A `httr2_request` request object to list #' all channels in a Slack team. -req_conversations_list <- function(exclude_archived = FALSE, - per_req = 200L, - team_id = NULL, - types = c( - "public_channel", - "private_channel", - "mpim", - "im" - ), - token = Sys.getenv("SLACK_API_TOKEN")) { - exclude_archived <- stbl::to_lgl_scalar(exclude_archived, FALSE, FALSE) +req_conversations_list <- function( + exclude_archived = FALSE, + per_req = 200L, + team_id = NULL, + types = c( + "public_channel", + "private_channel", + "mpim", + "im" + ), + token = Sys.getenv("SLACK_API_TOKEN") +) { + exclude_archived <- stbl::to_lgl_scalar(exclude_archived) per_req <- stbl::stabilize_int_scalar( per_req, - allow_null = FALSE, - allow_zero_length = FALSE, allow_na = FALSE, min_value = 1L, max_value = 1000L ) - team_id <- stbl::to_chr_scalar(team_id) + team_id <- stbl::to_chr_scalar(team_id, allow_null = TRUE) types <- rlang::arg_match(types, multiple = TRUE) slack_req_prepare( path = "/conversations.list", diff --git a/R/paths-conversations-conversations_members.R b/R/paths-conversations-conversations_members.R index 8e9a3cb..8cb0b17 100644 --- a/R/paths-conversations-conversations_members.R +++ b/R/paths-conversations-conversations_members.R @@ -11,11 +11,13 @@ #' @returns `conversations_members()`: A list of user IDs belonging to the #' members in a conversation. #' @export -conversations_members <- function(channel, - per_req = 200L, - max_reqs = Inf, - max_tries_per_req = 3, - token = Sys.getenv("SLACK_API_TOKEN")) { +conversations_members <- function( + channel, + per_req = 200L, + max_reqs = Inf, + max_tries_per_req = 3, + token = Sys.getenv("SLACK_API_TOKEN") +) { req <- req_conversations_members( channel = channel, per_req = per_req, @@ -32,18 +34,14 @@ conversations_members <- function(channel, #' @rdname conversations_members #' @returns `req_conversations_members()`: A `httr2_request` request object to #' retrieve members of a conversation. -req_conversations_members <- function(channel, - per_req = 200L, - token = Sys.getenv("SLACK_API_TOKEN")) { - channel <- stbl::to_chr_scalar( - channel, - allow_null = FALSE, - allow_zero_length = FALSE - ) +req_conversations_members <- function( + channel, + per_req = 200L, + token = Sys.getenv("SLACK_API_TOKEN") +) { + channel <- stbl::to_chr_scalar(channel) per_req <- stbl::stabilize_int_scalar( per_req, - allow_null = FALSE, - allow_zero_length = FALSE, allow_na = FALSE, min_value = 1L, max_value = 999L diff --git a/R/paths-conversations-conversations_replies.R b/R/paths-conversations-conversations_replies.R index 5401ecb..ef018b9 100644 --- a/R/paths-conversations-conversations_replies.R +++ b/R/paths-conversations-conversations_replies.R @@ -22,16 +22,18 @@ #' conversation as a tibble. Note: The parent message is always included in #' the response. #' @export -conversations_replies <- function(channel, - ts, - latest = lubridate::now(), - oldest = 0, - inclusive = TRUE, - include_all_metadata = FALSE, - per_req = 200L, - max_reqs = Inf, - max_tries_per_req = 3, - token = Sys.getenv("SLACK_API_TOKEN")) { +conversations_replies <- function( + channel, + ts, + latest = lubridate::now(), + oldest = 0, + inclusive = TRUE, + include_all_metadata = FALSE, + per_req = 200L, + max_reqs = Inf, + max_tries_per_req = 3, + token = Sys.getenv("SLACK_API_TOKEN") +) { req <- req_conversations_replies( channel = channel, ts = ts, @@ -54,34 +56,24 @@ conversations_replies <- function(channel, #' @returns `req_conversations_replies()`: A `httr2_request` request object to #' retrieve a thread of messages posted to a conversation as a tibble. Note: #' The parent message is always included in the response. -req_conversations_replies <- function(channel, - ts, - latest = lubridate::now(), - oldest = 0, - inclusive = TRUE, - include_all_metadata = FALSE, - per_req = 200L, - token = Sys.getenv("SLACK_API_TOKEN")) { - channel <- stbl::to_chr_scalar( - channel, - allow_null = FALSE, - allow_zero_length = FALSE - ) +req_conversations_replies <- function( + channel, + ts, + latest = lubridate::now(), + oldest = 0, + inclusive = TRUE, + include_all_metadata = FALSE, + per_req = 200L, + token = Sys.getenv("SLACK_API_TOKEN") +) { + channel <- stbl::to_chr_scalar(channel) ts <- as_slack_ts(ts) latest <- as_slack_ts(latest) oldest <- as_slack_ts(oldest) - inclusive <- stbl::to_lgl_scalar( - inclusive, - allow_null = FALSE - ) - include_all_metadata <- stbl::to_lgl_scalar( - include_all_metadata, - allow_null = FALSE - ) + inclusive <- stbl::to_lgl_scalar(inclusive) + include_all_metadata <- stbl::to_lgl_scalar(include_all_metadata) per_req <- stbl::stabilize_int_scalar( per_req, - allow_null = FALSE, - allow_zero_length = FALSE, allow_na = FALSE, min_value = 1L, max_value = 999L diff --git a/R/paths-users-users_list.R b/R/paths-users-users_list.R index 32f9002..bd0d7ea 100644 --- a/R/paths-users-users_list.R +++ b/R/paths-users-users_list.R @@ -11,12 +11,14 @@ #' @returns `users_list()`: A list of all users in the workspace. This includes #' both invited users and deleted/deactivated users. #' @export -users_list <- function(include_locale = FALSE, - team_id = NULL, - per_req = 200L, - max_reqs = Inf, - max_tries_per_req = 3, - token = Sys.getenv("SLACK_API_TOKEN")) { +users_list <- function( + include_locale = FALSE, + team_id = NULL, + per_req = 200L, + max_reqs = Inf, + max_tries_per_req = 3, + token = Sys.getenv("SLACK_API_TOKEN") +) { req <- req_users_list( include_locale = include_locale, team_id = team_id, @@ -34,23 +36,20 @@ users_list <- function(include_locale = FALSE, #' @rdname users_list #' @returns `req_users_list()`: A `httr2_request` request object that lists all #' users in a Slack team. -req_users_list <- function(include_locale = FALSE, - team_id = NULL, - per_req = 200L, - token = Sys.getenv("SLACK_API_TOKEN")) { - include_locale <- stbl::to_lgl_scalar( - include_locale, - allow_null = FALSE - ) +req_users_list <- function( + include_locale = FALSE, + team_id = NULL, + per_req = 200L, + token = Sys.getenv("SLACK_API_TOKEN") +) { + include_locale <- stbl::to_lgl_scalar(include_locale) per_req <- stbl::stabilize_int_scalar( per_req, - allow_null = FALSE, - allow_zero_length = FALSE, allow_na = FALSE, min_value = 1L, max_value = 999L ) - team_id <- stbl::to_chr_scalar(team_id) + team_id <- stbl::to_chr_scalar(team_id, allow_null = TRUE) slack_req_prepare( path = "/users.list", method = "get", diff --git a/air.toml b/air.toml new file mode 100644 index 0000000..e69de29 diff --git a/data-raw/archive-convos.R b/data-raw/archive-convos.R index cf566b4..c8f601e 100644 --- a/data-raw/archive-convos.R +++ b/data-raw/archive-convos.R @@ -195,10 +195,13 @@ if (nrow(convo_changes)) { tidyr::unnest("edited", names_sep = "_") |> dplyr::select("ts", "edited_ts") |> dplyr::summarize( - change_ts = max(c( - as.double(.data$ts), - as.double(.data$edited_ts) - ), na.rm = TRUE), + change_ts = max( + c( + as.double(.data$ts), + as.double(.data$edited_ts) + ), + na.rm = TRUE + ), .by = "ts" ) |> dplyr::pull(.data$change_ts) |> From 41e5f96e53b6ad99b89f84a620ca136689c462ff Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Thu, 30 Apr 2026 05:30:27 -0500 Subject: [PATCH 5/9] Remove beekeeper from suggests for easier load during dev --- DESCRIPTION | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 979ab2a..5d87eff 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: slackapi Title: `Slack Web API` Client Version: 0.0.0.9000 -Authors@R: +Authors@R: person("Jon", "Harmon", , "jonthegeek@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0003-4781-4346")) Description: A client to interact with the `Slack Web API` @@ -11,7 +11,7 @@ License: MIT + file LICENSE URL: https://github.com/jonthegeek/slackapi, https://jonthegeek.github.io/slackapi/ BugReports: https://github.com/jonthegeek/slackapi/issues -Imports: +Imports: cli, dplyr, httr2, @@ -22,12 +22,10 @@ Imports: tibble, tibblify, tidyr -Suggests: - beekeeper, +Suggests: httptest2, testthat (>= 3.0.0) Remotes: - jonthegeek/beekeeper, jonthegeek/nectar, jonthegeek/stbl Config/testthat/edition: 3 From 03371e53e39dc9713bc1e98c4c1b025dbd5ebf43 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Tue, 12 May 2026 05:27:47 -0500 Subject: [PATCH 6/9] Updates for new nectar, tibblify, stbl, and roxygen2 --- DESCRIPTION | 2 +- R/010-prepare.R | 23 ++++++++++--------- R/020-auth.R | 7 ++++++ R/paths-conversations-conversations_history.R | 2 +- R/paths-conversations-conversations_list.R | 2 +- R/paths-conversations-conversations_members.R | 2 +- R/paths-conversations-conversations_replies.R | 2 +- R/paths-users-users_list.R | 2 +- man/slack_req_prepare.Rd | 7 ++---- man/slackapi-package.Rd | 5 ++++ 10 files changed, 32 insertions(+), 22 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5d87eff..c998e2a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,4 +32,4 @@ Config/testthat/edition: 3 Encoding: UTF-8 Language: en-US Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 +Config/roxygen2/version: 8.0.0 diff --git a/R/010-prepare.R b/R/010-prepare.R index d71baaf..00654e2 100644 --- a/R/010-prepare.R +++ b/R/010-prepare.R @@ -8,14 +8,16 @@ #' @inheritParams nectar::req_prepare #' @inherit nectar::req_prepare return #' @keywords internal -slack_req_prepare <- function(path, - query = list(), - body = NULL, - method = NULL, - pagination_fn = NULL, - tidy_fn = nectar::resp_tidy_unknown, - token = Sys.getenv("SLACK_API_TOKEN"), - call = rlang::caller_env()) { +slack_req_prepare <- function( + path, + query = list(), + body = NULL, + method = NULL, + pagination_fn = NULL, + tidy_fn = nectar::resp_tidy_unknown, + token = Sys.getenv("SLACK_API_TOKEN"), + call = rlang::caller_env() +) { token <- .find_token(token, body, query) body <- .prepare_list(body) query <- .prepare_list(query) @@ -25,9 +27,8 @@ slack_req_prepare <- function(path, query = query, body = body, method = method, - auth_fn = .slack_req_auth, - auth_args = list(token = token), - tidy_fn = tidy_fn, + auth = .slack_auth(token), + tidy_policy = nectar::tidy_policy_prepare(tidy_fn), pagination_fn = pagination_fn, call = call ) diff --git a/R/020-auth.R b/R/020-auth.R index 61a7f43..3bbb348 100644 --- a/R/020-auth.R +++ b/R/020-auth.R @@ -5,6 +5,13 @@ return(req) } +.slack_auth <- function(token = NULL) { + nectar::auth_prepare( + auth_fn = .slack_req_auth, + token = token + ) +} + .find_token <- function(token = NULL, body = NULL, query = NULL) { token %||% body$token %||% query$token } diff --git a/R/paths-conversations-conversations_history.R b/R/paths-conversations-conversations_history.R index 06eb2c1..8a3b180 100644 --- a/R/paths-conversations-conversations_history.R +++ b/R/paths-conversations-conversations_history.R @@ -40,7 +40,7 @@ conversations_history <- function( max_reqs = max_reqs, max_tries_per_req = max_tries_per_req ) - return(nectar::resp_tidy(resps)) + return(nectar::resp_parse(resps)) } #' @rdname conversations_history diff --git a/R/paths-conversations-conversations_list.R b/R/paths-conversations-conversations_list.R index 72ee6bd..2f8010b 100644 --- a/R/paths-conversations-conversations_list.R +++ b/R/paths-conversations-conversations_list.R @@ -37,7 +37,7 @@ conversations_list <- function( max_reqs = max_reqs, max_tries_per_req = max_tries_per_req ) - return(nectar::resp_tidy(resps)) + return(nectar::resp_parse(resps)) } #' @rdname conversations_list diff --git a/R/paths-conversations-conversations_members.R b/R/paths-conversations-conversations_members.R index 8cb0b17..837d10b 100644 --- a/R/paths-conversations-conversations_members.R +++ b/R/paths-conversations-conversations_members.R @@ -28,7 +28,7 @@ conversations_members <- function( max_reqs = max_reqs, max_tries_per_req = max_tries_per_req ) - return(nectar::resp_tidy(resps)) + return(nectar::resp_parse(resps)) } #' @rdname conversations_members diff --git a/R/paths-conversations-conversations_replies.R b/R/paths-conversations-conversations_replies.R index ef018b9..20eecf6 100644 --- a/R/paths-conversations-conversations_replies.R +++ b/R/paths-conversations-conversations_replies.R @@ -49,7 +49,7 @@ conversations_replies <- function( max_reqs = max_reqs, max_tries_per_req = max_tries_per_req ) - return(nectar::resp_tidy(resps)) + return(nectar::resp_parse(resps)) } #' @rdname conversations_replies diff --git a/R/paths-users-users_list.R b/R/paths-users-users_list.R index bd0d7ea..49cddab 100644 --- a/R/paths-users-users_list.R +++ b/R/paths-users-users_list.R @@ -30,7 +30,7 @@ users_list <- function( max_reqs = max_reqs, max_tries_per_req = max_tries_per_req ) - return(nectar::resp_tidy(resps)) + return(nectar::resp_parse(resps)) } #' @rdname users_list diff --git a/man/slack_req_prepare.Rd b/man/slack_req_prepare.Rd index beaa34b..5b2319e 100644 --- a/man/slack_req_prepare.Rd +++ b/man/slack_req_prepare.Rd @@ -23,7 +23,7 @@ a list or character vector with the path as one or more unnamed arguments \item{query}{(\code{character} or \code{list}) An optional list or character vector of parameters to pass in the query portion of the request. Can also include a -\code{.multi} argument to pass to \code{\link[httr2:req_url]{httr2::req_url_query()}} to control how +\code{.multi} argument to pass to \code{\link[httr2:req_url_query]{httr2::req_url_query()}} to control how elements containing multiple values are handled.} \item{body}{(multiple types) An object to use as the body of the request. If @@ -40,15 +40,12 @@ use \code{\link[=slack_pagination_cursor]{slack_pagination_cursor()}} for \code{ the Slack API documentation. We do not currently support "Classic pagination".} -\item{tidy_fn}{(\code{function}) A function that will be invoked by \code{\link[nectar:resp_tidy]{resp_tidy()}} -to tidy the response.} - \item{token}{(\code{character}) A bearer token provided by Slack. A later enhancement will add the ability to generate this token. Slack token are long-lasting, and should be carefully guarded.} \item{call}{(\code{environment}) The environment from which a function was called, -e.g. \code{\link[rlang:stack]{rlang::caller_env()}} (the default). The environment will be mentioned +e.g. \code{\link[rlang:caller_env]{rlang::caller_env()}} (the default). The environment will be mentioned in error messages as the source of the error. This argument is particularly useful for functions that are intended to be called as utilities inside other functions.} diff --git a/man/slackapi-package.Rd b/man/slackapi-package.Rd index d11d143..14aaef5 100644 --- a/man/slackapi-package.Rd +++ b/man/slackapi-package.Rd @@ -20,5 +20,10 @@ Useful links: \author{ \strong{Maintainer}: Jon Harmon \email{jonthegeek@gmail.com} (\href{https://orcid.org/0000-0003-4781-4346}{ORCID}) +Authors: +\itemize{ + \item Jon Harmon \email{jonthegeek@gmail.com} (\href{https://orcid.org/0000-0003-4781-4346}{ORCID}) +} + } \keyword{internal} From 9763a41bccb819e451264039023269a0c0686eb3 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Tue, 12 May 2026 05:30:21 -0500 Subject: [PATCH 7/9] Add notes about forLydia.R file --- data-raw/forLydia.R | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data-raw/forLydia.R b/data-raw/forLydia.R index 26868df..6b4f82f 100644 --- a/data-raw/forLydia.R +++ b/data-raw/forLydia.R @@ -1,3 +1,7 @@ +# Get private conversations (with active user), then filter to a specific +# channel. This probably isn't generally useful, but I'm keeping it until I +# parse out what *is* useful for an example/vignette. + stop("Requires a re-made `users_info()` function.") convos <- conversations_history("C06DGLX8U4V") |> From 6cb99768bac9abb2f3ae4ec066d22b7dcabd6ee6 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Thu, 21 May 2026 06:01:12 -0500 Subject: [PATCH 8/9] Tweak data-raw scripts. --- data-raw/help_stats.R | 43 +++++++++++++++++++------- data-raw/{forLydia.R => user_convos.R} | 0 2 files changed, 31 insertions(+), 12 deletions(-) rename data-raw/{forLydia.R => user_convos.R} (100%) diff --git a/data-raw/help_stats.R b/data-raw/help_stats.R index 025cc8f..9e14da8 100644 --- a/data-raw/help_stats.R +++ b/data-raw/help_stats.R @@ -10,12 +10,14 @@ threads_all <- readRDS("data-raw/convos/threads_all.rds") if ("reply_count" %in% colnames(convos_all)) { convos_all$reply_count <- tidyr::replace_na( - convos_all$reply_count, 0L + convos_all$reply_count, + 0L ) } if ("reply_users_count" %in% colnames(convos_all)) { convos_all$reply_users_count <- tidyr::replace_na( - convos_all$reply_users_count, 0L + convos_all$reply_users_count, + 0L ) } @@ -69,14 +71,20 @@ mentor_ids <- conversations_members(mentor_channel_id) has_reaction <- function(rxnses, posters, target_reactions) { purrr::map2_lgl( - rxnses, posters, + rxnses, + posters, \(rxns, poster) { - if (is.null(rxns)) return(FALSE) + if (is.null(rxns)) { + return(FALSE) + } any( purrr::map2_lgl( - rxns$name, rxns$users, + rxns$name, + rxns$users, \(rxn, reactors) { - rxn %in% target_reactions && any(c(poster, mentor_ids) %in% reactors) + rxn %in% + target_reactions && + any(c(poster, mentor_ids) %in% reactors) } ) ) @@ -115,7 +123,9 @@ answer_tags <- help_convos |> c("heavy_check_mark", "question-answered", "white_check_mark") ), asker_replied_last = purrr::map2_lgl( - .data$user, .data$replies, \(this_user, these_replies) { + .data$user, + .data$replies, + \(this_user, these_replies) { if (!NROW(these_replies)) { return(TRUE) } @@ -131,7 +141,9 @@ answer_tags <- help_convos |> .data$user, c("speech_balloon", "question-more-info") ), - waiting_for_asker = !.data$tagged_answered & .data$tagged_more_info & !.data$asker_replied_last + waiting_for_asker = !.data$tagged_answered & + .data$tagged_more_info & + !.data$asker_replied_last ) answer_stats <- answer_tags |> @@ -139,14 +151,21 @@ answer_stats <- answer_tags |> .by = message_year, messages = dplyr::n(), has_replies = sum(.data$reply_count != 0), - p_has_replies = has_replies/messages * 100, + p_has_replies = has_replies / messages * 100, tagged_answered = sum(.data$tagged_answered), - p_tagged_answered = tagged_answered/messages * 100, + p_tagged_answered = tagged_answered / messages * 100, tagged_waiting_op = sum(.data$waiting_for_asker), - p_waiting_op = tagged_waiting_op/messages * 100, + p_waiting_op = tagged_waiting_op / messages * 100, p_waiting_us = 100 - p_tagged_answered - p_waiting_op ) |> - dplyr::select("message_year", "messages", "p_has_replies", "p_tagged_answered", "p_waiting_op", "p_waiting_us") + dplyr::select( + "message_year", + "messages", + "p_has_replies", + "p_tagged_answered", + "p_waiting_op", + "p_waiting_us" + ) answer_stats diff --git a/data-raw/forLydia.R b/data-raw/user_convos.R similarity index 100% rename from data-raw/forLydia.R rename to data-raw/user_convos.R From 64fecce992caaaf027154f0a42b97b0d3edd2861 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Thu, 21 May 2026 06:12:23 -0500 Subject: [PATCH 9/9] Update workflows --- .github/workflows/R-CMD-check.yaml | 36 +++--- .github/workflows/format-suggest.yaml | 46 ++++++++ .github/workflows/pkgdown-cleanup.yaml | 33 ++++++ .github/workflows/pkgdown.yaml | 53 ++++++--- .github/workflows/pr-commands.yaml | 56 ++------- .github/workflows/qcthat.yaml | 157 +++++++++++++++++++++++++ .github/workflows/test-coverage.yaml | 31 +++-- 7 files changed, 310 insertions(+), 102 deletions(-) create mode 100644 .github/workflows/format-suggest.yaml create mode 100644 .github/workflows/pkgdown-cleanup.yaml create mode 100644 .github/workflows/qcthat.yaml diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 562fe0f..473d2e1 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -4,14 +4,14 @@ on: push: branches: [main, master] pull_request: + branches: [main, master] -name: R-CMD-check.yaml - -permissions: read-all +name: R-CMD-check jobs: R-CMD-check: runs-on: ${{ matrix.config.os }} + container: ${{ matrix.config.container }} name: ${{ matrix.config.os }} (${{ matrix.config.r }}) @@ -19,33 +19,25 @@ jobs: fail-fast: false matrix: config: - - {os: macos-latest, r: 'release'} - - {os: windows-latest, r: 'release'} - - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} - - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - R_KEEP_PKG_SOURCE: yes + # - {os: macos-latest, r: 'release'} + # - {os: windows-latest, r: 'release'} + # - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release', container: 'ghcr.io/api2r/pkgskills-ci:devel'} + - {os: ubuntu-latest, r: 'release', container: 'ghcr.io/api2r/pkgskills-ci:release'} + # - {os: ubuntu-latest, r: 'oldrel-1', container: 'ghcr.io/api2r/pkgskills-ci:oldrel-1'} steps: - - uses: actions/checkout@v4 - - - uses: r-lib/actions/setup-pandoc@v2 + - uses: actions/checkout@v6 - - uses: r-lib/actions/setup-r@v2 + - uses: api2r/actions/install@v1 with: + use-container: "${{ matrix.config.container != '' }}" + token: ${{ secrets.GITHUB_TOKEN }} r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} - use-public-rspm: true - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: any::rcmdcheck needs: check + extra-packages: any::rcmdcheck + cache-version: "1" - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true - build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' diff --git a/.github/workflows/format-suggest.yaml b/.github/workflows/format-suggest.yaml new file mode 100644 index 0000000..af50210 --- /dev/null +++ b/.github/workflows/format-suggest.yaml @@ -0,0 +1,46 @@ +# Workflow derived from https://github.com/posit-dev/setup-air/tree/main/examples + +on: + # Using `pull_request_target` over `pull_request` for elevated `GITHUB_TOKEN` + # privileges, otherwise we can't set `pull-requests: write` when the pull + # request comes from a fork, which is our main use case (external contributors). + # + # `pull_request_target` runs in the context of the target branch (`main`, usually), + # rather than in the context of the pull request like `pull_request` does. Due + # to this, we must explicitly checkout `ref: ${{ github.event.pull_request.head.sha }}`. + # This is typically frowned upon by GitHub, as it exposes you to potentially running + # untrusted code in a context where you have elevated privileges, but they explicitly + # call out the use case of reformatting and committing back / commenting on the PR + # as a situation that should be safe (because we aren't actually running the untrusted + # code, we are just treating it as passive data). + # https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + pull_request_target: + +name: format-suggest.yaml + +jobs: + format-suggest: + name: format-suggest + runs-on: ubuntu-latest + + permissions: + # Required to push suggestion comments to the PR + pull-requests: write + + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Install + uses: posit-dev/setup-air@v1 + + - name: Format + run: air format . + + - name: Suggest + uses: reviewdog/action-suggester@v1 + with: + level: error + fail_level: error + tool_name: air diff --git a/.github/workflows/pkgdown-cleanup.yaml b/.github/workflows/pkgdown-cleanup.yaml new file mode 100644 index 0000000..d8ea7c2 --- /dev/null +++ b/.github/workflows/pkgdown-cleanup.yaml @@ -0,0 +1,33 @@ +# This workflow removes the pkgdown preview directory when a PR is closed. +name: Clean up pkgdown preview + +on: + pull_request: + types: [closed] + +permissions: + contents: write + +jobs: + clean-pr-preview: + runs-on: ubuntu-latest + steps: + - name: Checkout gh-pages branch + uses: actions/checkout@v6 + with: + repository: ${{ github.repository }} + token: ${{ secrets.GITHUB_TOKEN }} + ref: gh-pages + + - name: Remove PR preview directory + run: | + pr_dir="pr/${{ github.event.number }}" + if [ -d "$pr_dir" ]; then + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git rm -rf "$pr_dir" + git commit -m "Remove preview for PR #${{ github.event.number }}" + git push + else + echo "Directory $pr_dir does not exist, skipping cleanup." + fi diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index bfc9f4d..def2fff 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -2,47 +2,70 @@ # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: [main, master] + branches: [main] pull_request: + branches: [main] release: types: [published] workflow_dispatch: -name: pkgdown.yaml - -permissions: read-all +name: pkgdown jobs: pkgdown: runs-on: ubuntu-latest - # Only restrict concurrency for non-PR jobs + container: + image: ghcr.io/api2r/pkgskills-ci:release concurrency: group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} permissions: contents: write + pull-requests: write steps: - - uses: actions/checkout@v4 - - - uses: r-lib/actions/setup-pandoc@v2 - - - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true + - uses: actions/checkout@v6 - - uses: r-lib/actions/setup-r-dependencies@v2 + - uses: api2r/actions/install@v1 with: - extra-packages: any::pkgdown, local::. + use-container: "true" + token: ${{ secrets.GITHUB_TOKEN }} needs: website + extra-packages: any::pkgdown gilead-biostats/qcthat local::. any::glue - name: Build site run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) shell: Rscript {0} + - name: Deploy PR preview ๐Ÿงช + if: github.event_name == 'pull_request' + uses: JamesIves/github-pages-deploy-action@v4.8.0 + with: + clean: false + branch: gh-pages + folder: docs + target-folder: pr/${{ github.event.number }} + + - name: Comment with PR site URL ๐ŸŒ + if: github.event_name == 'pull_request' + run: | + intPRNumber <- ${{ github.event.pull_request.number }} + strOwner <- tolower(qcthat::GetGHOwner()) + strRepo <- qcthat::GetGHRepo() + strURL <- glue::glue( + "https://{strOwner}.github.io/{strRepo}/pr/{intPRNumber}/dev" + ) + print(paste("๐ŸŒ URL:", strURL)) + qcthat::CommentIssue( + intPRNumber, + glue::glue("๐ŸŒ [PR pkgdown deployed]({strURL})"), + NULL + ) + shell: Rscript {0} + - name: Deploy to GitHub pages ๐Ÿš€ if: github.event_name != 'pull_request' - uses: JamesIves/github-pages-deploy-action@v4.5.0 + uses: JamesIves/github-pages-deploy-action@v4.8.0 with: clean: false branch: gh-pages diff --git a/.github/workflows/pr-commands.yaml b/.github/workflows/pr-commands.yaml index 2edd93f..8c2f575 100644 --- a/.github/workflows/pr-commands.yaml +++ b/.github/workflows/pr-commands.yaml @@ -4,34 +4,30 @@ on: issue_comment: types: [created] -name: pr-commands.yaml - -permissions: read-all +name: Commands jobs: document: if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} name: document runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + container: + image: ghcr.io/api2r/pkgskills-ci:release permissions: contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: r-lib/actions/pr-fetch@v2 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true - - - uses: r-lib/actions/setup-r-dependencies@v2 + - uses: api2r/actions/install@v1 with: - extra-packages: any::roxygen2 + use-container: "true" + token: ${{ secrets.GITHUB_TOKEN }} needs: pr-document + extra-packages: any::roxygen2 - name: Document run: roxygen2::roxygenise() @@ -47,39 +43,3 @@ jobs: - uses: r-lib/actions/pr-push@v2 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - - style: - if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} - name: style - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - - uses: r-lib/actions/pr-fetch@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - uses: r-lib/actions/setup-r@v2 - - - name: Install dependencies - run: install.packages("styler") - shell: Rscript {0} - - - name: Style - run: styler::style_pkg() - shell: Rscript {0} - - - name: commit - run: | - git config --local user.name "$GITHUB_ACTOR" - git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" - git add \*.R - git commit -m 'Style' - - - uses: r-lib/actions/pr-push@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/qcthat.yaml b/.github/workflows/qcthat.yaml new file mode 100644 index 0000000..2adec84 --- /dev/null +++ b/.github/workflows/qcthat.yaml @@ -0,0 +1,157 @@ +# Workflow derived from +# https://github.com/Gilead-BioStats/qcthat/tree/v1.1.1/inst/workflows/qcthat.yaml. +on: + pull_request: + types: [opened, edited, reopened, synchronize, milestoned] + release: + types: [released] + issues: + types: [closed] + workflow_dispatch: + inputs: + pr: + description: PR number to which reports should be added (leave blank for none). + required: false + milestone: + description: Milestone name to use for the milestone report (leave blank for none). + required: false + tag: + description: Release tag to which the report should be attached (leave blank for none). + required: false + issueNumber: + description: The closed issue number to process to update user acceptance testing information. + required: false + +name: qcthat Quality Control + +permissions: + # read: Required for generating reports and updating UAT status. + # write: Required for initiating the UAT process. + issues: write + # read: Required for updating UAT status. + # write: Required for adding reports to pull requests. + pull-requests: write + # write: Required for attaching reports to releases. + contents: write + # write: Required for updating UAT status. + actions: write + +# Configuration variables for controlling workflow behavior +env: + qcthat_UAT: true + qcthat_PR_REPORT: true + qcthat_COMPLETED_REPORT: true + qcthat_MILESTONE_REPORT: true + qcthat_RELEASE_REPORT: true + qcthat_FAIL_FOR_TEST_FAILURES: true + +jobs: + qcthat: + runs-on: ubuntu-latest + container: + image: ghcr.io/api2r/pkgskills-ci:release + if: >- + (github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'qcthat-uat')) || + github.event_name != 'issues' + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + + - uses: api2r/actions/install@v1 + with: + use-container: "true" + token: ${{ secrets.GITHUB_TOKEN }} + extra-packages: Gilead-BioStats/qcthat@main local::. + + - name: Manage User Acceptance Testing + if: >- + env.qcthat_UAT == 'true' && ( + (github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'qcthat-uat')) || + (github.event_name == 'workflow_dispatch' && inputs.issueNumber != '') + ) + run: | + Rscript -e "qcthat::TriggerUAT()" + + - name: Generate Issue-Test Matrix + if: >- + (env.qcthat_PR_REPORT == 'true' || env.qcthat_RELEASE_REPORT == 'true' || env.qcthat_FAIL_FOR_TEST_FAILURES == 'true') && ( + github.event_name == 'pull_request' || + github.event_name == 'release' || + (github.event_name == 'workflow_dispatch' && inputs.issueNumber == '') + ) + run: | + # Generate the full matrix for the package + IssueTestMatrix <- qcthat::QCPackage() + print(IssueTestMatrix) + + # Save the matrix and UAT data for subsequent steps + saveRDS(IssueTestMatrix, "ITM.rds") + qcthat::SaveUATIssues() + shell: Rscript {0} + + - name: Update PR Reports + if: >- + env.qcthat_PR_REPORT == 'true' && ( + github.event_name == 'pull_request' || + (github.event_name == 'workflow_dispatch' && inputs.pr != '') + ) + run: | + issueTestMatrix <- readRDS("ITM.rds") + qcthat::LoadUATIssues() + qcthat::CommentAllReports( + dfITM = issueTestMatrix, + lglPR = as.logical("${{ env.qcthat_PR_REPORT }}"), + lglMilestone = as.logical("${{ env.qcthat_MILESTONE_REPORT }}"), + lglCompleted = as.logical("${{ env.qcthat_COMPLETED_REPORT }}"), + lglUAT = as.logical("${{ env.qcthat_UAT }}") + ) + shell: Rscript {0} + + - name: Update Release Reports + if: >- + env.qcthat_RELEASE_REPORT == 'true' && ( + github.event_name == 'release' || inputs.tag != '' + ) + run: | + issueTestMatrix <- readRDS("ITM.rds") + qcthat::LoadUATIssues() + qcthat::AttachReleaseReports( + dfITM = issueTestMatrix, + lglCompleted = as.logical("${{ env.qcthat_COMPLETED_REPORT }}"), + lglMilestone = as.logical("${{ env.qcthat_MILESTONE_REPORT }}") + ) + shell: Rscript {0} + + - name: Flag failure for PR + if: >- + env.qcthat_FAIL_FOR_TEST_FAILURES == 'true' && ( + github.event_name == 'pull_request' || + (github.event_name == 'workflow_dispatch' && inputs.pr != '') + ) + run: | + issueTestMatrix <- readRDS("ITM.rds") + dfPR <- qcthat::QCPR(dfITM = issueTestMatrix) + if (any(dfPR$Disposition == "fail", na.rm = TRUE)) { + cli::cli_abort( + "One or more tests failed or were skipped for PR-associated issues." + ) + } + shell: Rscript {0} + + - name: Flag failure for completed + if: >- + env.qcthat_FAIL_FOR_TEST_FAILURES == 'true' && ( + github.event_name == 'pull_request' || + github.event_name == 'release' || + (github.event_name == 'workflow_dispatch' && inputs.issueNumber == '') + ) + run: | + issueTestMatrix <- readRDS("ITM.rds") + dfCompleted = qcthat::QCCompletedIssues(dfITM = issueTestMatrix) + if (any(dfCompleted$Disposition == "fail", na.rm = TRUE)) { + cli::cli_abort( + "One or more tests failed or were skipped for completed issues." + ) + } + shell: Rscript {0} diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index e050312..8b7a5f3 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -4,28 +4,24 @@ on: push: branches: [main, master] pull_request: + branches: [main, master] -name: test-coverage.yaml - -permissions: read-all +name: test-coverage jobs: test-coverage: runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - + container: + image: ghcr.io/api2r/pkgskills-ci:release steps: - - uses: actions/checkout@v4 - - - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true + - uses: actions/checkout@v6 - - uses: r-lib/actions/setup-r-dependencies@v2 + - uses: api2r/actions/install@v1 with: - extra-packages: any::covr, any::xml2 + use-container: "true" + token: ${{ secrets.GITHUB_TOKEN }} needs: coverage + extra-packages: any::covr any::xml2 - name: Test coverage run: | @@ -34,15 +30,16 @@ jobs: clean = FALSE, install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") ) + print(cov) covr::to_cobertura(cov) shell: Rscript {0} - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 with: # Fail if error if not on PR, or if on PR and token is given fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }} - file: ./cobertura.xml - plugin: noop + files: ./cobertura.xml + plugins: noop disable_search: true token: ${{ secrets.CODECOV_TOKEN }} @@ -55,7 +52,7 @@ jobs: - name: Upload test results if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: coverage-test-failures path: ${{ runner.temp }}/package