Last active
December 11, 2025 19:13
-
-
Save ericboehs/7c741ccb7db890eaf5eb88e8c50dfb65 to your computer and use it in GitHub Desktop.
CLI tool to set Slack status from the command line (supports multiple workspaces, presets, age encryption)
This file contains hidden or 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
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| VERSION="1.1.0" | |
| SCRIPT_NAME="slack-status" | |
| # XDG Base Directory support | |
| CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/slack-status" | |
| TOKENS_FILE="$CONFIG_DIR/tokens.json" | |
| TOKENS_FILE_ENCRYPTED="$CONFIG_DIR/tokens.age" | |
| CONFIG_FILE="$CONFIG_DIR/config.json" | |
| PRESETS_FILE="$CONFIG_DIR/presets.json" | |
| # Colors (disabled if not a terminal) | |
| if [[ -t 1 ]]; then | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[0;33m' | |
| BLUE='\033[0;34m' | |
| BOLD='\033[1m' | |
| NC='\033[0m' | |
| else | |
| RED='' GREEN='' YELLOW='' BLUE='' BOLD='' NC='' | |
| fi | |
| # Verbosity | |
| VERBOSE=false | |
| QUIET=false | |
| ####################################### | |
| # Utility Functions | |
| ####################################### | |
| log_info() { | |
| $QUIET || echo -e "${GREEN}β${NC} $*" | |
| } | |
| log_warn() { | |
| $QUIET || echo -e "${YELLOW}β ${NC} $*" >&2 | |
| } | |
| log_error() { | |
| echo -e "${RED}β${NC} $*" >&2 | |
| } | |
| log_verbose() { | |
| $VERBOSE && echo -e "${BLUE}β${NC} $*" || true | |
| } | |
| die() { | |
| log_error "$@" | |
| exit 1 | |
| } | |
| check_dependency() { | |
| command -v "$1" &>/dev/null | |
| } | |
| ensure_jq() { | |
| check_dependency jq || die "jq is required but not installed. Install it with: brew install jq (macOS) or apt install jq (Linux)" | |
| } | |
| ensure_curl() { | |
| check_dependency curl || die "curl is required but not installed." | |
| } | |
| has_age() { | |
| check_dependency age | |
| } | |
| ####################################### | |
| # Duration Parsing | |
| ####################################### | |
| parse_duration() { | |
| local input="$1" | |
| local total_seconds=0 | |
| # If it's just a number, treat as seconds | |
| if [[ "$input" =~ ^[0-9]+$ ]]; then | |
| echo "$input" | |
| return | |
| fi | |
| # Parse human-readable format like 1h30m, 2h, 45m | |
| local remaining="$input" | |
| while [[ -n "$remaining" ]]; do | |
| if [[ "$remaining" =~ ^([0-9]+)h(.*)$ ]]; then | |
| total_seconds=$((total_seconds + ${BASH_REMATCH[1]} * 3600)) | |
| remaining="${BASH_REMATCH[2]}" | |
| elif [[ "$remaining" =~ ^([0-9]+)m(.*)$ ]]; then | |
| total_seconds=$((total_seconds + ${BASH_REMATCH[1]} * 60)) | |
| remaining="${BASH_REMATCH[2]}" | |
| elif [[ "$remaining" =~ ^([0-9]+)s(.*)$ ]]; then | |
| total_seconds=$((total_seconds + ${BASH_REMATCH[1]})) | |
| remaining="${BASH_REMATCH[2]}" | |
| else | |
| die "Invalid duration format: $input (use format like 1h30m, 2h, 45m, or seconds)" | |
| fi | |
| done | |
| echo "$total_seconds" | |
| } | |
| format_duration() { | |
| local seconds="$1" | |
| if [[ "$seconds" -eq 0 ]]; then | |
| echo "no expiration" | |
| return | |
| fi | |
| local hours=$((seconds / 3600)) | |
| local minutes=$(((seconds % 3600) / 60)) | |
| local result="" | |
| [[ $hours -gt 0 ]] && result="${hours}h" | |
| [[ $minutes -gt 0 ]] && result="${result}${minutes}m" | |
| [[ -z "$result" ]] && result="${seconds}s" | |
| echo "$result" | |
| } | |
| ####################################### | |
| # SSH Key Detection | |
| ####################################### | |
| detect_ssh_key() { | |
| local ed25519_key="$HOME/.ssh/id_ed25519" | |
| local rsa_key="$HOME/.ssh/id_rsa" | |
| local has_ed25519=false | |
| local has_rsa=false | |
| [[ -f "$ed25519_key" ]] && has_ed25519=true | |
| [[ -f "$rsa_key" ]] && has_rsa=true | |
| if $has_ed25519 && $has_rsa; then | |
| # Both exist, prompt with ed25519 as default | |
| echo "both:$ed25519_key:$rsa_key" | |
| elif $has_ed25519; then | |
| echo "ed25519:$ed25519_key" | |
| elif $has_rsa; then | |
| echo "rsa:$rsa_key" | |
| else | |
| echo "none" | |
| fi | |
| } | |
| ####################################### | |
| # Token Encryption/Decryption | |
| ####################################### | |
| encrypt_tokens() { | |
| local ssh_key="$1" | |
| local tokens_json="$2" | |
| if has_age; then | |
| echo "$tokens_json" | age -R "$ssh_key.pub" -o "$TOKENS_FILE_ENCRYPTED" | |
| rm -f "$TOKENS_FILE" | |
| log_verbose "Tokens encrypted with age" | |
| else | |
| echo "$tokens_json" > "$TOKENS_FILE" | |
| chmod 600 "$TOKENS_FILE" | |
| log_warn "age not found. Tokens stored unencrypted in $TOKENS_FILE" | |
| fi | |
| } | |
| decrypt_tokens() { | |
| if [[ -f "$TOKENS_FILE_ENCRYPTED" ]]; then | |
| local ssh_key | |
| ssh_key=$(get_config_value ".ssh_key" "") | |
| if [[ -z "$ssh_key" ]]; then | |
| die "No SSH key configured. Run '$SCRIPT_NAME config' to set up." | |
| fi | |
| if ! has_age; then | |
| die "Tokens are encrypted but age is not installed. Install it with: brew install age (macOS) or apt install age (Linux)" | |
| fi | |
| age -d -i "$ssh_key" "$TOKENS_FILE_ENCRYPTED" 2>/dev/null || die "Failed to decrypt tokens. Check your SSH key." | |
| elif [[ -f "$TOKENS_FILE" ]]; then | |
| cat "$TOKENS_FILE" | |
| else | |
| echo "{}" | |
| fi | |
| } | |
| ####################################### | |
| # Config Management | |
| ####################################### | |
| ensure_config_dir() { | |
| mkdir -p "$CONFIG_DIR" | |
| } | |
| get_config_value() { | |
| local key="$1" | |
| local default="${2:-}" | |
| if [[ -f "$CONFIG_FILE" ]]; then | |
| local value | |
| value=$(jq -r "$key // empty" "$CONFIG_FILE" 2>/dev/null) | |
| if [[ -n "$value" && "$value" != "null" ]]; then | |
| echo "$value" | |
| return | |
| fi | |
| fi | |
| echo "$default" | |
| } | |
| set_config_value() { | |
| local key="$1" | |
| local value="$2" | |
| ensure_config_dir | |
| if [[ -f "$CONFIG_FILE" ]]; then | |
| local tmp | |
| tmp=$(mktemp) | |
| jq "$key = $value" "$CONFIG_FILE" > "$tmp" && mv "$tmp" "$CONFIG_FILE" | |
| else | |
| echo "{}" | jq "$key = $value" > "$CONFIG_FILE" | |
| fi | |
| } | |
| get_primary_workspace() { | |
| get_config_value ".primary_workspace" "" | |
| } | |
| set_primary_workspace() { | |
| set_config_value ".primary_workspace" "\"$1\"" | |
| } | |
| ####################################### | |
| # Workspace Management | |
| ####################################### | |
| get_workspaces() { | |
| decrypt_tokens | jq -r 'keys[]' 2>/dev/null | |
| } | |
| get_workspace_token() { | |
| local workspace="$1" | |
| decrypt_tokens | jq -r ".\"$workspace\".token // empty" | |
| } | |
| get_workspace_cookie() { | |
| local workspace="$1" | |
| decrypt_tokens | jq -r ".\"$workspace\".cookie // empty" | |
| } | |
| workspace_exists() { | |
| local workspace="$1" | |
| local token | |
| token=$(get_workspace_token "$workspace") | |
| [[ -n "$token" ]] | |
| } | |
| add_workspace() { | |
| local name="$1" | |
| local token="$2" | |
| local cookie="${3:-}" | |
| local current_tokens | |
| current_tokens=$(decrypt_tokens) | |
| local new_tokens | |
| if [[ -n "$cookie" ]]; then | |
| new_tokens=$(echo "$current_tokens" | jq ".\"$name\" = {\"token\": \"$token\", \"cookie\": \"$cookie\"}") | |
| else | |
| new_tokens=$(echo "$current_tokens" | jq ".\"$name\" = {\"token\": \"$token\"}") | |
| fi | |
| local ssh_key | |
| ssh_key=$(get_config_value ".ssh_key" "") | |
| if [[ -n "$ssh_key" ]] && has_age; then | |
| encrypt_tokens "$ssh_key" "$new_tokens" | |
| else | |
| echo "$new_tokens" > "$TOKENS_FILE" | |
| chmod 600 "$TOKENS_FILE" | |
| fi | |
| } | |
| remove_workspace() { | |
| local name="$1" | |
| local current_tokens | |
| current_tokens=$(decrypt_tokens) | |
| local new_tokens | |
| new_tokens=$(echo "$current_tokens" | jq "del(.\"$name\")") | |
| local ssh_key | |
| ssh_key=$(get_config_value ".ssh_key" "") | |
| if [[ -n "$ssh_key" ]] && has_age; then | |
| encrypt_tokens "$ssh_key" "$new_tokens" | |
| else | |
| echo "$new_tokens" > "$TOKENS_FILE" | |
| chmod 600 "$TOKENS_FILE" | |
| fi | |
| } | |
| ####################################### | |
| # Preset Management | |
| ####################################### | |
| ensure_default_presets() { | |
| if [[ ! -f "$PRESETS_FILE" ]]; then | |
| ensure_config_dir | |
| cat > "$PRESETS_FILE" << 'EOF' | |
| { | |
| "meeting": { | |
| "text": "In a meeting", | |
| "emoji": ":spiral_calendar_pad:", | |
| "duration": "1h", | |
| "dnd": "1h" | |
| }, | |
| "lunch": { | |
| "text": "Out to lunch", | |
| "emoji": ":sandwich:", | |
| "duration": "1h" | |
| }, | |
| "focus": { | |
| "text": "Deep work", | |
| "emoji": ":headphones:", | |
| "duration": "2h", | |
| "presence": "away", | |
| "dnd": "2h" | |
| }, | |
| "afk": { | |
| "text": "Away from keyboard", | |
| "emoji": ":walking:", | |
| "duration": "30m" | |
| }, | |
| "brb": { | |
| "text": "Be right back", | |
| "emoji": ":brb:", | |
| "duration": "15m" | |
| }, | |
| "pto": { | |
| "text": "Out of office", | |
| "emoji": ":palm_tree:", | |
| "duration": "0" | |
| } | |
| } | |
| EOF | |
| fi | |
| } | |
| get_preset() { | |
| local name="$1" | |
| ensure_default_presets | |
| jq -r ".\"$name\" // empty" "$PRESETS_FILE" | |
| } | |
| list_presets() { | |
| ensure_default_presets | |
| jq -r 'to_entries[] | "\(.key): \(.value.emoji) \(.value.text) (\(.value.duration // "no expiration"))\(if .value.presence then " [presence: \(.value.presence)]" else "" end)\(if .value.dnd then " [dnd: \(.value.dnd)]" else "" end)"' "$PRESETS_FILE" | |
| } | |
| add_preset() { | |
| local name="$1" | |
| local text="$2" | |
| local emoji="$3" | |
| local duration="${4:-0}" | |
| local presence="${5:-}" | |
| local dnd="${6:-}" | |
| ensure_default_presets | |
| local tmp | |
| tmp=$(mktemp) | |
| local preset_obj="{\"text\": \"$text\", \"emoji\": \"$emoji\", \"duration\": \"$duration\"" | |
| [[ -n "$presence" ]] && preset_obj="$preset_obj, \"presence\": \"$presence\"" | |
| [[ -n "$dnd" ]] && preset_obj="$preset_obj, \"dnd\": \"$dnd\"" | |
| preset_obj="$preset_obj}" | |
| jq ".\"$name\" = $preset_obj" "$PRESETS_FILE" > "$tmp" && mv "$tmp" "$PRESETS_FILE" | |
| } | |
| delete_preset() { | |
| local name="$1" | |
| ensure_default_presets | |
| local tmp | |
| tmp=$(mktemp) | |
| jq "del(.\"$name\")" "$PRESETS_FILE" > "$tmp" && mv "$tmp" "$PRESETS_FILE" | |
| } | |
| ####################################### | |
| # Slack API | |
| ####################################### | |
| slack_api_call() { | |
| local workspace="$1" | |
| local method="$2" | |
| local data="${3:-}" | |
| local token cookie | |
| token=$(get_workspace_token "$workspace") | |
| cookie=$(get_workspace_cookie "$workspace") | |
| if [[ -z "$token" ]]; then | |
| die "No token found for workspace: $workspace" | |
| fi | |
| local curl_args=( | |
| -sS | |
| -X POST | |
| "https://slack.com/api/$method" | |
| -H "Authorization: Bearer $token" | |
| -H "Content-Type: application/json; charset=utf-8" | |
| ) | |
| # Add cookie header if present (for xoxc tokens) | |
| if [[ -n "$cookie" ]]; then | |
| curl_args+=(-H "Cookie: d=$cookie") | |
| fi | |
| if [[ -n "$data" ]]; then | |
| curl_args+=(--data "$data") | |
| fi | |
| curl "${curl_args[@]}" | |
| } | |
| set_status() { | |
| local workspace="$1" | |
| local text="$2" | |
| local emoji="$3" | |
| local duration="$4" | |
| local expiration=0 | |
| if [[ "$duration" -gt 0 ]]; then | |
| expiration=$(($(date +%s) + duration)) | |
| fi | |
| local payload | |
| payload=$(jq -n \ | |
| --arg text "$text" \ | |
| --arg emoji "$emoji" \ | |
| --argjson exp "$expiration" \ | |
| '{profile: {status_text: $text, status_emoji: $emoji, status_expiration: $exp}}') | |
| log_verbose "Setting status on $workspace: $text $emoji ($(format_duration "$duration"))" | |
| log_verbose "Payload: $payload" | |
| local response | |
| response=$(slack_api_call "$workspace" "users.profile.set" "$payload") | |
| log_verbose "Response: $response" | |
| local ok | |
| ok=$(echo "$response" | jq -r '.ok') | |
| if [[ "$ok" == "true" ]]; then | |
| return 0 | |
| else | |
| local error | |
| error=$(echo "$response" | jq -r '.error // "unknown error"') | |
| log_error "Failed to set status on $workspace: $error" | |
| return 1 | |
| fi | |
| } | |
| get_status() { | |
| local workspace="$1" | |
| local response | |
| response=$(slack_api_call "$workspace" "users.profile.get") | |
| log_verbose "Response: $response" | |
| local ok | |
| ok=$(echo "$response" | jq -r '.ok') | |
| if [[ "$ok" == "true" ]]; then | |
| local text emoji expiration | |
| text=$(echo "$response" | jq -r '.profile.status_text // ""') | |
| emoji=$(echo "$response" | jq -r '.profile.status_emoji // ""') | |
| expiration=$(echo "$response" | jq -r '.profile.status_expiration // 0') | |
| if [[ -z "$text" && -z "$emoji" ]]; then | |
| echo "No status set" | |
| else | |
| local exp_str="" | |
| if [[ "$expiration" -gt 0 ]]; then | |
| local now remaining | |
| now=$(date +%s) | |
| remaining=$((expiration - now)) | |
| if [[ $remaining -gt 0 ]]; then | |
| exp_str=" (expires in $(format_duration "$remaining"))" | |
| else | |
| exp_str=" (expired)" | |
| fi | |
| fi | |
| echo "$emoji $text$exp_str" | |
| fi | |
| return 0 | |
| else | |
| local error | |
| error=$(echo "$response" | jq -r '.error // "unknown error"') | |
| log_error "Failed to get status from $workspace: $error" | |
| return 1 | |
| fi | |
| } | |
| clear_status() { | |
| local workspace="$1" | |
| set_status "$workspace" "" "" 0 | |
| } | |
| ####################################### | |
| # Presence API | |
| ####################################### | |
| set_presence() { | |
| local workspace="$1" | |
| local presence="$2" # "away" or "auto" | |
| local payload | |
| payload=$(jq -n --arg presence "$presence" '{presence: $presence}') | |
| log_verbose "Setting presence on $workspace: $presence" | |
| local response | |
| response=$(slack_api_call "$workspace" "users.setPresence" "$payload") | |
| log_verbose "Response: $response" | |
| local ok | |
| ok=$(echo "$response" | jq -r '.ok') | |
| if [[ "$ok" == "true" ]]; then | |
| return 0 | |
| else | |
| local error | |
| error=$(echo "$response" | jq -r '.error // "unknown error"') | |
| log_error "Failed to set presence on $workspace: $error" | |
| return 1 | |
| fi | |
| } | |
| get_presence() { | |
| local workspace="$1" | |
| local response | |
| response=$(slack_api_call "$workspace" "users.getPresence") | |
| log_verbose "Response: $response" | |
| local ok | |
| ok=$(echo "$response" | jq -r '.ok') | |
| if [[ "$ok" == "true" ]]; then | |
| local presence manual_away online | |
| presence=$(echo "$response" | jq -r '.presence // "unknown"') | |
| manual_away=$(echo "$response" | jq -r '.manual_away // false') | |
| online=$(echo "$response" | jq -r '.online // false') | |
| local display_status | |
| if [[ "$manual_away" == "true" ]]; then | |
| display_status="Away (manual)" | |
| elif [[ "$online" == "true" ]]; then | |
| display_status="Auto (online)" | |
| elif [[ "$presence" == "away" ]]; then | |
| display_status="Away" | |
| else | |
| display_status="Active" | |
| fi | |
| echo "$display_status" | |
| return 0 | |
| else | |
| local error | |
| error=$(echo "$response" | jq -r '.error // "unknown error"') | |
| log_error "Failed to get presence from $workspace: $error" | |
| return 1 | |
| fi | |
| } | |
| ####################################### | |
| # DND (Do Not Disturb) API | |
| ####################################### | |
| set_dnd_snooze() { | |
| local workspace="$1" | |
| local minutes="$2" | |
| log_verbose "Setting DND snooze on $workspace for $minutes minutes" | |
| local response | |
| response=$(slack_api_call "$workspace" "dnd.setSnooze" "{\"num_minutes\": $minutes}") | |
| log_verbose "Response: $response" | |
| local ok | |
| ok=$(echo "$response" | jq -r '.ok') | |
| if [[ "$ok" == "true" ]]; then | |
| return 0 | |
| else | |
| local error | |
| error=$(echo "$response" | jq -r '.error // "unknown error"') | |
| log_error "Failed to set DND on $workspace: $error" | |
| return 1 | |
| fi | |
| } | |
| end_dnd_snooze() { | |
| local workspace="$1" | |
| log_verbose "Ending DND snooze on $workspace" | |
| local response | |
| response=$(slack_api_call "$workspace" "dnd.endSnooze") | |
| log_verbose "Response: $response" | |
| local ok | |
| ok=$(echo "$response" | jq -r '.ok') | |
| if [[ "$ok" == "true" ]]; then | |
| return 0 | |
| else | |
| local error | |
| error=$(echo "$response" | jq -r '.error // "unknown error"') | |
| # "snooze_not_active" is not really an error | |
| if [[ "$error" == "snooze_not_active" ]]; then | |
| return 0 | |
| fi | |
| log_error "Failed to end DND on $workspace: $error" | |
| return 1 | |
| fi | |
| } | |
| get_dnd_info() { | |
| local workspace="$1" | |
| local response | |
| response=$(slack_api_call "$workspace" "dnd.info") | |
| log_verbose "Response: $response" | |
| local ok | |
| ok=$(echo "$response" | jq -r '.ok') | |
| if [[ "$ok" == "true" ]]; then | |
| local snooze_enabled snooze_endtime dnd_enabled next_dnd_start next_dnd_end | |
| snooze_enabled=$(echo "$response" | jq -r '.snooze_enabled // false') | |
| snooze_endtime=$(echo "$response" | jq -r '.snooze_endtime // 0') | |
| dnd_enabled=$(echo "$response" | jq -r '.dnd_enabled // false') | |
| next_dnd_start=$(echo "$response" | jq -r '.next_dnd_start_ts // 0') | |
| next_dnd_end=$(echo "$response" | jq -r '.next_dnd_end_ts // 0') | |
| if [[ "$snooze_enabled" == "true" ]]; then | |
| local now remaining | |
| now=$(date +%s) | |
| remaining=$((snooze_endtime - now)) | |
| if [[ $remaining -gt 0 ]]; then | |
| echo "Snoozed ($(format_duration "$remaining") remaining)" | |
| else | |
| echo "Snooze expired" | |
| fi | |
| elif [[ "$dnd_enabled" == "true" ]]; then | |
| echo "DND scheduled" | |
| else | |
| echo "Not snoozed" | |
| fi | |
| return 0 | |
| else | |
| local error | |
| error=$(echo "$response" | jq -r '.error // "unknown error"') | |
| log_error "Failed to get DND info from $workspace: $error" | |
| return 1 | |
| fi | |
| } | |
| ####################################### | |
| # Setup Wizard | |
| ####################################### | |
| run_setup_wizard() { | |
| echo -e "${BOLD}Welcome to slack-status setup!${NC}" | |
| echo "" | |
| ensure_config_dir | |
| # Check for age | |
| if ! has_age; then | |
| echo -e "${YELLOW}Warning:${NC} 'age' encryption tool not found." | |
| echo "Your Slack tokens will be stored unencrypted." | |
| echo "Install age for encrypted storage: brew install age (macOS) or apt install age (Linux)" | |
| echo "" | |
| fi | |
| # SSH key selection | |
| local ssh_key="" | |
| if has_age; then | |
| echo -e "${BOLD}SSH Key for Encryption${NC}" | |
| local key_info | |
| key_info=$(detect_ssh_key) | |
| case "$key_info" in | |
| both:*) | |
| local ed25519_path rsa_path | |
| ed25519_path=$(echo "$key_info" | cut -d: -f2) | |
| rsa_path=$(echo "$key_info" | cut -d: -f3) | |
| echo "Found multiple SSH keys:" | |
| echo " 1) $ed25519_path (ed25519, recommended)" | |
| echo " 2) $rsa_path (rsa)" | |
| read -rp "Select key [1]: " key_choice | |
| key_choice="${key_choice:-1}" | |
| if [[ "$key_choice" == "2" ]]; then | |
| ssh_key="$rsa_path" | |
| else | |
| ssh_key="$ed25519_path" | |
| fi | |
| ;; | |
| ed25519:*|rsa:*) | |
| ssh_key=$(echo "$key_info" | cut -d: -f2) | |
| echo "Found SSH key: $ssh_key" | |
| read -rp "Use this key? [Y/n]: " use_key | |
| if [[ "${use_key,,}" == "n" ]]; then | |
| read -rp "Enter path to SSH key: " ssh_key | |
| fi | |
| ;; | |
| none) | |
| echo "No SSH key found at ~/.ssh/id_ed25519 or ~/.ssh/id_rsa" | |
| read -rp "Enter path to SSH key (or leave empty to skip encryption): " ssh_key | |
| ;; | |
| esac | |
| if [[ -n "$ssh_key" ]]; then | |
| if [[ ! -f "$ssh_key" ]]; then | |
| log_warn "SSH key not found: $ssh_key" | |
| log_warn "Tokens will be stored unencrypted." | |
| ssh_key="" | |
| elif [[ ! -f "$ssh_key.pub" ]]; then | |
| log_warn "Public key not found: $ssh_key.pub" | |
| log_warn "Tokens will be stored unencrypted." | |
| ssh_key="" | |
| else | |
| set_config_value ".ssh_key" "\"$ssh_key\"" | |
| echo -e "${GREEN}β${NC} Will use $ssh_key for encryption" | |
| fi | |
| fi | |
| echo "" | |
| fi | |
| # Add workspaces | |
| local workspace_count=0 | |
| local add_more="y" | |
| while [[ "${add_more,,}" == "y" ]]; do | |
| echo -e "${BOLD}Add Slack Workspace${NC}" | |
| read -rp "Workspace name (e.g., 'oddball', 'boehs'): " ws_name | |
| if [[ -z "$ws_name" ]]; then | |
| log_warn "Workspace name cannot be empty" | |
| continue | |
| fi | |
| # Normalize to lowercase | |
| ws_name="${ws_name,,}" | |
| echo "" | |
| echo "Enter your Slack token. This can be:" | |
| echo " - A user token (xoxp-...)" | |
| echo " - A client token with cookie (xoxc-...:xoxd-...)" | |
| echo "" | |
| read -rp "Token: " ws_token | |
| if [[ -z "$ws_token" ]]; then | |
| log_warn "Token cannot be empty" | |
| continue | |
| fi | |
| # Parse token:cookie format | |
| local token cookie="" | |
| if [[ "$ws_token" == *":"* ]]; then | |
| token="${ws_token%%:*}" | |
| cookie="${ws_token#*:}" | |
| else | |
| token="$ws_token" | |
| fi | |
| add_workspace "$ws_name" "$token" "$cookie" | |
| ((workspace_count++)) | |
| if [[ $workspace_count -eq 1 ]]; then | |
| set_primary_workspace "$ws_name" | |
| echo -e "${GREEN}β${NC} Added workspace '$ws_name' as primary" | |
| else | |
| echo -e "${GREEN}β${NC} Added workspace '$ws_name'" | |
| fi | |
| echo "" | |
| read -rp "Add another workspace? [y/N]: " add_more | |
| add_more="${add_more:-n}" | |
| echo "" | |
| done | |
| if [[ $workspace_count -eq 0 ]]; then | |
| log_warn "No workspaces configured. Run '$SCRIPT_NAME config' to add workspaces." | |
| else | |
| # Initialize default presets | |
| ensure_default_presets | |
| echo -e "${GREEN}Setup complete!${NC}" | |
| echo "" | |
| echo "Configured workspaces:" | |
| get_workspaces | while read -r ws; do | |
| local primary_indicator="" | |
| [[ "$ws" == "$(get_primary_workspace)" ]] && primary_indicator=" (primary)" | |
| echo " - $ws$primary_indicator" | |
| done | |
| echo "" | |
| echo "Try it out:" | |
| echo " $SCRIPT_NAME set \"Working\" :computer:" | |
| echo " $SCRIPT_NAME preset lunch" | |
| echo " $SCRIPT_NAME get" | |
| echo " $SCRIPT_NAME clear" | |
| fi | |
| } | |
| ####################################### | |
| # Command Handlers | |
| ####################################### | |
| cmd_set() { | |
| local workspace="" text="" emoji=":speech_balloon:" duration="0" | |
| local all_workspaces=false | |
| local presence="" dnd="" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -w|--workspace) | |
| workspace="$2" | |
| shift 2 | |
| ;; | |
| --all) | |
| all_workspaces=true | |
| shift | |
| ;; | |
| -v|--verbose) | |
| VERBOSE=true | |
| shift | |
| ;; | |
| -q|--quiet) | |
| QUIET=true | |
| shift | |
| ;; | |
| -p|--presence) | |
| presence="$2" | |
| shift 2 | |
| ;; | |
| -d|--dnd) | |
| dnd="$2" | |
| shift 2 | |
| ;; | |
| -*) | |
| die "Unknown option: $1" | |
| ;; | |
| *) | |
| if [[ -z "$text" ]]; then | |
| text="$1" | |
| elif [[ "$1" == :* ]]; then | |
| emoji="$1" | |
| else | |
| duration=$(parse_duration "$1") | |
| fi | |
| shift | |
| ;; | |
| esac | |
| done | |
| if [[ -z "$text" ]]; then | |
| die "Usage: $SCRIPT_NAME set <text> [emoji] [duration] [-w workspace|--all] [-p presence] [-d dnd]" | |
| fi | |
| local workspaces=() | |
| if $all_workspaces; then | |
| while IFS= read -r ws; do | |
| workspaces+=("$ws") | |
| done < <(get_workspaces) | |
| elif [[ -n "$workspace" ]]; then | |
| workspaces=("$workspace") | |
| else | |
| local primary | |
| primary=$(get_primary_workspace) | |
| if [[ -z "$primary" ]]; then | |
| die "No primary workspace configured. Run '$SCRIPT_NAME config' or use -w/--workspace." | |
| fi | |
| workspaces=("$primary") | |
| fi | |
| local exit_code=0 | |
| local duration_display | |
| duration_display=$(format_duration "$duration") | |
| for ws in "${workspaces[@]}"; do | |
| if ! workspace_exists "$ws"; then | |
| log_error "Workspace not found: $ws" | |
| exit_code=1 | |
| continue | |
| fi | |
| if set_status "$ws" "$text" "$emoji" "$duration"; then | |
| if [[ "$duration" -eq 0 ]]; then | |
| log_info "Status set on ${BOLD}$ws${NC}: $emoji $text (${YELLOW}no expiration${NC})" | |
| else | |
| log_info "Status set on ${BOLD}$ws${NC}: $emoji $text (expires in $duration_display)" | |
| fi | |
| else | |
| exit_code=1 | |
| fi | |
| # Apply presence if specified | |
| if [[ -n "$presence" ]]; then | |
| local p="$presence" | |
| [[ "$p" == "active" ]] && p="auto" | |
| if set_presence "$ws" "$p"; then | |
| local display_p="$p" | |
| [[ "$p" == "auto" ]] && display_p="active (auto)" | |
| log_info "Presence set on ${BOLD}$ws${NC}: $display_p" | |
| else | |
| exit_code=1 | |
| fi | |
| fi | |
| # Apply DND if specified | |
| if [[ -n "$dnd" ]]; then | |
| if [[ "$dnd" == "off" || "$dnd" == "end" ]]; then | |
| if end_dnd_snooze "$ws"; then | |
| log_info "Notifications resumed on ${BOLD}$ws${NC}" | |
| else | |
| exit_code=1 | |
| fi | |
| else | |
| local dnd_seconds dnd_minutes | |
| dnd_seconds=$(parse_duration "$dnd") | |
| dnd_minutes=$((dnd_seconds / 60)) | |
| [[ $dnd_minutes -lt 1 ]] && dnd_minutes=1 | |
| if set_dnd_snooze "$ws" "$dnd_minutes"; then | |
| log_info "Notifications snoozed on ${BOLD}$ws${NC} for $(format_duration "$dnd_seconds")" | |
| else | |
| exit_code=1 | |
| fi | |
| fi | |
| fi | |
| done | |
| return $exit_code | |
| } | |
| cmd_get() { | |
| local workspace="" | |
| local all_workspaces=false | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -w|--workspace) | |
| workspace="$2" | |
| shift 2 | |
| ;; | |
| --all) | |
| all_workspaces=true | |
| shift | |
| ;; | |
| -v|--verbose) | |
| VERBOSE=true | |
| shift | |
| ;; | |
| -q|--quiet) | |
| QUIET=true | |
| shift | |
| ;; | |
| *) | |
| die "Unknown option: $1" | |
| ;; | |
| esac | |
| done | |
| local workspaces=() | |
| if $all_workspaces; then | |
| while IFS= read -r ws; do | |
| workspaces+=("$ws") | |
| done < <(get_workspaces) | |
| elif [[ -n "$workspace" ]]; then | |
| workspaces=("$workspace") | |
| else | |
| local primary | |
| primary=$(get_primary_workspace) | |
| if [[ -z "$primary" ]]; then | |
| die "No primary workspace configured. Run '$SCRIPT_NAME config' or use -w/--workspace." | |
| fi | |
| workspaces=("$primary") | |
| fi | |
| local exit_code=0 | |
| for ws in "${workspaces[@]}"; do | |
| if ! workspace_exists "$ws"; then | |
| log_error "Workspace not found: $ws" | |
| exit_code=1 | |
| continue | |
| fi | |
| local status | |
| if status=$(get_status "$ws"); then | |
| echo -e "${BOLD}$ws${NC}: $status" | |
| else | |
| exit_code=1 | |
| fi | |
| done | |
| return $exit_code | |
| } | |
| cmd_clear() { | |
| local workspace="" | |
| local all_workspaces=false | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -w|--workspace) | |
| workspace="$2" | |
| shift 2 | |
| ;; | |
| --all) | |
| all_workspaces=true | |
| shift | |
| ;; | |
| -v|--verbose) | |
| VERBOSE=true | |
| shift | |
| ;; | |
| -q|--quiet) | |
| QUIET=true | |
| shift | |
| ;; | |
| *) | |
| die "Unknown option: $1" | |
| ;; | |
| esac | |
| done | |
| local workspaces=() | |
| if $all_workspaces; then | |
| while IFS= read -r ws; do | |
| workspaces+=("$ws") | |
| done < <(get_workspaces) | |
| elif [[ -n "$workspace" ]]; then | |
| workspaces=("$workspace") | |
| else | |
| local primary | |
| primary=$(get_primary_workspace) | |
| if [[ -z "$primary" ]]; then | |
| die "No primary workspace configured. Run '$SCRIPT_NAME config' or use -w/--workspace." | |
| fi | |
| workspaces=("$primary") | |
| fi | |
| local exit_code=0 | |
| for ws in "${workspaces[@]}"; do | |
| if ! workspace_exists "$ws"; then | |
| log_error "Workspace not found: $ws" | |
| exit_code=1 | |
| continue | |
| fi | |
| if clear_status "$ws"; then | |
| log_info "Status cleared on ${BOLD}$ws${NC}" | |
| else | |
| exit_code=1 | |
| fi | |
| done | |
| return $exit_code | |
| } | |
| cmd_preset() { | |
| local action="${1:-list}" | |
| shift || true | |
| case "$action" in | |
| list|ls) | |
| ensure_default_presets | |
| echo -e "${BOLD}Available presets:${NC}" | |
| list_presets | |
| ;; | |
| add) | |
| local name="${1:-}" | |
| if [[ -z "$name" ]]; then | |
| read -rp "Preset name: " name | |
| fi | |
| if [[ -z "$name" ]]; then | |
| die "Preset name is required" | |
| fi | |
| local text emoji duration presence dnd | |
| read -rp "Status text: " text | |
| read -rp "Emoji (e.g., :coffee:): " emoji | |
| read -rp "Duration (e.g., 1h, 30m, or 0 for no expiration): " duration | |
| duration="${duration:-0}" | |
| read -rp "Presence (away/auto, or blank to skip): " presence | |
| read -rp "DND snooze (e.g., 1h, or 'off', or blank to skip): " dnd | |
| add_preset "$name" "$text" "$emoji" "$duration" "$presence" "$dnd" | |
| log_info "Preset '$name' added" | |
| ;; | |
| edit) | |
| local name="${1:-}" | |
| if [[ -z "$name" ]]; then | |
| die "Usage: $SCRIPT_NAME preset edit <name>" | |
| fi | |
| local existing | |
| existing=$(get_preset "$name") | |
| if [[ -z "$existing" ]]; then | |
| die "Preset '$name' not found" | |
| fi | |
| local current_text current_emoji current_duration current_presence current_dnd | |
| current_text=$(echo "$existing" | jq -r '.text') | |
| current_emoji=$(echo "$existing" | jq -r '.emoji') | |
| current_duration=$(echo "$existing" | jq -r '.duration // "0"') | |
| current_presence=$(echo "$existing" | jq -r '.presence // ""') | |
| current_dnd=$(echo "$existing" | jq -r '.dnd // ""') | |
| echo "Editing preset '$name' (press Enter to keep current value, '-' to clear)" | |
| read -rp "Status text [$current_text]: " text | |
| text="${text:-$current_text}" | |
| read -rp "Emoji [$current_emoji]: " emoji | |
| emoji="${emoji:-$current_emoji}" | |
| read -rp "Duration [$current_duration]: " duration | |
| duration="${duration:-$current_duration}" | |
| read -rp "Presence [$current_presence]: " presence | |
| [[ "$presence" == "-" ]] && presence="" || presence="${presence:-$current_presence}" | |
| read -rp "DND snooze [$current_dnd]: " dnd | |
| [[ "$dnd" == "-" ]] && dnd="" || dnd="${dnd:-$current_dnd}" | |
| add_preset "$name" "$text" "$emoji" "$duration" "$presence" "$dnd" | |
| log_info "Preset '$name' updated" | |
| ;; | |
| delete|rm) | |
| local name="${1:-}" | |
| if [[ -z "$name" ]]; then | |
| die "Usage: $SCRIPT_NAME preset delete <name>" | |
| fi | |
| local existing | |
| existing=$(get_preset "$name") | |
| if [[ -z "$existing" ]]; then | |
| die "Preset '$name' not found" | |
| fi | |
| delete_preset "$name" | |
| log_info "Preset '$name' deleted" | |
| ;; | |
| *) | |
| # Assume it's a preset name to apply | |
| local preset_name="$action" | |
| local preset_data | |
| preset_data=$(get_preset "$preset_name") | |
| if [[ -z "$preset_data" ]]; then | |
| die "Preset '$preset_name' not found. Use '$SCRIPT_NAME preset list' to see available presets." | |
| fi | |
| local text emoji duration_str duration presence dnd | |
| text=$(echo "$preset_data" | jq -r '.text') | |
| emoji=$(echo "$preset_data" | jq -r '.emoji') | |
| duration_str=$(echo "$preset_data" | jq -r '.duration // "0"') | |
| duration=$(parse_duration "$duration_str") | |
| presence=$(echo "$preset_data" | jq -r '.presence // empty') | |
| dnd=$(echo "$preset_data" | jq -r '.dnd // empty') | |
| # Apply status first | |
| cmd_set "$text" "$emoji" "$duration" "$@" | |
| # Apply presence if set | |
| if [[ -n "$presence" ]]; then | |
| cmd_presence "$presence" "$@" | |
| fi | |
| # Apply DND if set | |
| if [[ -n "$dnd" ]]; then | |
| if [[ "$dnd" == "off" || "$dnd" == "end" ]]; then | |
| cmd_dnd off "$@" | |
| else | |
| cmd_dnd on "$dnd" "$@" | |
| fi | |
| fi | |
| ;; | |
| esac | |
| } | |
| cmd_workspaces() { | |
| local action="${1:-list}" | |
| shift || true | |
| case "$action" in | |
| list|ls) | |
| local primary | |
| primary=$(get_primary_workspace) | |
| echo -e "${BOLD}Configured workspaces:${NC}" | |
| get_workspaces | while read -r ws; do | |
| local primary_indicator="" | |
| [[ "$ws" == "$primary" ]] && primary_indicator=" ${GREEN}(primary)${NC}" | |
| echo -e " - $ws$primary_indicator" | |
| done | |
| ;; | |
| add) | |
| echo -e "${BOLD}Add Slack Workspace${NC}" | |
| read -rp "Workspace name: " ws_name | |
| ws_name="${ws_name,,}" | |
| if workspace_exists "$ws_name"; then | |
| die "Workspace '$ws_name' already exists" | |
| fi | |
| read -rp "Token (xoxp-... or xoxc-...:xoxd-...): " ws_token | |
| local token cookie="" | |
| if [[ "$ws_token" == *":"* ]]; then | |
| token="${ws_token%%:*}" | |
| cookie="${ws_token#*:}" | |
| else | |
| token="$ws_token" | |
| fi | |
| add_workspace "$ws_name" "$token" "$cookie" | |
| log_info "Workspace '$ws_name' added" | |
| ;; | |
| remove|rm) | |
| local name="${1:-}" | |
| if [[ -z "$name" ]]; then | |
| die "Usage: $SCRIPT_NAME workspaces remove <name>" | |
| fi | |
| if ! workspace_exists "$name"; then | |
| die "Workspace '$name' not found" | |
| fi | |
| remove_workspace "$name" | |
| log_info "Workspace '$name' removed" | |
| # If we removed the primary, clear it | |
| if [[ "$name" == "$(get_primary_workspace)" ]]; then | |
| set_config_value ".primary_workspace" '""' | |
| log_warn "Removed primary workspace. Set a new one with: $SCRIPT_NAME workspaces primary <name>" | |
| fi | |
| ;; | |
| primary) | |
| local name="${1:-}" | |
| if [[ -z "$name" ]]; then | |
| local current | |
| current=$(get_primary_workspace) | |
| if [[ -n "$current" ]]; then | |
| echo "Current primary workspace: $current" | |
| else | |
| echo "No primary workspace set" | |
| fi | |
| return | |
| fi | |
| if ! workspace_exists "$name"; then | |
| die "Workspace '$name' not found" | |
| fi | |
| set_primary_workspace "$name" | |
| log_info "Primary workspace set to '$name'" | |
| ;; | |
| *) | |
| die "Unknown workspaces action: $action" | |
| ;; | |
| esac | |
| } | |
| cmd_config() { | |
| run_setup_wizard | |
| } | |
| cmd_presence() { | |
| local workspace="" | |
| local all_workspaces=false | |
| local presence="" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -w|--workspace) | |
| workspace="$2" | |
| shift 2 | |
| ;; | |
| --all) | |
| all_workspaces=true | |
| shift | |
| ;; | |
| -v|--verbose) | |
| VERBOSE=true | |
| shift | |
| ;; | |
| -q|--quiet) | |
| QUIET=true | |
| shift | |
| ;; | |
| away|auto|active) | |
| # "active" is an alias for "auto" | |
| [[ "$1" == "active" ]] && presence="auto" || presence="$1" | |
| shift | |
| ;; | |
| *) | |
| die "Unknown option or presence value: $1 (use 'away' or 'auto')" | |
| ;; | |
| esac | |
| done | |
| local workspaces=() | |
| if $all_workspaces; then | |
| while IFS= read -r ws; do | |
| workspaces+=("$ws") | |
| done < <(get_workspaces) | |
| elif [[ -n "$workspace" ]]; then | |
| workspaces=("$workspace") | |
| else | |
| local primary | |
| primary=$(get_primary_workspace) | |
| if [[ -z "$primary" ]]; then | |
| die "No primary workspace configured. Run '$SCRIPT_NAME config' or use -w/--workspace." | |
| fi | |
| workspaces=("$primary") | |
| fi | |
| local exit_code=0 | |
| # If no presence specified, show current presence | |
| if [[ -z "$presence" ]]; then | |
| for ws in "${workspaces[@]}"; do | |
| if ! workspace_exists "$ws"; then | |
| log_error "Workspace not found: $ws" | |
| exit_code=1 | |
| continue | |
| fi | |
| local current_presence | |
| if current_presence=$(get_presence "$ws"); then | |
| echo -e "${BOLD}$ws${NC}: $current_presence" | |
| else | |
| exit_code=1 | |
| fi | |
| done | |
| else | |
| for ws in "${workspaces[@]}"; do | |
| if ! workspace_exists "$ws"; then | |
| log_error "Workspace not found: $ws" | |
| exit_code=1 | |
| continue | |
| fi | |
| if set_presence "$ws" "$presence"; then | |
| local display_presence="$presence" | |
| [[ "$presence" == "auto" ]] && display_presence="active (auto)" | |
| log_info "Presence set on ${BOLD}$ws${NC}: $display_presence" | |
| else | |
| exit_code=1 | |
| fi | |
| done | |
| fi | |
| return $exit_code | |
| } | |
| cmd_dnd() { | |
| local workspace="" | |
| local all_workspaces=false | |
| local action="" | |
| local duration="" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -w|--workspace) | |
| workspace="$2" | |
| shift 2 | |
| ;; | |
| --all) | |
| all_workspaces=true | |
| shift | |
| ;; | |
| -v|--verbose) | |
| VERBOSE=true | |
| shift | |
| ;; | |
| -q|--quiet) | |
| QUIET=true | |
| shift | |
| ;; | |
| on|snooze) | |
| action="snooze" | |
| shift | |
| # Check if next arg is a duration | |
| if [[ $# -gt 0 && ! "$1" =~ ^- ]]; then | |
| duration="$1" | |
| shift | |
| fi | |
| ;; | |
| off|end) | |
| action="end" | |
| shift | |
| ;; | |
| status|info) | |
| action="status" | |
| shift | |
| ;; | |
| *) | |
| # Could be a duration for snooze | |
| if [[ "$1" =~ ^[0-9] ]]; then | |
| action="snooze" | |
| duration="$1" | |
| shift | |
| else | |
| die "Unknown option: $1" | |
| fi | |
| ;; | |
| esac | |
| done | |
| local workspaces=() | |
| if $all_workspaces; then | |
| while IFS= read -r ws; do | |
| workspaces+=("$ws") | |
| done < <(get_workspaces) | |
| elif [[ -n "$workspace" ]]; then | |
| workspaces=("$workspace") | |
| else | |
| local primary | |
| primary=$(get_primary_workspace) | |
| if [[ -z "$primary" ]]; then | |
| die "No primary workspace configured. Run '$SCRIPT_NAME config' or use -w/--workspace." | |
| fi | |
| workspaces=("$primary") | |
| fi | |
| # Default action is status | |
| [[ -z "$action" ]] && action="status" | |
| # Default duration for snooze is 1 hour | |
| if [[ "$action" == "snooze" && -z "$duration" ]]; then | |
| duration="1h" | |
| fi | |
| local exit_code=0 | |
| case "$action" in | |
| status) | |
| for ws in "${workspaces[@]}"; do | |
| if ! workspace_exists "$ws"; then | |
| log_error "Workspace not found: $ws" | |
| exit_code=1 | |
| continue | |
| fi | |
| local dnd_info | |
| if dnd_info=$(get_dnd_info "$ws"); then | |
| echo -e "${BOLD}$ws${NC}: $dnd_info" | |
| else | |
| exit_code=1 | |
| fi | |
| done | |
| ;; | |
| snooze) | |
| local seconds minutes | |
| seconds=$(parse_duration "$duration") | |
| minutes=$((seconds / 60)) | |
| [[ $minutes -lt 1 ]] && minutes=1 | |
| for ws in "${workspaces[@]}"; do | |
| if ! workspace_exists "$ws"; then | |
| log_error "Workspace not found: $ws" | |
| exit_code=1 | |
| continue | |
| fi | |
| if set_dnd_snooze "$ws" "$minutes"; then | |
| log_info "Notifications snoozed on ${BOLD}$ws${NC} for $(format_duration "$seconds")" | |
| else | |
| exit_code=1 | |
| fi | |
| done | |
| ;; | |
| end) | |
| for ws in "${workspaces[@]}"; do | |
| if ! workspace_exists "$ws"; then | |
| log_error "Workspace not found: $ws" | |
| exit_code=1 | |
| continue | |
| fi | |
| if end_dnd_snooze "$ws"; then | |
| log_info "Notifications resumed on ${BOLD}$ws${NC}" | |
| else | |
| exit_code=1 | |
| fi | |
| done | |
| ;; | |
| esac | |
| return $exit_code | |
| } | |
| cmd_help() { | |
| echo -e "${BOLD}slack-status${NC} v$VERSION - Set your Slack status from the command line | |
| ${BOLD}USAGE${NC} | |
| $SCRIPT_NAME <command> [options] | |
| ${BOLD}COMMANDS${NC} | |
| set <text> [emoji] [duration] Set your status | |
| get Get current status | |
| clear Clear your status | |
| preset <name|action> Use or manage presets | |
| presence [away|auto] Get or set presence (away/active) | |
| dnd [on|off] [duration] Snooze or resume notifications | |
| workspaces <action> Manage workspaces | |
| config Run setup wizard | |
| help Show this help | |
| ${BOLD}OPTIONS${NC} | |
| -w, --workspace <name> Target a specific workspace | |
| --all Target all workspaces | |
| -p, --presence <value> Also set presence (away/auto/active) | |
| -d, --dnd <duration> Also snooze notifications (or 'off') | |
| -v, --verbose Show detailed output | |
| -q, --quiet Suppress output (for scripting) | |
| ${BOLD}EXAMPLES${NC} | |
| $SCRIPT_NAME set \"Deep work\" :headphones: 2h | |
| $SCRIPT_NAME set \"In a meeting\" :calendar: 1h30m -w oddball | |
| $SCRIPT_NAME set \"On PTO\" :palm_tree: --all | |
| $SCRIPT_NAME set \"Focus time\" :headphones: 2h -p away -d 2h | |
| $SCRIPT_NAME preset lunch | |
| $SCRIPT_NAME preset lunch --all | |
| $SCRIPT_NAME get --all | |
| $SCRIPT_NAME clear | |
| ${BOLD}PRESET COMMANDS${NC} | |
| preset list List all presets | |
| preset add Add a new preset (interactive) | |
| preset edit <name> Edit a preset | |
| preset delete <name> Delete a preset | |
| preset <name> Apply a preset | |
| ${BOLD}WORKSPACE COMMANDS${NC} | |
| workspaces list List configured workspaces | |
| workspaces add Add a workspace (interactive) | |
| workspaces remove <name> Remove a workspace | |
| workspaces primary [name] Get or set primary workspace | |
| ${BOLD}PRESENCE COMMANDS${NC} | |
| presence Show current presence | |
| presence away Set presence to away | |
| presence auto Set presence to active (auto) | |
| presence active Alias for 'presence auto' | |
| ${BOLD}DND (DO NOT DISTURB) COMMANDS${NC} | |
| dnd Show current DND status | |
| dnd on [duration] Snooze notifications (default: 1h) | |
| dnd off Resume notifications | |
| dnd 2h Snooze for specific duration | |
| ${BOLD}DURATION FORMAT${NC} | |
| Seconds: 3600 | |
| Human-readable: 1h, 30m, 1h30m, 2h15m | |
| ${BOLD}CONFIGURATION${NC} | |
| Config directory: $CONFIG_DIR | |
| Run '$SCRIPT_NAME config' to reconfigure | |
| " | |
| } | |
| ####################################### | |
| # Main | |
| ####################################### | |
| main() { | |
| ensure_jq | |
| ensure_curl | |
| local command="${1:-help}" | |
| shift || true | |
| # Check if setup is needed | |
| if [[ ! -f "$CONFIG_FILE" && "$command" != "config" && "$command" != "help" && "$command" != "--help" && "$command" != "-h" ]]; then | |
| echo "No configuration found. Let's set things up!" | |
| echo "" | |
| run_setup_wizard | |
| exit 0 | |
| fi | |
| case "$command" in | |
| set) | |
| cmd_set "$@" | |
| ;; | |
| get) | |
| cmd_get "$@" | |
| ;; | |
| clear) | |
| cmd_clear "$@" | |
| ;; | |
| preset) | |
| cmd_preset "$@" | |
| ;; | |
| presence) | |
| cmd_presence "$@" | |
| ;; | |
| dnd|snooze) | |
| cmd_dnd "$@" | |
| ;; | |
| workspaces|workspace|ws) | |
| cmd_workspaces "$@" | |
| ;; | |
| config|setup) | |
| cmd_config "$@" | |
| ;; | |
| help|--help|-h) | |
| cmd_help | |
| ;; | |
| version|--version|-V) | |
| echo "$SCRIPT_NAME v$VERSION" | |
| ;; | |
| *) | |
| # Maybe it's a preset name as a shortcut? | |
| local preset_data | |
| preset_data=$(get_preset "$command" 2>/dev/null || true) | |
| if [[ -n "$preset_data" ]]; then | |
| cmd_preset "$command" "$@" | |
| else | |
| die "Unknown command: $command. Run '$SCRIPT_NAME help' for usage." | |
| fi | |
| ;; | |
| esac | |
| } | |
| main "$@" |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
slack-status
A CLI tool to set your Slack status from the command line. Supports multiple workspaces, presets, presence, DND (snooze), and optional token encryption with
age.Features
slack-status lunchorslack-status focus1h30minstead of secondsagewith your SSH key (falls back to plaintext if age not installed)~/.config/slack-status/Installation
Dependencies
jq- JSON parsing (pre-installed on macOS,apt install jqon Linux)curl- API callsage(optional) - Token encryption (brew install ageorapt install age)Setup
Run
slack-statusand follow the wizard. You'll need a Slack token:xoxp-...) - From a Slack app withusers.profile:writescopexoxc-...:xoxd-...) - From browser dev tools (token:cookie format)Usage
Default Presets
meetinglunchfocusafkbrbptoDuration Format
36001h,30m,1h30m,2h15mConfig Files
Preset JSON Format
Presets can optionally include
presenceanddndfields:{ "focus": { "text": "Deep work", "emoji": ":headphones:", "duration": "2h", "presence": "away", "dnd": "2h" } }