#!/usr/bin/env bash ### # logger ### declare -A levels=( [debug]=1 [info]=2 [warn]=3 [error]=4 ) log() { if [[ -v LEVEL ]] && (( levels[$level] <= levels[$LEVEL] )); then printf "%s: $1\n" "${LEVEL^^}" "${@:2}" fi } debug() { LEVEL=debug log "$@" } info() { LEVEL=info log "$@" } warn() { LEVEL=warn log "$@" } error() { LEVEL=error log "$@" } ### # argument parser for parsing arguments ## declare -A opts while (( $# )); do case $1 in --*=*) key=${1#--} key=${key%%=*} opts[$key]=${1#--*=} ;; --no-*) key=${1#--no-} opts[$key]=no ;; --) shift break ;; --*) key=${1#--} opts[$key]=yes ;; *) break esac shift done ### # default config ## level=${opts[log-level]:-info} server=${opts[server]:-irc.libera.chat} tls=${opts[tls]:-no} if [[ $tls = yes ]]; then if ! hash socat 2>/dev/null; then printf 'please install socat to use tls with rowbot.\n' >&2 exit 1 fi port=${opts[port]:-6697} else port=${opts[port]:-6667} fi ### # net code ### if [[ $tls = yes ]]; then coproc sock { socat OPENSSL:"$server":"$port" -; } in_sock=${sock[0]} out_sock=${sock[1]} else exec {sock}<>/dev/tcp/"$server"/"$port" in_sock=$sock out_sock=$sock fi send() { local fmt printf -v fmt "$1" "${@:2}" printf '%s\r\n' "$fmt" >&"$out_sock" } recv() { declare -n sock_line=$1 IFS= read -r "$1" <&"$in_sock" sock_line=${sock_line%$'\r'} } ### # driver ### while recv line; do params=( ) has_words=no orig_line=$line if [[ ${line:0:1} = : ]]; then src=${line%% *} src=${src#:} line=${line#:"$src"} line=${line# } from=${src%@*} ident=${from#*!} from=${from%!*} host=${src#*@} fi cmd=${line%% *} line=${line#"$cmd"} line=${line# } while [[ $line ]]; do if [[ ${line:0:1} = : ]]; then params+=("${line:1}") line="" has_words=yes else param=${line%% *} params+=("$param") line=${line#"$param"} line=${line# } fi done if [[ $has_words = yes ]]; then read -ra words <<< "${params[@]:(-1)}" else words=( ) fi if hash "on_${cmd^^}" 2>/dev/null; then "on_${cmd^^}" else warn "unhandled line: %s" "$orig_line" fi done