#!/bin/bash # # Extra defaults file # # Options here override /etc and ~/, but are overrided # by command line options. # NOTIFIRC_RC=${NOTIFIRC_RC:-} usage() { # # Print usage message and exit with status 2 # local self # our name self=$(basename "$0") { echo "usage: $self [options] message" echo "usage: $self [options] [-l max_lines] [-L max_width] -f message_file|-" echo "options: [-h host] [-p port] [-n nick] [-c context] [-u user]" echo "" echo "user can be user name but also channel name, in that case notifirc" echo "will JOIN the channel first." echo "" echo "Defaults for options can be defined as POSIX shell assignments under" echo "/etc/notifirc.rc, ~/.notifirc.rc or file specified by NOTIFIRC_RC" echo "envvar. (E.g. 'nick=jdoe', 'max_width=40')." echo "" echo "All is logged under /var/log/notifirc.log, ~/.notifirc.log, or" echo "/tmp/notifirc.log, whichever $self can write to." } >&2 exit 2 } mkcommands() { # # Print all IRC commands # local user="$1" # target user local nick="$2" # our nickname local message echo "NICK $nick" echo "USER $nick 8 * :notifirc bot" if test "${user:0:1}" == '#'; then echo "JOIN $user" fi while read -r message; do echo "PRIVMSG $user :$message" done echo "QUIT" } mknick() { # # Compose bot's nickname # case "$context" in "") echo "$nick" ;; *) echo "$nick|$context" ;; esac } die() { # # End script with fatal error $1 # local msg="fatal: $1" # message log "$msg" log "-----END *-----" echo "$msg" >&2 exit 3 } warn() { # # Issue message $1 as warning to logfile and stderr # local msg="warning: $1" # full message echo "$msg" >&2 log "$msg" } log_pipe() { # # Log contents of standard input # local line # each line of input while IFS= read -r line; do log "$line" done } log() { # # Log message $1 to $logfile # echo "$1" >>"$logfile" } load_defaults() { # # Load defaults from RC file $1 # local rcfile=$1 # RC file to load test -e "$rcfile" || return 0 test -f "$rcfile" || { warn "defaults file is not a file: $rcfile" return 3 } test -r "$rcfile" || { warn "defaults file not readable: $rcfile" return 3 } bash -n "$rcfile" || { warn "syntax error in defaults file: $rcfile" return 3 } #shellcheck disable=SC1090 . "$rcfile" || { warn "error in defaults file: $rcfile" return 3 } } trim() { # # Trim to maximum $1 lines and $2 characters per line # local limit_l=$1 # max. lines per message local limit_c=$2 # max. chars per line local lines_read=0 # how many lines we read local suff="" # suffix (ellipsis) while true; do test $lines_read -ge "$limit_l" && break IFS= read -r line || break (( lines_read++ )) test ${#line} -gt "$limit_c" && suff=… line=${line:0:$limit_c}$suff echo "$line" done } choose_logfile() { # # Decide which log file to use (/var, /home or /tmp) # local path # path to log file { echo /var/log/notifirc.log echo "$HOME/.notifirc.log" echo /tmp/notifirc.log } \ | while read -r path; do if test -w "$(dirname "$path")" \ || test -w "$path"; then echo "$path" break fi done } main() { local context # context tag for nickname local nick # bot's nickname local host # IRC server hostname local port # ... ^^ ... port local user # user to notify local message # message to send local logfile # logfile to use local msgfile # file to read message from local f_maxlines=3 # maximum number of lines per notification local f_maxwidth=80 # maximum characters per notification line logfile=$(choose_logfile) test -n "$logfile" || { echo "could not find writable logfile location" >&2 echo "logging will be off!" >&2 logfile=/dev/null } load_defaults /etc/notifirc.rc load_defaults "$HOME/.notifirc.rc" load_defaults "$NOTIFIRC_RC" while true; do case $1 in -c) context=$2; shift 2 || usage ;; -f) msgfile=$2; shift 2 || usage ;; -h) host=$2; shift 2 || usage ;; -l) f_maxlines=$2; shift 2 || usage ;; -L) f_maxwidth=$2; shift 2 || usage ;; -n) nick=$2; shift 2 || usage ;; -p) port=$2; shift 2 || usage ;; -u) user=$2; shift 2 || usage ;; --) shift; break ;; -*) usage ;; *) break ;; esac done message="$*" test -n "$host" || usage test -n "$port" || usage test -n "$user" || usage test -n "$nick" || nick="notifirc" test -z "$message$msgfile" && usage log "-----BEGIN sending notification-----" log "host='$host'" log "port='$port'" log "nick='$nick'" log "context='$context'" log "user='$user'" log "msgfile='$msgfile'" log "f_maxlines='$f_maxlines'" log "f_maxwidth='$f_maxwidth'" log "message='$message'" { test -n "$message" && printf '%s\n' "$message" test -n "$msgfile" && grep . "$msgfile" | trim "$f_maxlines" "$f_maxwidth" } \ | mkcommands "$user" "$(mknick)" \ | nc "$host" "$port" 2>&1 \ | log_pipe log "-----END sending notification-----" } main "$@"