aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMirek Kratochvil <exa.exa@gmail.com>2025-10-13 21:29:09 +0200
committerMirek Kratochvil <exa.exa@gmail.com>2025-10-13 21:29:09 +0200
commitdaf737fa08a1478cebc5a48f871d81f33a73ebf4 (patch)
tree13bfbed5ee629961d712a11ef2fe565bdcc75336
parent9b6b4e6c616d43fc8647d4662eb4594e75ae5fd7 (diff)
downloadpatchodon-daf737fa08a1478cebc5a48f871d81f33a73ebf4.tar.gz
patchodon-daf737fa08a1478cebc5a48f871d81f33a73ebf4.tar.bz2
add visibility and dpaste expirations
-rw-r--r--src/patchodon/__init__.py98
1 files changed, 75 insertions, 23 deletions
diff --git a/src/patchodon/__init__.py b/src/patchodon/__init__.py
index 64e28b0..db21732 100644
--- a/src/patchodon/__init__.py
+++ b/src/patchodon/__init__.py
@@ -54,7 +54,22 @@ def auth_headers(args):
return {"Authorization": f"Bearer {token}"}
-def do_post_status(args, body, parent=None, optional=None):
+def post_visibility(args, is_head):
+ """
+ choose the status visibility based on args and head-ness
+ """
+ if args.direct:
+ return "direct"
+ if args.private:
+ return "private"
+ if args.public:
+ return "public" if is_head else "unlisted"
+ if args.all_public:
+ return "public"
+ return "public" if is_head else "unlisted"
+
+
+def do_post_status(args, body, is_head, parent=None, optional=None):
"""
POST a new status with body, optionally in reply-to `parent` post ID, and
with attached `optional` contents to body.
@@ -67,7 +82,7 @@ def do_post_status(args, body, parent=None, optional=None):
if optional
else ""
)
- data = {"status": st, "visibility": "direct"} # TODO parametrize direct
+ data = {"status": st, "visibility": post_visibility(args, is_head)}
# visibility options: public head+unlisted, all unlisted, all private, all direct
if parent:
data["in_reply_to_id"] = parent
@@ -86,29 +101,33 @@ def do_post_status(args, body, parent=None, optional=None):
return (rj["id"], rj["url"])
-def do_pastebin_file(file):
+def do_pastebin_file(args):
"""
Send the `file` to dpaste, returning URL for the raw file.
"""
- # DPASTE API USE RULES:
- # - user-agent must be set properly
- # - 1 second between requests
- trace(f"sending `{file}' to dpaste...")
- r = requests.post(
- DPASTE_URL + "/api/v2/",
- data={
- "content": Path(file).read_text(),
- "syntax": "diff",
- "title": os.path.basename(file),
- "expiry_days": 1, # TODO remove after testing
- },
- headers={"User-agent": f"patchodon v{__version__}"},
- timeout=300, # TODO passthrough args
- )
- time.sleep(1.1)
- if r.status_code != 201:
- raise RuntimeError("dpaste POST failed for `{file}'")
- return r.headers["location"] + ".txt"
+
+ def f(file):
+ # DPASTE API USE RULES:
+ # - user-agent must be set properly
+ # - 1 second between requests
+ trace(f"sending `{file}' to dpaste...")
+ r = requests.post(
+ DPASTE_URL + "/api/v2/",
+ data={
+ "content": Path(file).read_text(),
+ "syntax": "diff",
+ "title": os.path.basename(file),
+ "expiry_days": args.paste_expire_days,
+ },
+ headers={"User-agent": f"patchodon v{__version__}"},
+ timeout=args.timeout,
+ )
+ time.sleep(1.1)
+ if r.status_code != 201:
+ raise RuntimeError("dpaste POST failed for `{file}'")
+ return r.headers["location"] + ".txt"
+
+ return f
def split_off_diff(s):
@@ -150,12 +169,13 @@ def do_post(args):
)
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)
+ paste_raw_urls = mapl(do_pastebin_file(args), files)
trace("posting the header...")
parent_post_id, url = do_post_status(
args,
f"{mayline(args.recipient)}{mayline(args.subject)}"
f"[patchodon: {full_hash} / {' '.join(short_hashes)}]",
+ True,
)
for fn, pst, hsh, series in zip(
files, paste_raw_urls, hashes, range(n_patches)
@@ -166,6 +186,7 @@ def do_post(args):
f"{mayline(args.recipient)}"
f"[patchodon {series+1}/{n_patches} {hsh}]\n"
f"{pst}\n",
+ False,
parent=parent_post_id,
optional=split_off_diff(Path(fn).read_text()),
)
@@ -385,6 +406,12 @@ def main():
),
)
post.add_argument(
+ "-x",
+ "--paste-expire-days",
+ default=14,
+ help="how many days should dpaste.com hold the patches (default: 14)",
+ )
+ post.add_argument(
"patchfile",
nargs="*",
help=(
@@ -393,6 +420,31 @@ def main():
" into patchodon)"
),
)
+ visibility = post.add_mutually_exclusive_group()
+ visibility.add_argument(
+ "--public",
+ action="store_true",
+ help=(
+ "post head status publicly, patches unlisted (this is the default)"
+ ),
+ )
+ visibility.add_argument(
+ "--all-public",
+ action="store_true",
+ help="post head status and all patches publicly",
+ )
+ visibility.add_argument(
+ "--private",
+ action="store_true",
+ help=(
+ "post statuses as private (visible by followers and recipient only)"
+ ),
+ )
+ visibility.add_argument(
+ "--direct",
+ action="store_true",
+ help="post statuses as direct (visible only by the tagged recipients)",
+ )
get = cmds.add_parser("get")
get.add_argument(