-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Proactively clear old Zoom reminders (#9)
* R4DS to DSLC * Refactor reminder clearing. And add separate reminder clearer
- Loading branch information
1 parent
43c0476
commit 328ac91
Showing
24 changed files
with
387 additions
and
154 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
Package: bookclubcron | ||
Title: Automated Tasks for R4DS Book Clubs | ||
Title: Automated Tasks for DSLC Book Clubs | ||
Version: 0.0.1 | ||
Authors@R: | ||
person("Jon", "Harmon", , "[email protected]", role = c("aut", "cre"), | ||
|
@@ -13,10 +13,12 @@ BugReports: https://github.com/r4ds/bookclubcron/issues | |
Imports: | ||
cli, | ||
dplyr, | ||
fastmatch, | ||
fs, | ||
glue, | ||
googlesheets4, | ||
httr2, | ||
keyring, | ||
lubridate, | ||
memoise, | ||
purrr, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
# Generated by roxygen2: do not edit by hand | ||
|
||
export(dslc_slack_channels) | ||
export(dslc_youtube_playlists) | ||
export(process_youtube) | ||
export(process_zoom) | ||
export(r4ds_slack_channels) | ||
export(r4ds_youtube_playlists) | ||
export(reset_the) | ||
export(slack_default_token) | ||
export(slack_set_token) | ||
importFrom(fastmatch,"%fin%") | ||
importFrom(rlang,"%||%") | ||
importFrom(rlang,.data) | ||
importFrom(rlang,.env) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,250 @@ | ||
#' Cache or Fetch R4DS Slack channels | ||
#' Cache or Fetch DSLC Slack channels | ||
#' | ||
#' Fetch public and private R4DS Slack channel information. | ||
#' Fetch public and private DSLC Slack channel information. | ||
#' | ||
#' @inheritParams .fetch_dslc_slack_channels | ||
#' @param refresh Get fresh data? | ||
#' | ||
#' @inherit .fetch_r4ds_slack_channels return | ||
#' @inherit .fetch_dslc_slack_channels return | ||
#' @export | ||
r4ds_slack_channels <- function(refresh = FALSE) { | ||
dslc_slack_channels <- function(refresh = FALSE, | ||
token = slack_default_token()) { | ||
if (refresh) { | ||
.cache_r4ds_slack_channels() | ||
.cache_dslc_slack_channels(token) | ||
return(the$slack_channels) | ||
} | ||
|
||
return( | ||
rlang::env_cache( | ||
the, | ||
"slack_channels", | ||
.fetch_r4ds_slack_channels() | ||
.fetch_dslc_slack_channels(token) | ||
) | ||
) | ||
} | ||
|
||
#' Cache R4DS Slack channels | ||
#' Cache DSLC Slack channels | ||
#' | ||
#' Set R4DS slack channels in the package `the` environment. | ||
#' Set DSLC slack channels in the package `the` environment. | ||
#' | ||
#' @return A dataframe with information about R4DS Slack channels, invisibly. | ||
#' @inheritParams .fetch_dslc_slack_channels | ||
#' | ||
#' @return A dataframe with information about DSLC Slack channels, invisibly. | ||
#' @keywords internal | ||
.cache_r4ds_slack_channels <- function() { | ||
.cache_dslc_slack_channels <- function(token = slack_default_token()) { | ||
return( | ||
rlang::env_bind( | ||
the, | ||
slack_channels = .fetch_r4ds_slack_channels() | ||
slack_channels = .fetch_dslc_slack_channels(token) | ||
) | ||
) | ||
} | ||
|
||
#' Fetch R4DS Slack channels | ||
#' Fetch DSLC Slack channels | ||
#' | ||
#' @inheritParams slackteams::get_conversations_list | ||
#' | ||
#' @return A dataframe with information about R4DS Slack channels. | ||
#' @return A dataframe with information about DSLC Slack channels. | ||
#' @keywords internal | ||
.fetch_r4ds_slack_channels <- function() { | ||
.fetch_dslc_slack_channels <- function(token = slack_default_token()) { | ||
return( | ||
dplyr::select( | ||
slackteams::get_conversations_list( | ||
type = c("public_channel", "private_channel"), | ||
exclude_archived = TRUE | ||
exclude_archived = TRUE, | ||
token = token | ||
), | ||
"id", "name", "is_private", "created", "is_general" | ||
) | ||
) | ||
} | ||
|
||
#' Fetch a Slack token | ||
#' | ||
#' Fetch the Slack token using the keyring package (if available), or an | ||
#' environment variable with the same name. | ||
#' | ||
#' @param key_name The name of the keyring key or the environment variable. | ||
#' | ||
#' @return The token value as a string, or NULL (invisibly). | ||
#' @export | ||
#' | ||
#' @examples | ||
#' token <- slack_default_token() | ||
#' nchar(token) | ||
slack_default_token <- function(key_name = "SLACK_API_TOKEN") { | ||
key_value <- .keyring_try(key_name) %||% Sys.getenv(key_name) | ||
return(invisible(key_value)) | ||
} | ||
|
||
.keyring_try <- function(key_name, keyring = NULL) { | ||
tryCatch( | ||
keyring::key_get(key_name, keyring = keyring), | ||
error = function(e) NULL | ||
) | ||
} | ||
|
||
#' Set a Slack API token | ||
#' | ||
#' Use the keyring package to store an API key. | ||
#' | ||
#' @param key_name The name of the key. | ||
#' @param keyring The name of a specific keyring, passed on to | ||
#' `keyring::set_key` if available. | ||
#' | ||
#' @return The key, invisibly. | ||
#' @export | ||
slack_set_token <- function(key_name = "SLACK_API_TOKEN", keyring = NULL) { | ||
rlang::check_installed("keyring", "to save the API token securely.") # nocov | ||
keyring::key_set(key_name, prompt = "Slack API token", keyring = keyring) # nocov | ||
return(invisible(.keyring_try(key_name, keyring = keyring))) # nocov | ||
} | ||
|
||
remove_slack_reminders <- function(channel_name, | ||
min_age_minutes = 55, | ||
max_msgs_to_check = Inf, | ||
token = slack_default_token(), | ||
slack_channels = dslc_slack_channels( | ||
token = token | ||
)) { | ||
channel_id <- .slack_channel_name_to_id(channel_name, token, slack_channels) | ||
old_reminder_messages <- .slack_reminder_messages( | ||
channel_id, | ||
min_age_minutes = 55, | ||
max_msgs_to_check = max_msgs_to_check, | ||
token = token, | ||
slack_channels = slack_channels | ||
) | ||
.delete_slack_messages(old_reminder_messages, channel_id) | ||
} | ||
|
||
.slack_reminder_messages <- function(channel_id, | ||
min_age_minutes = NULL, | ||
max_msgs_to_check = Inf, | ||
token = slack_default_token(), | ||
slack_channels = dslc_slack_channels( | ||
token = token | ||
)) { | ||
channel_messages <- dslc_slack_channel_messages( | ||
channel_id, | ||
max_results = max_msgs_to_check, | ||
token = token, | ||
slack_channels = slack_channels | ||
) | ||
.filter_slack_messages( | ||
channel_messages, | ||
user = "USLACKBOT", | ||
text = "Join Zoom Meeting", | ||
min_age_minutes = min_age_minutes | ||
) | ||
} | ||
|
||
.slack_channel_name_to_id <- function(channel_name, | ||
token = slack_default_token(), | ||
slack_channels = dslc_slack_channels( | ||
token = token | ||
)) { | ||
channel_name <- .validate_channel_name(channel_name, token, slack_channels) | ||
slack_channels$id[slack_channels$name == channel_name] | ||
} | ||
|
||
.validate_channel_name <- function(channel_name, | ||
token = slack_default_token(), | ||
slack_channels = dslc_slack_channels( | ||
token = token | ||
)) { | ||
if (channel_name %in% slack_channels$name) { | ||
return(channel_name) | ||
} | ||
cli::cli_abort( | ||
"Cannot find channel {channel_name}.", | ||
class = "bookclubcron-error-channel_name" | ||
) | ||
} | ||
|
||
dslc_slack_channel_messages <- function(channel_id, | ||
max_results = Inf, | ||
token = slack_default_token(), | ||
slack_channels = dslc_slack_channels( | ||
token = token | ||
)) { | ||
slackthreads::conversations( | ||
channel_id, | ||
token = token, | ||
max_results = max_results, | ||
limit = min(1000, max_results) | ||
) | ||
} | ||
|
||
.filter_slack_messages <- function(channel_messages, | ||
user = NULL, | ||
text = NULL, | ||
min_age_minutes = NULL) { | ||
purrr::keep( | ||
channel_messages, | ||
\(x) { | ||
(is.null(user) || x[["user"]] %fin% user) && | ||
(is.null(text) || stringr::str_detect(x[["text"]], text)) && | ||
(is.null(min_age_minutes) || .ts_is_older(x[["ts"]], min_age_minutes)) | ||
} | ||
) | ||
} | ||
|
||
.ts_is_older <- function(ts, min_age_minutes) { | ||
.ts_age(ts) > lubridate::minutes(min_age_minutes) | ||
} | ||
|
||
.ts_age <- function(ts) { | ||
lubridate::now() - .ts_as_datetime(ts) | ||
} | ||
|
||
.ts_as_datetime <- function(ts) { | ||
lubridate::as_datetime(as.numeric(ts)) | ||
} | ||
|
||
.delete_slack_message <- function(channel_id, | ||
timestamp, | ||
token = slack_default_token()) { | ||
slackcalls::post_slack( | ||
slack_method = "chat.delete", | ||
channel = channel_id, | ||
ts = timestamp, | ||
token = token | ||
) | ||
} | ||
|
||
.delete_slack_messages <- function(messages, | ||
channel_id, | ||
token = slack_default_token()) { | ||
for (msg in messages) { | ||
.delete_slack_message(channel_id, msg$ts, token = token) | ||
} | ||
} | ||
|
||
dslc_book_club_channels <- function(token = slack_default_token(), | ||
slack_channels = dslc_slack_channels( | ||
token = token | ||
)) { | ||
slack_channels |> | ||
dplyr::filter(stringr::str_starts(name, "book_club-")) |> | ||
dplyr::pull(.data$name) | ||
} | ||
|
||
remove_all_club_reminders <- function(min_age_minutes = 55, | ||
token = slack_default_token(), | ||
slack_channels = dslc_slack_channels( | ||
token = token | ||
)) { | ||
club_channels <- dslc_book_club_channels( | ||
token = token, | ||
slack_channels = slack_channels | ||
) | ||
for (channel_name in club_channels) { | ||
remove_slack_reminders( | ||
channel_name, | ||
min_age_minutes = min_age_minutes, | ||
token = token, | ||
slack_channels = slack_channels | ||
) | ||
} | ||
} |
Oops, something went wrong.