| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 | #!/bin/bash
usage() {
    #
    # Print usage message and exit with status 2
    #
    local self      # our name
    self=$(basename "$0")
    echo "usage: $self [options] message" >&2
    echo "usage: $self [options] [-l max_lines] [-L max_width] -f message_file|-" >&2
    echo "options: [-h host] [-p port] [-n nick] [-c context] [-u user]" >&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"
    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"
    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 "$@"
 |