notifirc 5.5KB

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