notifirc 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #!/bin/bash
  2. usage() {
  3. #
  4. # Print usage message and exit with status 2
  5. #
  6. local self # our name
  7. self=$(basename "$0")
  8. echo "usage: $self [options] message" >&2
  9. echo "usage: $self [options] [-l max_lines] [-L max_width] -f message_file|-" >&2
  10. echo "options: [-h host] [-p port] [-n nick] [-c context] [-u user]" >&2
  11. exit 2
  12. }
  13. mkcommands() {
  14. #
  15. # Print all IRC commands
  16. #
  17. local user="$1" # target user
  18. local nick="$2" # our nickname
  19. local message
  20. echo "NICK $nick"
  21. echo "USER $nick 8 * :notifirc bot"
  22. while read -r message;
  23. do
  24. echo "PRIVMSG $user :$message"
  25. done
  26. echo "QUIT"
  27. }
  28. mknick() {
  29. #
  30. # Compose bot's nickname
  31. #
  32. case "$context" in
  33. "") echo "$nick" ;;
  34. *) echo "$nick|$context" ;;
  35. esac
  36. }
  37. die() {
  38. #
  39. # End script with fatal error $1
  40. #
  41. local msg="fatal: $1" # message
  42. log "$msg"
  43. log "-----END *-----"
  44. echo "$msg" >&2
  45. exit 3
  46. }
  47. warn() {
  48. #
  49. # Issue message $1 as warning to logfile and stderr
  50. #
  51. local msg="warning: $1" # full message
  52. echo "$msg" >&2
  53. log "$msg"
  54. }
  55. log_pipe() {
  56. #
  57. # Log contents of standard input
  58. #
  59. local line # each line of input
  60. while IFS= read -r line;
  61. do
  62. log "$line"
  63. done
  64. }
  65. log() {
  66. #
  67. # Log message $1 to $logfile
  68. #
  69. echo "$1" >>"$logfile"
  70. }
  71. load_defaults() {
  72. #
  73. # Load defaults from RC file $1
  74. #
  75. local rcfile=$1 # RC file to load
  76. test -e "$rcfile" || return 0
  77. test -f "$rcfile" || {
  78. warn "defaults file is not a file: $rcfile"
  79. return 3
  80. }
  81. test -r "$rcfile" || {
  82. warn "defaults file not readable: $rcfile"
  83. return 3
  84. }
  85. bash -n "$rcfile" || {
  86. warn "syntax error in defaults file: $rcfile"
  87. return 3
  88. }
  89. #shellcheck disable=SC1090
  90. . "$rcfile" || {
  91. warn "error in defaults file: $rcfile"
  92. return 3
  93. }
  94. }
  95. trim() {
  96. #
  97. # Trim to maximum $1 lines and $2 characters per line
  98. #
  99. local limit_l=$1 # max. lines per message
  100. local limit_c=$2 # max. chars per line
  101. local lines_read=0 # how many lines we read
  102. local suff="" # suffix (ellipsis)
  103. while true;
  104. do
  105. test $lines_read -ge "$limit_l" && break
  106. IFS= read -r line || break
  107. (( lines_read++ ))
  108. test ${#line} -gt "$limit_c" && suff=…
  109. line=${line:0:$limit_c}$suff
  110. echo "$line"
  111. done
  112. }
  113. choose_logfile() {
  114. #
  115. # Decide which log file to use (/var, /home or /tmp)
  116. #
  117. local path # path to log file
  118. {
  119. echo /var/log/notifirc.log
  120. echo "$HOME/.notifirc.log"
  121. echo /tmp/notifirc.log
  122. } \
  123. | while read -r path;
  124. do
  125. if test -w "$(dirname "$path")" \
  126. || test -w "$path";
  127. then
  128. echo "$path"
  129. break
  130. fi
  131. done
  132. }
  133. main() {
  134. local context # context tag for nickname
  135. local nick # bot's nickname
  136. local host # IRC server hostname
  137. local port # ... ^^ ... port
  138. local user # user to notify
  139. local message # message to send
  140. local logfile # logfile to use
  141. local msgfile # file to read message from
  142. local f_maxlines=3 # maximum number of lines per notification
  143. local f_maxwidth=80 # maximum characters per notification line
  144. logfile=$(choose_logfile)
  145. test -n "$logfile" || {
  146. echo "could not find writable logfile location" >&2
  147. echo "logging will be off!" >&2
  148. logfile=/dev/null
  149. }
  150. load_defaults /etc/notifirc.rc
  151. load_defaults "$HOME/.notifirc.rc"
  152. while true; do case $1 in
  153. -c) context=$2; shift 2 || usage ;;
  154. -f) msgfile=$2; shift 2 || usage ;;
  155. -h) host=$2; shift 2 || usage ;;
  156. -l) f_maxlines=$2; shift 2 || usage ;;
  157. -L) f_maxwidth=$2; shift 2 || usage ;;
  158. -n) nick=$2; shift 2 || usage ;;
  159. -p) port=$2; shift 2 || usage ;;
  160. -u) user=$2; shift 2 || usage ;;
  161. --) shift; break ;;
  162. -*) usage ;;
  163. *) break ;;
  164. esac done
  165. message="$*"
  166. test -n "$host" || usage
  167. test -n "$port" || usage
  168. test -n "$user" || usage
  169. test -n "$nick" || nick="notifirc"
  170. test -z "$message$msgfile" && usage
  171. log "-----BEGIN sending notification-----"
  172. log "host='$host'"
  173. log "port='$port'"
  174. log "nick='$nick'"
  175. log "context='$context'"
  176. log "user='$user'"
  177. log "msgfile='$msgfile'"
  178. log "f_maxlines='$f_maxlines'"
  179. log "f_maxwidth='$f_maxwidth'"
  180. log "message='$message'"
  181. {
  182. test -n "$message" && printf '%s\n' "$message"
  183. test -n "$msgfile" && grep . "$msgfile" | trim "$f_maxlines" "$f_maxwidth"
  184. } \
  185. | mkcommands "$user" "$(mknick)" \
  186. | nc "$host" "$port" 2>&1 \
  187. | log_pipe
  188. log "-----END sending notification-----"
  189. }
  190. main "$@"