notifirc 5.3KB

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