About some scripts I made this week

Jobber: a job/task runner bot
There is a UNIX system utility called cron. It runs tasks at specified times or intervals. It is an old archaic program that will NOT run a task if the computer is off or sleeping, because UNIX computers were expected to always be on. It doesn't check to see if it has missed any tasks either. I consider it to be useless because of this. So I made my own task runner that NEVER misses a job! It was quite simple, actually.
[Update July 13: Ok, I understand to "never say never". I will explore some of the things that can go wrong at a later date. Anacron won't miss a job, but it won't run the jobs often enough for my intended use, so it is a different kind of useless. I won't be using it. Systemd might be an option. I might show how to use it, and then not use it. I have my reasons. I also have a much simpler solution to the reminder task that will be featured in an upcoming blog post. ]
jobber.py is a program that checks a csv file for available jobs by time and date. If the current time is equal to or past the job time, jobber.sh calls jobber.py which calls a script to run the job. jobber.sh then sleeps for ten minutes, wakes up, and repeats the cycle.
#id, datetime, script, scriptargs 101, 2025-08-08 03:30:00, reminder.py, simon@home.arpa, "Get 50% off" , "Get some savings."
Right now, the only task script I have for it is "reminder.py", a script that sends an email reminder, but this is the main reason I wrote jobber, so I am pleased.
Jobber keeps a log of jobs referenced by a job-id. The job completion time is recorded as well as the return code (a report gets generated based on this).
To start and stop jobber, I use the desktop session. In Gnome, I use a startup script to start jobber.py (which is easy to do) and gendsession https://github.com/backloop/gendsession to detect the desktop logoff and shut down jobber.py .
What I learned?
- csv files written by Python have Windows/DOS line endings.
- How to daemonize a process, and then how to do it correctly.
Moon phase report ultra extreme
I used the pyEphem library https://rhodesmill.org/pyephem to compute the locations of Moon and Sun. The moon phase was computed by taking the difference of the angle between the Moon and Sun. pyEphem gives the illumination percentage directly, but I don't know how it computes this, so I used the angles for classifying the moon phase. The program uses my venv technique (described in this blog post) to install pyEphem into a Python venv. For ease-of-use, the symbolic link moonphase is used to call my Python venv wrapper moonphase.sh that then calls moonphase-ultra.py with the correct args for the user's location:
# location lat long timezone moonphase-ultra.py "Detroit" "42.33" "-83.04" "US/Eastern"
$ moonphase Location: Detroit Date: 2025/07/11 08:57 PM Moon Phase: Waning Gibbous Age of the moon: 16 days Illumination: 98% Next new moon is on 2025/07/24 03:11 PM: 12 days, 18 hours, 13 minutes from now.
I heard you like reports, so I made a reporting script that runs reports and reports on the reports.
Report generation is really easy. You can just write the software in a straight line from start to finish. I should have done this sooner.
$ report -h Usage: report [options] Options: -h, --help Show this help message. -e, --emoji Use emojis. -w, --weather Show weather information. -j, --jobs Check status of jobber jobs. -p, --postfix Check mail daemon status. -m, --mail Check for unread mail. -l, --lunar Show moon phase report.
The default report:
$ report -e ✅ Jobber is running. Jobber PIDs: jobber.sh 151860 ✅ All completed jobs were successful. ✅ Postfix service: running ✅ Mail queue is empty. ✅ No Postfix errors or warnings in the last 2 days. ✅ 0 emails.
The moonphase report adds a warning for the full moon:
$ report -e -l ❌ What a terrible night to have a curse. Location: Detroit Date: 2025/07/10 07:39 PM Moon Phase: Full Moon Age of the moon: 15 days Illumination: 100% Next new moon is on 2025/07/24 03:11 PM: 13 days, 19 hours, 32 minutes from now.
So, I did this and it changed my life.