Basic website monitoring using cronjobs and ntfy.sh
There are many reasons to monitor a website, but for very simple use cases like checking for a keyword, the existing tools are overkill.
In my homelab i use a bash script and a cronjob to monitor some sites, and ntfy.sh for notifications.
Here are some examples:
Monitoring availability of articles in online-shops
I’m reading Dune and am waiting for the updated german translation of the fifth entry. So this command checks if “pre-order” is found in the html and sends me a notification if not - meaning the book is now available.
curl -s https://www.buchhaus.ch/de/buecher/taschenbuch/fantasy/detail/ISBN-9783453320918 | grep -q "vorbestellen" || curl -d "Dune available!" ntfy.sh/foo
This simple pattern is already very useful, despite no dynamic information being passed to the notification.
If instead of watching for the absence of a string you want to send
a notification if a string is present, replace
||
with
&&
.
Monitoring HackerNews
Another more complex example would be to search for posts from a website (say lwn.net) on the HackerNews frontpage:
This example is very artificial, HN has an RSS feed and if you really want to monitor for posts, you should use that feed and a RSS reader.
curl -s https://news.ycombinator.com | grep "sitestr\">lwn.net" | sed -E 's/.*[^\(]<a href=\"([^"]*)\"[^<]*>([^<]*)<\/a>.*/\2\t\1/g' | while IFS=$'\t' read -r title url; do curl -d "$title" -H "Actions: view, Open Post, $url" ntfy.sh/foo; done
This makes use of some more complex parsing of the HTML respone and dynamically sends the filtered posts to ntfy.sh. It also uses actions to add a link to the notification.
Steam Item Price monitoring using the Steam API
For even more complex scripts i use python, for example to monitor the price of CSGO Cases. Since i’m scheduling these commands on my Unraid server, which does not have python installed by default, i use the python docker image.
docker run --rm --name csgo-case-notify -v /mnt/user/appdata/python/scripts:/usr/src -w /usr/src python python csgocasenotify.py
The python script itself isn’t really the focus of this post, but for completeness sake:
csgocasenotify.py
import urllib.request
import json
import time
from datetime import date
class Case:
def __init__(self, name: str, item_id: int, count: int):
self.fees = 0.45
self.name = name
self.item_id = item_id
self.count = count
self.price = 0
def refresh_price(self) -> int:
url = "https://steamcommunity.com/market/itemordershistogram?country=CH&language=english¤cy=4&item_nameid=" + str(self.item_id) + "&two_factor=0"
response = urllib.request.urlopen(url)
data = json.load(response)
highest_buy_order = int(data['highest_buy_order']) * 0.01
self.price = highest_buy_order
@property
def total(self) -> float:
return self.price * self.count * (1-self.fees)
def __str__(self):
return "{}x {} @ CHF {:.2f} => CHF {:.2f}".format(self.count, self.name, self.price, self.total)
if __name__ == "__main__":
cases = [
Case("Prisma Case", 176042493, 100),
Case("Chroma 3 Case", 149865785, 100),
Case("Horizon Case", 175999886, 100),
Case("Spectrum 2 Case", 175917239, 100),
Case("Clutch Case", 175966708, 100),
Case("Gamma 2 Case", 165027636, 100),
Case("Danger Zone Case", 176024744, 100),
Case("Prisma 2 Case", 176118270, 100),
]
for i, case in enumerate(cases):
if i > 0:
time.sleep(30) # don't get rate limited
case.refresh_price()
total_sum = sum(case.total for case in cases)
with open("price_history.txt", "a+") as file:
file.seek(0)
lines = file.readlines()
if lines:
old_total = float(lines[-1].strip().split(";")[1])
change_percent = (total_sum - old_total) / old_total * 100
title = "Case Update - Total Netto: CHF {:.2f} ({:+.2f}%)".format(total_sum, change_percent)
else:
title = "Case Update - Total Netto: CHF {:.2f}".format(total_sum)
file.write(date.today().isoformat() + ";" + str(total_sum)+ "\n")
text = "\n".join(str(case) for case in cases)
print(title)
print(text)
req = urllib.request.Request("https://ntfy.sh/foo", data=text.encode(encoding='utf-8'))
req.add_header("Title", title)
urllib.request.urlopen(req)