summary refs log tree commit diff
path: root/bin/vpnm
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2026-04-08 13:18:14 +0900
committerRobert Günzler <r@gnzler.io>2026-04-08 13:18:14 +0900
commitb61b58ce2c1371d6f904f108fa7feed3abfbe5c7 (patch)
tree008a5cf54ecbfffb97ddac30e44c8ab224395ab0 /bin/vpnm
parentd4620f3ac119263fee90bd819eadaf84fc8d50b7 (diff)
*
Automated-commit-by: dots
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'bin/vpnm')
-rwxr-xr-xbin/vpnm124
1 files changed, 124 insertions, 0 deletions
diff --git a/bin/vpnm b/bin/vpnm
new file mode 100755
index 0000000..f09b41a
--- /dev/null
+++ b/bin/vpnm
@@ -0,0 +1,124 @@
+#!/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 "down" "󰦝" "Connected to $conn"
+	fuzl_action_add "networks" "󰾰" "Available networks"
+	fuzl_action_add "profile" "󱗼" "Switch profile"
+
+	fuzl_action_menu || return
+}
+
+#
+# helpers
+#
+
+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 '/.tail2d6ce.ts.net/{print $2, $1}'
+		awk -F '[ ]{2,}' '/.mullvad.ts.net/{print $2, $1, ($3 "/" $4)}'
+	) | 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_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=$(tailscale switch --list 2>/dev/null |
+		tail -n+2 |
+		awk '{printf "%s\t%s\n", $3, $2}' |
+		fuzl_menu $FUZL_MENU_FILTER --accept-nth=1)
+
+	[ -n "$profile" ] || main
+	fuzl_try -- tailscale switch "$profile"
+	sleep 2
+	fuzl_try -- pkill -SIGRTMIN+2 waybar
+}
+
+main