# ------------------------------------------------------------------------------ # Mr. WaterBlogged! # Stephen Niedzielski 2009-06-01 # checks for entry and comment RSS updates. formats and emails updates to # subscribers # notes: # * no multi-job support # * updates provided in HTML format only # * designed for single password front page WordPress feeds only # ------------------------------------------------------------------------------ # Make version check make_expected_ver := 3.81 ifneq '$(make_expected_ver)' '$(MAKE_VERSION)' $(error error: bad Make version. expected $(make_expected_ver), got \ $(MAKE_VERSION)) endif # ------------------------------------------------------------------------------ # defs: suffixes adr_suf := .adr cookie_suf := .cookie feed_suf := .rss htm_suf := .htm prereq_suf := .prereq py_suf := .py # ------------------------------------------------------------------------------ # defs: files # file containing address' to email on script err admin_adr_file := ./admin$(adr_suf) # file containing address' to email updates. subscriber_adr_file := ./subscriber$(adr_suf) crontab_file := ~/.crontab # contains cookie data needed to access password protected posts cookie_file := ./feed$(cookie_suf) # existence of this file indicates all the environment meets all prerequisites # for execution of this script prereq_file := ./mr_wb$(prereq_suf) # files formatted for email htm_entry_file := ./entry$(htm_suf) htm_comment_file := ./comment$(htm_suf) # feeds downloaded entry_feed_file := ./entry$(feed_suf) comment_feed_file := ./comment$(feed_suf) susbscriber_file := ./subscriber$(adr_suf) admin_file := ./admin$(adr_suf) clean_files := \ $(htm_entry_file) \ $(htm_comment_file) \ $(entry_feed_file) \ $(comment_feed_file) \ $(cookie_file) clobber_files := \ $(prereq_file) superclobber_files := this_file := ./mr_wb.mk empty := space := $(empty) $(empty) # credit: GNU Make Manual # $1 file path_search = $(firstword $(wildcard $(addsuffix /$(strip $(1)),$(subst :,$(space),$(PATH))))) # ------------------------------------------------------------------------------ # defs: misc # since we supply our own feedparser module, we need to specify where Python may # find it export PYTHONPATH += :../ export export TZ = EST #/usr/share/zoneinfo/US/Central # password to use when getting the cookie cookie_pw := heydiddlediddle blog_url := http://www.waterblogged.net blog_entry_feed_url := $(blog_url)/feed blog_comment_feed_url := $(blog_url)/comments/feed # time of current execution exec_date := $(shell date +'%F-%H-%M') # frequency of execution in crontab format job_freq := */9\t*\t*\t*\t* # ------------------------------------------------------------------------------ # defs: prereqs ifneq '$(call path_search,email.exe)' '' email = email else email = mail endif print_missing_modules := ~/scripts/print_missing_modules$(py_suf) format_feed := ./format_feed$(py_suf) # TODO: complete... tool_prereqs := \ bash \ crontab \ $(email) \ grep \ make \ $(print_missing_modules) \ python \ wget # ------------------------------------------------------------------------------ # defs: rule helpers red := '\e[1;31m' colorless := '\e[0m' export SHELL := /bin/bash # TODO: uninstall crontab job on err # TODO: send email to admin on err # basic err handling. begin_rule = \ set -e; \ set -o pipefail; \ function err_trap() \ { \ echo "error: $$BASH_COMMAND returned $$1 at time $(exec_date)."; \ }; \ trap 'err_trap $$?;' ERR; \ echo -e $(red)--- rule: $@ ---$(colorless) end_rule := \ echo --- done --- # $1 files to remove. remove_files = \ if [ "$(strip $1)" != "" ]; \ then \ rm -f $1; \ echo $1 | \ xargs -0 test ! -e; \ fi # ------------------------------------------------------------------------------ # rules: default # top-most rule in file is default. .PHONY: all all: mail # ------------------------------------------------------------------------------ # rules: send mail .PHONY: mail mail: mail_entry mail_comment .PHONY: mail_entry mail_entry: $(htm_entry_file) @$(begin_rule); \ echo checking file, $<, for non-zero size...; \ if [ $$(stat -c %s $<) -ne 0 ]; \ then \ echo sending message...; \ if [ '$(email)' = 'email' ]; \ then \ cat $<|email \ -r smtp.gmail.com \ --tls \ -m LOGIN \ -u stephen.niedzielski \ -i maybeicoulddosomestretchestoo \ -H 'From: "Mr. WaterBlogged!" mr.wb@waterblogged.net;' \ -H 'Content-Type: text/html;' \ -s 'WaterBlogged! Entries $(exec_date)' \ `cat $(susbscriber_file)`; \ else \ cat $<|mail \ -s 'WaterBlogged! Entries $(exec_date)' \ -a 'From: "Mr. WaterBlogged!" mr.wb@waterblogged.net;' \ -a 'Content-Type: text/html;' \ `cat $(susbscriber_file)|sed -r 's_(.*)_-b \1_'` stephen.niedzielski@gmail.com; \ fi; \ fi; \ $(end_rule); mail_comment: $(htm_comment_file) @$(begin_rule); \ echo checking file, $<, for non-zero size...; \ if [ $$(stat -c %s $<) -ne 0 ]; \ then \ echo sending message...; \ if [ '$(email)' = 'email' ]; \ then \ cat $<|email \ -r smtp.gmail.com \ --tls \ -m LOGIN \ -u stephen.niedzielski \ -i barnacle \ -s 'WaterBlogged! Comments $(exec_date)' \ -H 'From: "Mr. WaterBlogged!" mr.wb@waterblogged.net;' \ -H 'Content-Type: text/html;' \ `cat $(susbscriber_file)`; \ else \ cat $<|mail \ -s 'WaterBlogged! Comments $(exec_date)' \ -a 'From: "Mr. WaterBlogged!" mr.wb@waterblogged.net;' \ -a 'Content-Type: text/html;' \ `cat $(susbscriber_file)|sed -r 's_(.*)_-b \1_'` stephen.niedzielski@gmail.com; \ fi; \ fi; \ $(end_rule); # ------------------------------------------------------------------------------ # rules: format feeds to HTML .PHONY: htm_entry htm_entry: $(htm_entry_file) .PHONY: htm_comment htm_comment: $(htm_comment_file) $(htm_entry_file): $(entry_feed_file) $(htm_comment_file): $(comment_feed_file) $(htm_entry_file) $(htm_comment_file): @$(begin_rule); \ echo getting last execution time...; \ if [ -e $@ ]; \ then \ last_exec="`stat -c %Y $@`"; \ else \ last_exec=0; \ fi; \ echo converting to HTML...; \ $(format_feed) $< $@ "$$last_exec"; \ $(end_rule); # ------------------------------------------------------------------------------ # rules: get feeds $(entry_feed_file): dl_entry .PHONY: dl_entry dl_entry: | $(cookie_file) @$(begin_rule); \ echo downloading entry feed...; \ wget \ -q \ --load-cookies $(cookie_file) \ '$(blog_entry_feed_url)' \ -O $(entry_feed_file); \ touch $(entry_feed_file); \ $(end_rule); $(comment_feed_file): dl_comment .PHONY: dl_comment dl_comment: | $(cookie_file) @$(begin_rule); \ echo downloading comment feed...; \ wget \ -q \ --load-cookies $(cookie_file) \ '$(blog_comment_feed_url)' \ -O $(comment_feed_file); \ touch $(comment_feed_file); \ $(end_rule); # ------------------------------------------------------------------------------ # rules: get cookies # TODO: does cookie expire? .PHONY: dl_cookie dl_cookie: $(cookie_file) $(cookie_file): $(prereq_file) @$(begin_rule); \ echo downloading cookie...; \ wget \ -q \ --save-cookies $@ \ --post-data='post_password=$(cookie_pw)&Submit=Submit' \ '$(blog_url)/wp-pass.php'; \ echo removing extra temporary file...; \ $(call remove_files, wp-pass.php); \ $(end_rule); # ------------------------------------------------------------------------------ # rules: check prereqs # note: file prereqs can also be checked using the path_search macro, but I # don't think there's a good way to do complete Python module checking in # makescript. either we suffer the burden of an extra file signifying that # prereqs have been confirmed previously or the overhead of $(shell) on each # invocation .PHONY: check_prereq check_prereq: $(prereq_file) $(prereq_file): @$(begin_rule); \ echo checking tool prereqs...; \ type -a $(tool_prereqs) > /dev/null; \ echo checking Python module prereqs...; \ $(print_missing_modules) $(format_feed); \ echo all prereqs satisfied; \ touch $@; \ $(end_rule); # ------------------------------------------------------------------------------ # rules: crontab .PHONY: install_crontab_job install_crontab_job: @$(begin_rule); \ echo check for crontab file, $(crontab_file)...; \ if [ ! -e $(crontab_file) ]; \ then \ echo crontab file not found, making a new one...; \ echo '#!'$$(which crontab) > $(crontab_file); \ else \ echo checking if already installed...; \ ! grep '$(this_file)' $(crontab_file); \ echo not installed; \ fi; \ echo adding this file, $(this_file), as a job with frequency "$(job_freq)"...; echo -e "$(job_freq)\tmake -C $(PWD)/scripts/mr_wb -f $(this_file) -rR --warn-undefined-variables;" >> $(crontab_file); \ echo restarting crontab service...; \ crontab $(crontab_file); \ $(end_rule); .PHONY: uninstall_crontab_job uninstall_crontab_job: @$(begin_rule); \ echo check for crontab file, $(crontab_file)...; \ if [ -e $(crontab_file) ]; \ then \ echo removing occurences of this file, $(this_file)...; \ grep -v '$(this_file)' $(crontab_file) > $(crontab_file); \ echo restarting crontab service...; \ crontab $(crontab_file); \ fi; \ $(end_rule); # ------------------------------------------------------------------------------ # rules: cleanup .PHONY: clean clean: @$(begin_rule); \ echo cleaning...; \ $(call remove_files, $(clean_files)); \ $(end_rule); # dumps files we otherwise might keep .PHONY: clobber clobber: clean @$(begin_rule); \ echo clobbering...; \ $(call remove_files, $(clobber_files)); \ $(end_rule); # undo everything .PHONY: superclobber superclobber: clobber uninstall_crontab_job @$(begin_rule); \ echo superclobbering...; \ $(call remove_files, $(superclobber_files)); \ $(end_rule);