add launchd scheduling

adds --schedule and --time flags
improves CLI feedback
resolves #5
This commit is contained in:
Hugh Rundle 2020-04-25 08:53:19 +10:00
parent 3f6562b423
commit abc7e07677
5 changed files with 147 additions and 15 deletions

View File

@ -88,6 +88,10 @@ ephemetoot
Depending on how many toots you have and how long you want to keep them, it may take a minute or two before you see any results.
## --schedule
For setting up scheduling on MacOS - see [Scheduling](#scheduling).
## Specifying the config location
By default ephemetoot expects there to be a config file called `config.yaml` in the directory from where you run the `ephemetoot` command. If you want to call it from elsewhere (e.g. with `cron`), you need to specify where your config file is:
@ -126,12 +130,39 @@ As of v2.7.2 the Mastodon API has a rate limit of 30 deletions per 30 minutes. `
Deleting old toots daily is the best approach to keeping your timeline clean and avoiding problems wiht the API rate limit.
### Linux and FreeBSD/Unix
To run automatically every day on a n*x server you could try using crontab:
1. `crontab -e`
2. `@daily ephemetoot`
2. enter a new line: `@daily ephemetoot --config /path/to/ephemetoot/config.yaml`
3. exit with `:qw` (Vi/Vim) or `Ctrl + x` (nano)
Alternatively on MacOS you could use [launchd](https://www.launchd.info/). Some further work on an example setup for launchd is coming soonish.
### MacOS
On **MacOS** you can use the `--schedule` flag to schedule a daily job with [launchd](https://www.launchd.info/). Note that this feature has not been widely tested so **please log an issue if you notice anything go wrong**.
Run from within your `ephemetoot` directory:
```shell
ephemetoot --schedule
```
or from anywhere else run:
```shell
ephemetoot --schedule directory
```
where `directory` is where you installed `ephemetoot`. For example if `ephemetoot` is saved to `/User/hugh/python/ephemetoot`:
```shell
ephemetoot --schedule /User/hugh/python/ephemetoot
```
By default, `ephemetoot` will run at 9am every day (as long as your machine is logged in and connected to the internet). You can change the time it is scheduled to run, using the `--time` flag with `--schedule`:
```shell
ephemetoot --schedule [directory] --time hour minute
```
For example to run at 2.25pm every day:
```shell
ephemetoot --schedule --time 14 25
```
# ASCII / utf-8 errors
@ -142,10 +173,16 @@ Prior to Python 3.7, running a Python script on some BSD and Linux systems may t
# Uninstalling
Uninstall using pip;
```
```shell
pip uninstall ephemetoot
```
If you scheduled a `launchd` job on MacOS using `--schedule`, you will also need to unload and remove the scheduling file:
```shell
launchctl unload ~/Library/LaunchAgents/ephemetoot.scheduler.plist
rm ~/Library/LaunchAgents/ephemetoot.scheduler.plist
```
# Bugs and suggestions
Please check existing [issues](https://github.com/hughrun/ephemetoot/issues) and if your issue is not already listed, create a new one with as much detail as possible (but don't include your access token!).

View File

@ -28,6 +28,7 @@ import yaml
# from standard library
from argparse import ArgumentParser
from datetime import datetime
import os
# local files
@ -40,6 +41,12 @@ parser.add_argument(
parser.add_argument(
"--test", action="store_true", help="do a test run without deleting any toots"
)
parser.add_argument(
"--schedule", action="store", metavar="'filepath'", nargs="?", const=".", help="save and load plist file on MacOS"
)
parser.add_argument(
"--time", action="store", metavar="'hours minutes'", nargs="*", help="hour and minute to schedule: e.g. 9 30 for 9.30am"
)
options = parser.parse_args()
if options.config[0] == '~':
@ -50,7 +57,15 @@ else:
config_file = os.path.join( os.getcwd(), options.config )
if __name__ == "__main__":
with open(config_file) as config:
for accounts in yaml.safe_load_all(config):
for user in accounts:
ephemetoot.checkToots(user, options)
if options.schedule:
ephemetoot.schedule(options)
else:
print('')
print('=========== EPHEMETOOT ================')
print('Running at ' + str(datetime.now() ))
print('=======================================')
print('')
with open(config_file) as config:
for accounts in yaml.safe_load_all(config):
for user in accounts:
ephemetoot.checkToots(user, options)

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ephemetoot.scheduler</string>
<key>WorkingDirectory</key>
<string>/FILEPATH/ephemetoot</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/ephemetoot</string>
<string>--config</string>
<string>/FILEPATH/config.yaml</string>
</array>
<key>StandardOutPath</key>
<string>ephemetoot.log</string>
<key>StandardErrorPath</key>
<string>ephemetoot.error.log</string>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>9</integer>
<key>Minute</key>
<integer>00</integer>
</dict>
</dict>
</plist>

View File

@ -1,10 +1,52 @@
from datetime import datetime, timedelta, timezone
import json
from mastodon import Mastodon, MastodonError, MastodonAPIError, MastodonNetworkError
from datetime import datetime, timedelta, timezone
import time
import os
import requests
import subprocess
import sys
import time
def checkToots(config, options, deleted_count=0):
def schedule(options):
try:
with open(options.schedule + '/ephemetoot.scheduler.plist', 'r') as file:
lines = file.readlines()
if options.schedule == ".":
working_dir = os.getcwd()
else:
working_dir = options.schedule
lines[7] = " <string>" + working_dir + "</string>\n"
lines[10] = " <string>" + sys.argv[0] + "</string>\n"
lines[12] = " <string>" + working_dir + "/config.yaml</string>\n"
if options.time:
lines[21] = " <integer>" + options.time[0] + "</integer>\n"
lines[23] = " <integer>" + options.time[1] + "</integer>\n"
with open('ephemetoot.scheduler.plist', 'w') as file:
file.writelines(lines)
sys.tracebacklimit = 0 # suppress Tracebacks
# save the plist file into ~/Library/LaunchAgents
subprocess.run(
["cp " + options.schedule + "/ephemetoot.scheduler.plist" + " ~/Library/LaunchAgents/"],
shell=True
)
# unload any existing file (i.e. if this is an update to the file) and suppress any errors
subprocess.run(
["launchctl unload ~/Library/LaunchAgents/ephemetoot.scheduler.plist"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
shell=True
)
# load the new file and suppress any errors
subprocess.run(
["launchctl load ~/Library/LaunchAgents/ephemetoot.scheduler.plist"],
shell=True
)
print('⏰ Scheduled!')
except Exception:
print('🙁 Scheduling failed.')
def checkToots(config, options, deleted_count=0, retry_count=0):
if options.test:
print("This is a test run...")
print("Fetching account details for @" + config['username'] + "@" + config['base_url'] + "...")
@ -108,6 +150,12 @@ def checkToots(config, options, deleted_count=0):
)
else:
print("Removed " + str(deleted_count) + " toots.")
print('')
print('---------------------------------------')
print('🥳 ==> 🧼 ==> 😇 User cleanup complete!')
print('---------------------------------------')
except IndexError:
print("No toots found!")
@ -115,6 +163,11 @@ def checkToots(config, options, deleted_count=0):
print('User and/or access token does not exist or has been deleted')
except MastodonNetworkError:
print('ephemetoot cannot connect to the server - are you online?')
finally:
print('🥳 ==> 🧼 ==> 😇 User cleanup complete!')
print('---------------------------------------')
if retry_count < 4:
print('Waiting 1 minute before trying again')
time.sleep(60)
retry_count += 1
print( 'Attempt ' + str(retry_count + 1) )
checkToots(config, options, 0, retry_count)
else:
print('Gave up waiting for network')

View File

@ -1,12 +1,12 @@
from setuptools import setup, find_packages
setup(name='ephemetoot',
version='2.0.0',
version='2.1.0',
url='https://github.com/hughrun/ephemetoot',
license='GPL-3.0-or-later',
packages=find_packages(),
scripts=['bin/ephemetoot'],
install_requires=['Mastodon.py', 'pyyaml'],
install_requires=['Mastodon.py', 'pyyaml', 'requests'],
zip_safe=False,
author='Hugh Rundle',
author_email='hugh@hughrundle.net',