diff --git a/DESCRIPTION b/DESCRIPTION index 5319be7..c0c9bcc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Type: Package Package: slackr Title: Send Messages, Images, R Objects and Files to 'Slack' Channels/Users -Version: 3.2.0 +Version: 3.3.0 Author: Bob Rudis [aut, cre], Jay Jacobs [ctb], David Severski [ctb], Quinn Weber [ctb], Konrad Karczewski [ctb], Shinya Uryu [ctb], Gregory Jefferis [ctb], Ed Niles [ctb], Rick Saporta [ctb], Jonathan Sidi @@ -23,16 +23,16 @@ URL: https://github.com/mrkaye97/slackr, https://mrkaye97.github.io/slackr/ BugReports: https://github.com/mrkaye97/slackr/issues Depends: R (>= 3.3.0) -Imports: cachem (>= 1.0.4), dplyr, ggplot2, graphics, grDevices, httr - (>= 1.4.2), jsonlite, magrittr, memoise (>= 2.0.0), methods, - rlang, tibble, utils, withr -Suggests: covr, knitr, rmarkdown, svglite, testthat (>= 3.0.0), - texPreview +Imports: cachem (>= 1.0.4), dplyr, graphics, grDevices, httr (>= + 1.4.2), jsonlite, magrittr, memoise (>= 2.0.0), rlang, tibble, + utils, withr +Suggests: covr, ggplot2, knitr, rmarkdown, svglite, testthat (>= + 3.0.0), texPreview VignetteBuilder: knitr Config/testthat/edition: 3 Encoding: UTF-8 -RoxygenNote: 7.1.1 +RoxygenNote: 7.2.3 NeedsCompilation: no -Packaged: 2021-09-20 14:54:49 UTC; matt +Packaged: 2023-02-20 16:23:04 UTC; matt Repository: CRAN -Date/Publication: 2021-09-20 15:10:02 UTC +Date/Publication: 2023-02-20 16:40:02 UTC diff --git a/MD5 b/MD5 index 1d0bc0f..061023c 100644 --- a/MD5 +++ b/MD5 @@ -1,56 +1,57 @@ -3f862cd5465b8931b7d165f37d40c8fb *DESCRIPTION +bb49a3120050692970fd5807fb8823ef *DESCRIPTION b5c243c35e6b712ecfe223711365f790 *LICENSE -d3cde3e3f8253fec0c771c4b9ae659a5 *NAMESPACE -c205826b56e76e29272baff9a4b6a773 *NEWS.md -303a6b14aea92f1cec82012bfb49d704 *R/call_slack_api.R -d98dc0166408a6b5b4178942ecbec893 *R/call_slack_internals.R -0faf10c4e51332c0d4ba6eed42da7db0 *R/gg_slackr.R +15da68dcd2cd5d6a5ba142aee2beff2b *NAMESPACE +8f2d907529aae441ac2d04c5de3593fb *NEWS.md +ae44f30f17edd19187647f9a0ae00b6a *R/call_slack_api.R +8b86a70ec6f77bd7fea7c93caa3455fb *R/call_slack_internals.R +a417e2c39e0b79fe597d6ee581f64f84 *R/gg_slackr.R 3763cb9344d9528905647e6fc5c3ba92 *R/globals.R -9efcb1db246edb1eac937e2d7bc2fd6a *R/internals.R -237c5a9cd2d0ef882eebb1a47d98aa84 *R/register_onexit.R -a97a7b33a6be86759170c051eeb3c0a3 *R/slackr.R -528b3993c3e30256502fa77abcfaa31e *R/slackr_bot.r -50620febe5b1afe513446f27c2ad4bca *R/slackr_csv.R -c756edfb9f2864b1b7f90087785f6d1d *R/slackr_delete.R -831bb33ff735b5837b0764c928ebf8a5 *R/slackr_dev.R -6d8943d49aacf152def7de81635dd685 *R/slackr_history.R -16d0a1ca41c004d4fe75447dddf6ab55 *R/slackr_save.R -501dd5393661f4e0bfe8875da8a3a7db *R/slackr_setup.r -c0303983f5cb7fe4b28c870d4f89635d *R/slackr_tex.R -1cd361bab10f354f9c04d7951e92ca8e *R/slackr_upload.R -4b3ae55640f6896cdddde268c5b0f9c1 *R/slackr_utils.R +04ddfa82a8751369fd88205d52d51791 *R/internals.R +f1a75daa0bdd214e9368100eef031980 *R/register_onexit.R +a42b9ee0dc9230eade6e66a1301a71f5 *R/slackr.R +a4e2674c997685ee0db41b30686ec409 *R/slackr_bot.r +4c1de13243971ef17ce80678fd429bce *R/slackr_csv.R +ba38b2c2908149c45592c39647c33c56 *R/slackr_delete.R +6b9f248a75fdca67c106f3a171a251af *R/slackr_dev.R +89cc4c92e572e93391e84f6025edeb40 *R/slackr_history.R +814a2cd5ac1d09beecb7199a6b145942 *R/slackr_save.R +db707f01ae7148d27b90ecb0a9cdd369 *R/slackr_setup.r +dbe5fd32f50cd4775d05e1bca315e1a7 *R/slackr_tex.R +441f0af66cfeba4d400c55098a6d10ec *R/slackr_upload.R +54875529f97fb0112bc3aaa0127a8b63 *R/slackr_utils.R +99fe491896dd21d912531d569f6cc362 *R/testthat-helpers.R b56ff2b193ecba52f9ec7f3b10cca008 *R/utils-pipe.R -7fb1ed813aa49d6c7980016f8cb49f02 *build/vignette.rds -9bc38243f4829f69142c1e2d89ca557f *inst/doc/scoped-bot-setup.R -5fb60af98e6b0b06ce7995ad944b0702 *inst/doc/scoped-bot-setup.Rmd -eabe3dd88970ac4eab6baf0a78d02fc0 *inst/doc/scoped-bot-setup.html -9764a54045d1cc21e13c64ed7aafedab *inst/doc/using-slackr.R -bf707a12850358a074bc561b0aab9a26 *inst/doc/using-slackr.Rmd -1fae48c6d28796af8ed0a225d0c29050 *inst/doc/using-slackr.html +7c4e5df64d463152a6b83e7f3028f0fc *build/vignette.rds +b52ecbc2f340680295dc36f80e13ca61 *inst/doc/scoped-bot-setup.R +7c89f7dd6001659d706e7c7769886531 *inst/doc/scoped-bot-setup.Rmd +ecbeb57ec134a0cd514ad16d8abc12b1 *inst/doc/scoped-bot-setup.html +2a67c2f0aa4f9de9269943f51204021c *inst/doc/using-slackr.R +a20362e12811991fe559d9297c0492d0 *inst/doc/using-slackr.Rmd +da27d75c71c0b3edb5098a375a6f2030 *inst/doc/using-slackr.html 3c9e2d06e8560272e30c98b0c827478b *inst/doc/webhook-setup.R 4a4e9871bf3b7814a7dc69d9af28f0af *inst/doc/webhook-setup.Rmd -9608124c945451152a8edff5eb99928c *inst/doc/webhook-setup.html +5dd92e89806ad42c27ce5dc2985d46ba *inst/doc/webhook-setup.html e692e26589972134a8345cf07a99209f *man/auth_test.Rd 53e16714e58dd252e22b0940033b32d2 *man/call_slack_api.Rd 4b46fe2772f75880f3289966495bcac8 *man/convert_response_to_tibble.Rd -ebfe2beeac5e5cec8b60261fc628b6eb *man/create_config_file.Rd +df25d203cdb13d6de6e107fca889266a *man/create_config_file.Rd 306d03a83a7503a539f3e823903f64f2 *man/figures/logo.png -764058b15c861aaac6f152cc6045b6b6 *man/ggslackr.Rd +4151515c7c27158a6fd5e4e73a73769d *man/ggslackr.Rd 8f4aad003a999fae004ba9361f9a99d6 *man/pipe.Rd -4217725495d65c6b8e4730cc9af0e57f *man/register_onexit.Rd +df8708d656fa87329d25f09a4fb57957 *man/register_onexit.Rd decaf6965ce1e621d6221aee9961f498 *man/slackr.Rd -c338b1b9c9622aac57c0e2eacba5f80e *man/slackr_bot.Rd -8378ccc73ab8c08104de39d12014aa31 *man/slackr_channels.Rd +c4873055bd99b46f9e65d6d48024e99e *man/slackr_bot.Rd +170eba18067ab8840f534b3d0219a6b2 *man/slackr_channels.Rd ae521ba5313abff1c2f3b0590ec47e1a *man/slackr_chtrans.Rd d5aa72b33d2b389711edc8bef1926e85 *man/slackr_csv.Rd 7959ff38baff344d0d641c31b59f8bed *man/slackr_delete.Rd 311d9302d50d7943035417a8d7310e04 *man/slackr_dev.Rd -7a3934132c6cdae5adaedb04da900ae1 *man/slackr_history.Rd +b9fb485017c59f5786ff1a120280a84e *man/slackr_history.Rd 658aebc8add35b573110c3da65f3102f *man/slackr_ims.Rd 74c97cecd92cbabacac469a38dd23c85 *man/slackr_msg.Rd 878a0261cc5373c65b8391ec5fc66e09 *man/slackr_save.Rd -8cc664fcc2145fa442a96abab23f2b13 *man/slackr_setup.Rd -e85902bc1833b665ec152d8769648daa *man/slackr_teardown.Rd +650b54d9a4497a5a14f0f71df62c3c7e *man/slackr_setup.Rd +3fe6e6afbd532af354fd6359d7acc57d *man/slackr_teardown.Rd 8c0302b39b81fa6c869e8682b65c2510 *man/slackr_tex.Rd b5df0bc161b8ba14f8a66cc2d2092657 *man/slackr_upload.Rd 639e4c8a9106dfc1a754ccff7e86934e *man/slackr_users.Rd @@ -58,10 +59,13 @@ b5df0bc161b8ba14f8a66cc2d2092657 *man/slackr_upload.Rd f7d80f8c10a360df110c62280304c1af *man/with_pagination.Rd a60ccca7306def1046c65860b91ca6f5 *tests/testthat.R 0daf7d92b763992f5b78ec3c42b48e1a *tests/testthat/Rplots.pdf -18d23525e7ababf8d18d9d5675435852 *tests/testthat/test-connection.R -678c7164c7dd2092511dddd2d472821f *tests/testthat/test-helpers.R -3dc5c77329e42a8daa8b5acf8dab60d0 *tests/testthat/test-posting.R -e132e71ab7654adde8f4f8e2269ae24e *tests/testthat/test-zconfig.R -5fb60af98e6b0b06ce7995ad944b0702 *vignettes/scoped-bot-setup.Rmd -bf707a12850358a074bc561b0aab9a26 *vignettes/using-slackr.Rmd +2033315e06011f6106f0bb59bc1a320b *tests/testthat/test-aaa-setup.R +ff43db2f4c2059e0da36ccc578dff71e *tests/testthat/test-connection.R +0b52cb527142b2f30e8c9412ea698a45 *tests/testthat/test-dev-tex.R +6fdf95996d5184553f1e28ea2cee6a01 *tests/testthat/test-history.R +99b4d4e33f210bc5fa3593b24a3ab64e *tests/testthat/test-internals.R +bfcc13c29ff97f0ab123a722cc26071e *tests/testthat/test-posting.R +08c1d391d32cad08605636127a45a1c6 *tests/testthat/test-zzz-teardown.R +7c89f7dd6001659d706e7c7769886531 *vignettes/scoped-bot-setup.Rmd +a20362e12811991fe559d9297c0492d0 *vignettes/using-slackr.Rmd 4a4e9871bf3b7814a7dc69d9af28f0af *vignettes/webhook-setup.Rmd diff --git a/NAMESPACE b/NAMESPACE index fc0c486..6425961 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -32,15 +32,9 @@ importFrom(dplyr,distinct) importFrom(dplyr,left_join) importFrom(dplyr,rename) importFrom(dplyr,setdiff) -importFrom(ggplot2,aes) -importFrom(ggplot2,geom_point) -importFrom(ggplot2,ggplot) -importFrom(ggplot2,ggsave) -importFrom(ggplot2,last_plot) importFrom(grDevices,dev.copy) importFrom(grDevices,dev.off) importFrom(grDevices,png) -importFrom(graphics,par) importFrom(httr,GET) importFrom(httr,POST) importFrom(httr,add_headers) @@ -56,10 +50,12 @@ importFrom(magrittr,"%>%") importFrom(memoise,memoise) importFrom(rlang,abort) importFrom(rlang,call2) +importFrom(rlang,check_installed) importFrom(rlang,inform) importFrom(rlang,warn) importFrom(tibble,as_tibble) importFrom(tibble,tibble) +importFrom(tools,file_ext) importFrom(utils,URLencode) importFrom(utils,write.csv) importFrom(withr,local_options) diff --git a/NEWS.md b/NEWS.md index 0f8dfdf..71191fa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,25 @@ +# slackr 3.3.0 + User-facing changes: + + * `ggslackr` now relies on dots (`...`) to pass arguments through to `ggsave` + * `ggplot2` is now in `suggests`, so the user doesn't need it installed to use `slackr`. `ggslackr` will prompt to install if it's not already. + * Fixes a "bug" with pagination in `slackr_history` causing an almost infinite loop + * Fixes a bug in `slackr_save` where `initial_comment` would do nothing + * Improves documentation of `slackr_history` to be more helpful in pointing the user to the Slack API docs + + Backend changes: + + * Gets rid of default args in some un-exported functions to make tracking down bugs easier + * Adds significant test coverage + +# slackr 3.2.2 + +* Fixes a bug where specifying specifying `duration` had no effect in `slackr_history()` if posted_from_time was not specified also. [linked issue](https://github.com/mrkaye97/slackr/issues/181) + +# slackr 3.2.1 + +* Fixes a bug where specifying a `thread_ts` or `reply_broadcast` in `slackr` did nothing + # slackr 3.2.0 * `slackr` and `slackr_bot` no longer rely on `reprex`, as `prex_r` would fail when an eval environment needed to be specified, but couldn't. diff --git a/R/call_slack_api.R b/R/call_slack_api.R index c922030..d05eeb8 100644 --- a/R/call_slack_api.R +++ b/R/call_slack_api.R @@ -14,8 +14,8 @@ POST <- "POST" #' @noRd #' stop_for_status <- function(r) { - # note that httr::stop_for_status should be called explicitly + # note that httr::stop_for_status should be called explicitly httr::stop_for_status(r) cr <- content(r, encoding = "UTF-8") @@ -56,7 +56,7 @@ with_retry <- function(fun) { r <- fun() if (r$status_code == 429) { retry_after <- headers(r)[["retry-after"]] - inform("\nPausing for ", retry_after, " seconds due to Slack API rate limit") + inform(paste("\nPausing for", retry_after, "seconds due to Slack API rate limit")) Sys.sleep(retry_after) } else { ok <- TRUE @@ -80,10 +80,16 @@ with_retry <- function(fun) { #' @return The API response (a named list) #' @export #' -call_slack_api <- function(path, ..., body = NULL, .method = c("GET", "POST"), - token, - .verbose = Sys.getenv("SLACKR_VERBOSE", "FALSE"), - .next_cursor = "") { +call_slack_api <- function( + path, + ..., + body = NULL, + .method = c("GET", "POST"), + token, + .verbose = Sys.getenv("SLACKR_VERBOSE", "FALSE"), + .next_cursor = "" +) { + if (missing(token) || is.null(token)) { token <- Sys.getenv("SLACK_TOKEN", "") } @@ -98,9 +104,7 @@ call_slack_api <- function(path, ..., body = NULL, .method = c("GET", "POST"), if (.verbose == "TRUE") { old_config <- set_config(verbose()) on.exit(set_config(old_config), add = TRUE) - } # else { - # set_config(httr::verbose(data_out = FALSE, data_in = FALSE, info = FALSE, ssl = FALSE)) - # } + } # Set up the API call call_api <- function() { @@ -112,7 +116,6 @@ call_slack_api <- function(path, ..., body = NULL, .method = c("GET", "POST"), .headers = c(Authorization = paste("Bearer", token)) ), query = add_cursor_get(..., .next_cursor = .next_cursor) - # ... ) } else if (.method == "POST") { POST( @@ -135,7 +138,6 @@ call_slack_api <- function(path, ..., body = NULL, .method = c("GET", "POST"), add_cursor_get <- function(..., .next_cursor = "") { z <- list(...) if (!is.null(.next_cursor) && .next_cursor != "") { - # inform("Appending cursor to query") z <- append(z, list(cursor = .next_cursor)) } z @@ -207,7 +209,8 @@ with_pagination <- function(fun, extract) { result <- convert_response_to_tibble(r, extract) } else { result <- bind_rows( - result, convert_response_to_tibble(r, extract) + result, + convert_response_to_tibble(r, extract) ) } } diff --git a/R/call_slack_internals.R b/R/call_slack_internals.R index 5db85ca..19a5d62 100644 --- a/R/call_slack_internals.R +++ b/R/call_slack_internals.R @@ -9,7 +9,7 @@ #' @keywords internal #' @noRd #' @references https://api.slack.com/methods/conversations.list -list_channels <- function(token = Sys.getenv("SLACK_TOKEN"), types = "public_channel", exclude_archived = TRUE, ...) { +list_channels <- function(token, types, exclude_archived, ...) { with_pagination( function(cursor) { call_slack_api( @@ -33,7 +33,7 @@ list_channels <- function(token = Sys.getenv("SLACK_TOKEN"), types = "public_cha #' @keywords internal #' @noRd #' @references https://api.slack.com/methods/users.list -list_users <- function(token = Sys.getenv("SLACK_TOKEN"), ...) { +list_users <- function(token, ...) { with_pagination( function(cursor) { call_slack_api( @@ -61,21 +61,22 @@ list_users <- function(token = Sys.getenv("SLACK_TOKEN"), ...) { #' @param token A Slack API token. #' #' @references https://api.slack.com/methods/chat.postMessage -post_message <- function(txt, - channel, - emoji = "", - username = Sys.getenv("SLACK_USERNAME"), - token = Sys.getenv("SLACK_TOKEN"), - ...) { - r <- - call_slack_api( +post_message <- function( + txt, + channel, + emoji, + username, + token, + ... +) { + r <- call_slack_api( "/api/chat.postMessage", .method = POST, token = token, body = list( - text = txt, - channel = channel, - username = username, + text = txt, + channel = channel, + username = username, link_names = 1, icon_emoji = emoji, ... @@ -104,27 +105,30 @@ post_message <- function(txt, #' #' #' @references https://api.slack.com/methods/files.upload -files_upload <- function(file, - channels, - initial_comment = NULL, - token = Sys.getenv("SLACK_TOKEN"), - ...) { +files_upload <- function( + file, + channels, + initial_comment, + token, + ... +) { r <- call_slack_api( "/api/files.upload", .method = POST, token = token, body = list( - file = upload_file(file), + file = upload_file(file), initial_comment = initial_comment, - channels = paste(channels, collapse = ","), + channels = paste(channels, collapse = ","), ... ) ) + invisible(content(r)) } -list_scopes <- function(token = Sys.getenv("SLACK_TOKEN")) { +list_scopes <- function(token) { r <- call_slack_api( "/api/apps.permissions.scopes.list", .method = GET, diff --git a/R/gg_slackr.R b/R/gg_slackr.R index 9279890..01d75a4 100644 --- a/R/gg_slackr.R +++ b/R/gg_slackr.R @@ -3,65 +3,49 @@ #' Unlike the [slackr_dev()] function, this one takes a `ggplot` object, #' eliminating the need to have a graphics device (think use in scripts). #' +#' @importFrom rlang check_installed +#' @importFrom tools file_ext +#' #' @param plot ggplot object to save, defaults to last plot displayed. #' @param channels Comma-separated list of channel names or IDs where the file will be shared. -#' @param scale scaling factor. -#' @param width width (defaults to the width of current plotting window). -#' @param height height (defaults to the height of current plotting window). -#' @param units units for width and height when either one is explicitly specified -#' (in, cm, or mm). -#' @param dpi dpi to use for raster graphics. -#' @param limitsize when TRUE (the default), ggsave will not save images larger -#' than 50x50 inches, to prevent the common error of specifying dimensions in pixels. #' @param token Authentication token bearing required scopes. #' @param file Prefix for filenames (defaults to `ggplot`). #' @param initial_comment The message text introducing the file in specified channels. #' @param thread_ts Provide another message's ts value to upload this file as a reply. Never use a reply's ts value; use its parent instead. #' @param title Title of file. -#' @param device the file extension to use. Options: "png", "eps", "ps", "pdf", "jpeg", "tiff", "bmp", "svg". Default: "png". -#' @param ... other arguments passed to graphics device. -#' @importFrom ggplot2 ggsave last_plot ggplot aes geom_point -#' @importFrom graphics par +#' @param \dots other arguments passed to \link[ggplot2]{ggsave} +#' #' @return `httr` response object (invisibly) +#' #' @examples #' \dontrun{ #' slackr_setup() #' ggslackr(qplot(mpg, wt, data = mtcars)) #' } #' @export -ggslackr <- function(plot = last_plot(), - channels = Sys.getenv("SLACK_CHANNEL"), - scale = 1, - width = par("din")[1], - height = par("din")[2], - units = NULL, - dpi = 300, - limitsize = TRUE, - token = Sys.getenv("SLACK_TOKEN"), - file = "ggplot", - initial_comment = NULL, - thread_ts = NULL, - title = NULL, - device = c("png", "eps", "ps", "pdf", "jpeg", "tiff", "bmp", "svg"), - ...) { +ggslackr <- function( + plot = ggplot2::last_plot(), + channels = Sys.getenv("SLACK_CHANNEL"), + token = Sys.getenv("SLACK_TOKEN"), + file = "ggplot.png", + initial_comment = NULL, + thread_ts = NULL, + title = NULL, + ... +) { + + check_installed("ggplot2") - ext <- paste0(".", match.arg(device)) + ext <- paste0(".", file_ext(file)) ftmp <- tempfile(file, fileext = ext) - ggsave( + ggplot2::ggsave( filename = ftmp, plot = plot, - scale = scale, - width = width, - height = height, - units = units, - dpi = dpi, - limitsize = limitsize, ... = ... ) - res <- - files_upload( + res <- files_upload( file = ftmp, channels = channels, token = token, diff --git a/R/internals.R b/R/internals.R index 26bc522..40f81e9 100644 --- a/R/internals.R +++ b/R/internals.R @@ -4,7 +4,7 @@ #' @param ... Additiional arguments passed to the function called. #' @return No return value. Called for side effects warn_for_args <- function(token, ...) { - if (missing(token) | is.na(token) | is.null(token)) { + if (missing(token) || is.na(token) || is.null(token)) { abort("You must supply a token.") } diff --git a/R/register_onexit.R b/R/register_onexit.R index 4ab8cc4..eea9d90 100644 --- a/R/register_onexit.R +++ b/R/register_onexit.R @@ -27,7 +27,9 @@ #' summary(lm.D9) #' #' # pass a message to Slack channel 'general' with a header message to begin output -#' register_onexit(lm, "bazinga!", +#' register_onexit( +#' lm, +#' "bazinga!", #' channel = "#general", #' header_msg = "This is a message to begin" #' ) @@ -35,7 +37,8 @@ #' lm.D9 <- slack_lm(weight ~ group) #' #' # onexit with an expression that calls lm.plot -#' register_onexit(lm, +#' register_onexit( +#' lm, #' { #' par(mfrow = c(2, 2), oma = c(0, 0, 2, 0)) #' plot(z) @@ -55,15 +58,17 @@ #' [slackr_msg()] #' @author Jonathan Sidi (aut) #' @export -register_onexit <- function(f, - ..., - header_msg = NULL, - use_device = FALSE, - env = parent.frame(2), - channel = Sys.getenv("SLACK_CHANNEL"), - username = Sys.getenv("SLACK_USERNAME"), - icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), - token = Sys.getenv("SLACK_TOKEN")) { +register_onexit <- function( + f, + ..., + header_msg = NULL, + use_device = FALSE, + env = parent.frame(2), + channel = Sys.getenv("SLACK_CHANNEL"), + username = Sys.getenv("SLACK_USERNAME"), + icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), + token = Sys.getenv("SLACK_TOKEN") +) { warn_for_args( token, username = username, diff --git a/R/slackr.R b/R/slackr.R index 65a09b3..6d6ff7a 100644 --- a/R/slackr.R +++ b/R/slackr.R @@ -27,14 +27,15 @@ #' slackr("iris info", head(iris), str(iris)) #' } #' @export -slackr <- function(..., - channel = Sys.getenv("SLACK_CHANNEL"), - username = Sys.getenv("SLACK_USERNAME"), - icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), - token = Sys.getenv("SLACK_TOKEN"), - thread_ts = NULL, - reply_broadcast = FALSE) { - +slackr <- function( + ..., + channel = Sys.getenv("SLACK_CHANNEL"), + username = Sys.getenv("SLACK_USERNAME"), + icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), + token = Sys.getenv("SLACK_TOKEN"), + thread_ts = NULL, + reply_broadcast = FALSE +) { local_options(list(cli.num_colors = 1)) warn_for_args( @@ -44,7 +45,6 @@ slackr <- function(..., ) if (!missing(...)) { - # get the arglist args <- substitute(list(...))[-1L] @@ -69,31 +69,31 @@ slackr <- function(..., expr <- args[[i]] # do something, note all the newlines...Slack ``` needs them - tmp <- switch(mode(expr), - # if it's actually an expresison, iterate over it - expression = { - cat(sprintf("> %s\n", deparse(expr))) - lapply(expr, evalVis) - }, - # if it's a call or a name, eval, printing run output as if in console - call = , - name = { - cat(sprintf("> %s\n", deparse(expr))) - list(evalVis(expr)) - }, - # if pretty much anything else (i.e. a bare value) just output it - integer = , - double = , - complex = , - raw = , - logical = , - numeric = cat(sprintf("%s\n\n", as.character(expr))), - character = cat(sprintf("%s\n\n", expr)), - abort("mode of argument not handled at present by slackr") + tmp <- switch( + mode(expr), + # if it's actually an expresison, iterate over it + expression = { + cat(sprintf("> %s\n", deparse(expr))) + lapply(expr, evalVis) + }, + # if it's a call or a name, eval, printing run output as if in console + call = , + name = { + cat(sprintf("> %s\n", deparse(expr))) + list(evalVis(expr)) + }, + # if pretty much anything else (i.e. a bare value) just output it + integer = , + double = , + complex = , + raw = , + logical = , + numeric = cat(sprintf("%s\n\n", as.character(expr))), + character = cat(sprintf("%s\n\n", expr)), + abort("mode of argument not handled at present by slackr") ) for (item in tmp) { - if (item$visible) { print(item$value) cat("\n") @@ -109,16 +109,16 @@ slackr <- function(..., # combined all of them (rval is a character vector) output <- paste0(rval, collapse = "\n") - resp <- - post_message( - token = token, - channel = channel, - username = username, - emoji = icon_emoji, - txt = sprintf("```%s```", output), - link_names = 1 - ) - + resp <- post_message( + token = token, + channel = channel, + username = username, + emoji = icon_emoji, + txt = sprintf("```%s```", output), + link_names = 1, + thread_ts = thread_ts, + reply_broadcast = reply_broadcast + ) } invisible(resp) @@ -149,14 +149,16 @@ slackr <- function(..., #' slackr_msg("Hi") #' } #' @export -slackr_msg <- function(txt = "", - channel = Sys.getenv("SLACK_CHANNEL"), - username = Sys.getenv("SLACK_USERNAME"), - icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), - token = Sys.getenv("SLACK_TOKEN"), - thread_ts = NULL, - reply_broadcast = FALSE, - ...) { +slackr_msg <- function( + txt = "", + channel = Sys.getenv("SLACK_CHANNEL"), + username = Sys.getenv("SLACK_USERNAME"), + icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), + token = Sys.getenv("SLACK_TOKEN"), + thread_ts = NULL, + reply_broadcast = FALSE, + ... +) { warn_for_args( token, username = username, @@ -165,18 +167,17 @@ slackr_msg <- function(txt = "", output <- paste0(txt, collapse = "\n\n") - z <- - post_message( - txt = output, - emoji = icon_emoji, - channel = channel, - token = token, - username = username, - link_names = 1, - thread_ts = thread_ts, - reply_broadcast = reply_broadcast, - ... - ) + z <- post_message( + txt = output, + emoji = icon_emoji, + channel = channel, + token = token, + username = username, + link_names = 1, + thread_ts = thread_ts, + reply_broadcast = reply_broadcast, + ... + ) invisible(z) } diff --git a/R/slackr_bot.r b/R/slackr_bot.r index ae57d4a..9578980 100644 --- a/R/slackr_bot.r +++ b/R/slackr_bot.r @@ -34,7 +34,8 @@ #' slackr_bot("iris info", head(iris), str(iris)) #' #' # or directly -#' slackr_bot("Test message", +#' slackr_bot( +#' "Test message", #' incoming_webhook_url = "https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX" #' ) #' } @@ -47,7 +48,6 @@ slackr_bot <- function(..., incoming_webhook_url = Sys.getenv("SLACK_INCOMING_WE } if (!missing(...)) { - # get the arglist args <- substitute(list(...))[-1L] @@ -72,27 +72,28 @@ slackr_bot <- function(..., incoming_webhook_url = Sys.getenv("SLACK_INCOMING_WE expr <- args[[i]] # do something, note all the newlines...Slack ``` needs them - tmp <- switch(mode(expr), - # if it's actually an expression, iterate over it - expression = { - cat(sprintf("> %s\n", deparse(expr))) - lapply(expr, evalVis) - }, - # if it's a call or a name, eval, printing run output as if in console - call = , - name = { - cat(sprintf("> %s\n", deparse(expr))) - list(evalVis(expr)) - }, - # if pretty much anything else (i.e. a bare value) just output it - integer = , - double = , - complex = , - raw = , - logical = , - numeric = cat(sprintf("%s\n\n", as.character(expr))), - character = cat(sprintf("%s\n\n", expr)), - abort("mode of argument not handled at present by slackr") + tmp <- switch( + mode(expr), + # if it's actually an expression, iterate over it + expression = { + cat(sprintf("> %s\n", deparse(expr))) + lapply(expr, evalVis) + }, + # if it's a call or a name, eval, printing run output as if in console + call = , + name = { + cat(sprintf("> %s\n", deparse(expr))) + list(evalVis(expr)) + }, + # if pretty much anything else (i.e. a bare value) just output it + integer = , + double = , + complex = , + raw = , + logical = , + numeric = cat(sprintf("%s\n\n", as.character(expr))), + character = cat(sprintf("%s\n\n", expr)), + abort("mode of argument not handled at present by slackr") ) for (item in tmp) { @@ -126,7 +127,6 @@ slackr_bot <- function(..., incoming_webhook_url = Sys.getenv("SLACK_INCOMING_WE ) stop_for_status(resp) - } return(invisible(resp)) diff --git a/R/slackr_csv.R b/R/slackr_csv.R index ae95dda..ea0cd0a 100644 --- a/R/slackr_csv.R +++ b/R/slackr_csv.R @@ -17,14 +17,16 @@ #' @seealso [slackr_upload()] #' @return `httr` response object from `POST` call (invisibly) #' @export -slackr_csv <- function(data, - filename = tempfile(fileext = ".csv"), - title = NULL, - initial_comment = NULL, - channels = Sys.getenv("SLACK_CHANNEL"), - token = Sys.getenv("SLACK_TOKEN"), - thread_ts = NULL, - ...) { +slackr_csv <- function( + data, + filename = tempfile(fileext = ".csv"), + title = NULL, + initial_comment = NULL, + channels = Sys.getenv("SLACK_CHANNEL"), + token = Sys.getenv("SLACK_TOKEN"), + thread_ts = NULL, + ... +) { write.csv(data, filename, ...) res <- files_upload( diff --git a/R/slackr_delete.R b/R/slackr_delete.R index 57f4bbc..08602ed 100644 --- a/R/slackr_delete.R +++ b/R/slackr_delete.R @@ -5,9 +5,11 @@ #' @param channel Channel, private group, or IM channel to delete messages from. Can be an encoded ID, or a name. See the \href{https://api.slack.com/methods/chat.postMessage#channels}{chat.postMessage endpoint documentation} for details. #' @param token Authentication token bearing required scopes. #' @export -slackr_delete <- function(count, - channel = Sys.getenv("SLACK_CHANNEL"), - token = Sys.getenv("SLACK_TOKEN")) { +slackr_delete <- function( + count, + channel = Sys.getenv("SLACK_CHANNEL"), + token = Sys.getenv("SLACK_TOKEN") +) { if (!is.character(channel) | length(channel) > 1) { abort("channel must be a character vector of length one") } diff --git a/R/slackr_dev.R b/R/slackr_dev.R index 9316cbb..a1676f8 100644 --- a/R/slackr_dev.R +++ b/R/slackr_dev.R @@ -28,12 +28,14 @@ #' slackr_dev("@@jayjacobs") #' } #' @export -slackr_dev <- function(channels = Sys.getenv("SLACK_CHANNEL"), - token = Sys.getenv("SLACK_TOKEN"), - file = "plot", - initial_comment = NULL, - title = NULL, - thread_ts = NULL) { +slackr_dev <- function( + channels = Sys.getenv("SLACK_CHANNEL"), + token = Sys.getenv("SLACK_TOKEN"), + file = "plot", + initial_comment = NULL, + title = NULL, + thread_ts = NULL +) { ftmp <- tempfile(file, fileext = ".png") dev.copy(png, file = ftmp) dev.off() diff --git a/R/slackr_history.R b/R/slackr_history.R index 1220354..f80ff83 100644 --- a/R/slackr_history.R +++ b/R/slackr_history.R @@ -9,64 +9,103 @@ #' #' @param token Authentication token bearing required scopes. #' @param channel Channel, private group, or IM channel to send message to. Can be an encoded ID, or a name. -#' @param posted_from_time Timestamp of the first post time to consider. -#' @param duration Number of hours of history to retrieve. By default retrieves -#' 24 hours of history. -#' @param posted_to_time Timestamp of the last post to consider (default: -#' current time). +#' @param posted_from_time Timestamp of the first post time to consider. If both +#' posted_to_time and duration is specifed, they take precedence. +#' Corresponds to `oldest` in the \href{https://api.slack.com/methods/conversations.history}{conversations.history} +#' docs. +#' Default: 0 +#' @param duration Number of hours of history to retrieve. If neither `duration` +#' nor `posted_from_time` is specified, there is no time limit on the retrieved +#' history. (default: `NULL`) +#' @param posted_to_time Timestamp of the last post to consider. +#' Corresponds to `latest` in the \href{https://api.slack.com/methods/conversations.history}{conversations.history} docs. +#' Default: current time. #' @param paginate If TRUE, uses the Slack API pagination mechanism, and will retrieve all history inside the timeframe. Otherwise, makes a single call to the API and retrieves a maximum of `message_count` messages. #' @param message_count The number of messages to retrieve (only when `paginate = FALSE`). +#' Corresponds to `limit` in the \href{https://api.slack.com/methods/conversations.history}{conversations.history} docs. +#' Note: If using pagination, setting a value of (e.g.) 1 will result in paginating +#' through the channel's history one message at a time. Slack recommends using a value of 200. +#' @param inclusive Include messages with oldest or latest timestamps in results. Ignored unless either timestamp is specified. #' @export #' #' @return A `tibble` with message metadata #' @references #' -slackr_history <- function(message_count, - channel = Sys.getenv("SLACK_CHANNEL"), - token = Sys.getenv("SLACK_TOKEN"), - posted_to_time = as.numeric(Sys.time()), - duration, - posted_from_time, - paginate = FALSE) { - +slackr_history <- function( + message_count, + channel = Sys.getenv("SLACK_CHANNEL"), + token = Sys.getenv("SLACK_TOKEN"), + posted_to_time = as.numeric(Sys.time()), + duration = NULL, + posted_from_time = 0, + paginate = FALSE, + inclusive = TRUE +) { channel <- slackr_chtrans(channel, token) - if (!missing(duration) && !is.null(duration) && !missing(posted_from_time) && !is.null(posted_from_time)) { + if (!missing(duration) && !is.null(duration) && !missing(posted_to_time) && !is.null(posted_to_time)) { + if (!missing(posted_from_time) & !is.null(posted_from_time)) { + warn( + paste( + "You specified all three of `duration`, `posted_to_time`,", + "and `posted_from_time`.", + "Doing this makes `slackr` infer `posted_from_time`", + "from a combination of the `duration` you specified and the", + "`posted_to_time`.", + "If you meant to retrieve history between your specified", + "`posted_from_time` and `posted_to_time`, please remove", + "the `duration` you specified." + ), + .frequency = "regularly", + .frequency_id = "slackr_history_posted_from_infer_warning" + ) + } posted_from_time <- posted_to_time - duration * 3600 - } else { - posted_from_time <- "" } - resp <- - if (!paginate) { - resp <- call_slack_api( - "/api/conversations.history", - .method = GET, - token = token, - channel = channel, - latest = posted_to_time, - oldest = posted_from_time, - inclusive = "true", - limit = message_count + if (!paginate) { + resp <- call_slack_api( + "/api/conversations.history", + .method = GET, + token = token, + channel = channel, + latest = posted_to_time, + oldest = posted_from_time, + inclusive = inclusive, + limit = message_count + ) + + resp <- convert_response_to_tibble(resp, "messages") + } else { + if (missing(posted_from_time) | is.null(posted_to_time)) { + abort( + paste( + "To use pagination with `slackr_history`, you must", + "specify a value for `posted_from_time`.", + "Not doing so will result in paginating through the entire", + "history of the channel up to `posted_to_time`", + "(i.e. from `posted_from_time = 0` until `posted_to_time`)." + ) ) - convert_response_to_tibble(resp, "messages") - } else { - with_pagination( + } + + resp <- with_pagination( function(cursor) { call_slack_api( "/api/conversations.history", - .method = GET, + .method = GET, token = token, - channel = channel, - latest = posted_to_time, - oldest = posted_from_time, - inclusive = "true", - limit = message_count, + channel = channel, + latest = posted_to_time, + oldest = posted_from_time, + inclusive = inclusive, + limit = message_count, .next_cursor = cursor ) }, extract = "messages" ) - } + } + resp } diff --git a/R/slackr_save.R b/R/slackr_save.R index a3f5720..acf776a 100644 --- a/R/slackr_save.R +++ b/R/slackr_save.R @@ -20,13 +20,15 @@ #' slackr_setup() #' slackr_save(mtcars, channels = "#slackr", file = "mtcars") #' } -slackr_save <- function(..., - channels = Sys.getenv("SLACK_CHANNEL"), - file = "slackr", - token = Sys.getenv("SLACK_TOKEN"), - initial_comment = NULL, - title = NULL, - thread_ts = NULL) { +slackr_save <- function( + ..., + channels = Sys.getenv("SLACK_CHANNEL"), + file = "slackr", + token = Sys.getenv("SLACK_TOKEN"), + initial_comment = NULL, + title = NULL, + thread_ts = NULL +) { if (channels == "") abort("No channels specified. Did you forget select which channels to post to with the 'channels' argument?") ftmp <- tempfile(file, fileext = ".Rdata") @@ -37,7 +39,7 @@ slackr_save <- function(..., res <- files_upload( file = ftmp, channels = channels, - txt = initial_comment, + initial_comment = initial_comment, token = token, filename = sprintf("%s.Rdata", file), title = title, diff --git a/R/slackr_setup.r b/R/slackr_setup.r index 29b9423..aefc0a0 100644 --- a/R/slackr_setup.r +++ b/R/slackr_setup.r @@ -32,52 +32,60 @@ #' to use this package. #' @seealso [slackr()], [slackr_dev()], [slackr_save()], #' [slackr_upload()] +#' #' @examples #' \dontrun{ #' # reads from default file (i.e. ~/.slackr) #' slackr_setup() #' #' # reads from alternate config -#' slackr_setup(config_file="/path/to/my/slackrconfig) +#' slackr_setup(config_file = "/path/to/my/slackrconfig") #' #' # the hard way -#' slackr_setup(channel="#code", -#' incoming_webhook_url="https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX") +#' slackr_setup( +#' channel = "#code", +#' incoming_webhook_url = "https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX" +#' ) #' } +#' #' @export -slackr_setup <- function(channel="#general", - username="slackr", - icon_emoji="", - incoming_webhook_url="", - token="", - config_file="~/.slackr", - echo=FALSE, - cache_dir = '') { - +slackr_setup <- function( + channel = "#general", + username = "slackr", + icon_emoji = "", + incoming_webhook_url = "", + token = "", + config_file = "~/.slackr", + echo = FALSE, + cache_dir = "" +) { Sys.setenv(SLACK_CACHE_DIR = cache_dir) if (file.exists(config_file)) { - config <- read.dcf( config_file, - fields=c("channel", "icon_emoji", - "username", "incoming_webhook_url", "token") + fields = c( + "channel", + "icon_emoji", + "username", + "incoming_webhook_url", + "token" ) + ) warn_for_args( - config[,"token"], - username = config[,"username"], - icon_emoji = config[,"icon_emoji"] + config[, "token"], + username = config[, "username"], + icon_emoji = config[, "icon_emoji"] ) - Sys.setenv(SLACK_CHANNEL=config[,"channel"]) - Sys.setenv(SLACK_USERNAME=config[,"username"]) - Sys.setenv(SLACK_ICON_EMOJI=config[,"icon_emoji"]) - Sys.setenv(SLACK_INCOMING_WEBHOOK_URL=config[,"incoming_webhook_url"]) - Sys.setenv(SLACK_TOKEN=config[,"token"]) - + Sys.setenv(SLACK_CHANNEL = config[, "channel"]) + Sys.setenv(SLACK_USERNAME = config[, "username"]) + Sys.setenv(SLACK_ICON_EMOJI = config[, "icon_emoji"]) + Sys.setenv(SLACK_INCOMING_WEBHOOK_URL = config[, "incoming_webhook_url"]) + Sys.setenv(SLACK_TOKEN = config[, "token"]) } else { - if (token == '' | is.null(token) | is.na(token) | missing(token)) { + if (token == "" | is.null(token) | is.na(token) | missing(token)) { abort("No config file found. Please specify your Slack OAuth token\n with the token argument in slackr_setup().") } @@ -87,33 +95,38 @@ slackr_setup <- function(channel="#general", icon_emoji = icon_emoji ) - Sys.setenv(SLACK_CHANNEL=channel) - Sys.setenv(SLACK_USERNAME=username) - Sys.setenv(SLACK_ICON_EMOJI=icon_emoji) - Sys.setenv(SLACK_INCOMING_WEBHOOK_URL=incoming_webhook_url) - Sys.setenv(SLACK_TOKEN=token) - + Sys.setenv(SLACK_CHANNEL = channel) + Sys.setenv(SLACK_USERNAME = username) + Sys.setenv(SLACK_ICON_EMOJI = icon_emoji) + Sys.setenv(SLACK_INCOMING_WEBHOOK_URL = incoming_webhook_url) + Sys.setenv(SLACK_TOKEN = token) } if (!grepl("?$", Sys.getenv("SLACK_INCOMING_WEBHOOK_URL"))) { - Sys.setenv(SLACK_INCOMING_WEBHOOK_URL=sprintf("%s?", config[,"incoming_webhook_url"])) + Sys.setenv(SLACK_INCOMING_WEBHOOK_URL = sprintf("%s?", config[, "incoming_webhook_url"])) } - if (length(Sys.getenv("SLACK_CHANNEL"))==0) { + if (length(Sys.getenv("SLACK_CHANNEL")) == 0) { Sys.setenv("SLACK_CHANNEL", "#general") } - if (length(Sys.getenv("SLACK_USERNAME"))==0) { + if (length(Sys.getenv("SLACK_USERNAME")) == 0) { Sys.setenv("SLACK_USERNAME", "slackr") } if (echo) { - print(toJSON(as.list( - Sys.getenv(c("SLACK_CHANNEL", "SLACK_USERNAME", - "SLACK_ICON_EMOJI", - "SLACK_INCOMING_WEBHOOK_URL", "SLACK_TOKEN") - )), - pretty=TRUE)) + print(toJSON( + as.list( + Sys.getenv(c( + "SLACK_CHANNEL", + "SLACK_USERNAME", + "SLACK_ICON_EMOJI", + "SLACK_INCOMING_WEBHOOK_URL", + "SLACK_TOKEN" + )) + ), + pretty = TRUE + )) } auth <- quiet_auth( @@ -121,7 +134,7 @@ slackr_setup <- function(channel="#general", ) if (auth) { - return('Successfully connected to Slack') + return("Successfully connected to Slack") } else { abort("Could not connect to Slack with the token you provided. Are you sure you've set up your app correctly?") } @@ -142,25 +155,28 @@ slackr_setup <- function(channel="#general", #' create_config_file() #' #' # using `create_config_file()` before `slackr_setup()` -#' create_config_file(token = 'xox-', -#' incoming_webhook_url = 'https://hooks-', -#' channel = '#general', -#' username = 'slackr', -#' icon_emoji = 'tada') +#' create_config_file( +#' token = "xox-", +#' incoming_webhook_url = "https://hooks-", +#' channel = "#general", +#' username = "slackr", +#' icon_emoji = "tada" +#' ) #' #' slackr_setup() #' } #' @return TRUE if successful (invisibly) #' @export -create_config_file <- function(filename = '~/.slackr', - token = Sys.getenv("SLACK_TOKEN"), - incoming_webhook_url = Sys.getenv("SLACK_INCOMING_WEBHOOK_URL"), - icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), - username = Sys.getenv("SLACK_USERNAME"), - channel = Sys.getenv("SLACK_CHANNEL")) { - - username <- if (username == '') 'slackr' else username - channel <- if (channel == '') '#general' else channel +create_config_file <- function( + filename = "~/.slackr", + token = Sys.getenv("SLACK_TOKEN"), + incoming_webhook_url = Sys.getenv("SLACK_INCOMING_WEBHOOK_URL"), + icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), + username = Sys.getenv("SLACK_USERNAME"), + channel = Sys.getenv("SLACK_CHANNEL") +) { + username <- if (username == "") "slackr" else username + channel <- if (channel == "") "#general" else channel write.dcf( list( @@ -175,7 +191,7 @@ create_config_file <- function(filename = '~/.slackr', ) inform( - paste('Successfully wrote config file to', filename) + paste("Successfully wrote config file to", filename) ) return( @@ -187,32 +203,30 @@ create_config_file <- function(filename = '~/.slackr', #' @seealso [slackr_setup()] #' @examples #' \dontrun{ -#' slackr_teardown() +#' slackr_teardown() #' } #' @return TRUE if successful (invisibly) #' @export slackr_teardown <- function() { env_vars <- c( - 'SLACK_TOKEN', - 'SLACK_CACHE_DIR', - 'SLACK_CHANNEL', - 'SLACK_ICON_EMOJI', - 'SLACK_INCOMING_WEBHOOK_URL', - 'SLACK_USERNAME' + "SLACK_TOKEN", + "SLACK_CACHE_DIR", + "SLACK_CHANNEL", + "SLACK_ICON_EMOJI", + "SLACK_INCOMING_WEBHOOK_URL", + "SLACK_USERNAME" ) invisible( lapply( env_vars, Sys.unsetenv - ) ) + ) - inform('Successfully tore down environment variables created by slackr_setup()') + inform("Successfully tore down environment variables created by slackr_setup()") return( invisible(TRUE) ) } - - diff --git a/R/slackr_tex.R b/R/slackr_tex.R index bba97a4..65d9a2c 100644 --- a/R/slackr_tex.R +++ b/R/slackr_tex.R @@ -24,18 +24,19 @@ #' [texPreview::tex_preview()] #' @author Jonathan Sidi (aut) #' @export -slackr_tex <- function(obj, - channels = Sys.getenv("SLACK_CHANNEL"), - token = Sys.getenv("SLACK_TOKEN"), - ext = "png", - path = NULL, - title = NULL, - initial_comment = NULL, - thread_ts = NULL, - ...) { +slackr_tex <- function( + obj, + channels = Sys.getenv("SLACK_CHANNEL"), + token = Sys.getenv("SLACK_TOKEN"), + ext = "png", + path = NULL, + title = NULL, + initial_comment = NULL, + thread_ts = NULL, + ... +) { - # check if texPreview is installed, if not provide feedback - check_tex_pkg() + check_installed("texPreview") if (!is.null(path)) { td <- path @@ -56,9 +57,9 @@ slackr_tex <- function(obj, fname <- file.path( td, paste0( - 'slack.', + "slack.", ifelse(ext == "tex", "png", ext) - ) + ) ) res <- files_upload( @@ -75,25 +76,3 @@ slackr_tex <- function(obj, invisible(res) } - - - -#' check_tex_pkg -#' -#' Check if texPreview is intalled -#' @description Install or load texPreview package, -#' inspired by the `parsnip` package -#' @noRd -#' -check_tex_pkg <- function() { - is_installed <- try( - suppressPackageStartupMessages( - requireNamespace("texPreview", quietly = TRUE) - ), - silent = TRUE - ) - - if (!is_installed) { - abort("texPreview package is not installed, run ?slackr_tex and see Details.") - } -} diff --git a/R/slackr_upload.R b/R/slackr_upload.R index 1e445f2..baad4e1 100644 --- a/R/slackr_upload.R +++ b/R/slackr_upload.R @@ -16,12 +16,14 @@ #' @return `httr` response object from `POST` call (invisibly) #' @importFrom httr add_headers upload_file #' @export -slackr_upload <- function(filename, - title = NULL, - initial_comment = NULL, - channels = Sys.getenv("SLACK_CHANNEL"), - token = Sys.getenv("SLACK_TOKEN"), - thread_ts = NULL) { +slackr_upload <- function( + filename, + title = NULL, + initial_comment = NULL, + channels = Sys.getenv("SLACK_CHANNEL"), + token = Sys.getenv("SLACK_TOKEN"), + thread_ts = NULL +) { if (channels == "") abort("No channels specified. Did you forget select which channels to post to with the 'channels' argument?") f_path <- path.expand(filename) diff --git a/R/slackr_utils.R b/R/slackr_utils.R index 739c942..a767c57 100644 --- a/R/slackr_utils.R +++ b/R/slackr_utils.R @@ -16,12 +16,15 @@ slackr_users <- function(token = Sys.getenv("SLACK_TOKEN")) { #' Get a data frame of Slack channels #' #' @param token Authentication token bearing required scopes. +#' @param exclude_archived A boolean indicating whether or not to exclude archived channels. +#' #' @importFrom dplyr bind_rows -#' @return data.table of channels +#' +#' @return A data.frame of channels #' @export -slackr_channels <- function(token = Sys.getenv("SLACK_TOKEN")) { - c1 <- list_channels(token = token, types = "public_channel") - c2 <- list_channels(token = token, types = "private_channel") +slackr_channels <- function(token = Sys.getenv("SLACK_TOKEN"), exclude_archived = TRUE) { + c1 <- list_channels(token = token, types = "public_channel", exclude_archived = exclude_archived) + c2 <- list_channels(token = token, types = "private_channel", exclude_archived = exclude_archived) bind_rows(c1, c2) } @@ -36,7 +39,7 @@ slackr_channels <- function(token = Sys.getenv("SLACK_TOKEN")) { #' @return `data.frame` of im ids and user names #' @export slackr_ims <- function(token = Sys.getenv("SLACK_TOKEN")) { - ims <- list_channels(token = token, types = "im") %>% + ims <- list_channels(token = token, types = "im", exclude_archived = TRUE) %>% rename(channel = "id") users <- slackr_users(token = token) @@ -63,9 +66,11 @@ slackr_chtrans <- function(channels, token = Sys.getenv("SLACK_TOKEN")) { channel_cache <- slackr_census(token) chan_xref <- - channel_cache[(channel_cache$name %in% channels) | - (channel_cache$real_name %in% channels) | - (channel_cache$id %in% channels), ] + channel_cache[ + (channel_cache$name %in% channels) | + (channel_cache$real_name %in% channels) | + (channel_cache$id %in% channels), + ] ifelse( is.na(chan_xref$id), diff --git a/R/testthat-helpers.R b/R/testthat-helpers.R new file mode 100644 index 0000000..763c23b --- /dev/null +++ b/R/testthat-helpers.R @@ -0,0 +1,27 @@ +set_up_test_env <- function(..., env = Sys.getenv("ENVIRONMENT")) { + if (env == "production") { + slackr_setup( + channel = Sys.getenv("SLACK_CHANNEL"), + username = Sys.getenv("SLACK_USERNAME"), + icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"), + incoming_webhook_url = Sys.getenv("SLACK_INCOMING_WEBHOOK_URL"), + token = Sys.getenv("SLACK_TOKEN") + ) + } else { + slackr_setup( + config_file = "~/.slackr_config" + ) + } +} + +with_slack_config <- function(code, env = Sys.getenv("ENVIRONMENT")) { + impl <- withr::with_( + set = set_up_test_env, + reset = set_up_test_env + ) + + impl( + code = code, + env = env + ) +} diff --git a/build/vignette.rds b/build/vignette.rds index f1413dd..190372d 100644 Binary files a/build/vignette.rds and b/build/vignette.rds differ diff --git a/inst/doc/scoped-bot-setup.R b/inst/doc/scoped-bot-setup.R index bc982be..df7d1ab 100644 --- a/inst/doc/scoped-bot-setup.R +++ b/inst/doc/scoped-bot-setup.R @@ -9,19 +9,18 @@ knitr::opts_chunk$set( # slackr_setup( # channel = "<< channel >>", # incoming_webhook_url = "https://hooks.slack.com/services/<< hook >>", -# token = 'xoxb-<< token >>' +# token = "xoxb-<< token >>" # ) # # slackr(str(iris)) # # # send images # library(ggplot2) -# qplot(mpg, wt, data=mtcars) +# qplot(mpg, wt, data = mtcars) # slackr_dev("#results") # # barplot(VADeaths) # slackr_dev("@jayjacobs") # -# ggslackr(qplot(mpg, wt, data=mtcars)) -# +# ggslackr(qplot(mpg, wt, data = mtcars)) diff --git a/inst/doc/scoped-bot-setup.Rmd b/inst/doc/scoped-bot-setup.Rmd index f0b08f6..e371d2b 100644 --- a/inst/doc/scoped-bot-setup.Rmd +++ b/inst/doc/scoped-bot-setup.Rmd @@ -59,23 +59,22 @@ Without all of the scopes enabled, only certain functions will work. Which ones ```{r demo2, eval=FALSE} library(slackr) slackr_setup( - channel = "<< channel >>", + channel = "<< channel >>", incoming_webhook_url = "https://hooks.slack.com/services/<< hook >>", - token = 'xoxb-<< token >>' + token = "xoxb-<< token >>" ) slackr(str(iris)) # send images library(ggplot2) -qplot(mpg, wt, data=mtcars) +qplot(mpg, wt, data = mtcars) slackr_dev("#results") barplot(VADeaths) slackr_dev("@jayjacobs") -ggslackr(qplot(mpg, wt, data=mtcars)) - +ggslackr(qplot(mpg, wt, data = mtcars)) ``` diff --git a/inst/doc/scoped-bot-setup.html b/inst/doc/scoped-bot-setup.html index 0b9c234..93b597a 100644 --- a/inst/doc/scoped-bot-setup.html +++ b/inst/doc/scoped-bot-setup.html @@ -14,25 +14,37 @@ Multi-Functional Bot Setup - + +code{white-space: pre-wrap;} +span.smallcaps{font-variant: small-caps;} +span.underline{text-decoration: underline;} +div.column{display: inline-block; vertical-align: top; width: 50%;} +div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} +ul.task-list{list-style: none;} + + +code{white-space: pre-wrap;} +span.smallcaps{font-variant: small-caps;} +span.underline{text-decoration: underline;} +div.column{display: inline-block; vertical-align: top; width: 50%;} +div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} +ul.task-list{list-style: none;} + + +code{white-space: pre-wrap;} +span.smallcaps{font-variant: small-caps;} +span.underline{text-decoration: underline;} +div.column{display: inline-block; vertical-align: top; width: 50%;} +div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} +ul.task-list{list-style: none;} +