| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 | #!/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
    local stamp
    while IFS= read -r line;
    do
    stamp=$(date -Isec)
        log "$stamp $line"
    done
}
log() {
    #
    # Log message $1 to $logfile
    #
    local stamp
    stamp=$(date -Isec)
    echo "$stamp | $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 "$@"
 |