Last active
November 9, 2019 12:08
-
-
Save baines/344d0483810ad518c4d4 to your computer and use it in GitHub Desktop.
Twitch.tv desktop notification bash script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# twitch-notify.sh by Alex Baines | |
# Desktop notifications about twitch.tv streams. | |
# dependencies: | |
# curl | |
# underscore-cli | |
# notify-send | |
# md5sum | |
# user-agent sent to twitch. | |
ua="twitch-notify.sh" | |
# put lowercase channel names, one per line, in this file. | |
infile="twitch-channels.txt" | |
# this file will be kept up-to-date with a list of online streams. | |
outfile="twitch-status.txt" | |
# directories for temporary files. | |
tmpbase="/tmp/twitch" | |
tmpimgdir="$tmpbase/imgs" | |
# sounds to play when the status of a stream changes. | |
new_stream_snd="" | |
upd_stream_snd="" | |
del_stream_snd="" | |
# command to play sounds. | |
play_snd_cmd="paplay" | |
# read new stream notifications out loud with espeak or not. | |
use_espeak=1 | |
# update rate in seconds. | |
interval=300 | |
declare -A online | |
function get_img_path { | |
echo ${tmpimgdir}"/"$(md5sum <<<"$1" | cut -d\ -f 1) | |
} | |
function add_stream { | |
local img=$(get_img_path "$1") | |
if [ "$4" == "null" ]; then | |
img="video-x-generic" | |
else | |
curl -s -A "$ua" "$4" -o "$img" | |
fi | |
notify-send -i "$img" "$1 is streaming now." "$2 [$3]" | |
if [ -n "$play_snd_cmd" -a -n "$new_stream_snd" ]; then | |
$play_snd_cmd "$new_stream_snd" & | |
fi | |
if [ $use_espeak ]; then | |
echo "$1 - $3" | espeak 2>/dev/null | |
fi | |
online[$1]="\"$2\"\n\t[$3]" | |
} | |
function del_stream { | |
local img=$(get_img_path "$1") | |
notify-send -i "$img" "$1 is no longer streaming." | |
if [ -n "$play_snd_cmd" -a -n "$del_stream_snd" ]; then | |
$play_snd_cmd "$del_stream_snd" & | |
fi | |
rm "$img" | |
unset online[$1] | |
} | |
function upd_stream { | |
local img=$(get_img_path "$1") | |
notify-send -i "$img" "$1 changed game." "Now streaming $2. [$3]" | |
if [ -n "$play_snd_cmd" -a -n "$upd_stream_snd" ]; then | |
$play_snd_cmd "$upd_stream_snd" & | |
fi | |
online[$1]="\"$2\"\n\t[$3]" | |
} | |
function writefile { | |
echo -e "Last Update: $(date)\n------------\n">"$outfile" | |
for i in "${!online[@]}" ; do | |
echo -e "$i:\n\t${online[$i]}\n" >> "$outfile" | |
done | |
} | |
function contains { | |
local e | |
for e in "${@:2}"; do [[ "$e" == "$1" ]] && echo "true"; done | |
echo "false" | |
} | |
function dl { | |
curl -A "$ua" -s -w "%{http_code}" -o /dev/stderr "https://api.twitch.tv/kraken/streams?channel=$chans" | |
} | |
[ -d "$tmpbase" ] || mkdir "$tmpbase" | |
[ -d "$tmpimgdir" ] || mkdir "$tmpimgdir" | |
while true; do | |
t=$(date +%s) | |
chans=$(tr "\n" "," <"$infile") | |
unset t_out t_err | |
#ugly hack to get both stdout and stderr from curl | |
eval "$( (dl) 2> >(readarray -t t_err; typeset -p t_err) > >(readarray -t t_out; typeset -p t_out) )" | |
js="$t_err" | |
if [[ "$t_out" == "200" ]]; then | |
js2=$(underscore --coffee process "data.streams.map (x) -> {"` | |
`"n: x.channel.name,"` | |
`"g: x.channel.game || x.game || 'null',"` | |
`"s: x.channel.status || 'null',"` | |
`"l: x.channel.logo || 'null' }" <<<"$js") | |
underscore pretty <<< "$js2" | |
# the Twitch API likes to lie and return 0 results sometimes, try to filter them out. | |
if [[ "$js2" == "[ ]" && "${#online[@]}" > 1 && -z "$twitch_lies" ]]; then | |
echo "Skipping potentially erroneous update..." | |
twitch_lies=1 | |
d=$(($(date +%s)-$t)) | |
if [ "$d" -lt "$interval" ] ; then | |
sleep $(($interval-$d)) | |
fi | |
continue | |
fi | |
unset twitch_lies | |
IFS=$'\n' | |
names=($(underscore select '.n' --outfmt text <<< "$js2")) | |
games=($(underscore select '.g' --outfmt text <<< "$js2")) | |
logos=($(underscore select '.l' --outfmt text <<< "$js2")) | |
statuses=($(underscore select '.s' --outfmt text <<< "$js2")) | |
unset $IFS | |
for x in "${!online[@]}"; do | |
if [ "$(contains $x ${names[@]})" == "false" ]; then | |
del_stream "$x" | |
fi | |
done | |
for ((x=0; x<${#names[@]}; x++)); do | |
n="${names[$x]}" | |
g="${games[$x]}" | |
l="${logos[$x]}" | |
s="${statuses[$x]}" | |
if [[ -z "${online[$n]}" ]]; then | |
add_stream "$n" "$s" "$g" "$l" | |
elif [[ "$s" != "null" && "${online[$n]}" != "\"$s\"\n\t[$g]" ]]; then | |
upd_stream "$n" "$s" "$g" | |
fi | |
done | |
writefile | |
else | |
echo "$0: HTTP error: $t_out." | |
fi | |
d=$(($(date +%s)-$t)) | |
if [ "$d" -lt "$interval" ] ; then | |
sleep $(($interval-$d)) | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment