Here in Austria, this summer has once again brought us a record breaking heat wave (aren't we all glad there is no climate crisis?). Fortunately, I've managed to acquire a portable split air conditioner in time for this summer. Less fortunately, there is only one model of portable air conditioner that that isn't extremely loud and ineffective; the Midea PortaSplit. And even less fortunately, it's constantly sold out.
I decided to help out a family member who desperately needed one, in the only way I knew: by writing a few lines of shell, and idling on IRC all day. And I finally get another use out of ircpipe, the scriptable IRC client I have written a few years ago, just for occasional notification jobs like this.
First, reconnaissance: There is essentially only one chain of home improvement stores in their area that carries the AC, and their website even lists current stock numbers online. Some minutes of fiddling with the browser devtools later, I had figured out that the store locator widget uses a REST API to get the current stock, from up to ten stores at a time. And there is a handy API that lists all stores' IDs, names, and their locations.
~ % curl -s 'https://www.obi.at/api/disc/store/locator/country/at' -A "$firefox" |
jq -cr '.stores[]|[.storeNumber,.name]|@tsv' |
grep Wien | head -4
034 Wien Triester Straße
038 Wien, Hadikgasse
048 Wien Nord
049 Wien Sankt Marx
So I simply picked the locations closest to said relative (in this article I will instead use Vienna, to protect their privacy). These IDs are static, so the request can be made interactively once, and the results hard-coded into the next request. Let's try it:
~ % curl -s 'https://www.obi.at/api/pdp/v1/stock/3586245?storeIds=034,038,048,049' -A "$firefox" |
jq -r '.[]|[.availableQuantity, .storeId]|@tsv'
0 034
0 038
0 048
0 049
Neat! Right now, all those stores are sold out, but with a little bit of jq and sed magic, we can filter out empty stores and resolve their names. And with that, the final script, that is simply called every few minutes via crontab:
#!/bin/sh
pgrep ircpipe || tail -F /tmp/ircpipe.in | ircpipe -n girst_bot -s irc.libera.chat >/dev/null & disown
curl -s 'https://www.obi.at/api/pdp/v1/stock/3586245?storeIds=034,038,048,049,066,068,069,040,051,058' \
-A 'Mozilla/5.0 (X11; Linux x86_64; rv:153.0) Gecko/20100101 Firefox/153.0' |
jq -r '.[]|select(.availableQuantity>0)|[.availableQuantity, .storeId]|@tsv' |
sed -e 's/034$/Wien Triester Straße/' -e 's/038$/Wien, Hadikgasse/' \
-e 's/048$/Wien Nord/' -e 's/049$/Wien Sankt Marx/' \
-e 's/066$/Wien (17. Bezirk)/' -e 's/068$/Wien (21. Bezirk)/' \
-e 's/069$/Wien (22. Bezirk)/' -e 's/040$/Vösendorf/' \
-e 's/051$/Klosterneuburg/' -e 's/058$/Schwechat/' |
while read stockcount storename
do
printf 'PRIVMSG girst :%d Portasplits available in %s!\r\n' "$stockcount" "$storename"
sleep 1 # prevent getting floodkicked
done >> /tmp/ircpipe.in
The first statement is a poor man's service supervisor: it simply makes sure ircpipe is running, and starts it if it isn't. Since this script is run from cron, I don't want the session to be reestablished all the time but kept alive in the background. ircpipe will handle the connection setup, as well as responding to the regular PINGs from the IRC server - that's its main (and only) feature after all. For IPC, a simple temporary file is all we need.
Whenever cron runs the script, it'll check stock quantities and emit a private message to myself for each location where an AC is available. And then, some time ago, it happened: My phone beeped, and I've received the following message over IRC, repeated every time cron ran.
13:37 girst_bot: 1 Portasplits available in Wien Sankt Marx!
13:42 girst_bot: 1 Portasplits available in Wien Sankt Marx!
13:47 girst_bot: 1 Portasplits available in Wien Sankt Marx!
I called the relative in question, they in turn called the store and - after convincing them that they indeed had just received a delivery - arranged for holding it for them. Yes: we knew about their delivery before they even did! I have been told the staff were really surprised but intrigued about the whole affair.