diff --git a/patchodon.py b/patchodon.py index 8fdf95f..9086666 100755 --- a/patchodon.py +++ b/patchodon.py @@ -13,9 +13,9 @@ import time VERSION = "0.1.0" -DPASTE_URL = "https://dpaste.com" +DPASTE_URL = "https://dpaste.com" # TODO any good way to parametrize this? -STATUS_LENGTH_LIMIT = 400 +STATUS_LENGTH_LIMIT = 400 # TODO obtain from instance def trace(x): @@ -45,17 +45,19 @@ def do_post_status(args, body, parent=None, optional=None): if optional else "" ) - data = {status: st, visibility: "unlisted"} + data = {"status": st, "visibility": "direct"} # TODO parametrize direct if parent: data["in_reply_to_id"] = parent r = requests.post( args.instance_url + "/api/v1/statuses", data=data, headers=headers ) + if r.status_code != 200: raise ("mastodon status posting failed ({r.status_code})") - return r.json.id + rj = r.json() + return (rj["id"], rj["url"]) def do_pastebin_file(file): @@ -66,16 +68,16 @@ def do_pastebin_file(file): r = requests.post( DPASTE_URL + "/api/v2/", data={ - "contents": open(file, "r").read(), + "content": open(file, "r").read(), "syntax": "diff", "title": os.path.basename(file), - "expiry_days": 30, + "expiry_days": 1, # TODO remove after testing }, headers={"User-agent": f"patchodon v{VERSION}"}, ) if r.status_code != 201: raise (f"dpaste POST failed for `{file}'") - sleep(1.1) + time.sleep(1.1) return r.headers["location"] + ".txt" @@ -83,21 +85,34 @@ def split_off_diff(s): return s.split("\ndiff --git ")[0] +def mapl(f, xs): + return list(map(f, xs)) + + +def mayline(s): + if s: + return s + "\n" + else: + return "" + + def do_post(args): files = args.patchfile if not files: trace("reading patchfile series from stdin") - files = sys.stdin.readlines().map(lambda x: x.rstrip(chars="\n")) + files = mapl(lambda x: x.rstrip(chars="\n"), sys.stdin.readlines()) n_patches = len(files) - hashes = files.map(lambda x: hashlib.sha1(open(x, "r").read()).hexdigest()) - short_hashes = hashes.map(lambda x: x[0:8]) - full_hash = hashlib.sha1(hashes.join(" ")).hexdigest() - paste_raw_urls = map(do_pastebin_file, files) + hashes = mapl( + lambda x: hashlib.sha1(open(x, "r").read().encode()).hexdigest(), files + ) + short_hashes = mapl(lambda x: x[0:8], hashes) + full_hash = hashlib.sha1(" ".join(hashes).encode()).hexdigest() + paste_raw_urls = mapl(do_pastebin_file, files) trace("posting the header...") parent_post_id, url = do_post_status( args, - args.subject - + f"\n[patchodon hash {full_hash} shorts {short_hashes.join(' ')}]", + f"{mayline(args.recipient)} {mayline(args.subject)}\n" + f"[patchodon hash {full_hash} / {' '.join(short_hashes)}]", ) for fn, pst, hsh, series in zip( files, paste_raw_urls, hashes, range(n_patches) @@ -105,7 +120,9 @@ def do_post(args): trace(f"posting patch {series+1}/{n_patches}...") parent_post_id, _ = do_post_status( args, - f"[patchodon {series+1}/{n_patches} {hsh}\n{pst}\n", + f"{mayline(args.recipient)}" + f"[patchodon {series+1}/{n_patches} {hsh}]\n" + f"{pst}\n", parent=parent_post_id, optional=split_off_diff(open(fn, "r").read()), ) @@ -132,8 +149,8 @@ def main(): group.add_argument( "--debug-api-token", help=( - "specify the API token (not very secure, good for debugging" - " only)" + "specify the API token on command line (not very secure," + " good for debugging only)" ), ) group.add_argument( @@ -144,15 +161,29 @@ def main(): ) post.add_argument( - "-i", "--instance-url", help="mastodon instance URL to post to" + "-i", + "--instance-url", + help=( + "mastodon instance URL to post to, such as" + " `https://mastodon.example/'" + ), + ) + post.add_argument( + "-r", + "--recipient", + default=None, + help=( + "user tag to prepend to all posted statuses (required esp. for" + " direct sending of statuses))" + ), ) post.add_argument( "-s", "--subject", - default="", + default=None, help=( - "start of the initial post (e.g. to @tag someone and name the" - " project and topic)" + "start text of the initial post (e.g. to @tag someone and name" + " the project and topic)" ), ) post.add_argument(