diff --git a/README.rst b/README.rst index a6bbecb..52892cc 100644 --- a/README.rst +++ b/README.rst @@ -159,6 +159,7 @@ Here’s an example ``conf`` file, showing every currently supported option: limit_timeline = 20 timeout = 5.0 sorting = descending + pre_tweet_hook = "scp buckket@example.org:~/public_html/twtxt.txt {twtfile}" post_tweet_hook = "scp {twtfile} buckket@example.org:~/public_html/twtxt.txt" # post_tweet_hook = "aws s3 {twtfile} s3://mybucket.org/twtxt.txt --acl public-read --storage-class REDUCED_REDUNDANCY --cache-control 'max-age=60,public'" @@ -192,10 +193,12 @@ Here’s an example ``conf`` file, showing every currently supported option: +-------------------+-------+------------+---------------------------------------------------+ | sorting | TEXT | descending | sort timeline either descending or ascending | +-------------------+-------+------------+---------------------------------------------------+ +| pre_tweet_hook | TEXT | | command to be executed before tweeting | ++-------------------+-------+------------+---------------------------------------------------+ | post_tweet_hook | TEXT | | command to be executed after tweeting | +-------------------+-------+------------+---------------------------------------------------+ -``post_tweet_hook`` is very useful if you want to push your twtxt file to a remote (web) server. Check the example above tho see how it’s used with ``scp``. +``pre_tweet_hook`` and ``post_tweet_hook`` are very useful if you want to push your twtxt file to a remote (web) server. Check the example above tho see how it’s used with ``scp``. [followings] section: ===================== diff --git a/config.example b/config.example index 74a62c1..b19cd5d 100644 --- a/config.example +++ b/config.example @@ -9,6 +9,7 @@ porcelain = False limit_timeline = 20 timeout = 5.0 sorting = descending +pre_tweet_hook = "scp buckket@example.org:~/public_html/twtxt.txt {twtfile}" post_tweet_hook = "scp {twtfile} buckket@example.org:~/public_html/twtxt.txt" [following] diff --git a/tests/test_config.py b/tests/test_config.py index be2e39e..1b758a4 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -23,6 +23,7 @@ def config_dir(tmpdir_factory): cfg.set("twtxt", "timeout", "1.0") cfg.set("twtxt", "sorting", "ascending") cfg.set("twtxt", "post_tweet_hook", "echo {twtfile") + cfg.set("twtxt", "pre_tweet_hook", "echo {twtfile") cfg.add_section("following") cfg.set("following", "foo", "https://example.org/foo.twtxt") @@ -50,6 +51,7 @@ def test_defaults(): assert empty_conf.timeout == 5.0 assert empty_conf.sorting == "descending" assert empty_conf.post_tweet_hook is None + assert empty_conf.pre_tweet_hook is None def check_cfg(cfg): @@ -64,6 +66,7 @@ def check_cfg(cfg): assert cfg.timeout == 1.0 assert cfg.sorting == "ascending" assert cfg.post_tweet_hook == "echo {twtfile" + assert cfg.pre_tweet_hook == "echo {twtfile" def test_from_file(config_dir): diff --git a/twtxt/cli.py b/twtxt/cli.py index 942d21f..1108c37 100644 --- a/twtxt/cli.py +++ b/twtxt/cli.py @@ -17,7 +17,7 @@ from twtxt.cache import Cache from twtxt.config import Config -from twtxt.helper import run_post_tweet_hook +from twtxt.helper import run_pre_tweet_hook, run_post_tweet_hook from twtxt.helper import sort_and_truncate_tweets from twtxt.helper import style_timeline, style_source, style_source_with_status from twtxt.helper import validate_created_at, validate_text @@ -72,12 +72,19 @@ def tweet(ctx, created_at, twtfile, text): """Append a new tweet to your twtxt file.""" text = expand_mentions(text) tweet = Tweet(text, created_at) if created_at else Tweet(text) + + pre_tweet_hook = ctx.obj["conf"].pre_tweet_hook + if pre_tweet_hook: + if not run_pre_tweet_hook(pre_tweet_hook, ctx.obj["conf"].options): + click.echo("✗ pre_tweet_hook returned non-zero") + raise click.Abort + if not add_local_tweet(tweet, twtfile): click.echo("✗ Couldn’t write to file.") else: - hook = ctx.obj["conf"].post_tweet_hook - if hook: - run_post_tweet_hook(hook, ctx.obj["conf"].options) + post_tweet_hook = ctx.obj["conf"].post_tweet_hook + if post_tweet_hook: + run_post_tweet_hook(post_tweet_hook, ctx.obj["conf"].options) @cli.command() diff --git a/twtxt/config.py b/twtxt/config.py index c8cde67..ff0e730 100644 --- a/twtxt/config.py +++ b/twtxt/config.py @@ -142,6 +142,10 @@ def sorting(self): def source(self): return Source(self.nick, self.twturl) + @property + def pre_tweet_hook(self): + return self.cfg.get("twtxt", "pre_tweet_hook", fallback=None) + @property def post_tweet_hook(self): return self.cfg.get("twtxt", "post_tweet_hook", fallback=None) diff --git a/twtxt/helper.py b/twtxt/helper.py index f75a7bb..c51ba65 100644 --- a/twtxt/helper.py +++ b/twtxt/helper.py @@ -95,13 +95,22 @@ def validate_text(ctx, param, value): raise click.BadArgumentUsage("Text can’t be empty.") +def run_pre_tweet_hook(hook, options): + try: + command = shlex.split(hook.format(**options)) + except KeyError: + click.echo("✗ Invalid variables in pre_tweet_hook.") + return False + return not subprocess.call(command, shell=True, stdout=subprocess.PIPE) + + def run_post_tweet_hook(hook, options): try: command = shlex.split(hook.format(**options)) except KeyError: click.echo("✗ Invalid variables in post_tweet_hook.") return False - subprocess.call(command, shell=True, stdout=subprocess.PIPE) + return not subprocess.call(command, shell=True, stdout=subprocess.PIPE) def sort_and_truncate_tweets(tweets, direction, limit):