#!/bin/bash set -eu # shellcheck disable=2034 description="vpn manager" FUZL_PROMPT=vpnm FUZL_ICON=󰦝 . libfuzl.sh main() { local conn conn=$(get_current_connection) [ -n "$conn" ] && fuzl_action_add "exitnode_down" "󰦝" "Connected to $conn" fuzl_action_add "networks" "󰾰" "Available networks" profile=$(ts_profile_current) fuzl_action_add "profile" "󱗼" "Profile: $profile" if ts_state; then fuzl_action_add "down" "" "Disable tailscale" else fuzl_action_add "up" "" "Enable tailscale" fi fuzl_action_menu || return } # # helpers # ts_state() { tailscale wait --timeout=1s } ts_profile_list() { fuzl_try -- tailscale switch --list --json } ts_profile_current() { ts_profile_list | jq -rc '.[]|select(.selected)|.nickname' } ts_profile_switch() { local profile=${1?} fuzl_try -- tailscale switch "$profile" sleep 2 fuzl_try -- pkill -SIGRTMIN+2 waybar } get_emoji_flag() { code= # TODO: calc flag # U\1F1E6 A # U\200D zero-w joiner case "$*" in au-*) code=$'🦘' ;; be-*) code=$'🇧🇪' ;; br-*) code=$'🇧🇷' ;; ca-*) code=$'🇨🇦' ;; de-*) code=$'🇩🇪' ;; fr-*) code=$'🇫🇷' ;; gb-*) code=$'🇬🇧' ;; jp-*) code=$'🇯🇵' ;; no-*) code=$'🇳🇴' ;; se-*) code=$'🇸🇪' ;; us-*) code=$'🇺🇸' ;; *tail2d6ce*) code=$'󰠜' ;; *) code=$'🌐' ;; esac echo -n $code } get_vpn_connections() { ( tailscale exit-node list | awk '/.tail2d6ce.ts.net/{print $2, $1}' tailscale exit-node list | awk -F '[ ]{2,}' '/.mullvad.ts.net/{print $2, $1, ($3 "/" $4)}' | sort -u ) | while read -r conn ip location; do printf '%s\t' "$ip" printf '%s\t' "$(get_emoji_flag "$conn")" if grep -q 'mullvad.ts.net' <<<"$conn"; then printf '%s\t' "$location" else printf '%s\t' "$conn" fi printf '\n' done } get_current_connection() { tailscale exit-node list | grep -e selected | grep -v -e Any | awk -F '[ ]{2,}' '/.mullvad.ts.net/{print $2, $1, ($3 "/" $4)} /.tail2d6ce.ts.net/{print $2, $1}' | while read -r conn ip location; do printf '%s' "$(get_emoji_flag "$conn")" printf ' ' if grep -q 'mullvad.ts.net' <<<"$conn"; then printf '%s' "$location" else printf '%s' "$conn" fi done } vpnup() { fuzl_try -- tailscale set --exit-node=${1:?} fuzl_try -- pkill -RTMIN+2 -x waybar } vpndown() { fuzl_try -- tailscale set --exit-node= fuzl_try -- pkill -RTMIN+2 -x waybar } # # actions # vpnm_exitnode_down() { fuzl_try -- vpndown } vpnm_networks() { ip=$(get_vpn_connections | fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} {3}' --accept-nth=1) [ -n "$ip" ] || main fuzl_try -- vpnup "$ip" } vpnm_profile() { profile=$( ts_profile_list | jq -rc '.[]|"\(.nickname)\t\(.tailnet)\t\(if .selected then "*" else "" end)"' | fuzl_menu $FUZL_MENU_FILTER --accept-nth=1 ) [ -n "$profile" ] || main ts_profile_switch "$profile" } vpnm_up() { fuzl_try -- tailscale up } vpnm_down() { fuzl_try -- tailscale down } main