notifirc 5.7KB

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