R/team_management.R

Defines functions auth_url update_cache slack_team_info remove_team add_team_code add_team_interactive add_team_token add_team activate_team

Documented in activate_team add_team add_team_code add_team_interactive add_team_token auth_url remove_team

#' @title Team Management Functions
#' @description Manage teams that can be accessed
#' @param team character, team name
#' @param verbose logical, Print messages to console, Default: TRUE
#' @return NULL
#' @concept management
#' @rdname manage_team
#' @importFrom jsonlite validate
#' @export
activate_team <- function(team, verbose = TRUE) {
  validate_team(team)

  slack_setenv(team)
  .slack$activeteam <- team
  if (verbose) {
    slack_setenv_msg(team)
  }
}

#' @rdname manage_team
#' @param token character, a token returned from Slack
#' @export
add_team <- function(team, token) {

  if(team %in% names(.slack$teams)){
    .slack$teams[[team]] <- token
  }else{
    new_team <- list(token)
    names(new_team) <- team
    .slack$teams <- append(.slack$teams, new_team)
  }


}

#' @rdname manage_team
#' @export
add_team_token <- function(team,
                           token,
                           verbose = TRUE) {
  add_team(team = team,token = token)
  .slack$file[[team]] <- ""
  .slack$creds <- list(
    api_token = token
  )

  if (verbose) {
    message(sprintf(
      "The following teams are loaded:\n  %s",
      paste0(get_teams(), collapse = ", ")
    ))
  }
}

#' @title Interactive Team Management
#' @description Add a team interactively
#' @param scopes character, scopes to request. Must include "users:read",
#'   "channels:read", "groups:read", "im:read", and "mpim:read" at minimum.
#' @details Launch a browser window to interactively grant slackteams permission
#'   to act on your behalf on a Slack team.
#'
#'   Two environment variables control this function:
#'   \itemize{
#'     \item{SLACK_CLIENT_ID: character, the client_id of a Slack app. If this
#'       is not provided, the function will use the built-in R4DS Slack app.}
#'     \item{SLACK_CLIENT_SECRET: character, the client_secret of a Slack app.
#'       If this is not provided, the function will use the built-in R4DS Slack
#'       app.}
#'   }
#' @note This function does not currently work in an Rstudio Server setup. We
#'   are exploring options to remedy this situation.
#' @return NULL
#' @concept management
#' @export
add_team_interactive <- function(scopes = load_scopes()) {
  min_scopes <- c(
    "users:read", "channels:read", "groups:read", "im:read", "mpim:read","team:read"
  )
  if (!all(min_scopes %in% scopes)) {
    stop(
      "At a minimum, scopes must include\n",
      "users:read, channels:read, groups:read, im:read, and mpim:read"
    )
  }

  slack_oauth_app <- httr::oauth_app(
    appname = "slackteams",
    key = Sys.getenv('SLACK_CLIENT_ID', unset = client_id),
    secret = Sys.getenv('SLACK_CLIENT_SECRET', unset = client_secret)
  )
  full_token <- httr::oauth2.0_token(
    endpoint = slack_oauth_endpoint,
    app = slack_oauth_app,
    query_authorize_extra = list(
      user_scope = paste(
        scopes,
        collapse = ","
      )
    ),
    cache = FALSE,
    use_oob = FALSE
  )
  if (full_token$credentials$ok) {
    token <- full_token$credentials$authed_user$access_token
    team <- full_token$credentials$team$name

    message(sprintf('\nAdding %s to loaded teams ...',team))
    add_team(team, token)

    message(sprintf('\nActivating %s ...',team))
    activate_team(team)

  } else {
    # I suspect httr might fail for us in this case, but it mirror the case that
    # ISN'T handled in add_team_code, so I added the safety in case.
    stop(
      "Failed to authenticate. Slack returned: ",
      full_token$credentials$error
    )
  }
}

#' @title Semi-Interactive Team Management
#' @description Add a team using a Slack response code.
#' @param code character, a code returned by the slack oauth2 v2 api.
#' @param redirect_uri character, the uri to which the user was redirected when
#'   the code was generated.
#' @inheritParams activate_team
#' @details Launch a browser window to interactively grant slackteams permission
#'   to act on your behalf on a Slack team.
#'
#'   Two environment variables control this function:
#'   \itemize{
#'     \item{SLACK_CLIENT_ID: character, the client_id of a Slack app. If this
#'       is not provided, the function will use the built-in R4DS Slack app.}
#'     \item{SLACK_CLIENT_SECRET: character, the client_secret of a Slack app.
#'       If this is not provided, the function will use the built-in R4DS Slack
#'       app.}
#'   }
#' @note This function does not currently work in an Rstudio Server setup. We
#'   are exploring options to remedy this situation.
#' @return The token (invisibly)
#' @concept management
#' @export
add_team_code <- function(code,
                          redirect_uri = NULL,
                          verbose = TRUE) {

  access_url <- paste0(
    access_root,
    "?code=", code,
    query_piece(redirect_uri, "redirect_uri"),
    query_piece(
      Sys.getenv('SLACK_CLIENT_ID', unset = client_id), "client_id"
    ),
    query_piece(
      Sys.getenv('SLACK_CLIENT_SECRET', unset = client_secret), "client_secret"
    )
  )

  response <- httr::GET(
    access_url
  )
  httr::stop_for_status(response, "convert code to token")

  full_token <- httr::content(response)
  if (full_token$ok) {
    token <- full_token$authed_user$access_token
    team <- full_token$team$name
    add_team_token(team, token, verbose)
    invisible(token)
  } else {
    stop(
      "Failed to validate code. Slack returned: ",
      full_token$error
    )
  }
}

#' @rdname manage_team
#' @export
remove_team <- function(team) {
  idx <- which(team %in% get_teams())

  if (length(idx) > 0) {
    .slack$teams <- .slack$teams[-idx]
  }
}

slack_team_info <- function(team) {
  .slack$users[[team]] <- clean_users(get_users_list())
  .slack$channels[[team]] <- clean_channel(get_conversations_list(), team)
}

update_cache <- function() {
  slack_team_info(get_active_team())
}

#' @title Slack Authorization URL
#' @description Generate an authorization URL to add a team manually. Useful,
#'   for example, for authorizing a team in a Shiny app.
#' @param scopes character, scopes to request.
#' @param redirect_uri character, the uri to which the user should be directed
#'   after authorization. If this is NULL (default), the user will be directed
#'   to the default redirection set up in the Slack app.
#' @param team_code character, a team code to restrict the user to (in case they
#'   have multiple Slack teams authorized in their browser).
#' @param state character, a code to send to your redirect_uri indicating a
#'   state. It is recommended to use a non-human-readable format for this
#'   string.
#' @return character, an authorization URL.
#' @details An environment variable controls this function:
#'   \itemize{
#'     \item{SLACK_CLIENT_ID: character, the client_id of a Slack app. If this
#'       is not provided, the function will use the built-in R4DS Slack app.}
#'   }
#' @export
#' @concept management
#' @examples
#' auth_url()
#' auth_url(redirect_uri = "http://127.0.0.1:4242")
#' auth_url(redirect_uri = "http://127.0.0.1:4242", team_code = "T6UC1DKJQ")
#' auth_url(state = "aabbCCddeeFF")
auth_url <- function(scopes = load_scopes(),
                     redirect_uri = NULL,
                     team_code = NULL,
                     state = NULL) {
  paste0(
    auth_root,
    "?user_scope=", paste(scopes, collapse = ","),
    "&client_id=", Sys.getenv('SLACK_CLIENT_ID', unset = client_id),
    query_piece(redirect_uri, "redirect_uri"),
    query_piece(team_code, "team"),
    query_piece(state, "state")
  )
}
yonicd/slackteams documentation built on March 13, 2023, 6:26 a.m.