notify-send-improved 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #!/usr/bin/env bash
  2. # notify-send.sh - drop-in replacement for notify-send with more features
  3. # Copyright (C) 2015 Vyacheslav Levit <dev@vlevit.org>
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. # You should have received a copy of the GNU General Public License
  13. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. # Desktop Notifications Specification
  15. # https://developer.gnome.org/notification-spec/
  16. VERSION=0.1
  17. NOTIFY_ARGS=(--session
  18. --dest org.freedesktop.Notifications
  19. --object-path /org/freedesktop/Notifications)
  20. EXPIRE_TIME=-1
  21. APP_NAME="${0##*/}"
  22. REPLACE_ID=0
  23. URGENCY=1
  24. HINTS=()
  25. help() {
  26. cat <<EOF
  27. Usage:
  28. notify-send.sh [OPTION...] <SUMMARY> [BODY] - create a notification
  29. Help Options:
  30. -?|--help Show help options
  31. Application Options:
  32. -u, --urgency=LEVEL Specifies the urgency level (low, normal, critical).
  33. -t, --expire-time=TIME Specifies the timeout in milliseconds at which to expire the notification.
  34. -a, --app-name=APP_NAME Specifies the app name for the icon
  35. -i, --icon=ICON[,ICON...] Specifies an icon filename or stock icon to display.
  36. -c, --category=TYPE[,TYPE...] Specifies the notification category.
  37. -h, --hint=TYPE:NAME:VALUE Specifies basic extra data to pass. Valid types are int, double, string and byte.
  38. -p, --print-id Print the notification ID to the standard output.
  39. -r, --replace=ID Replace existing notification.
  40. -R, --replace-file=FILE Store and load notification replace ID to/from this file.
  41. -s, --close=ID Close notification.
  42. -v, --version Version of the package.
  43. EOF
  44. }
  45. convert_type() {
  46. case "$1" in
  47. int) echo int32 ;;
  48. double|string|byte) echo "$1" ;;
  49. *) echo error; return 1 ;;
  50. esac
  51. }
  52. make_hint() {
  53. type=$(convert_type "$1")
  54. [[ ! $? = 0 ]] && return 1
  55. name="$2"
  56. [[ "$type" = string ]] && value="\"$3\"" || value="$3"
  57. echo "\"$name\": <$type $value>"
  58. }
  59. concat_hints() {
  60. local result="$1"
  61. shift
  62. for s in "$@"; do
  63. result="$result, $s"
  64. done
  65. echo "{$result}"
  66. }
  67. handle_output() {
  68. if [[ -n "$STORE_ID" ]] ; then
  69. sed 's/(uint32 \([0-9]\+\),)/\1/g' > $STORE_ID
  70. elif [[ -z "$PRINT_ID" ]] ; then
  71. cat > /dev/null
  72. else
  73. sed 's/(uint32 \([0-9]\+\),)/\1/g'
  74. fi
  75. }
  76. notify () {
  77. gdbus call "${NOTIFY_ARGS[@]}" --method org.freedesktop.Notifications.Notify \
  78. "$APP_NAME" "$REPLACE_ID" "$ICON" "$SUMMARY" "$BODY" \
  79. [] "$(concat_hints "${HINTS[@]}")" "int32 $EXPIRE_TIME" | handle_output
  80. }
  81. notify_close () {
  82. gdbus call "${NOTIFY_ARGS[@]}" --method org.freedesktop.Notifications.CloseNotification "$1" >/dev/null
  83. }
  84. process_urgency() {
  85. case "$1" in
  86. low) URGENCY=0 ;;
  87. normal) URGENCY=1 ;;
  88. critical) URGENCY=2 ;;
  89. *) echo "Unknown urgency $URGENCY specified. Known urgency levels: low, normal, critical."
  90. exit 1
  91. ;;
  92. esac
  93. }
  94. process_category() {
  95. IFS=, read -a categories <<< "$1"
  96. for category in "${categories[@]}"; do
  97. hint="$(make_hint string category "$category")"
  98. HINTS=("${HINTS[@]}" "$hint")
  99. done
  100. }
  101. process_hint() {
  102. IFS=: read type name value <<< "$1"
  103. if [[ -z "$name" ]] || [[ -z "$value" ]] ; then
  104. echo "Invalid hint syntax specified. Use TYPE:NAME:VALUE."
  105. exit 1
  106. fi
  107. hint="$(make_hint "$type" "$name" "$value")"
  108. if [[ ! $? = 0 ]] ; then
  109. echo "Invalid hint type \"$type\". Valid types are int, double, string and byte."
  110. exit 1
  111. fi
  112. HINTS=("${HINTS[@]}" "$hint")
  113. }
  114. process_posargs() {
  115. if [[ "$1" = -* ]] && ! [[ "$positional" = yes ]] ; then
  116. echo "Unknown option $1"
  117. exit 1
  118. else
  119. [[ -z "$SUMMARY" ]] && SUMMARY="$1" || BODY="$1"
  120. fi
  121. }
  122. while (( $# > 0 )) ; do
  123. case "$1" in
  124. -\?|--help)
  125. help
  126. exit 0
  127. ;;
  128. -v|--version)
  129. echo "${0##*/} $VERSION"
  130. exit 0
  131. ;;
  132. -u|--urgency|--urgency=*)
  133. [[ "$1" = --urgency=* ]] && urgency="${1#*=}" || { shift; urgency="$1"; }
  134. process_urgency "$urgency"
  135. ;;
  136. -t|--expire-time|--expire-time=*)
  137. [[ "$1" = --expire-time=* ]] && EXPIRE_TIME="${1#*=}" || { shift; EXPIRE_TIME="$1"; }
  138. ;;
  139. -a|--app-name|--app-name=*)
  140. [[ "$1" = --app-name=* ]] && APP_NAME="${1#*=}" || { shift; APP_NAME="$1"; }
  141. ;;
  142. -i|--icon|--icon=*)
  143. [[ "$1" = --icon=* ]] && ICON="${1#*=}" || { shift; ICON="$1"; }
  144. ;;
  145. -c|--category|--category=*)
  146. [[ "$1" = --category=* ]] && category="${1#*=}" || { shift; category="$1"; }
  147. process_category "$category"
  148. ;;
  149. -h|--hint|--hint=*)
  150. [[ "$1" = --hint=* ]] && hint="${1#*=}" || { shift; hint="$1"; }
  151. process_hint "$hint"
  152. ;;
  153. -p|--print-id)
  154. PRINT_ID=yes
  155. ;;
  156. -r|--replace|--replace=*)
  157. [[ "$1" = --replace=* ]] && REPLACE_ID="${1#*=}" || { shift; REPLACE_ID="$1"; }
  158. ;;
  159. -R|--replace-file|--replace-file=*)
  160. [[ "$1" = --replace-file=* ]] && filename="${1#*=}" || { shift; filename="$1"; }
  161. if [[ -s "$filename" ]]; then
  162. REPLACE_ID="$(< $filename)"
  163. fi
  164. STORE_ID="$filename"
  165. ;;
  166. -s|--close|--close=*)
  167. [[ "$1" = --close=* ]] && close_id="${1#*=}" || { shift; close_id="$1"; }
  168. notify_close "$close_id"
  169. exit $?
  170. ;;
  171. --)
  172. positional=yes
  173. ;;
  174. *)
  175. process_posargs "$1"
  176. ;;
  177. esac
  178. shift
  179. done
  180. # urgency is always set
  181. HINTS=("$(make_hint byte urgency "$URGENCY")" "${HINTS[@]}")
  182. if [[ -z "$SUMMARY" ]] ; then
  183. help
  184. exit 1
  185. else
  186. notify
  187. fi