Last active
January 16, 2025 08:09
-
-
Save MattJDavidson/1d4c7edb43a5304e685721efb9300233 to your computer and use it in GitHub Desktop.
lazy_hosts_blocker
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
| #!/bin/bash | |
| CONFIG_DIR="$HOME/.config/blocklists" | |
| HOSTS_FILE="/etc/hosts" | |
| DEFAULT_IP="0.0.0.0" | |
| ACTION="block" | |
| LIST_NAME="" | |
| DRYRUN=false | |
| CHANGED_DOMAINS=() | |
| show_help() { | |
| echo "Usage: $0 [--unblock|-U] [--dryrun] <list_name>" | |
| echo "Options:" | |
| echo " --unblock, -U Unblock domains in the specified list in ~/.config/blocklists." | |
| echo " --dryrun Show what would be done without making changes." | |
| echo " --help Show this help message." | |
| } | |
| exit_with_error() { | |
| echo "Error: $1" >&2 | |
| exit "$2" | |
| } | |
| ensure_root() { | |
| if [ "$EUID" -ne 0 ]; then | |
| exit_with_error "Please run as root (sudo)." 1 | |
| fi | |
| } | |
| parse_arguments() { | |
| for arg in "$@"; do | |
| case $arg in | |
| --unblock|-U) | |
| ACTION="unblock" | |
| ;; | |
| --dryrun) | |
| DRYRUN=true | |
| ;; | |
| --help) | |
| show_help | |
| exit 0 | |
| ;; | |
| *) | |
| if [ -z "$LIST_NAME" ]; then | |
| LIST_NAME="$arg" | |
| else | |
| exit_with_error "Unexpected argument: $arg" 2 | |
| fi | |
| ;; | |
| esac | |
| done | |
| if [ -z "$LIST_NAME" ]; then | |
| exit_with_error "No list name provided. Use --help for usage instructions." 3 | |
| fi | |
| } | |
| validate_blocklist() { | |
| LIST_FILE="$CONFIG_DIR/$LIST_NAME" | |
| if [ ! -f "$LIST_FILE" ]; then | |
| exit_with_error "The blocklist '$LIST_NAME' does not exist at $LIST_FILE. Available lists are in $CONFIG_DIR." 4 | |
| fi | |
| } | |
| block_domain() { | |
| local domain="$1" | |
| local subdomain="www.$domain" | |
| # Detect which sed style is supported | |
| # On macOS, `sed -i '' ...` is required. | |
| # On Linux (GNU sed), `sed -i ...` suffices. | |
| if sed --version 2>/dev/null | grep -q "GNU"; then | |
| SED_INPLACE=(-i) | |
| else | |
| SED_INPLACE=(-i '') | |
| fi | |
| for entry in "$domain" "$subdomain"; do | |
| # If the domain is already a sub-subdomain (e.g., old.reddit.com), | |
| # skip adding "www." version. | |
| if [[ "$domain" == *.*.* ]]; then | |
| [[ "$entry" == "$subdomain" ]] && continue | |
| fi | |
| # Check if the line exists (commented or uncommented) | |
| if grep -Eq "^[#[:space:]]*0\.0\.0\.0[[:space:]]+$entry" "$HOSTS_FILE"; then | |
| if [ "$DRYRUN" = false ]; then | |
| # 1) Remove all leading # and whitespace | |
| sed "${SED_INPLACE[@]}" -E \ | |
| "/^[#[:space:]]*0\.0\.0\.0[[:space:]]+$entry/ s/^[#[:space:]]+//" \ | |
| "$HOSTS_FILE" | |
| # 2) Normalize spacing (replace runs of whitespace with a single tab) | |
| sed "${SED_INPLACE[@]}" -E \ | |
| "/^0\.0\.0\.0[[:space:]]+$entry/ s/[[:space:]]+/\t/" \ | |
| "$HOSTS_FILE" | |
| fi | |
| CHANGED_DOMAINS+=("Uncommented $entry") | |
| else | |
| # If domain not found in the file, append it | |
| if [ "$DRYRUN" = false ]; then | |
| echo -e "$DEFAULT_IP\t$entry" >> "$HOSTS_FILE" | |
| fi | |
| CHANGED_DOMAINS+=("Blocked $entry") | |
| fi | |
| done | |
| } | |
| unblock_domain() { | |
| local domain="$1" | |
| local subdomain="www.$domain" | |
| for entry in "$domain" "$subdomain"; do | |
| if [[ "$domain" == *.*.* ]]; then | |
| [[ "$entry" == "$subdomain" ]] && continue | |
| fi | |
| if grep -Eq "^[#[:space:]]*.*$entry" "$HOSTS_FILE"; then | |
| if [ "$DRYRUN" = false ]; then | |
| sed -i -E "/^[#[:space:]]*.*$entry/ s/^#*[[:space:]]*/# /" "$HOSTS_FILE" | |
| fi | |
| CHANGED_DOMAINS+=("Commented out $entry") | |
| fi | |
| done | |
| } | |
| process_list() { | |
| while IFS= read -r domain || [ -n "$domain" ]; do | |
| [[ -z "$domain" || "$domain" == \#* ]] && continue | |
| if [[ "$ACTION" == "block" ]]; then | |
| block_domain "$domain" | |
| elif [[ "$ACTION" == "unblock" ]]; then | |
| unblock_domain "$domain" | |
| fi | |
| done < "$LIST_FILE" | |
| } | |
| display_summary() { | |
| if [ "${#CHANGED_DOMAINS[@]}" -gt 0 ]; then | |
| echo "Changes made:" | |
| for change in "${CHANGED_DOMAINS[@]}"; do | |
| echo "- $change" | |
| done | |
| else | |
| echo "No changes were made." | |
| fi | |
| if [ "$DRYRUN" = true ]; then | |
| echo "This was a dry run. No changes were made to $HOSTS_FILE." | |
| fi | |
| } | |
| main() { | |
| parse_arguments "$@" | |
| validate_blocklist | |
| ensure_root | |
| process_list | |
| display_summary | |
| echo "Operation completed successfully for the '$LIST_NAME' blocklist." | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment