From e827b5bbebcd31c5025f9f85845bc9747341e893 Mon Sep 17 00:00:00 2001 From: Fabian 'xx4h' Sylvester Date: Thu, 30 May 2024 16:26:27 +0200 Subject: [PATCH] New segment github_notifications * add new util function "is_tmp_valid" to make handling of temp files based on last update reusable * add new segment * add jq requirement to README.md fixes #431 --- README.md | 1 + lib/util.sh | 30 ++++++ segments/github_notifications.sh | 164 +++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 segments/github_notifications.sh diff --git a/README.md b/README.md index fe7af034..6512f114 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ Some segments have their own requirements. If you enable them in your theme, mak * `tmux_mem_cpu_load.sh`: [tmux-mem-cpu-load](https://github.com/thewtex/tmux-mem-cpu-load) * `rainbarf.sh`: [rainbarf](https://github.com/creaktive/rainbarf) * `weather.sh`: GNU `grep` with Perl regular expression enabled (FreeBSD specific), `jq` for yrno weather API. +* `github_notifications.sh`: `jq` for GitHub API. ## FreeBSD specific requirements Preinstalled `grep` in FreeBSD doesn't support Perl regular expressions. Solution is rather simple -- you need to use `textproc/gnugrep` port instead. You also need to make sure, that it has support for PCRE and is compiled with `--enable-perl-regexp` flag. diff --git a/lib/util.sh b/lib/util.sh index a9ca2c69..39da893c 100644 --- a/lib/util.sh +++ b/lib/util.sh @@ -12,3 +12,33 @@ is_flag_enabled() { ;; esac } + +is_tmp_valid() { + local tmp_file="$1" + local tmp_validity="$2" + + if [ -f "$tmp_file" ]; then + if shell_is_osx || shell_is_bsd; then + stat >/dev/null 2>&1 && is_gnu_stat=false || is_gnu_stat=true + if [ "$is_gnu_stat" == "true" ]; then + last_update=$(stat -c "%Y" "${tmp_file}") + else + last_update=$(stat -f "%m" "${tmp_file}") + fi + elif shell_is_linux || [ -z "$is_gnu_stat" ]; then + last_update=$(stat -c "%Y" "${tmp_file}") + fi + + time_now=$(date +%s) + valid=$(echo "(${time_now}-${last_update}) < ${tmp_validity}" | bc) + + if [ "$valid" -eq 1 ]; then + return 0 + else + return 1 + fi + else + return 1 + fi + +} diff --git a/segments/github_notifications.sh b/segments/github_notifications.sh new file mode 100644 index 00000000..9475cdf9 --- /dev/null +++ b/segments/github_notifications.sh @@ -0,0 +1,164 @@ +# shellcheck shell=bash + +# shellcheck source=lib/util.sh +source "${TMUX_POWERLINE_DIR_LIB}/util.sh" + +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SINCE="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SINCE:-$(date +%Y-%m-%dT00:00:00Z)}" +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SINCE_ENABLE="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SINCE_ENABLE:-no}" +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_PER_PAGE="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_PER_PAGE:-50}" +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_MAX_PAGES="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_MAX_PAGES:-10}" +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_UPDATE_INTERVAL="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_UPDATE_INTERVAL:-60}" +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SUMMARIZE="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SUMMARIZE:-no}" +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SYMBOL_MODE="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SYMBOL_MODE:-yes}" +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_HIDE_NO_NOTIFICATIONS="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_HIDE_NO_NOTIFICATIONS:-yes}" +TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_TEST_MODE="${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_TEST_MODE:-no}" + +_GITHUB_NOTIFICATIONS_TEST_RESPONSE=' +[ + {"reason": "approval_requested"}, + {"reason": "assign"}, + {"reason": "author"}, + {"reason": "comment"}, + {"reason": "ci_activity"}, + {"reason": "invitation"}, + {"reason": "manual"}, + {"reason": "mention"}, + {"reason": "review_requested"}, + {"reason": "security_alert"}, + {"reason": "state_change"}, + {"reason": "subscribed"}, + {"reason": "team_mention"} +] +' + +generate_segmentrc() { + read -r -d '' rccontents </dev/null 2>&1; then + return 0 + fi + + if [ -z "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_TOKEN" ] && ! is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_TEST_MODE"; then + return 0 + fi + + local tmp_file + local api_url + local api_query + local auth_header + + tmp_file="${TMUX_POWERLINE_DIR_TEMPORARY}/github_notifications.stat" + tmp_headers_file="$TMUX_POWERLINE_DIR_TEMPORARY/github_notifications.headers" + + api_url="https://api.github.com/notifications" + + if is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SINCE_ENABLE"; then + api_query="since=${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SINCE}&" + fi + api_query="${api_query}per_page=${TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_PER_PAGE}" + + auth_header="Authorization: Bearer $TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_TOKEN" + + if ! is_tmp_valid "$tmp_file" "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_UPDATE_INTERVAL"; then + if is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_TEST_MODE"; then + notifications=$_GITHUB_NOTIFICATIONS_TEST_RESPONSE + else + read -r -d '' notifications < <(curl -s --url "${api_url}?${api_query}" -D "$tmp_headers_file" --header "$auth_header") + # Handle paging (we only get "link" header when there are more pages) + # For more information see: https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api?apiVersion=2022-11-28#using-link-headers + if link=$(grep '^link: ' "$tmp_headers_file"); then + # Get last page number + last_page=$(echo "$link" | sed -r 's/.*page=([0-9]+)>; rel="last".*/\1/' | tr -d '\r\n') + # We already got the first page, so we start now with page 2 + page=2 + # Get pages until we got the last page or up until configured max pages + until [ "$page" -gt "$last_page" ] || [ "$page" -gt "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_MAX_PAGES" ]; do + read -r -d '' _tmp_notifications < <(curl -s --url "${api_url}?${api_query}&page=$page" --header "$auth_header") + notifications=$(jq -s 'add' <(echo "$notifications") <(echo "$_tmp_notifications")) + ((page++)) + done + fi + fi + + all_count=0 + result="" + + IFS=$'|' read -r -a reasons_list <<<"$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_REASONS" + + for reason_entry in "${reasons_list[@]}"; do + count=0 + IFS=$':' read -r reason separator <<<"$reason_entry" + count="$(echo "$notifications" | jq -c '.[] | select( .reason == "'"$reason"'" )' | jq -s 'length')" + + if [ "$count" -eq 0 ] && is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_HIDE_NO_NOTIFICATIONS"; then + continue + fi + + if is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SUMMARIZE"; then + all_count="$((all_count + count))" + else + if ! [[ "$separator" =~ ^-.*$ ]]; then + result="$result $separator:$count" + else + result="$result ${separator:1}$count" + fi + fi + done + + if is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SUMMARIZE"; then + if [ "$all_count" -gt 0 ] || ! is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_HIDE_NO_NOTIFICATIONS"; then + echo " $all_count" >"$tmp_file" + else + echo -n >"$tmp_file" + fi + else + if [ -n "$result" ] || ! is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_HIDE_NO_NOTIFICATIONS"; then + echo " $result" >"$tmp_file" + else + echo -n >"$tmp_file" + fi + fi + fi + + cat "$tmp_file" +} + +__process_settings() { + if [ -z "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_REASONS" ]; then + if is_flag_enabled "$TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_SYMBOL_MODE"; then + export TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_REASONS="approval_requested:-󰴄 |assign:-󰎔 |author:-󰔗 |comment:- |ci_activity:-󰙨 |invitation:- |manual:-󱥃 |mention:- |review_requested:- |security_alert:-󰒃 |state_change:-󱇯 |subscribed:- |team_mention:- " + else + export TMUX_POWERLINE_SEG_GITHUB_NOTIFICATIONS_REASONS="approval_requested:areq|assign:as|author:au|comment:co|ci_activity:ci|invitation:in|manual:ma|mention:me|review_requested:rreq|security_alert:sec|state_change:st|subscribed:sub|team_mention:team" + fi + fi +}