├── 300px-LWN.net_(logo).png ├── 900px-Arch-linux-logo.png ├── Debian-Logo-.png ├── Fedora-logo.svg.png ├── LICENSE ├── OMGUbuntu.png ├── README.md ├── discord-rss-bot ├── gamingonlinux.png ├── ilya-1.png ├── openSUSE.png ├── openSUSEneon.png ├── phoronix.png ├── rms.png ├── solus.png ├── void_bg.png └── xubuntu.png /300px-LWN.net_(logo).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/300px-LWN.net_(logo).png -------------------------------------------------------------------------------- /900px-Arch-linux-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/900px-Arch-linux-logo.png -------------------------------------------------------------------------------- /Debian-Logo-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/Debian-Logo-.png -------------------------------------------------------------------------------- /Fedora-logo.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/Fedora-logo.svg.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 simonizor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OMGUbuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/OMGUbuntu.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # discord-rss-bot 2 | A simple bash script that posts RSS feeds to Discord webhooks 3 | 4 | ![embed-preview](https://u.teknik.io/w5AtY.PNG) 5 | -------------------------------------------------------------------------------- /discord-rss-bot: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Title: discord-rss-bot 3 | # Author: simonizor 4 | # Description: Post RSS feeds to Discord using a webhook 5 | # License: MIT 6 | # Dependencies: rsstail, pandoc, curl 7 | 8 | # run a for loop that checks the Pub.date of each feed in RSS_FEEDS 9 | function checkfeeds { 10 | mkdir -p ~/.cache/discord-rss 11 | cd ~/.local/share/discord-rss-bot/feeds 12 | for feed in *; do 13 | # unset used variables 14 | unset WEBHOOK_URL FEED_URL FEED_NAME BOT_USERNAME FEED_COLOR AVATAR_URL POST_LINK NEW_POST_LINK 15 | source ~/.local/share/discord-rss-bot/config 16 | source ~/.local/share/discord-rss-bot/feeds/"$feed" 17 | BOT_USERNAME="$(echo "$FEED_NAME" | tr '-' ' ')" 18 | echo "Checking $BOT_USERNAME..." 19 | # if last entry from feed stored, get POST_LINK for feed using grep and cut 20 | if [[ -f "$HOME/.cache/discord-rss/$FEED_NAME" ]]; then 21 | POST_LINK="$(grep -m1 '^Link:' ~/.cache/discord-rss/$FEED_NAME | cut -f2- -d' ')" 22 | fi 23 | # get NEW_POST_LINK for feed using getfeed function, grep, and cut 24 | NEW_POST_LINK="$(getfeed $FEED_URL | grep -m1 '^Link:' | cut -f2- -d' ')" 25 | # if NEW_POST_LINK not empty and does not match POST_LINK, write latest entry to ~/.cache/discord-rss/$FEED_NAME 26 | if [[ ! -z "$NEW_POST_LINK" ]] && [[ ! "$NEW_POST_LINK" == "$POST_LINK" ]]; then 27 | echo "[$(date +'%D %T')] New post in $BOT_USERNAME" 28 | getfeed "$FEED_URL" > ~/.cache/discord-rss/"$FEED_NAME" 29 | # run a for loop to post to multiple webhook urls if present 30 | for webhook in $(echo "$WEBHOOK_URL" | tr ',' '\n'); do 31 | postfeed "$webhook" 32 | # sleep 1 to avoid potentially hitting rate limit 33 | sleep 1 34 | done 35 | fi 36 | done 37 | cd 38 | } 39 | # use rsstail to get the latest feed 40 | function getfeed { 41 | local RSS_URL="$1" 42 | case "$FEED_NAME" in 43 | # reverse output for feeds that are weird and backwards 44 | openSUSE-Factory|openSUSE-Support|openSUSE-Bugs) 45 | FEED_CONTENT="$(rsstail -1pdlru "$RSS_URL" -n 1 -b $CHARACTER_LIMIT)" 46 | ;; 47 | *) 48 | FEED_CONTENT="$(rsstail -1pdlu "$RSS_URL" -n 1 -b $CHARACTER_LIMIT)" 49 | ;; 50 | esac 51 | case $? in 52 | 0) 53 | echo -e "$FEED_CONTENT" 54 | unset FEED_CONTENT 55 | ;; 56 | *) 57 | echo "null" 58 | ;; 59 | esac 60 | } 61 | # create json file containing embed data to upload to webhook URL 62 | function createjson { 63 | cat > /tmp/discord-rss.json << EOL 64 | { 65 | "username": "bot.sh", 66 | "avatar_url": "$AVATAR_URL", 67 | "embeds": [{ 68 | "title": "$FEED_TITLE", 69 | "url": "$FEED_LINK", 70 | "description": "$FEED_DESC", 71 | "color": "$FEED_COLOR", 72 | "timestamp": "$(date -d "$FEED_DATE" '+%Y-%m-%dT%TZ' -u)", 73 | "$IMAGE_TYPE": { 74 | "url": "$THUMBNAIL_URL" 75 | }, 76 | "author": { 77 | "name": "$BOT_USERNAME", 78 | "url": "$FEED_LINK", 79 | "icon_url": "$AVATAR_URL" 80 | } 81 | }] 82 | } 83 | 84 | EOL 85 | } 86 | # post feed to Discord using curl 87 | function postfeed { 88 | local UPLOAD_URL="$1" 89 | # get feed data for json file using grep and cut 90 | FEED_TITLE="$(grep -m1 '^Title:' ~/.cache/discord-rss/$FEED_NAME | cut -f2- -d' ' | tr '"' "'" | cut -c-255)" 91 | FEED_LINK="$(grep -m1 '^Link:' ~/.cache/discord-rss/$FEED_NAME | cut -f2- -d' ')" 92 | FEED_DATE="$(grep -m1 '^Pub.date:' ~/.cache/discord-rss/$FEED_NAME | cut -f2- -d' ')" 93 | if [[ -z "$FEED_DATE" ]]; then 94 | FEED_DATE="$(date -u)" 95 | fi 96 | # try to find picture links for thumbnail in embed 97 | THUMBNAIL_URL="$(rsstail -d1u "$FEED_URL" -n 1 | grep -om1 'http.*\.png\|http.*\.jpg')" 98 | case "$THUMBNAIL_URL" in 99 | *youtube*) THUMBNAIL_URL="$(echo "$THUMBNAIL_URL" | cut -f3 -d'"')";; 100 | *) THUMBNAIL_URL="$(echo "$THUMBNAIL_URL" | cut -f1 -d' ' | tr -d '"')";; 101 | esac 102 | # if no THUMBNAIL_URL found, just use 1x1 dummy image 103 | if [[ "$THUMBNAIL_URL" =~ "@" ]]; then 104 | THUMBNAIL_URL="https://dummyimage.com/1x1/000/fff" 105 | IMAGE_TYPE="thumbnail" 106 | elif [[ -z "$THUMBNAIL_URL" ]]; then 107 | THUMBNAIL_URL="https://dummyimage.com/1x1/000/fff" 108 | IMAGE_TYPE="thumbnail" 109 | else 110 | if [[ "$FEED_NAME" == "xkcd" ]]; then 111 | IMAGE_TYPE="image" 112 | else 113 | IMAGE_TYPE="thumbnail" 114 | fi 115 | fi 116 | # use grep to get rid of everything except the description then use sed to get rid of the Description label and replace new lines with '\n' 117 | # also use recode to turn html escapes into normal symbols then use sed to replace a couple of weird ones that recode adds 118 | case "$FEED_NAME" in 119 | # get rid of new lines in LWN posts; too messy with them 120 | LWN) 121 | FEED_DESC="$(cat ~/.cache/discord-rss/$FEED_NAME | grep -v '^Title:' | grep -v '^Link:' | grep -v '^Pub.date:' | sed "s%Description:%%" | pandoc --wrap=none -s -f html -t markdown | tr -d '\\' | sed 's%{.*}%%g;s%!\[.*\](.*)%%g' | tr '\n' ' ' | tr '"' "'" | tr '\t' ' ' | tr -d '\r' | grep . --color=never)" 122 | ;; 123 | # find description from HTML for xkcd 124 | xkcd) 125 | FEED_DESC="$(cat ~/.cache/discord-rss/$FEED_NAME | grep -v '^Title:' | grep -v '^Link:' | grep -v '^Pub.date:' | sed "s%Description:%%" | pandoc --wrap=none -s -f html -t markdown | cut -f2- -d'(' | cut -f2- -d' ' | grep '^"' | cut -f2 -d'"')" 126 | ;; 127 | *) 128 | FEED_DESC="$(cat ~/.cache/discord-rss/$FEED_NAME | grep -v '^Title:' | grep -v '^Link:' | grep -v '^Pub.date:' | sed "s%Description:%%" | pandoc --wrap=none -s -f html -t markdown | grep -v '^<\!-' | tr -d '\\' | sed 's%{.*}%%g;s%!\[.*\](.*)%%g' | sed 's%^\[$%%g;s%^Watch video%\[Watch video%g' | grep . --color=never | sed 's%^.*%&\\n%g' | tr -d '\n' | tr '"' "'" | tr '\t' ' ' | tr -d '\r' | grep -v '^:::')" 129 | ;; 130 | esac 131 | # check amount of characters; assume that more than 650 is trunicated and add '[...]' 132 | if [[ $(echo $FEED_DESC | wc -c) -gt 1100 ]]; then 133 | FEED_DESC="$(echo "$FEED_DESC" | rev | cut -f2- -d' ' | rev) [...]" 134 | fi 135 | # skip posting certain feeds 136 | case "$FEED_TITLE" in 137 | # dont post LWN.net paid articles 138 | \[\$\]*) 139 | echo "[$(date +'%D %T')] Skipping feed $BOT_USERNAME title $FEED_TITLE" 140 | ;; 141 | *) 142 | if [[ "$FEED_NAME" == "openSUSE-Bugs" ]]; then 143 | case "$FEED_TITLE" in 144 | *New:*) 145 | # create json file containing embed data to upload to webhook URL 146 | createjson 147 | # use curl to send /tmp/discord-rss.json to WEBHOOK_URL 148 | curl -iL -X POST -H "Content-Type: application/json" -T "/tmp/discord-rss.json" "$UPLOAD_URL" || echo "Upload failed" 149 | echo 150 | ;; 151 | *) echo "Skipping $BOT_USERNAME non new post $FEED_TITLE" ;; 152 | esac 153 | elif [[ "$FEED_NAME" == "openSUSE-Factory" ]] || [[ "$FEED_NAME" == "openSUSE-Support" ]]; then 154 | case "$FEED_TITLE" in 155 | *Re:*) echo "Skipping $BOT_USERNAME non new post $FEED_TITLE" ;; 156 | *) 157 | if [[ "$FEED_TITLE" =~ "[opensuse-factory] New Tumbleweed snapshot" ]]; then 158 | AVATAR_URL="https://cdn.discordapp.com/emojis/426479426474213414.gif?v=1" 159 | FEED_DESC='**New openSUSE Tumbleweed snapshot released! Time to `dup`!**' 160 | fi 161 | # create json file containing embed data to upload to webhook URL 162 | createjson 163 | # use curl to send /tmp/discord-rss.json to WEBHOOK_URL 164 | curl -iL -X POST -H "Content-Type: application/json" -T "/tmp/discord-rss.json" "$UPLOAD_URL" || echo "Upload failed" 165 | echo 166 | ;; 167 | esac 168 | elif [[ "$FEED_NAME" == "LKML.ORG" ]]; then 169 | case "$FEED_TITLE" in 170 | Re:*) echo "Skipping $BOT_USERNAME non new post $FEED_TITLE" ;; 171 | *) 172 | # create json file containing embed data to upload to webhook URL 173 | createjson 174 | # use curl to send /tmp/discord-rss.json to WEBHOOK_URL 175 | curl -iL -X POST -H "Content-Type: application/json" -T "/tmp/discord-rss.json" "$UPLOAD_URL" || echo "Upload failed" 176 | echo 177 | ;; 178 | esac 179 | else 180 | # create json file containing embed data to upload to webhook URL 181 | createjson 182 | # use curl to send /tmp/discord-rss.json to WEBHOOK_URL 183 | curl -iL -X POST -H "Content-Type: application/json" -T "/tmp/discord-rss.json" "$UPLOAD_URL" || echo "Upload failed" 184 | echo 185 | fi 186 | ;; 187 | esac 188 | # rm -f /tmp/discord-rss.json 189 | # unset used variables 190 | unset FEED_TITLE FEED_LINK FEED_DESC THUMBNAIL_URL 191 | } 192 | # create config if not found 193 | if [[ ! -d "$HOME/.local/share/discord-rss-bot/feeds" ]] || [[ ! -f "$HOME/.local/share/discord-rss-bot/config" ]]; then 194 | mkdir -p ~/.local/share/discord-rss-bot/feeds 195 | cat > ~/.local/share/discord-rss-bot/config << EOF 196 | # default webhook url to use for feeds. 197 | # if webhook url is set in feed config file, that url will be used instead 198 | WEBHOOK_URL="https://discordapp.com/api/webhooks/channelidhere/tokenhere?wait=true" 199 | # time to sleep between feed check cycles 200 | RSS_CHECK_TIME=180 201 | # max amount of characters to be sent in embed description (Discord's max is 2048) 202 | CHARACTER_LIMIT=1800 203 | EOF 204 | fi 205 | # create example feed config and exit if none exist 206 | if [[ $(ls -Cw1 ~/.local/share/discord-rss-bot/feeds | wc -l) -eq 0 ]]; then 207 | cat > ~/.local/share/discord-rss-bot/Example-Feed << EOF 208 | # webhook url to use for this feed; leave blank to use default 209 | WEBHOOK_URL="" 210 | # must be a valid RSS feed that is readable by 'rsstail' 211 | FEED_URL="https://www.phoronix.com/rss.php" 212 | # will be used as the bot's username when posting; '-' in the 'name' will be replaced with a space 213 | FEED_NAME="Phoronix-News" 214 | # 'color' must be in decimal format; see https://www.mathsisfun.com/hexadecimal-decimal-colors.html 215 | FEED_COLOR=6523985 216 | # 'avatar_url' must be a valid image acceptable for Discord avatars 217 | AVATAR_URL="https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/master/avatars/phoronix.png" 218 | EOF 219 | echo "No feeds found in '~/.local/share/discord-rss-bot/feeds'; see example in '~/.local/share/discord-rss-bot/Example-Feed'" 220 | exit 0 221 | fi 222 | 223 | source ~/.local/share/discord-rss-bot/config 224 | # start while loop that runs every RSS_CHECK_TIME seconds 225 | CURRENT_LOOP=$RSS_CHECK_TIME 226 | while true; do 227 | if [[ "$CURRENT_LOOP" == "$RSS_CHECK_TIME" ]]; then 228 | echo "[$(date +'%D %T')] Checking RSS feeds..." 229 | checkfeeds 230 | echo "[$(date +'%D %T')] Done checking RSS feeds" 231 | echo "[$(date +'%D %T')] Sleeping for $RSS_CHECK_TIME seconds..." 232 | CURRENT_LOOP=0 233 | else 234 | sleep 1 235 | ((CURRENT_LOOP++)) 236 | fi 237 | done 238 | echo "" 239 | exit 0 240 | -------------------------------------------------------------------------------- /gamingonlinux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/gamingonlinux.png -------------------------------------------------------------------------------- /ilya-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/ilya-1.png -------------------------------------------------------------------------------- /openSUSE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/openSUSE.png -------------------------------------------------------------------------------- /openSUSEneon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/openSUSEneon.png -------------------------------------------------------------------------------- /phoronix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/phoronix.png -------------------------------------------------------------------------------- /rms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/rms.png -------------------------------------------------------------------------------- /solus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/solus.png -------------------------------------------------------------------------------- /void_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/void_bg.png -------------------------------------------------------------------------------- /xubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoniz0r/discord-rss-bot/24036202cf274f37a845d44bfb2bc60d97776640/xubuntu.png --------------------------------------------------------------------------------