config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

commit ca46ea85643711b7021d33c7174b70df22176985
parent 63382dc40be538d110852c8dd77439ac490258f2
Author: dwrz <dwrz@dwrz.net>
Date:   Wed,  7 Dec 2022 20:50:25 +0000

Add scripts

Diffstat:
Ascripts/backup | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/backup-mobile | 39+++++++++++++++++++++++++++++++++++++++
Ascripts/borg-backup | 34++++++++++++++++++++++++++++++++++
Ascripts/cameras | 39+++++++++++++++++++++++++++++++++++++++
Ascripts/dict | 9+++++++++
Ascripts/err | 9+++++++++
Ascripts/generate-pin | 7+++++++
Ascripts/gps | 15+++++++++++++++
Ascripts/image | 29+++++++++++++++++++++++++++++
Ascripts/ip | 9+++++++++
Ascripts/map-bg | 32++++++++++++++++++++++++++++++++
Ascripts/mfa | 15+++++++++++++++
Ascripts/modname | 5+++++
Ascripts/monitor-hotplug | 26++++++++++++++++++++++++++
Ascripts/monitor-light | 4++++
Ascripts/pkg_update | 8++++++++
Ascripts/reveille | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/screenshot | 5+++++
Ascripts/search | 10++++++++++
Ascripts/snapshot | 13+++++++++++++
Ascripts/sync-email | 30++++++++++++++++++++++++++++++
Ascripts/uncommitted | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/unixify | 3+++
Ascripts/video | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/volume | 8++++++++
Ascripts/webcam | 8++++++++
Ascripts/wisdom | 3+++
27 files changed, 640 insertions(+), 0 deletions(-)

diff --git a/scripts/backup b/scripts/backup @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +readonly TARGETS=( + "$HOME/.config/gnupg/" + "$HOME/.config/pass/" +) + +readonly HOSTS=( + "mobile" + "555.dwrz.net" + "516.dwrz.net" +) + +backup-b2() { + local bucket id key destination + id="$(pass backblaze/duplicity/keyID)" + key="$(pass backblaze/duplicity/applicationKey)" + + env "B2_APPLICATION_KEY_ID"="${id}" "B2_APPLICATION_KEY"="${key}" \ + b2 sync "$@" + if [[ "$?" -ne 0 ]]; then + err "$0: failed b2 sync ${t} to ${destination}" + fi +} + +backup-hosts() { + local target="$1" + local t="$2" + + for h in "${HOSTS[@]}"; do + if [[ "${h}" == "mobile" ]]; then + rsync --archive --delete --progress --verbose "${target}" \ + "$USER"@"${h}":/data/data/com.termux/files/home/"${t}" + else + rsync --archive --delete --progress --verbose "${target}" \ + "$USER"@"${h}":/home/"$USER"/"${t}" + fi + if [[ "$?" -ne 0 ]]; then + err "$0: failed rsync ${t} to ${h}" + fi + done +} + +main() { + # Backup to B2. + local bucket + bucket="$(pass backblaze/bucket)" + + # Config + backup-b2 --delete --excludeDirRegex="Signal" \ + "${HOME}/.config/" "b2://${bucket}/${HOSTNAME}/config" + + # Org + backup-b2 --delete --excludeDirRegex="email" \ + "${HOME}/org/" "b2://${bucket}/${HOSTNAME}/org" + + # Projects + backup-b2 --delete "${HOME}/projects/" "b2://${bucket}/${HOSTNAME}/projects" + + # Backup targets. + for target in "${TARGETS[@]}"; do + local t + t="$(basename "${target}")" + + if [[ ! -e "${target}" ]]; then + err "$0: failed to backup ${t}: not found" + continue + fi + + backup-hosts "${target}" "${t}" + done +} + +main "$@" diff --git a/scripts/backup-mobile b/scripts/backup-mobile @@ -0,0 +1,39 @@ +#!/bin/sh + +readonly DCIM="${HOME}/storage/dcim/" +readonly PICTURES="${HOME}/storage/pictures/" +readonly DOWNLOADS="${HOME}/storage/downloads/" + +if [ "$(uname -o)" != "Android" ]; then + err "$0: not on mobile device" + exit 1 +fi + +host="$1" + +if [ -d "${DCIM}" ]; then + printf "backing up %s\n" "${DCIM}" + + rsync -av --mkpath "${DCIM}" "dwrz@${host}:/home/dwrz/mobile/dcim/" + if [ "$?" -eq 0 ]; then + rm -rf "${DCIM}" + fi +fi + +if [ -d "${PICTURES}" ]; then + printf "backing up %s\n" "${PICTURES}" + + rsync -av --mkpath "${PICTURES}" "dwrz@${host}:/home/dwrz/mobile/pictures/" + if [ "$?" -eq 0 ]; then + rm -rf "${PICTURES}" + fi +fi + +if [ -d "${DOWNLOADS}" ]; then + printf "backing up %s\n" "${DOWNLOADS}" + + rsync -av --mkpath "${DOWNLOADS}" "dwrz@${host}:/home/dwrz/mobile/downloads/" + if [ "$?" -eq 0 ]; then + rm -rf "${DOWNLOADS}" + fi +fi diff --git a/scripts/borg-backup b/scripts/borg-backup @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +case "$1" in + "close") + doas umount /mnt/archive + doas cryptsetup luksClose archive + ;; + + "create") + name="dwrz@main.dwrz.net-$(TZ=UTC date '+%FT%T%z')" + borg create -v --progress --exclude /home/dwrz/.cache/ \ + /mnt/archive/dwrz-backup/::"${name}" /home/dwrz/ + ;; + + "list") borg list /mnt/archive/dwrz-backup/ ;; + + "mount") borg mount /mnt/archive/dwrz-backup/ \ + /mnt/archive/mnt + ;; + + "open") + doas cryptsetup luksOpen /dev/sda1 archive + doas mount /dev/mapper/archive /mnt/archive/ + ;; + + "prune") borg prune --keep-last 1 -m 12 --save-space \ + /mnt/archive/dwrz-backup/ + borg compact /mnt/archive/dwrz-backup/ + ;; + + "unmount") borg umount /mnt/archive/mnt ;; + + *) err "$0: unrecognized command: $1" ;; +esac diff --git a/scripts/cameras b/scripts/cameras @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +cameras=( + "https://$(pass kitchen.516.dwrz.net/ip):\ +$(pass kitchen.516.dwrz.net/motion/port)/0" + "https://$(pass living.516.dwrz.net/ip):\ +$(pass living.516.dwrz.net/motion/port)/0" + "https://$(pass bedroom.516.dwrz.net/ip):\ +$(pass bedroom.516.dwrz.net/motion/port)/0" +) + +auth=( + "$(pass kitchen.516.dwrz.net/motion/user):\ +$(pass kitchen.516.dwrz.net/motion/pw)" + "$(pass living.516.dwrz.net/motion/user):\ +$(pass living.516.dwrz.net/motion/pw)" + "$(pass bedroom.516.dwrz.net/motion/user):\ +$(pass bedroom.516.dwrz.net/motion/pw)" +) + +url="detection/status" + +main() { + case "$1" in + "capture"|"c") url="detection/snapshot" ;; + "pause"|"p") url="detection/pause" ;; + "quit"|"q") url="action/quit" ;; + "restart"|"r") url="action/restart" ;; + "start"|"s") url="detection/start" ;; + "status"|"") url="detection/status" ;; + *) err "unrecognized command: $1"; exit 1 + esac + + for i in "${!cameras[@]}"; do + curl --digest --insecure --user "${auth[i]}" "${cameras[i]}/${url}" + done +} + +main "$@" diff --git a/scripts/dict b/scripts/dict @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ -z "$1" ]; then + sdcv --utf8-output --color +else + sdcv --non-interactive --utf8-output --color "$@" 2>&1 | \ + fold --width=80 --spaces | \ + less -FRX +fi diff --git a/scripts/err b/scripts/err @@ -0,0 +1,9 @@ +#!/bin/sh + +time="$(date -u +'%Y-%m-%dT%H:%M:%S%:z')" + +echo "[${time}]: $*" >&2 + +if [ -x "$(command -v notify-send)" ]; then + notify-send --urgency=critical "ERROR" "$*" +fi diff --git a/scripts/generate-pin b/scripts/generate-pin @@ -0,0 +1,7 @@ +#!/bin/sh + +digits="$1" +shuf --random-source=/dev/urandom -i 0-9 -r -n "${digits}" | \ + paste -sd '' | \ + tee /dev/tty | \ + xclip diff --git a/scripts/gps b/scripts/gps @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +modem="$(mmcli --list-modems | basename "$(awk -F ' ' '{print $1}')")" + +mmcli -m "${modem}" --enable + +case "$1" in + "disable") mmcli -m "${modem}" --location-disable-gps-raw ;; + + "enable") mmcli -m "${modem}" --location-enable-gps-raw ;; + + "get") mmcli --location-get -m "${modem}" ;; + + *) err "unrecognized command: $1" ;; +esac diff --git a/scripts/image b/scripts/image @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +compress-jpg() { + local resolution="$1" + local quality="$2" + + for p in *.jpg; do + name="${p%.*}" + + convert -strip \ + -resize "${resolution}" \ + -quality "${quality}" \ + "${p}" \ + "./${name}-${resolution}.jpg" + done +} + +compress-tiff() { + find . -iname "*.tif" -exec mogrify -verbose -compress zip {} + +} + +main() { + local cmd="$1"; shift + case "${cmd}" in + "compress") compress-jpg "$@" ;; + esac +} + +main "$@" diff --git a/scripts/ip b/scripts/ip @@ -0,0 +1,9 @@ +#!/bin/sh + +echo "GOOGLE" +dig -4 TXT +short o-o.myaddr.l.google.com @ns1.google.com +dig -6 TXT +short o-o.myaddr.l.google.com @ns1.google.com + +echo "OPENDNS" +dig A +short myip.opendns.com @resolver1.opendns.com +dig AAAA +short myip.opendns.com @resolver1.opendns.com diff --git a/scripts/map-bg b/scripts/map-bg @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +dimensions="$(xrandr --current | grep '*' | uniq | awk '{print $1}')" +height="$(echo $dimensions | cut -d 'x' -f2)" +width="$(echo $dimensions | cut -d 'x' -f1)" + +coordinates="$1" +zoom="$2" + +# If no coordinates were provided, take them from the password store. +if [[ -z "$1" ]]; then + dir="${PASSWORD_STORE_DIR}/coordinates" + locations="$(find "${dir}" -mindepth 1 -exec basename -- {} .gpg \;)" + location="$(echo "${locations}" | shuf -n 1)" + coordinates="$(pass coordinates/"${location}")" +fi + +# If no zoom was provided, use a number between 8 and 18, inclusive. +if [[ -z "$2" ]]; then + zoom="$(shuf -i 8-18 -n 1)" +fi + +# Create the map and store it in the maps directory. +# If we fail, use an existing map. +create-static-map \ + --width "${width}" --height "${height}" \ + -o "${HOME}/.cache/map-${coordinates}-${zoom}.png" \ + -c "${coordinates}" -z "${zoom}" && \ + feh --bg-fill "${HOME}/.cache/map-${coordinates}-${zoom}.png" +if [[ "$?" -ne 0 ]]; then + feh --bg-fill --randomize "/home/dwrz/archive/images/maps/"* +fi diff --git a/scripts/mfa b/scripts/mfa @@ -0,0 +1,15 @@ +#!/bin/sh + +service="$1" +if [ -z "${service}" ]; then + pass mfa + exit 0 +fi + +code="$(pass mfa/"${service}")" +if [ -z "${code}" ]; then + err "unrecognized service: ${service}" + exit 1 +fi + +oathtool -b --totp "${code}" | tee /dev/tty | xclip diff --git a/scripts/modname b/scripts/modname @@ -0,0 +1,5 @@ +#!/bin/sh + +for f in *.jpg; do + mv -n "$f" "$(date -r "$f" +"%Y%m%dT%H%M%S").jpg" +done diff --git a/scripts/monitor-hotplug b/scripts/monitor-hotplug @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +export DISPLAY=:0.0 + +connect() { + xrandr --output eDP-1 --off --output DP-3 --auto && \ + notify-send "Connected external monitor." +} + +disconnect() { + xrandr --output eDP-1 --auto --output DP-3 --off && \ + notify-send "Disconnected external monitor." +} + +main() { + if xrandr | grep -q "DP-3 connected"; then + connect + else + disconnect + fi + + feh --bg-fill --randomize "/home/dwrz/archive/images/maps/*" + xmodmap "${HOME}/.config/X11/xmodmap" +} + +main "$@" diff --git a/scripts/monitor-light b/scripts/monitor-light @@ -0,0 +1,4 @@ +#!/bin/sh + +doas ddcutil getvcp 10 | grep "current value" +doas ddcutil setvcp 10 "$1" > /dev/null 2>&1 diff --git a/scripts/pkg_update b/scripts/pkg_update @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +if [ -x "$(command -v pacman)" ]; then + doas pacman -Syu + doas pacman -Qtdq | pacman -Rns - + doas paccache -r + doas paccache -ruk0 +fi diff --git a/scripts/reveille b/scripts/reveille @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +readonly DAY_SECONDS=86400 + +# TODO: replace this constant with an API request. +# https://www.ssa.gov/cgi-bin/longevity.cgi +readonly ESTIMATED_REMAINING_YEARS=50 +readonly ESTIMATED_REMAINING_DAYS="$(( "${ESTIMATED_REMAINING_YEARS}" * 365))" + +age() { + local start, end + + start="$(date -d "$1" +%s)" + end="$(date -d "$2" +%s)" + + echo $((("${end}" - "${start}") / "${DAY_SECONDS}")) +} + +birthday() { + local year + local month + local day + + year="$(pass dwrz/birth-year)" + month="$(pass dwrz/birth-month)" + day="$(pass dwrz/birth-day)" + + echo "${year}-${month}-${day}" +} + +draft_message() { + email="/tmp/reveille-$(date +%s)" + current_age_days="$(age "$(birthday)" "$(date '+%Y-%m-%d')" )" + + cat > "$email" << EOF + +You have been on Earth, in this form, for $(numfmt --grouping \ +"${current_age_days}") days. + +You have $(numfmt --grouping ${ESTIMATED_REMAINING_DAYS}) days left. \ +You might have more. +Or today could be your last day. + +Make the most of the time you have. + +Remember: +- If you have the essentials, you have the foundations for happiness. +- You are surrounded by people largely like youself: imperfect and + evanescent. Treasure them; they, like you, may not be around for much + longer. +- People have invested in you -- with time, energy, money, lessons, and + perspectives. Living things have given their life to sustain you. You + owe the world the best version of yourself. +- The ego does not always act in its best interests. +- Beware the sirens' song. +- No plan ever survives initial contact with reality. +- Fatigue does not equal fitness. +- Slow is smooth, and smooth is fast. + +QOTD: + +$("${HOME}/.local/bin/wisdom") + +EOF + + echo "${email}" +} + +main() { + mail -r "$(pass dwrz/email)" \ + -s "$(date '+%Y-%m-%d %j/365 %W/52 %u/7')" \ + "$(pass dwrz/email)" < "$(draft_message)" +} + +main "$@" diff --git a/scripts/screenshot b/scripts/screenshot @@ -0,0 +1,5 @@ +#!/bin/sh + +sleep 2; + +maim -s > screenshot-"$(date '+%Y-%m-%dT%H:%M:%S%z')".jpg diff --git a/scripts/search b/scripts/search @@ -0,0 +1,10 @@ +#!/bin/sh + +engine="$1"; shift; +case "$engine" in + "ecosia"|"e") firefox "https://www.ecosia.org/search?q=$*" ;; + "google"|"g") firefox "https://www.google.com/search?q=$*" ;; + "maps"|"m") firefox "https://maps.google.com/maps?q=$*" ;; + "wikipedia"|"w") firefox "https://en.wikipedia.org/wiki/$*" ;; + *) err "unrecognized engine: %{engine}" ;; +esac diff --git a/scripts/snapshot b/scripts/snapshot @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +device="/dev/video0" +if [[ $1 != "" ]]; then + device="${/dev/video$1}" +fi + +date="$(date -u +'%Y-%m-%dT%H:%M:%S%:z')" + +ffmpeg -f video4linux2 \ + -i "$device" \ + -vframes 1 \ + "snapshot-${date}.jpg" diff --git a/scripts/sync-email b/scripts/sync-email @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +err() { + echo "[$(date -u +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +main() { + # Sync email. + if ! mbsync -c "${XDG_CONFIG_HOME}/isync/mbsyncrc" -qqa ; then + notify-send --urgency=low "sync-email" "failed to sync email" + fi + + # Delete emails tagged as deleted or spam. + notmuch search --output=files --format=text0 tag:deleted tag:spam | \ + xargs -r0 rm + + # Import new email into notmuch. + notmuch new + + # Tag emails. + tag-email + + # Notify unread count. + unread="$(notmuch count tag:unread)" + if [[ "${unread}" -gt 0 ]]; then + notify-send "Email" "${unread} unread email(s)." + fi +} + +main "$@" diff --git a/scripts/uncommitted b/scripts/uncommitted @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +# How many minutes to wait before displaying a warning. +readonly WARNING_MINUTES=15 + +readonly repos=( + "$HOME/.config/" + "$HOME/journal/2022/" + "$HOME/org/" + "$HOME/projects/src/" +) + +check_repo() { + local d="$1" + + # Change into the directory. + cd "${d}" || exit 1 + + # Check for uncommitted changes. + git update-index --refresh > /dev/null 2>&1 + if git diff-index --quiet HEAD -- ; then + # OK; nothing to commit. + return + fi + + # If changes exist, check their age relative to the last commit. + last_commit_time=$(git log -1 --date=unix --format=%cd) + current_time=$(date +%s) + elapsed_sec=$(("${current_time}" - "${last_commit_time}")) + elapsed_min=$(("${elapsed_sec}" / 60)) + + # Ignore changes that aren't old enough to warrant a warning. + if ! [[ "${elapsed_min}" -ge "${WARNING_MINUTES}" ]]; then + return + fi + + notify-send --urgency=low "$d" \ + "${elapsed_min} minutes since the last commit." +} + +main() { + for d in "${repos[@]}"; do + if ! [[ -d "$d" ]]; then + err "$0: ${d} is not a directory" + continue + fi + if ! [[ -d "$d/.git/" ]]; then + err "$0: %{d} is not a git repo; ignoring" + continue + fi + + check_repo "${d}" + + done +} + +main "$@" diff --git a/scripts/unixify b/scripts/unixify @@ -0,0 +1,3 @@ +#!/bin/sh + +find . -type f -print0 | xargs -0 -n 1 -P 4 dos2unix diff --git a/scripts/video b/scripts/video @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +compress() { + local filetype crf + filetype="$(printf "*.%s" "$1")" + # "The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, + # and 51 is worst quality possible. A lower value generally leads to higher + # quality, and a subjectively sane range is 17–28. Consider 17 or 18 to be + # visually lossless or nearly so; it should look the same or nearly the same + # as the input but it isn't technically lossless." + crf="$2" + if [[ -z "$crf" ]]; then + crf=32 # Use a default CRF of 32. + fi + + mkdir compressed + + for v in ${filetype}; do + ffmpeg -i "file:${v}" -vcodec libx264 -crf "${crf}" "./compressed/${v}"; + done + + notify-send "video: compress: done" +} + +compress_x265() { + local filetype crf + filetype="$(printf "*.%s" "$1")" + # "The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, + # and 51 is worst quality possible. A lower value generally leads to higher + # quality, and a subjectively sane range is 17–28. Consider 17 or 18 to be + # visually lossless or nearly so; it should look the same or nearly the same + # as the input but it isn't technically lossless." + crf="$2" + if [[ -z "$crf" ]]; then + crf=23 # Use a default CRF of 23. + fi + + mkdir compressed + + for v in ${filetype}; do + ffmpeg -i "file:${v}" \ + -c:v libx265 \ + -vtag hvc1 \ + -crf "${crf}" \ + -c:a copy \ + "./compressed/${v}"; + done + + notify-send "video: compress-x265: done" +} + +trim() { + input="$1" + output="${input%.*}-trim.${input#*.}" + start="$2" # 00:00:00 + end="$3" # 00:00:00 + + ffmpeg -i "${input}" -ss "${start}" -to "${end}" "${output}" + + notify-send "video: trim: done" +} + +main() { + local cmd="$1"; shift + + case "${cmd}" in + "compress") compress "$@" ;; + "compress-x265") compress_x265 "$@" ;; + "trim") trim "$@" ;; + *) err "unrecognized command: ${cmd}" ;; + esac +} + +main "$@" diff --git a/scripts/volume b/scripts/volume @@ -0,0 +1,8 @@ +#!/bin/sh + +if [ -n "$1" ]; then + pactl set-sink-volume @DEFAULT_SINK@ "$1%" +fi + +pactl get-sink-volume @DEFAULT_SINK@ +paplay /usr/share/sounds/woodenbeaver/audio-volume-change.ogg diff --git a/scripts/webcam b/scripts/webcam @@ -0,0 +1,8 @@ +#!/bin/sh + +n="$1" +if [ -z "${n}" ]; then + n="0" +fi + +mpv av://v4l2:/dev/video"${n}" diff --git a/scripts/wisdom b/scripts/wisdom @@ -0,0 +1,3 @@ +#!/bin/sh + +fortune -a "$HOME"/archive/library/wisdom/*.fortune